@@ -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