@@ -38,9 +38,6 @@ pub fn extract_calls(content: &str, language_name: &str) -> Result<Vec<CallSite>
3838 . set_language ( & ts_language)
3939 . map_err ( |e| anyhow ! ( "Failed to set language: {}" , e) ) ?;
4040
41- // Set a timeout to prevent infinite parsing loops
42- parser. set_timeout_micros ( 5_000_000 ) ; // 5 seconds timeout
43-
4441 let tree = parser
4542 . parse ( content, None )
4643 . ok_or_else ( || anyhow ! ( "Parse failed" ) ) ?;
@@ -63,31 +60,15 @@ pub fn extract_calls(content: &str, language_name: &str) -> Result<Vec<CallSite>
6360 let query = Query :: new ( & ts_language, query_source)
6461 . map_err ( |e| anyhow ! ( "Failed to compile query: {}" , e) ) ?;
6562
66- // Disable query analysis that causes the crash (tree-sitter bug with analysis_state__compare_position)
6763 let callee_name_idx = query. capture_index_for_name ( "callee.name" ) ;
6864 let call_idx = query. capture_index_for_name ( "call" ) ;
65+ let method_call_idx = query. capture_index_for_name ( "method.call" ) ;
66+ let static_call_idx = query. capture_index_for_name ( "static.call" ) ;
6967 let constructor_idx = query. capture_index_for_name ( "constructor" ) ;
7068 let import_name_idx = query. capture_index_for_name ( "import.name" ) ;
7169 let import_default_idx = query. capture_index_for_name ( "import.default" ) ;
7270 let import_namespace_idx = query. capture_index_for_name ( "import.namespace" ) ;
7371
74- let method_parent_kinds: & [ & str ] = match language {
75- Language :: TypeScript
76- | Language :: TypeScriptTsx
77- | Language :: JavaScript
78- | Language :: JavaScriptJsx => & [ "member_expression" ] ,
79- Language :: Python => & [ "attribute" ] ,
80- Language :: Rust => & [ "field_expression" ] ,
81- Language :: Go => & [ "selector_expression" ] ,
82- Language :: Php => & [
83- "member_call_expression" ,
84- "scoped_call_expression" ,
85- "nullsafe_member_call_expression" ,
86- ] ,
87- _ => & [ ] ,
88- } ;
89- let _method_parent_kinds = method_parent_kinds;
90-
9172 let mut cursor = QueryCursor :: new ( ) ;
9273 let mut calls = Vec :: new ( ) ;
9374 let text_bytes = content. as_bytes ( ) ;
@@ -114,8 +95,31 @@ pub fn extract_calls(content: &str, language_name: &str) -> Result<Vec<CallSite>
11495 }
11596
11697 if let Some ( idx) = call_idx {
98+ if capture. index == idx {
99+ // Check if this is actually a method call by looking at other captures
100+ // If method_call_idx or static_call_idx also matches, it's a method call
101+ let is_method_call = match_. captures . iter ( ) . any ( |c| {
102+ method_call_idx. map ( |idx| c. index == idx) . unwrap_or ( false )
103+ || static_call_idx. map ( |idx| c. index == idx) . unwrap_or ( false )
104+ } ) ;
105+
106+ if is_method_call {
107+ call_type = Some ( CallType :: MethodCall ) ;
108+ } else {
109+ call_type = Some ( CallType :: Call ) ;
110+ }
111+ }
112+ }
113+
114+ if let Some ( idx) = method_call_idx {
117115 if capture. index == idx && call_type. is_none ( ) {
118- call_type = Some ( CallType :: Call ) ;
116+ call_type = Some ( CallType :: MethodCall ) ;
117+ }
118+ }
119+
120+ if let Some ( idx) = static_call_idx {
121+ if capture. index == idx && call_type. is_none ( ) {
122+ call_type = Some ( CallType :: MethodCall ) ;
119123 }
120124 }
121125
@@ -153,49 +157,35 @@ pub fn extract_calls(content: &str, language_name: &str) -> Result<Vec<CallSite>
153157 }
154158 }
155159
156- // Safely detect method calls by checking the callee node's parent
157- // instead of using query analysis which triggers tree-sitter bug
158- // The callee.name node is a property_identifier for method calls,
159- // and its parent is member_expression/attribute/etc.
160+ // Safely detect method calls using query context analysis
161+ // For PHP: check if the call is wrapped in a method call pattern
160162 if let ( Some ( name) , Some ( CallType :: Call ) , Some ( pos) ) = ( & callee_name, call_type, position) {
161- // Find the callee.name capture node
162- let callee_name_node = match_
163- . captures
164- . iter ( )
165- . find ( |c| callee_name_idx. map ( |idx| c. index == idx) . unwrap_or ( false ) )
166- . map ( |c| c. node ) ;
167-
168163 let is_method_call = match language {
169164 Language :: TypeScript
170165 | Language :: TypeScriptTsx
171166 | Language :: JavaScript
172- | Language :: JavaScriptJsx => callee_name_node
173- . and_then ( |n| n. parent ( ) )
174- . map ( |p| p. kind ( ) == "member_expression" )
175- . unwrap_or ( false ) ,
176- Language :: Python => callee_name_node
177- . and_then ( |n| n. parent ( ) )
178- . map ( |p| p. kind ( ) == "attribute" )
179- . unwrap_or ( false ) ,
180- Language :: Rust => callee_name_node
181- . and_then ( |n| n. parent ( ) )
182- . map ( |p| p. kind ( ) == "field_expression" )
183- . unwrap_or ( false ) ,
184- Language :: Go => callee_name_node
185- . and_then ( |n| n. parent ( ) )
186- . map ( |p| p. kind ( ) == "selector_expression" )
187- . unwrap_or ( false ) ,
188- Language :: Php => callee_name_node
189- . and_then ( |n| n. parent ( ) )
190- . map ( |p| {
191- matches ! (
192- p. kind( ) ,
193- "member_call_expression"
194- | "scoped_call_expression"
195- | "nullsafe_member_call_expression"
196- )
197- } )
198- . unwrap_or ( false ) ,
167+ | Language :: JavaScriptJsx => match_. captures . iter ( ) . any ( |c| {
168+ callee_name_idx. map ( |idx| c. index == idx) . unwrap_or ( false )
169+ && ( c. node . kind ( ) == "property_identifier" || c. node . kind ( ) == "identifier" )
170+ } ) ,
171+ Language :: Python => match_. captures . iter ( ) . any ( |c| {
172+ callee_name_idx. map ( |idx| c. index == idx) . unwrap_or ( false )
173+ && c. node . kind ( ) == "identifier"
174+ } ) ,
175+ Language :: Rust => match_. captures . iter ( ) . any ( |c| {
176+ callee_name_idx. map ( |idx| c. index == idx) . unwrap_or ( false )
177+ && c. node . kind ( ) == "field_identifier"
178+ } ) ,
179+ Language :: Go => match_. captures . iter ( ) . any ( |c| {
180+ callee_name_idx. map ( |idx| c. index == idx) . unwrap_or ( false )
181+ && c. node . kind ( ) == "field_identifier"
182+ } ) ,
183+ Language :: Php => {
184+ // PHP method calls are already marked in query (@method.call, @static.call)
185+ // @call is only for direct function calls
186+ // So if we reach here with CallType::Call, it's definitely not a method call
187+ false
188+ }
199189 _ => false ,
200190 } ;
201191
0 commit comments