@@ -763,10 +763,14 @@ fn match_js_call_assignments(node: &Node, source: &[u8], symbols: &mut FileSymbo
763763fn match_js_node ( node : & Node , source : & [ u8 ] , symbols : & mut FileSymbols , _depth : usize ) {
764764 match node. kind ( ) {
765765 "function_declaration" | "generator_function_declaration" => handle_function_decl ( node, source, symbols) ,
766- "class_declaration" | "abstract_class_declaration" => {
766+ "class_declaration" | "abstract_class_declaration"
767+ // class expressions: `return class Foo extends Bar { ... }` or `const X = class Foo { ... }`
768+ | "class" => {
767769 handle_class_decl ( node, source, symbols)
768770 }
771+ "class_static_block" => handle_static_block ( node, source, symbols) ,
769772 "method_definition" => handle_method_def ( node, source, symbols) ,
773+ "field_definition" | "public_field_definition" => handle_field_def ( node, source, symbols) ,
770774 "interface_declaration" => handle_interface_decl ( node, source, symbols) ,
771775 "type_alias_declaration" => handle_type_alias ( node, source, symbols) ,
772776 "enum_declaration" => handle_enum_decl ( node, source, symbols) ,
@@ -776,11 +780,6 @@ fn match_js_node(node: &Node, source: &[u8], symbols: &mut FileSymbols, _depth:
776780 "import_statement" => handle_import_stmt ( node, source, symbols) ,
777781 "export_statement" => handle_export_stmt ( node, source, symbols) ,
778782 "expression_statement" => handle_expr_stmt ( node, source, symbols) ,
779- // Synthetic definitions for class field initializers and static blocks.
780- // These give `findCaller` a narrower span with a kind that passes the SQL
781- // call-edge filter (`kind IN ('function','method')`), matching WASM behaviour.
782- "field_definition" | "public_field_definition" => handle_field_def ( node, source, symbols) ,
783- "class_static_block" => handle_static_block ( node, source, symbols) ,
784783 _ => { }
785784 }
786785}
@@ -864,6 +863,29 @@ fn handle_method_def(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
864863 }
865864}
866865
866+ /// Create a synthetic `ClassName.<static:L:C>` definition for a class static block
867+ /// so that calls inside the block are attributed to a method-kind node and
868+ /// `super.method()` dispatch can walk up to the parent class.
869+ ///
870+ /// The start line and column are appended to the name to ensure uniqueness when a
871+ /// class has multiple `static { }` blocks (each has a distinct start position even
872+ /// if on the same line).
873+ fn handle_static_block ( node : & Node , source : & [ u8 ] , symbols : & mut FileSymbols ) {
874+ let Some ( class_name) = find_parent_class ( node, source) else { return } ;
875+ let line = start_line ( node) ;
876+ let col = node. start_position ( ) . column ;
877+ symbols. definitions . push ( Definition {
878+ name : format ! ( "{}.<static:{}:{}>" , class_name, line, col) ,
879+ kind : "method" . to_string ( ) ,
880+ line,
881+ end_line : Some ( end_line ( node) ) ,
882+ decorators : None ,
883+ complexity : None ,
884+ cfg : None ,
885+ children : None ,
886+ } ) ;
887+ }
888+
867889/// Emit a `ClassName.fieldName` synthetic definition for each `class { field = ... }` node.
868890/// Only fired when a value node is present (skips bare `x;` declarations), mirroring the WASM
869891/// `handleFieldDef` guard. The synthetic definition has `kind = "method"` so that the SQL
@@ -881,7 +903,12 @@ fn handle_field_def(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
881903 return ;
882904 }
883905 // Skip uninitialised fields (`class C { x; }`) — must have a value node.
884- let Some ( _value_node) = node. child_by_field_name ( "value" ) else { return } ;
906+ let Some ( value_node) = node. child_by_field_name ( "value" ) else { return } ;
907+ // Only emit a callable definition when the initializer is a function/arrow expression.
908+ // Scalar fields like `static x = 42` should not appear as method-kind nodes.
909+ if !matches ! ( value_node. kind( ) , "arrow_function" | "function_expression" | "generator_function" ) {
910+ return ;
911+ }
885912 let field_name = node_text ( & name_node, source) ;
886913 if field_name. is_empty ( ) { return ; }
887914 let Some ( class_name) = find_parent_class ( node, source) else { return } ;
@@ -897,23 +924,6 @@ fn handle_field_def(node: &Node, source: &[u8], symbols: &mut FileSymbols) {
897924 } ) ;
898925}
899926
900- /// Emit a `ClassName.<static>` synthetic definition for each `static { }` block.
901- /// Enables `findCaller` to attribute calls inside static initializer blocks to this
902- /// synthetic node rather than to the enclosing class node, matching WASM behaviour.
903- fn handle_static_block ( node : & Node , source : & [ u8 ] , symbols : & mut FileSymbols ) {
904- let Some ( class_name) = find_parent_class ( node, source) else { return } ;
905- symbols. definitions . push ( Definition {
906- name : format ! ( "{}.<static>" , class_name) ,
907- kind : "function" . to_string ( ) ,
908- line : start_line ( node) ,
909- end_line : Some ( end_line ( node) ) ,
910- decorators : None ,
911- complexity : None ,
912- cfg : None ,
913- children : None ,
914- } ) ;
915- }
916-
917927fn handle_interface_decl ( node : & Node , source : & [ u8 ] , symbols : & mut FileSymbols ) {
918928 let Some ( name_node) = node. child_by_field_name ( "name" ) else { return } ;
919929 let iface_name = node_text ( & name_node, source) . to_string ( ) ;
0 commit comments