@@ -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,43 @@ mod tests {
12141216 _ => unreachable ! ( ) ,
12151217 }
12161218 }
1219+
1220+ #[ test]
1221+ fn does_not_overflow_callstack_on_smaller_treesitter_child ( ) {
1222+ let query = format ! (
1223+ r#"select * from persons where id = @i{}d;"# ,
1224+ QueryWithCursorPosition :: cursor_marker( )
1225+ ) ;
1226+
1227+ /*
1228+ The query (currently) yields the following treesitter tree for the WHERE clause:
1229+
1230+ where [29..43] 'where id = @id'
1231+ keyword_where [29..34] 'where'
1232+ binary_expression [35..43] 'id = @id'
1233+ field [35..37] 'id'
1234+ identifier [35..37] 'id'
1235+ = [38..39] '='
1236+ field [40..43] '@id'
1237+ identifier [40..43] '@id'
1238+ @ [40..41] '@'
1239+
1240+ You can see that the '@' is a child of the "identifier" but has a range smaller than its parent's.
1241+ This would crash our context parsing because, at position 42, we weren't at the leaf node but also couldn't
1242+ go to a child on that position.
1243+ */
1244+
1245+ let ( position, text) = QueryWithCursorPosition :: from ( query) . get_text_and_position ( ) ;
1246+
1247+ let tree = get_tree ( text. as_str ( ) ) ;
1248+
1249+ let params = TreeSitterContextParams {
1250+ position : ( position as u32 ) . into ( ) ,
1251+ text : & text,
1252+ tree : & tree,
1253+ } ;
1254+
1255+ // should simply not panic
1256+ let _ = TreesitterContext :: new ( params) ;
1257+ }
12171258}
0 commit comments