@@ -470,7 +470,9 @@ impl<'a> TreesitterContext<'a> {
470470 }
471471
472472 // We have arrived at the leaf node
473- if current_node. child_count ( ) == 0 {
473+ if current_node. child_count ( ) == 0
474+ || current_node. first_child_for_byte ( self . position ) . is_none ( )
475+ {
474476 self . node_under_cursor = Some ( NodeUnderCursor :: from ( current_node) ) ;
475477 return ;
476478 }
@@ -1214,4 +1216,45 @@ mod tests {
12141216 _ => unreachable ! ( ) ,
12151217 }
12161218 }
1219+
1220+ #[ test]
1221+ fn does_not_overflow_callstack_on_smaller_treesitter_child ( ) {
1222+ // Instead of autocompleting "FROM", we'll assume that the user
1223+ // is selecting a certain column name, such as `frozen_account`.
1224+ let query = format ! (
1225+ r#"select * from persons where id = @i{}d;"# ,
1226+ QueryWithCursorPosition :: cursor_marker( )
1227+ ) ;
1228+
1229+ /*
1230+ The query (currently) yields the following treesitter tree for the WHERE clause:
1231+
1232+ where [29..43] 'where id = @id'
1233+ keyword_where [29..34] 'where'
1234+ binary_expression [35..43] 'id = @id'
1235+ field [35..37] 'id'
1236+ identifier [35..37] 'id'
1237+ = [38..39] '='
1238+ field [40..43] '@id'
1239+ identifier [40..43] '@id'
1240+ @ [40..41] '@'
1241+
1242+ You can see that the '@' is a child of the "identifier" but has a range smaller than its parent's.
1243+ This would crash our context parsing because, at position 42, we weren't at the leaf node but also couldn't
1244+ go to a child on that position.
1245+ */
1246+
1247+ let ( position, text) = QueryWithCursorPosition :: from ( query) . get_text_and_position ( ) ;
1248+
1249+ let tree = get_tree ( text. as_str ( ) ) ;
1250+
1251+ let params = TreeSitterContextParams {
1252+ position : ( position as u32 ) . into ( ) ,
1253+ text : & text,
1254+ tree : & tree,
1255+ } ;
1256+
1257+ // should simply not panic
1258+ let _ = TreesitterContext :: new ( params) ;
1259+ }
12171260}
0 commit comments