Skip to main content

gonzalo_graph/
builder.rs

1//! Build a [`CodeGraph`] from source using tree-sitter. Parsing is
2//! language-parameterized ([`Language`]); Rust, Python, JavaScript,
3//! TypeScript/TSX, Go, Java, C#, C, C++, Ruby, PHP, Bash, Kotlin, Swift, Lua,
4//! Scala, and Elixir are supported, and a new grammar is a matter of adding its
5//! node-kind mappings.
6
7use crate::model::{CodeGraph, RefKind, Reference, Symbol, SymbolKind};
8use serde::{Deserialize, Serialize};
9use tree_sitter::{Node, Parser};
10
11/// A source language the graph builder understands.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13pub enum Language {
14    Rust,
15    Python,
16    JavaScript,
17    TypeScript,
18    /// TypeScript with JSX (`.tsx`).
19    Tsx,
20    Go,
21    Java,
22    CSharp,
23    C,
24    Cpp,
25    Ruby,
26    Php,
27    Bash,
28    Kotlin,
29    Swift,
30    Lua,
31    Scala,
32    Elixir,
33}
34
35impl Language {
36    /// The [`Language`] for a file extension (without the dot), or `None` if
37    /// unsupported.
38    pub fn from_extension(ext: &str) -> Option<Self> {
39        match ext {
40            "rs" => Some(Self::Rust),
41            "py" => Some(Self::Python),
42            "js" | "jsx" | "mjs" | "cjs" => Some(Self::JavaScript),
43            "ts" | "mts" | "cts" => Some(Self::TypeScript),
44            "tsx" => Some(Self::Tsx),
45            "go" => Some(Self::Go),
46            "java" => Some(Self::Java),
47            "cs" => Some(Self::CSharp),
48            "c" | "h" => Some(Self::C),
49            "cpp" | "cc" | "cxx" | "hpp" | "hh" => Some(Self::Cpp),
50            "rb" => Some(Self::Ruby),
51            "php" => Some(Self::Php),
52            "sh" | "bash" => Some(Self::Bash),
53            "kt" | "kts" => Some(Self::Kotlin),
54            "swift" => Some(Self::Swift),
55            "lua" => Some(Self::Lua),
56            "scala" | "sc" => Some(Self::Scala),
57            "ex" | "exs" => Some(Self::Elixir),
58            _ => None,
59        }
60    }
61
62    /// Lowercase name, used as a stable key when bucketing symbols by language.
63    pub fn as_str(self) -> &'static str {
64        match self {
65            Self::Rust => "rust",
66            Self::Python => "python",
67            Self::JavaScript => "javascript",
68            Self::TypeScript => "typescript",
69            Self::Tsx => "tsx",
70            Self::Go => "go",
71            Self::Java => "java",
72            Self::CSharp => "csharp",
73            Self::C => "c",
74            Self::Cpp => "cpp",
75            Self::Ruby => "ruby",
76            Self::Php => "php",
77            Self::Bash => "bash",
78            Self::Kotlin => "kotlin",
79            Self::Swift => "swift",
80            Self::Lua => "lua",
81            Self::Scala => "scala",
82            Self::Elixir => "elixir",
83        }
84    }
85
86    fn ts_language(self) -> tree_sitter::Language {
87        match self {
88            Self::Rust => tree_sitter_rust::LANGUAGE.into(),
89            Self::Python => tree_sitter_python::LANGUAGE.into(),
90            Self::JavaScript => tree_sitter_javascript::LANGUAGE.into(),
91            Self::TypeScript => tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
92            Self::Tsx => tree_sitter_typescript::LANGUAGE_TSX.into(),
93            Self::Go => tree_sitter_go::LANGUAGE.into(),
94            Self::Java => tree_sitter_java::LANGUAGE.into(),
95            Self::CSharp => tree_sitter_c_sharp::LANGUAGE.into(),
96            Self::C => tree_sitter_c::LANGUAGE.into(),
97            Self::Cpp => tree_sitter_cpp::LANGUAGE.into(),
98            Self::Ruby => tree_sitter_ruby::LANGUAGE.into(),
99            Self::Php => tree_sitter_php::LANGUAGE_PHP.into(),
100            Self::Bash => tree_sitter_bash::LANGUAGE.into(),
101            Self::Kotlin => tree_sitter_kotlin_ng::LANGUAGE.into(),
102            Self::Swift => tree_sitter_swift::LANGUAGE.into(),
103            Self::Lua => tree_sitter_lua::LANGUAGE.into(),
104            Self::Scala => tree_sitter_scala::LANGUAGE.into(),
105            Self::Elixir => tree_sitter_elixir::LANGUAGE.into(),
106        }
107    }
108
109    /// Map a node to the symbol it defines, if any. Takes the whole node (not
110    /// just its kind) because some languages need to inspect children — e.g. a
111    /// JS `variable_declarator` is only a function when its value is an
112    /// arrow/function expression, and Swift/Kotlin distinguish struct/enum/
113    /// interface by a keyword child.
114    fn item_kind(self, node: Node<'_>, bytes: &[u8]) -> Option<SymbolKind> {
115        let node_kind = node.kind();
116        match self {
117            Self::Rust => match node_kind {
118                "function_item" => Some(SymbolKind::Function),
119                "struct_item" => Some(SymbolKind::Struct),
120                "enum_item" => Some(SymbolKind::Enum),
121                "trait_item" => Some(SymbolKind::Trait),
122                "impl_item" => Some(SymbolKind::Impl),
123                "mod_item" => Some(SymbolKind::Module),
124                "const_item" => Some(SymbolKind::Const),
125                "static_item" => Some(SymbolKind::Static),
126                "type_item" => Some(SymbolKind::TypeAlias),
127                _ => None,
128            },
129            Self::Python => match node_kind {
130                "function_definition" => Some(SymbolKind::Function),
131                "class_definition" => Some(SymbolKind::Class),
132                _ => None,
133            },
134            Self::JavaScript => js_item_kind(node),
135            // TypeScript/TSX are a superset of JavaScript's declarations.
136            Self::TypeScript | Self::Tsx => js_item_kind(node).or(match node_kind {
137                "interface_declaration" => Some(SymbolKind::Interface),
138                "type_alias_declaration" => Some(SymbolKind::TypeAlias),
139                "enum_declaration" => Some(SymbolKind::Enum),
140                _ => None,
141            }),
142            // Go names a type on the `type_spec`, but struct vs interface is
143            // determined by its inner `type` node — so the symbol is defined at
144            // the `struct_type`/`interface_type` node, and `item_name` reaches
145            // back to the enclosing `type_spec` for the name. `const`/`var` specs
146            // are best-effort (first name of a possibly multi-name spec).
147            Self::Go => match node_kind {
148                "function_declaration" | "method_declaration" => Some(SymbolKind::Function),
149                "struct_type" => Some(SymbolKind::Struct),
150                "interface_type" => Some(SymbolKind::Interface),
151                "const_spec" => Some(SymbolKind::Const),
152                "var_spec" => Some(SymbolKind::Static),
153                _ => None,
154            },
155            Self::Java => match node_kind {
156                "class_declaration" => Some(SymbolKind::Class),
157                "interface_declaration" => Some(SymbolKind::Interface),
158                "enum_declaration" => Some(SymbolKind::Enum),
159                "method_declaration" | "constructor_declaration" => Some(SymbolKind::Function),
160                _ => None,
161            },
162            Self::CSharp => match node_kind {
163                "class_declaration" => Some(SymbolKind::Class),
164                "interface_declaration" => Some(SymbolKind::Interface),
165                "struct_declaration" => Some(SymbolKind::Struct),
166                "enum_declaration" => Some(SymbolKind::Enum),
167                "method_declaration" | "constructor_declaration" => Some(SymbolKind::Function),
168                _ => None,
169            },
170            Self::C => c_item_kind(node_kind),
171            // C++ is a superset of C's declarations.
172            Self::Cpp => c_item_kind(node_kind).or(match node_kind {
173                "class_specifier" => Some(SymbolKind::Class),
174                "namespace_definition" => Some(SymbolKind::Module),
175                _ => None,
176            }),
177            Self::Ruby => match node_kind {
178                "method" | "singleton_method" => Some(SymbolKind::Function),
179                "class" => Some(SymbolKind::Class),
180                "module" => Some(SymbolKind::Module),
181                _ => None,
182            },
183            Self::Php => match node_kind {
184                "function_definition" | "method_declaration" => Some(SymbolKind::Function),
185                "class_declaration" => Some(SymbolKind::Class),
186                "interface_declaration" => Some(SymbolKind::Interface),
187                "trait_declaration" => Some(SymbolKind::Trait),
188                "enum_declaration" => Some(SymbolKind::Enum),
189                _ => None,
190            },
191            // Bash has only functions.
192            Self::Bash => match node_kind {
193                "function_definition" => Some(SymbolKind::Function),
194                _ => None,
195            },
196            // Kotlin `class_declaration` covers both `class` and `interface`
197            // (distinguished by a leading keyword child); `object` (a named
198            // singleton) is its own `object_declaration` node and reads as Class.
199            Self::Kotlin => match node_kind {
200                "function_declaration" => Some(SymbolKind::Function),
201                "class_declaration" => Some(kotlin_class_kind(node)),
202                "object_declaration" => Some(SymbolKind::Class),
203                _ => None,
204            },
205            // Swift `class_declaration` covers class/struct/enum/actor,
206            // distinguished by a `declaration_kind` keyword child; `protocol`
207            // maps to Interface.
208            Self::Swift => match node_kind {
209                "function_declaration" => Some(SymbolKind::Function),
210                "class_declaration" => Some(swift_type_kind(node)),
211                "protocol_declaration" => Some(SymbolKind::Interface),
212                _ => None,
213            },
214            // Lua has only functions (named `function_declaration`; anonymous
215            // `function_definition` carries no name and is skipped).
216            Self::Lua => match node_kind {
217                "function_declaration" => Some(SymbolKind::Function),
218                _ => None,
219            },
220            // Scala `object` (a named singleton) surfaces as a class-like type.
221            Self::Scala => match node_kind {
222                "function_definition" | "function_declaration" => Some(SymbolKind::Function),
223                "class_definition" | "object_definition" => Some(SymbolKind::Class),
224                "trait_definition" => Some(SymbolKind::Trait),
225                "enum_definition" => Some(SymbolKind::Enum),
226                _ => None,
227            },
228            // Elixir is homoiconic: `def`/`defp`/`defmacro`/`defmacrop` and
229            // `defmodule` all parse as ordinary `call` nodes distinguished by
230            // their target identifier's *text*, not by node kind.
231            Self::Elixir => elixir_target_name(node, bytes).and_then(|t| match t.as_str() {
232                "defmodule" => Some(SymbolKind::Module),
233                "def" | "defp" | "defmacro" | "defmacrop" => Some(SymbolKind::Function),
234                _ => None,
235            }),
236        }
237    }
238
239    /// The display name of an item node.
240    fn item_name(self, node: Node<'_>, kind: SymbolKind, bytes: &[u8]) -> Option<String> {
241        match (self, kind) {
242            // Rust `impl` has no `name` field; use its `type` (e.g. `Foo` in `impl Foo`).
243            (Self::Rust, SymbolKind::Impl) => node
244                .child_by_field_name("type")
245                .and_then(|n| node_text(n, bytes))
246                .map(str::to_string),
247            // Go `struct_type`/`interface_type` carry no name; the name lives on
248            // the enclosing `type_spec`. Anonymous types (no `type_spec` parent
249            // with a name) yield `None` and are skipped.
250            (Self::Go, SymbolKind::Struct | SymbolKind::Interface) => node
251                .parent()
252                .and_then(|p| p.child_by_field_name("name"))
253                .and_then(|n| node_text(n, bytes))
254                .map(str::to_string),
255            // C/C++ name a function or typedef through nested `declarator` nodes,
256            // not a flat `name` field. Struct/enum/class/namespace do use `name`.
257            (Self::C | Self::Cpp, SymbolKind::Function | SymbolKind::TypeAlias) => {
258                c_declarator_name(node, bytes)
259            }
260            // Elixir defs carry no `name` field; the defined name is the head of
261            // the first argument — a nested `call` (`def add(a, b)`), a bare
262            // `identifier` (`def run`), or an `alias` (`defmodule Math`).
263            (Self::Elixir, _) => elixir_defined_name(node, bytes),
264            _ => name_field(node, bytes),
265        }
266    }
267
268    /// Whether `node_kind` is a call expression for this language.
269    fn is_call(self, node_kind: &str) -> bool {
270        match self {
271            Self::Rust
272            | Self::JavaScript
273            | Self::TypeScript
274            | Self::Tsx
275            | Self::Go
276            | Self::C
277            | Self::Cpp => node_kind == "call_expression",
278            Self::Python => node_kind == "call",
279            Self::Java => node_kind == "method_invocation",
280            Self::CSharp => node_kind == "invocation_expression",
281            Self::Ruby => node_kind == "call",
282            // PHP: plain `f()`, method `$x->m()` / `$x?->m()`, and static `A::b()`.
283            Self::Php => matches!(
284                node_kind,
285                "function_call_expression"
286                    | "member_call_expression"
287                    | "nullsafe_member_call_expression"
288                    | "scoped_call_expression"
289            ),
290            // Bash "calls" are commands (`helper arg`).
291            Self::Bash => node_kind == "command",
292            Self::Kotlin | Self::Swift | Self::Scala => node_kind == "call_expression",
293            Self::Lua => node_kind == "function_call",
294            Self::Elixir => node_kind == "call",
295        }
296    }
297
298    /// Whether a call reaches its callee through a receiver expression, so the
299    /// callee belongs to a value whose type the graph does not know.
300    ///
301    /// `x.foo()` is [`RefKind::Method`]; `foo()` and path calls like
302    /// `a::b::foo()` are [`RefKind::Free`]. The distinction is what stops the
303    /// resolver attributing a std or dependency method to a same-named free
304    /// function that happens to be the view's only definition (#223).
305    ///
306    /// Languages whose grammar does not surface a receiver here fall through to
307    /// `Free`, which is the behaviour they had before this existed — no worse,
308    /// just not yet improved.
309    fn callee_kind(self, call: Node<'_>) -> RefKind {
310        // A few grammars mark a method call on the call node itself.
311        let by_call_node = match self {
312            Self::Php => matches!(
313                call.kind(),
314                "member_call_expression" | "nullsafe_member_call_expression"
315            ),
316            // `obj.m()` carries an `object`; a bare `m()` does not.
317            Self::Java => call.child_by_field_name("object").is_some(),
318            // Ruby's `call` names its receiver explicitly.
319            Self::Ruby => call.child_by_field_name("receiver").is_some(),
320            _ => false,
321        };
322        if by_call_node {
323            return RefKind::Method;
324        }
325
326        // Otherwise the shape is visible on the callee expression.
327        let callee = match self {
328            Self::Kotlin | Self::Swift => call.named_child(0),
329            Self::Lua => call.child_by_field_name("name"),
330            _ => call.child_by_field_name("function"),
331        };
332        let Some(callee) = callee else {
333            return RefKind::Free;
334        };
335        // Note what is deliberately absent: Rust `scoped_identifier`, C++
336        // `qualified_identifier` and Go's package-qualified `selector_expression`
337        // are paths, not receivers. Go cannot tell `pkg.Func()` from `x.Method()`
338        // at this level, so it stays Free rather than guessing.
339        let member_like = matches!(
340            (self, callee.kind()),
341            (
342                Self::Rust | Self::Scala | Self::C | Self::Cpp,
343                "field_expression"
344            ) | (Self::Python, "attribute")
345                | (
346                    Self::JavaScript | Self::TypeScript | Self::Tsx,
347                    "member_expression"
348                )
349                | (Self::CSharp, "member_access_expression")
350                | (Self::Kotlin | Self::Swift, "navigation_expression")
351                | (
352                    Self::Lua,
353                    "dot_index_expression" | "method_index_expression"
354                )
355        );
356        if member_like {
357            RefKind::Method
358        } else {
359            RefKind::Free
360        }
361    }
362
363    /// Calls hidden inside an opaque macro-argument node, as `(name, line)`.
364    ///
365    /// Rust macro arguments parse as a `token_tree` of raw tokens rather than
366    /// expressions, so `assert_eq!(f(), 1)` contains no `call_expression` and
367    /// the call to `f` is invisible to [`is_call`](Self::is_call). Since
368    /// assertions are where much of a codebase is exercised, that silently
369    /// removed a large share of the call graph (#216).
370    ///
371    /// Inside a token tree a call is an `identifier` whose immediate next
372    /// sibling is another `token_tree` — `f` followed by `()`. A nested macro
373    /// (`matches!(..)`) has a `!` between the two, so it is naturally excluded.
374    /// Each token tree inspects only its own direct children, and [`walk`]
375    /// recurses into nested trees, so nothing is counted twice.
376    ///
377    /// This is a token-level heuristic, not type resolution: a token tree is not
378    /// type-checked, so a tuple-struct pattern like `Some(_)` reads as a call.
379    /// That matches the base graph, which already records constructors and enum
380    /// variants as calls.
381    fn macro_arg_calls(self, node: Node<'_>, bytes: &[u8]) -> Vec<(String, usize)> {
382        if self != Self::Rust || node.kind() != "token_tree" {
383            return Vec::new();
384        }
385        let mut out = Vec::new();
386        let mut cursor = node.walk();
387        for child in node.children(&mut cursor) {
388            if child.kind() != "identifier" {
389                continue;
390            }
391            let Some(next) = child.next_sibling() else {
392                continue;
393            };
394            // Only a parenthesised tree is an argument list; `vec![..]`'s own
395            // brackets belong to the macro, not to a call.
396            if next.kind() != "token_tree"
397                || !next.utf8_text(bytes).is_ok_and(|t| t.starts_with('('))
398            {
399                continue;
400            }
401            if let Some(name) = node_text(child, bytes) {
402                out.push((name.to_string(), child.start_position().row + 1));
403            }
404        }
405        out
406    }
407
408    /// The called name from a call node's `function` field.
409    fn call_name(self, func: Node<'_>, bytes: &[u8]) -> Option<String> {
410        match self {
411            Self::Rust => match func.kind() {
412                "identifier" => node_text(func, bytes).map(str::to_string),
413                // a::b::c -> the `name` field (last segment)
414                "scoped_identifier" => func
415                    .child_by_field_name("name")
416                    .and_then(|n| node_text(n, bytes))
417                    .map(str::to_string),
418                // x.method(...) -> the `field` field
419                "field_expression" => func
420                    .child_by_field_name("field")
421                    .and_then(|n| node_text(n, bytes))
422                    .map(str::to_string),
423                _ => node_text(func, bytes).map(str::to_string),
424            },
425            Self::Python => match func.kind() {
426                "identifier" => node_text(func, bytes).map(str::to_string),
427                // obj.method(...) -> the `attribute` field (method name)
428                "attribute" => func
429                    .child_by_field_name("attribute")
430                    .and_then(|n| node_text(n, bytes))
431                    .map(str::to_string),
432                _ => node_text(func, bytes).map(str::to_string),
433            },
434            Self::JavaScript | Self::TypeScript | Self::Tsx => match func.kind() {
435                "identifier" => node_text(func, bytes).map(str::to_string),
436                // obj.method(...) -> the member expression's `property` field
437                "member_expression" => func
438                    .child_by_field_name("property")
439                    .and_then(|n| node_text(n, bytes))
440                    .map(str::to_string),
441                _ => node_text(func, bytes).map(str::to_string),
442            },
443            Self::Go => match func.kind() {
444                "identifier" => node_text(func, bytes).map(str::to_string),
445                // pkg.Func(...) / x.Method(...) -> the selector's `field`.
446                "selector_expression" => func
447                    .child_by_field_name("field")
448                    .and_then(|n| node_text(n, bytes))
449                    .map(str::to_string),
450                _ => node_text(func, bytes).map(str::to_string),
451            },
452            Self::CSharp => match func.kind() {
453                "identifier" => node_text(func, bytes).map(str::to_string),
454                // obj.Method(...) -> the member access's `name` field.
455                "member_access_expression" => func
456                    .child_by_field_name("name")
457                    .and_then(|n| node_text(n, bytes))
458                    .map(str::to_string),
459                _ => node_text(func, bytes).map(str::to_string),
460            },
461            Self::C | Self::Cpp => match func.kind() {
462                "identifier" => node_text(func, bytes).map(str::to_string),
463                // x.m(...) / x->m(...) -> the field expression's `field`.
464                "field_expression" => func
465                    .child_by_field_name("field")
466                    .and_then(|n| node_text(n, bytes))
467                    .map(str::to_string),
468                // C++ `Ns::func(...)` -> the qualified id's `name` (last segment).
469                "qualified_identifier" => func
470                    .child_by_field_name("name")
471                    .and_then(|n| node_text(n, bytes))
472                    .map(str::to_string),
473                _ => node_text(func, bytes).map(str::to_string),
474            },
475            // Scala `call_expression` holds the callee in a `function` field — a
476            // bare `identifier` (`helper(..)`) or a `field_expression`
477            // (`obj.method(..)`); take the trailing identifier.
478            Self::Scala => last_identifier(func, bytes),
479            // PHP `function_call_expression` holds the callee in a `function`
480            // field — a `name` (or `qualified_name`) node; take its text.
481            Self::Php => node_text(func, bytes).map(str::to_string),
482            // Java, Ruby, Bash, Kotlin, Swift, and Lua route through `callee_name`
483            // (their callee is a dedicated field/child on the call node, not a
484            // nested `function` node); these arms only keep the match exhaustive.
485            Self::Java
486            | Self::Ruby
487            | Self::Bash
488            | Self::Kotlin
489            | Self::Swift
490            | Self::Lua
491            | Self::Elixir => node_text(func, bytes).map(str::to_string),
492        }
493    }
494
495    /// The called name from a call node. Most languages hold the callee in a
496    /// `function` field (dispatched by [`call_name`]); Java's `method_invocation`
497    /// instead carries the method name directly in its `name` field.
498    fn callee_name(self, call: Node<'_>, bytes: &[u8]) -> Option<String> {
499        match self {
500            // Java's `method_invocation` and Bash's `command` carry the callee in
501            // a `name` field (an identifier / a `command_name` node).
502            Self::Java | Self::Bash => call
503                .child_by_field_name("name")
504                .and_then(|n| node_text(n, bytes))
505                .map(str::to_string),
506            // Ruby's `call` names the callee in a `method` field.
507            Self::Ruby => call
508                .child_by_field_name("method")
509                .and_then(|n| node_text(n, bytes))
510                .map(str::to_string),
511            // Kotlin and Swift `call_expression` have no field; the callee is the
512            // first named child — an `identifier`/`simple_identifier`
513            // (`helper(..)`) or a navigation/member expression (`a.b.method(..)`),
514            // whose trailing identifier is the invoked member.
515            Self::Kotlin | Self::Swift => call
516                .named_child(0)
517                .and_then(|callee| last_identifier(callee, bytes)),
518            // Lua's `function_call` names the callee in a `name` field — an
519            // `identifier` (`helper(..)`) or a dotted/method index (`m.f`/`o:m`),
520            // whose trailing identifier is the invoked function.
521            Self::Lua => call
522                .child_by_field_name("name")
523                .and_then(|n| last_identifier(n, bytes)),
524            // PHP: plain calls carry the callee in a `function` field (a
525            // `name`/`qualified_name`); method (`$x->m()`) and static (`A::b()`)
526            // calls carry the invoked member in a `name` field.
527            Self::Php => match call.kind() {
528                "function_call_expression" => call
529                    .child_by_field_name("function")
530                    .and_then(|func| self.call_name(func, bytes)),
531                _ => call
532                    .child_by_field_name("name")
533                    .and_then(|n| node_text(n, bytes))
534                    .map(str::to_string),
535            },
536            // Elixir: every `call` carries its callee in a `target` field. A
537            // definition call (`def`/`defp`/`defmacro`/`defmacrop`/`defmodule`)
538            // and a definition *head* (`add(a, b)` in `def add(a, b)`) are not
539            // references; every other call is, keyed by the target's trailing
540            // identifier (`helper` for `helper(..)`, `add` for `Mod.add(..)`).
541            Self::Elixir => match elixir_target_name(call, bytes) {
542                Some(name)
543                    if !matches!(
544                        name.as_str(),
545                        "def" | "defp" | "defmacro" | "defmacrop" | "defmodule"
546                    ) && !elixir_is_def_head(call, bytes) =>
547                {
548                    Some(name)
549                }
550                _ => None,
551            },
552            _ => call
553                .child_by_field_name("function")
554                .and_then(|func| self.call_name(func, bytes)),
555        }
556    }
557}
558
559/// The last `identifier`/`simple_identifier` in `node`'s subtree (depth-first).
560/// For a Kotlin/Swift callee that is a bare identifier this is the node itself;
561/// for a navigation/member expression (`a.b.method`) it is the trailing member.
562fn last_identifier(node: Node<'_>, bytes: &[u8]) -> Option<String> {
563    let mut result = if matches!(node.kind(), "identifier" | "simple_identifier") {
564        node_text(node, bytes).map(str::to_string)
565    } else {
566        None
567    };
568    let mut cursor = node.walk();
569    for child in node.children(&mut cursor) {
570        if let Some(name) = last_identifier(child, bytes) {
571            result = Some(name);
572        }
573    }
574    result
575}
576
577/// The first named child of `node` whose kind is `kind`, if any.
578fn child_of_kind<'a>(node: Node<'a>, kind: &str) -> Option<Node<'a>> {
579    let mut cursor = node.walk();
580    node.children(&mut cursor).find(|c| c.kind() == kind)
581}
582
583/// The trailing identifier text of an Elixir `call` node's `target` — a bare
584/// `identifier` (`helper(..)`) or the `right` member of a `dot` (`Mod.fun(..)`).
585/// `None` when `node` is not a call (no `target` field).
586fn elixir_target_name(node: Node<'_>, bytes: &[u8]) -> Option<String> {
587    let target = node.child_by_field_name("target")?;
588    last_identifier(target, bytes)
589}
590
591/// The name defined by an Elixir definition call. The signature is the first
592/// argument: a nested `call` (`def add(a, b)` → `add`), a bare `identifier`
593/// (`def run` → `run`), or an `alias` (`defmodule Math` → `Math`).
594fn elixir_defined_name(node: Node<'_>, bytes: &[u8]) -> Option<String> {
595    let head = child_of_kind(node, "arguments")?.named_child(0)?;
596    match head.kind() {
597        "call" => elixir_target_name(head, bytes),
598        _ => last_identifier(head, bytes).or_else(|| node_text(head, bytes).map(str::to_string)),
599    }
600}
601
602/// Whether an Elixir `call` is the *head* of a definition — the first argument
603/// of a `def`/`defp`/`defmacro`/`defmacrop`/`defmodule` call (e.g. `add(a, b)`
604/// in `def add(a, b)`). Such a head names the defined symbol, not a call.
605fn elixir_is_def_head(node: Node<'_>, bytes: &[u8]) -> bool {
606    let Some(args) = node.parent().filter(|p| p.kind() == "arguments") else {
607        return false;
608    };
609    if args.named_child(0).map(|h| h.id()) != Some(node.id()) {
610        return false;
611    }
612    let Some(def_call) = args.parent().filter(|g| g.kind() == "call") else {
613        return false;
614    };
615    matches!(
616        elixir_target_name(def_call, bytes).as_deref(),
617        Some("def" | "defp" | "defmacro" | "defmacrop" | "defmodule")
618    )
619}
620
621/// JavaScript declaration node kinds shared by JS and TS/TSX.
622fn js_item_kind(node: Node<'_>) -> Option<SymbolKind> {
623    match node.kind() {
624        "function_declaration" | "generator_function_declaration" | "method_definition" => {
625            Some(SymbolKind::Function)
626        }
627        "class_declaration" | "abstract_class_declaration" => Some(SymbolKind::Class),
628        // `const foo = () => {}` / `const foo = function () {}` and class-field
629        // `foo = () => {}`: a binding whose value is an arrow/function expression
630        // is a named function. The name lives on the binding's `name` field.
631        "variable_declarator" | "public_field_definition" => {
632            match node.child_by_field_name("value").map(|v| v.kind()) {
633                Some("arrow_function" | "function_expression") => Some(SymbolKind::Function),
634                _ => None,
635            }
636        }
637        _ => None,
638    }
639}
640
641/// Swift `class_declaration` keyword (`struct`/`enum`/`actor`/`class`) → kind.
642/// `actor` (a reference type) and `class` both read as Class.
643fn swift_type_kind(node: Node<'_>) -> SymbolKind {
644    match node
645        .child_by_field_name("declaration_kind")
646        .map(|k| k.kind())
647    {
648        Some("struct") => SymbolKind::Struct,
649        Some("enum") => SymbolKind::Enum,
650        _ => SymbolKind::Class,
651    }
652}
653
654/// Kotlin `class_declaration` is an `interface` when it has a leading
655/// `interface` keyword child; otherwise a `class`.
656fn kotlin_class_kind(node: Node<'_>) -> SymbolKind {
657    let mut cursor = node.walk();
658    if node.children(&mut cursor).any(|c| c.kind() == "interface") {
659        SymbolKind::Interface
660    } else {
661        SymbolKind::Class
662    }
663}
664
665/// C declaration node kinds shared by C and C++ (C++ adds classes/namespaces).
666fn c_item_kind(node_kind: &str) -> Option<SymbolKind> {
667    match node_kind {
668        "function_definition" => Some(SymbolKind::Function),
669        "struct_specifier" => Some(SymbolKind::Struct),
670        "enum_specifier" => Some(SymbolKind::Enum),
671        "type_definition" => Some(SymbolKind::TypeAlias),
672        _ => None,
673    }
674}
675
676/// Extract the identifier from a C/C++ `declarator` chain: descend the nested
677/// `declarator` field (through pointer/function/parenthesized declarators) until
678/// an identifier-like leaf is reached. Anonymous declarators yield `None`.
679fn c_declarator_name(node: Node<'_>, bytes: &[u8]) -> Option<String> {
680    let mut n = node;
681    loop {
682        if matches!(
683            n.kind(),
684            "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier"
685        ) {
686            return node_text(n, bytes).map(str::to_string);
687        }
688        n = n.child_by_field_name("declarator")?;
689    }
690}
691
692/// Parse `src` as `language` into a **path-agnostic** slice: its symbols and
693/// name-based references, with no file path (ADR 0012). The path is supplied
694/// later at assembly from the manifest.
695pub fn build(language: Language, src: &str) -> CodeGraph {
696    let mut parser = Parser::new();
697    if parser.set_language(&language.ts_language()).is_err() {
698        return CodeGraph::default();
699    }
700    let Some(tree) = parser.parse(src, None) else {
701        return CodeGraph::default();
702    };
703    let mut graph = CodeGraph::default();
704    walk(language, tree.root_node(), src.as_bytes(), None, &mut graph);
705    graph
706}
707
708/// Parse Rust source. Back-compatible shorthand for `build(Language::Rust, src)`.
709pub fn build_rust(src: &str) -> CodeGraph {
710    build(Language::Rust, src)
711}
712
713fn node_text<'a>(node: Node<'_>, bytes: &'a [u8]) -> Option<&'a str> {
714    node.utf8_text(bytes).ok()
715}
716
717fn name_field(node: Node<'_>, bytes: &[u8]) -> Option<String> {
718    node.child_by_field_name("name")
719        .and_then(|n| node_text(n, bytes))
720        .map(str::to_string)
721}
722
723fn walk(
724    language: Language,
725    node: Node<'_>,
726    bytes: &[u8],
727    current_fn: Option<&str>,
728    graph: &mut CodeGraph,
729) {
730    let mut enclosing = current_fn.map(str::to_string);
731
732    if let Some(kind) = language.item_kind(node, bytes)
733        && let Some(name) = language.item_name(node, kind, bytes)
734    {
735        graph.symbols.push(Symbol {
736            name: name.clone(),
737            kind,
738            start_line: node.start_position().row + 1,
739            end_line: node.end_position().row + 1,
740        });
741        if kind == SymbolKind::Function {
742            enclosing = Some(name);
743        }
744    }
745
746    if language.is_call(node.kind())
747        && let Some(name) = language.callee_name(node, bytes)
748    {
749        graph.references.push(Reference {
750            name,
751            from: enclosing.clone(),
752            line: node.start_position().row + 1,
753            kind: language.callee_kind(node),
754        });
755    }
756
757    // Calls the grammar hides inside an opaque macro-argument node (#216).
758    for (name, line) in language.macro_arg_calls(node, bytes) {
759        graph.references.push(Reference {
760            name,
761            from: enclosing.clone(),
762            line,
763            // A token-tree call is a bare `ident(` by construction (#216).
764            kind: RefKind::Free,
765        });
766    }
767
768    let mut cursor = node.walk();
769    for child in node.children(&mut cursor) {
770        walk(language, child, bytes, enclosing.as_deref(), graph);
771    }
772}
773
774#[cfg(test)]
775mod tests {
776    use super::*;
777
778    const RUST_SRC: &str = r#"
779struct Widget { n: u32 }
780
781fn helper(x: u32) -> u32 { x + 1 }
782
783fn main() {
784    let w = Widget { n: 1 };
785    let y = helper(w.n);
786    println!("{y}");
787}
788"#;
789
790    #[test]
791    fn rust_extracts_definitions() {
792        let g = build_rust(RUST_SRC);
793        let names: Vec<(&str, SymbolKind)> = g
794            .symbols
795            .iter()
796            .map(|s| (s.name.as_str(), s.kind))
797            .collect();
798        assert!(names.contains(&("Widget", SymbolKind::Struct)));
799        assert!(names.contains(&("helper", SymbolKind::Function)));
800        assert!(names.contains(&("main", SymbolKind::Function)));
801    }
802
803    #[test]
804    fn rust_records_call_with_enclosing_fn() {
805        let g = build_rust(RUST_SRC);
806        let call = g
807            .references
808            .iter()
809            .find(|r| r.name == "helper")
810            .expect("helper call recorded");
811        assert_eq!(call.from.as_deref(), Some("main"));
812    }
813
814    // ---- calls inside macro arguments (#216) ------------------------------
815
816    /// Names referenced in `src`, for the macro-argument cases below.
817    fn rust_ref_names(src: &str) -> Vec<String> {
818        build_rust(src)
819            .references
820            .iter()
821            .map(|r| r.name.clone())
822            .collect()
823    }
824
825    #[test]
826    fn rust_records_a_call_inside_assert_eq() {
827        // Rust macro bodies parse as token trees, so this call used to vanish —
828        // the dominant false positive behind `unreferenced` (#216).
829        let names = rust_ref_names("fn g() { assert_eq!(f(), 1); }");
830        assert!(names.contains(&"f".to_string()), "got {names:?}");
831    }
832
833    #[test]
834    fn rust_records_calls_inside_common_macros() {
835        for (src, want) in [
836            ("fn g() { println!(\"{}\", h()); }", "h"),
837            ("fn g() { let v = vec![mk()]; }", "mk"),
838            ("fn g() { panic!(\"{}\", why()); }", "why"),
839            ("fn g() { write!(w, \"{}\", val()); }", "val"),
840        ] {
841            let names = rust_ref_names(src);
842            assert!(names.contains(&want.to_string()), "{want} in {names:?}");
843        }
844    }
845
846    #[test]
847    fn rust_records_a_qualified_call_inside_a_macro_by_last_segment() {
848        // The real #216 repro: `Language::from_extension` is called only from
849        // assertions, so it looked uncalled.
850        let names =
851            rust_ref_names("fn g() { assert_eq!(Language::from_extension(\"rs\"), None); }");
852        assert!(names.contains(&"from_extension".to_string()), "{names:?}");
853    }
854
855    #[test]
856    fn rust_records_nested_calls_inside_a_macro() {
857        let names = rust_ref_names("fn g() { assert_eq!(outer(inner()), 1); }");
858        assert!(names.contains(&"outer".to_string()), "{names:?}");
859        assert!(names.contains(&"inner".to_string()), "{names:?}");
860    }
861
862    #[test]
863    fn rust_records_a_macro_arg_call_once() {
864        let names = rust_ref_names("fn g() { assert_eq!(f(), 1); }");
865        assert_eq!(
866            names.iter().filter(|n| *n == "f").count(),
867            1,
868            "nested token trees must not double-count: {names:?}"
869        );
870    }
871
872    #[test]
873    fn rust_macro_arg_calls_carry_the_enclosing_function() {
874        let g = build_rust("fn outer_fn() { assert_eq!(f(), 1); }");
875        let r = g.references.iter().find(|r| r.name == "f").expect("f");
876        assert_eq!(r.from.as_deref(), Some("outer_fn"));
877    }
878
879    #[test]
880    fn rust_does_not_treat_a_nested_macro_name_as_a_call() {
881        // `matches!` is a macro, not a function: the `!` between the identifier
882        // and the token tree is what distinguishes them.
883        let names = rust_ref_names("fn g() { assert!(matches!(a, Some(_))); }");
884        assert!(!names.contains(&"matches".to_string()), "{names:?}");
885    }
886
887    #[test]
888    fn rust_does_not_invent_calls_from_plain_macro_arguments() {
889        // Bare identifiers and literals are not calls — only an identifier
890        // immediately followed by a parenthesised token tree is.
891        let names = rust_ref_names("fn g() { println!(\"{}\", x); }");
892        assert!(!names.contains(&"x".to_string()), "{names:?}");
893        let names = rust_ref_names("fn g() { let v = vec![1, 2, 3]; }");
894        assert!(names.is_empty(), "{names:?}");
895    }
896
897    #[test]
898    fn rust_still_records_ordinary_calls_alongside_macro_ones() {
899        let names = rust_ref_names("fn g() { plain(); assert_eq!(inside(), 1); }");
900        assert!(names.contains(&"plain".to_string()), "{names:?}");
901        assert!(names.contains(&"inside".to_string()), "{names:?}");
902    }
903
904    #[test]
905    fn rust_macro_arg_call_line_is_one_based() {
906        let g = build_rust("fn g() {\n    assert_eq!(f(), 1);\n}");
907        let r = g.references.iter().find(|r| r.name == "f").expect("f");
908        assert_eq!(r.line, 2);
909    }
910
911    // ---- grammar audit for the same opaque-node hole (#216) ----------------
912
913    #[test]
914    fn c_records_macro_invocations_at_the_call_site() {
915        // A C macro *use* is indistinguishable from a call to the grammar, so
916        // it and its neighbours are recorded — no Rust-style hole here.
917        let g = build(
918            Language::C,
919            "#define M() foo()\nvoid g(void) { M(); bar(); }",
920        );
921        let names: Vec<&str> = g.references.iter().map(|r| r.name.as_str()).collect();
922        assert!(names.contains(&"M"));
923        assert!(names.contains(&"bar"));
924    }
925
926    #[test]
927    fn c_does_not_record_calls_inside_a_define_body() {
928        // The one analogous hole the audit found: a `#define` body is a single
929        // opaque `preproc_arg` token, not an expression tree, so `foo()` here is
930        // invisible. Unlike Rust's token tree there are no child nodes to read,
931        // so recovering it means lexing macro text — deliberately out of scope.
932        // Pinned so the gap is discoverable rather than silent.
933        let g = build(Language::C, "#define M() foo()\nvoid g(void) { M(); }");
934        let names: Vec<&str> = g.references.iter().map(|r| r.name.as_str()).collect();
935        assert!(!names.contains(&"foo"), "known gap: {names:?}");
936    }
937
938    #[test]
939    fn elixir_records_calls_inside_a_quote_block() {
940        // `quote do: foo()` parses as real expressions — no hole.
941        let g = build(
942            Language::Elixir,
943            "defmodule A do\n  def g do\n    quote do: foo()\n  end\nend",
944        );
945        let names: Vec<&str> = g.references.iter().map(|r| r.name.as_str()).collect();
946        assert!(names.contains(&"foo"), "{names:?}");
947    }
948
949    #[test]
950    fn ruby_records_calls_inside_a_block() {
951        let g = build(Language::Ruby, "def g\n  define_method(:x) { foo() }\nend");
952        let names: Vec<&str> = g.references.iter().map(|r| r.name.as_str()).collect();
953        assert!(names.contains(&"foo"), "{names:?}");
954    }
955
956    #[test]
957    fn rust_symbol_lines_are_one_based() {
958        let g = build_rust(RUST_SRC);
959        let main = g.symbols.iter().find(|s| s.name == "main").unwrap();
960        assert!(main.start_line >= 1 && main.end_line >= main.start_line);
961    }
962
963    const PY_SRC: &str = r#"
964class Widget:
965    def area(self):
966        return helper(self.n)
967
968def helper(x):
969    return x + 1
970
971def main():
972    w = Widget()
973    print(w.area())
974"#;
975
976    #[test]
977    fn python_extracts_functions_and_classes() {
978        let g = build(Language::Python, PY_SRC);
979        let names: Vec<(&str, SymbolKind)> = g
980            .symbols
981            .iter()
982            .map(|s| (s.name.as_str(), s.kind))
983            .collect();
984        assert!(names.contains(&("Widget", SymbolKind::Class)));
985        assert!(names.contains(&("helper", SymbolKind::Function)));
986        assert!(names.contains(&("area", SymbolKind::Function)));
987    }
988
989    #[test]
990    fn python_records_calls_including_methods() {
991        let g = build(Language::Python, PY_SRC);
992        // Plain call `helper(...)` from inside `area`.
993        let helper_call = g
994            .references
995            .iter()
996            .find(|r| r.name == "helper")
997            .expect("helper call");
998        assert_eq!(helper_call.from.as_deref(), Some("area"));
999        // Method call `w.area()` from inside `main` -> the attribute name `area`.
1000        assert!(
1001            g.references
1002                .iter()
1003                .any(|r| r.name == "area" && r.from.as_deref() == Some("main"))
1004        );
1005    }
1006
1007    #[test]
1008    fn language_from_extension() {
1009        assert_eq!(Language::from_extension("rs"), Some(Language::Rust));
1010        assert_eq!(Language::from_extension("py"), Some(Language::Python));
1011        assert_eq!(Language::from_extension("js"), Some(Language::JavaScript));
1012        assert_eq!(Language::from_extension("jsx"), Some(Language::JavaScript));
1013        assert_eq!(Language::from_extension("ts"), Some(Language::TypeScript));
1014        assert_eq!(Language::from_extension("tsx"), Some(Language::Tsx));
1015        assert_eq!(Language::from_extension("go"), Some(Language::Go));
1016        assert_eq!(Language::from_extension("java"), Some(Language::Java));
1017        assert_eq!(Language::from_extension("cs"), Some(Language::CSharp));
1018        assert_eq!(Language::from_extension("c"), Some(Language::C));
1019        assert_eq!(Language::from_extension("h"), Some(Language::C));
1020        assert_eq!(Language::from_extension("cpp"), Some(Language::Cpp));
1021        assert_eq!(Language::from_extension("cc"), Some(Language::Cpp));
1022        assert_eq!(Language::from_extension("cxx"), Some(Language::Cpp));
1023        assert_eq!(Language::from_extension("hpp"), Some(Language::Cpp));
1024        assert_eq!(Language::from_extension("hh"), Some(Language::Cpp));
1025        assert_eq!(Language::from_extension("rb"), Some(Language::Ruby));
1026        assert_eq!(Language::from_extension("php"), Some(Language::Php));
1027        assert_eq!(Language::from_extension("sh"), Some(Language::Bash));
1028        assert_eq!(Language::from_extension("bash"), Some(Language::Bash));
1029        assert_eq!(Language::from_extension("kt"), Some(Language::Kotlin));
1030        assert_eq!(Language::from_extension("kts"), Some(Language::Kotlin));
1031        assert_eq!(Language::from_extension("swift"), Some(Language::Swift));
1032        assert_eq!(Language::from_extension("lua"), Some(Language::Lua));
1033        assert_eq!(Language::from_extension("scala"), Some(Language::Scala));
1034        assert_eq!(Language::from_extension("ex"), Some(Language::Elixir));
1035        assert_eq!(Language::from_extension("exs"), Some(Language::Elixir));
1036        assert_eq!(Language::from_extension("txt"), None);
1037    }
1038
1039    const JS_SRC: &str = r#"
1040class Widget {
1041  area() {
1042    return helper(this.n);
1043  }
1044}
1045function helper(x) {
1046  return x + 1;
1047}
1048function main() {
1049  const w = new Widget();
1050  console.log(w.area());
1051}
1052"#;
1053
1054    #[test]
1055    fn javascript_extracts_symbols_and_calls() {
1056        let g = build(Language::JavaScript, JS_SRC);
1057        let names: Vec<(&str, SymbolKind)> = g
1058            .symbols
1059            .iter()
1060            .map(|s| (s.name.as_str(), s.kind))
1061            .collect();
1062        assert!(names.contains(&("Widget", SymbolKind::Class)));
1063        assert!(names.contains(&("area", SymbolKind::Function)));
1064        assert!(names.contains(&("helper", SymbolKind::Function)));
1065        assert!(names.contains(&("main", SymbolKind::Function)));
1066
1067        // `helper(...)` called from inside `area`.
1068        assert!(
1069            g.references
1070                .iter()
1071                .any(|r| r.name == "helper" && r.from.as_deref() == Some("area"))
1072        );
1073        // Method call `w.area()` -> member-expression property `area`, from `main`.
1074        assert!(
1075            g.references
1076                .iter()
1077                .any(|r| r.name == "area" && r.from.as_deref() == Some("main"))
1078        );
1079    }
1080
1081    const TS_SRC: &str = r#"
1082interface Shape { area(): number; }
1083type Id = string;
1084enum Color { Red, Green }
1085
1086class Circle implements Shape {
1087  area(): number { return compute(this.r); }
1088}
1089
1090function compute(r: number): number { return r * r; }
1091"#;
1092
1093    #[test]
1094    fn typescript_extracts_ts_specific_kinds() {
1095        let g = build(Language::TypeScript, TS_SRC);
1096        let named = |n: &str| g.symbols.iter().find(|s| s.name == n).map(|s| s.kind);
1097        assert_eq!(named("Shape"), Some(SymbolKind::Interface));
1098        assert_eq!(named("Id"), Some(SymbolKind::TypeAlias));
1099        assert_eq!(named("Color"), Some(SymbolKind::Enum));
1100        assert_eq!(named("Circle"), Some(SymbolKind::Class));
1101        assert_eq!(named("compute"), Some(SymbolKind::Function));
1102
1103        assert!(
1104            g.references
1105                .iter()
1106                .any(|r| r.name == "compute" && r.from.as_deref() == Some("area"))
1107        );
1108    }
1109
1110    const GO_SRC: &str = r#"
1111package main
1112
1113type Widget struct { n int }
1114
1115type Shape interface { Area() int }
1116
1117const Limit = 10
1118
1119var counter = 0
1120
1121func helper(x int) int { return x + 1 }
1122
1123func (w Widget) Area() int { return helper(w.n) }
1124
1125func main() {
1126	w := Widget{n: 1}
1127	_ = w.Area()
1128	_ = helper(2)
1129}
1130"#;
1131
1132    #[test]
1133    fn go_extracts_definitions() {
1134        let g = build(Language::Go, GO_SRC);
1135        let names: Vec<(&str, SymbolKind)> = g
1136            .symbols
1137            .iter()
1138            .map(|s| (s.name.as_str(), s.kind))
1139            .collect();
1140        assert!(names.contains(&("Widget", SymbolKind::Struct)));
1141        assert!(names.contains(&("Shape", SymbolKind::Interface)));
1142        assert!(names.contains(&("helper", SymbolKind::Function)));
1143        assert!(names.contains(&("Area", SymbolKind::Function)));
1144        assert!(names.contains(&("main", SymbolKind::Function)));
1145        assert!(names.contains(&("Limit", SymbolKind::Const)));
1146        assert!(names.contains(&("counter", SymbolKind::Static)));
1147    }
1148
1149    #[test]
1150    fn go_records_calls_including_methods() {
1151        let g = build(Language::Go, GO_SRC);
1152        // Plain call `helper(...)` from inside the `Area` method.
1153        assert!(
1154            g.references
1155                .iter()
1156                .any(|r| r.name == "helper" && r.from.as_deref() == Some("Area")),
1157            "helper call from Area"
1158        );
1159        // Method call `w.Area()` -> selector-expression field `Area`, from `main`.
1160        assert!(
1161            g.references
1162                .iter()
1163                .any(|r| r.name == "Area" && r.from.as_deref() == Some("main")),
1164            "w.Area() call from main"
1165        );
1166    }
1167
1168    #[test]
1169    fn tsx_parses_with_jsx() {
1170        // The TSX grammar must accept JSX syntax that plain TS would reject.
1171        let src = r#"
1172function App(): JSX.Element {
1173  return greet();
1174}
1175function greet() { return <div>hi</div>; }
1176"#;
1177        let g = build(Language::Tsx, src);
1178        assert!(g.symbols.iter().any(|s| s.name == "App"));
1179        assert!(
1180            g.references
1181                .iter()
1182                .any(|r| r.name == "greet" && r.from.as_deref() == Some("App"))
1183        );
1184    }
1185
1186    const JAVA_SRC: &str = r#"
1187interface Shape { int area(); }
1188
1189enum Color { RED, GREEN }
1190
1191class Widget {
1192    int n;
1193    Widget(int n) { this.n = n; }
1194    int area() { return helper(this.n); }
1195}
1196
1197class Main {
1198    static int helper(int x) { return x + 1; }
1199    static void main(String[] args) {
1200        Widget w = new Widget(1);
1201        w.area();
1202    }
1203}
1204"#;
1205
1206    #[test]
1207    fn java_extracts_definitions() {
1208        let g = build(Language::Java, JAVA_SRC);
1209        let named = |n: &str| g.symbols.iter().find(|s| s.name == n).map(|s| s.kind);
1210        assert_eq!(named("Shape"), Some(SymbolKind::Interface));
1211        assert_eq!(named("Color"), Some(SymbolKind::Enum));
1212        assert_eq!(named("Widget"), Some(SymbolKind::Class));
1213        assert_eq!(named("area"), Some(SymbolKind::Function));
1214        assert_eq!(named("helper"), Some(SymbolKind::Function));
1215        // Constructor is recorded as a Function named for its class.
1216        assert!(
1217            g.symbols
1218                .iter()
1219                .any(|s| s.name == "Widget" && s.kind == SymbolKind::Function)
1220        );
1221    }
1222
1223    #[test]
1224    fn java_records_calls_with_enclosing_fn() {
1225        let g = build(Language::Java, JAVA_SRC);
1226        assert!(
1227            g.references
1228                .iter()
1229                .any(|r| r.name == "helper" && r.from.as_deref() == Some("area")),
1230            "helper() call from area"
1231        );
1232        assert!(
1233            g.references
1234                .iter()
1235                .any(|r| r.name == "area" && r.from.as_deref() == Some("main")),
1236            "w.area() call from main"
1237        );
1238    }
1239
1240    const CS_SRC: &str = r#"
1241interface IShape { int Area(); }
1242enum Color { Red, Green }
1243struct Point { public int X; }
1244
1245class Widget {
1246    int n;
1247    public Widget(int n) { this.n = n; }
1248    public int Area() { return Helper(this.n); }
1249}
1250
1251class Program {
1252    static int Helper(int x) { return x + 1; }
1253    static void Main() {
1254        var w = new Widget(1);
1255        w.Area();
1256    }
1257}
1258"#;
1259
1260    #[test]
1261    fn csharp_extracts_definitions() {
1262        let g = build(Language::CSharp, CS_SRC);
1263        let named = |n: &str| g.symbols.iter().find(|s| s.name == n).map(|s| s.kind);
1264        assert_eq!(named("IShape"), Some(SymbolKind::Interface));
1265        assert_eq!(named("Color"), Some(SymbolKind::Enum));
1266        assert_eq!(named("Point"), Some(SymbolKind::Struct));
1267        assert_eq!(named("Widget"), Some(SymbolKind::Class));
1268        assert_eq!(named("Area"), Some(SymbolKind::Function));
1269        assert_eq!(named("Helper"), Some(SymbolKind::Function));
1270    }
1271
1272    #[test]
1273    fn csharp_records_calls_with_enclosing_fn() {
1274        let g = build(Language::CSharp, CS_SRC);
1275        assert!(
1276            g.references
1277                .iter()
1278                .any(|r| r.name == "Helper" && r.from.as_deref() == Some("Area")),
1279            "Helper() call from Area"
1280        );
1281        assert!(
1282            g.references
1283                .iter()
1284                .any(|r| r.name == "Area" && r.from.as_deref() == Some("Main")),
1285            "w.Area() call from Main"
1286        );
1287    }
1288
1289    const C_SRC: &str = r#"
1290struct Widget { int n; };
1291
1292typedef int Id;
1293
1294enum Color { RED, GREEN };
1295
1296int helper(int x) { return x + 1; }
1297
1298int main(void) {
1299    int y = helper(2);
1300    return y;
1301}
1302"#;
1303
1304    #[test]
1305    fn c_extracts_definitions() {
1306        let g = build(Language::C, C_SRC);
1307        let named = |n: &str| g.symbols.iter().find(|s| s.name == n).map(|s| s.kind);
1308        assert_eq!(named("Widget"), Some(SymbolKind::Struct));
1309        assert_eq!(named("Id"), Some(SymbolKind::TypeAlias));
1310        assert_eq!(named("Color"), Some(SymbolKind::Enum));
1311        assert_eq!(named("helper"), Some(SymbolKind::Function));
1312        assert_eq!(named("main"), Some(SymbolKind::Function));
1313    }
1314
1315    #[test]
1316    fn c_records_call_with_enclosing_fn() {
1317        let g = build(Language::C, C_SRC);
1318        assert!(
1319            g.references
1320                .iter()
1321                .any(|r| r.name == "helper" && r.from.as_deref() == Some("main")),
1322            "helper() call from main"
1323        );
1324    }
1325
1326    const CPP_SRC: &str = r#"
1327namespace geo {
1328
1329class Widget {
1330public:
1331    int n;
1332    int area() { return helper(this->n); }
1333};
1334
1335int helper(int x) { return x + 1; }
1336
1337}
1338
1339int main() {
1340    geo::Widget w;
1341    return w.area();
1342}
1343"#;
1344
1345    #[test]
1346    fn cpp_extracts_definitions() {
1347        let g = build(Language::Cpp, CPP_SRC);
1348        let named = |n: &str| g.symbols.iter().find(|s| s.name == n).map(|s| s.kind);
1349        assert_eq!(named("geo"), Some(SymbolKind::Module));
1350        assert_eq!(named("Widget"), Some(SymbolKind::Class));
1351        assert_eq!(named("area"), Some(SymbolKind::Function));
1352        assert_eq!(named("helper"), Some(SymbolKind::Function));
1353        assert_eq!(named("main"), Some(SymbolKind::Function));
1354    }
1355
1356    #[test]
1357    fn cpp_records_calls_including_methods() {
1358        let g = build(Language::Cpp, CPP_SRC);
1359        // this->helper(...) -> field_expression `field`, from `area`.
1360        assert!(
1361            g.references
1362                .iter()
1363                .any(|r| r.name == "helper" && r.from.as_deref() == Some("area")),
1364            "helper() call from area"
1365        );
1366        // w.area() -> field_expression `field`, from `main`.
1367        assert!(
1368            g.references
1369                .iter()
1370                .any(|r| r.name == "area" && r.from.as_deref() == Some("main")),
1371            "w.area() call from main"
1372        );
1373    }
1374
1375    const RUBY_SRC: &str = r#"
1376class Widget
1377  def area
1378    helper(1)
1379  end
1380end
1381
1382module Util
1383end
1384
1385def helper(x)
1386  x + 1
1387end
1388
1389def main
1390  helper(2)
1391end
1392"#;
1393
1394    #[test]
1395    fn ruby_extracts_definitions() {
1396        let g = build(Language::Ruby, RUBY_SRC);
1397        let named = |n: &str| g.symbols.iter().find(|s| s.name == n).map(|s| s.kind);
1398        assert_eq!(named("Widget"), Some(SymbolKind::Class));
1399        assert_eq!(named("Util"), Some(SymbolKind::Module));
1400        assert_eq!(named("area"), Some(SymbolKind::Function));
1401        assert_eq!(named("helper"), Some(SymbolKind::Function));
1402        assert_eq!(named("main"), Some(SymbolKind::Function));
1403    }
1404
1405    #[test]
1406    fn ruby_records_call_with_enclosing_fn() {
1407        let g = build(Language::Ruby, RUBY_SRC);
1408        // `helper(2)` is called from the `main` method (callee is the `method` field).
1409        assert!(
1410            g.references
1411                .iter()
1412                .any(|r| r.name == "helper" && r.from.as_deref() == Some("main")),
1413            "helper call from main"
1414        );
1415    }
1416
1417    const PHP_SRC: &str = r#"<?php
1418class Widget {
1419    function area() {
1420        return helper(1);
1421    }
1422}
1423
1424interface Shape {}
1425
1426trait Named {}
1427
1428function helper($x) {
1429    return $x + 1;
1430}
1431
1432function main() {
1433    return helper(2);
1434}
1435"#;
1436
1437    #[test]
1438    fn php_extracts_definitions() {
1439        let g = build(Language::Php, PHP_SRC);
1440        let named = |n: &str| g.symbols.iter().find(|s| s.name == n).map(|s| s.kind);
1441        assert_eq!(named("Widget"), Some(SymbolKind::Class));
1442        assert_eq!(named("Shape"), Some(SymbolKind::Interface));
1443        assert_eq!(named("Named"), Some(SymbolKind::Trait));
1444        assert_eq!(named("area"), Some(SymbolKind::Function));
1445        assert_eq!(named("helper"), Some(SymbolKind::Function));
1446        assert_eq!(named("main"), Some(SymbolKind::Function));
1447    }
1448
1449    #[test]
1450    fn php_records_call_with_enclosing_fn() {
1451        let g = build(Language::Php, PHP_SRC);
1452        // `helper(2)` -> function_call_expression `function` field, from `main`.
1453        assert!(
1454            g.references
1455                .iter()
1456                .any(|r| r.name == "helper" && r.from.as_deref() == Some("main")),
1457            "helper() call from main"
1458        );
1459    }
1460
1461    const BASH_SRC: &str = r#"
1462helper() {
1463  echo "$1"
1464}
1465
1466main() {
1467  helper hello
1468}
1469"#;
1470
1471    #[test]
1472    fn bash_extracts_definitions() {
1473        let g = build(Language::Bash, BASH_SRC);
1474        let named = |n: &str| g.symbols.iter().find(|s| s.name == n).map(|s| s.kind);
1475        assert_eq!(named("helper"), Some(SymbolKind::Function));
1476        assert_eq!(named("main"), Some(SymbolKind::Function));
1477    }
1478
1479    #[test]
1480    fn bash_records_call_with_enclosing_fn() {
1481        let g = build(Language::Bash, BASH_SRC);
1482        // `helper hello` is a command whose `name` field is the callee, from `main`.
1483        assert!(
1484            g.references
1485                .iter()
1486                .any(|r| r.name == "helper" && r.from.as_deref() == Some("main")),
1487            "helper command from main"
1488        );
1489    }
1490
1491    const KOTLIN_SRC: &str = r#"
1492class Widget {
1493    fun area(): Int {
1494        return helper(1)
1495    }
1496}
1497
1498object Config
1499
1500fun helper(x: Int): Int {
1501    return x + 1
1502}
1503
1504fun main() {
1505    helper(2)
1506}
1507"#;
1508
1509    #[test]
1510    fn kotlin_extracts_definitions() {
1511        let g = build(Language::Kotlin, KOTLIN_SRC);
1512        let named = |n: &str| g.symbols.iter().find(|s| s.name == n).map(|s| s.kind);
1513        assert_eq!(named("Widget"), Some(SymbolKind::Class));
1514        assert_eq!(named("Config"), Some(SymbolKind::Class)); // `object` singleton
1515        assert_eq!(named("area"), Some(SymbolKind::Function));
1516        assert_eq!(named("helper"), Some(SymbolKind::Function));
1517        assert_eq!(named("main"), Some(SymbolKind::Function));
1518    }
1519
1520    #[test]
1521    fn kotlin_records_call_with_enclosing_fn() {
1522        let g = build(Language::Kotlin, KOTLIN_SRC);
1523        // `helper(2)` -> call_expression whose first child is an `identifier`,
1524        // from `main`.
1525        assert!(
1526            g.references
1527                .iter()
1528                .any(|r| r.name == "helper" && r.from.as_deref() == Some("main")),
1529            "helper() call from main"
1530        );
1531    }
1532
1533    const SWIFT_SRC: &str = r#"
1534class Widget {
1535    func area() -> Int {
1536        return helper(1)
1537    }
1538}
1539
1540protocol Shape {}
1541
1542func helper(x: Int) -> Int {
1543    return x + 1
1544}
1545
1546func main() {
1547    helper(2)
1548}
1549"#;
1550
1551    #[test]
1552    fn swift_extracts_definitions() {
1553        let g = build(Language::Swift, SWIFT_SRC);
1554        let named = |n: &str| g.symbols.iter().find(|s| s.name == n).map(|s| s.kind);
1555        assert_eq!(named("Widget"), Some(SymbolKind::Class));
1556        assert_eq!(named("Shape"), Some(SymbolKind::Interface)); // `protocol`
1557        assert_eq!(named("area"), Some(SymbolKind::Function));
1558        assert_eq!(named("helper"), Some(SymbolKind::Function));
1559        assert_eq!(named("main"), Some(SymbolKind::Function));
1560    }
1561
1562    #[test]
1563    fn swift_records_call_with_enclosing_fn() {
1564        let g = build(Language::Swift, SWIFT_SRC);
1565        // `helper(2)` -> call_expression whose first child is a `simple_identifier`,
1566        // from `main`.
1567        assert!(
1568            g.references
1569                .iter()
1570                .any(|r| r.name == "helper" && r.from.as_deref() == Some("main")),
1571            "helper() call from main"
1572        );
1573    }
1574
1575    const LUA_SRC: &str = r#"
1576function helper(x)
1577  return x + 1
1578end
1579
1580function main()
1581  return helper(2)
1582end
1583"#;
1584
1585    #[test]
1586    fn lua_extracts_definitions() {
1587        let g = build(Language::Lua, LUA_SRC);
1588        let named = |n: &str| g.symbols.iter().find(|s| s.name == n).map(|s| s.kind);
1589        assert_eq!(named("helper"), Some(SymbolKind::Function));
1590        assert_eq!(named("main"), Some(SymbolKind::Function));
1591    }
1592
1593    #[test]
1594    fn lua_records_call_with_enclosing_fn() {
1595        let g = build(Language::Lua, LUA_SRC);
1596        // `helper(2)` -> function_call whose `name` field is the callee, from `main`.
1597        assert!(
1598            g.references
1599                .iter()
1600                .any(|r| r.name == "helper" && r.from.as_deref() == Some("main")),
1601            "helper() call from main"
1602        );
1603    }
1604
1605    const SCALA_SRC: &str = r#"
1606class Widget {
1607  def area(): Int = { helper(1) }
1608}
1609
1610object Config
1611
1612trait Shape
1613
1614def helper(x: Int): Int = x + 1
1615
1616def main(): Unit = { helper(2) }
1617"#;
1618
1619    #[test]
1620    fn scala_extracts_definitions() {
1621        let g = build(Language::Scala, SCALA_SRC);
1622        let named = |n: &str| g.symbols.iter().find(|s| s.name == n).map(|s| s.kind);
1623        assert_eq!(named("Widget"), Some(SymbolKind::Class));
1624        assert_eq!(named("Config"), Some(SymbolKind::Class)); // `object` singleton
1625        assert_eq!(named("Shape"), Some(SymbolKind::Trait));
1626        assert_eq!(named("area"), Some(SymbolKind::Function));
1627        assert_eq!(named("helper"), Some(SymbolKind::Function));
1628        assert_eq!(named("main"), Some(SymbolKind::Function));
1629    }
1630
1631    #[test]
1632    fn scala_records_call_with_enclosing_fn() {
1633        let g = build(Language::Scala, SCALA_SRC);
1634        // `helper(2)` -> call_expression `function` field, from `main`.
1635        assert!(
1636            g.references
1637                .iter()
1638                .any(|r| r.name == "helper" && r.from.as_deref() == Some("main")),
1639            "helper() call from main"
1640        );
1641    }
1642
1643    const ELIXIR_SRC: &str = r#"
1644defmodule Math do
1645  def add(a, b) do
1646    helper(a) + b
1647  end
1648
1649  defp helper(x), do: x
1650
1651  def run do
1652    Remote.compute(1)
1653  end
1654end
1655"#;
1656
1657    #[test]
1658    fn elixir_extracts_definitions() {
1659        let g = build(Language::Elixir, ELIXIR_SRC);
1660        let named = |n: &str| g.symbols.iter().find(|s| s.name == n).map(|s| s.kind);
1661        // `defmodule Math` -> Module (name is the `alias`).
1662        assert_eq!(named("Math"), Some(SymbolKind::Module));
1663        // `def add(a, b)` -> Function (name is the nested-call head).
1664        assert_eq!(named("add"), Some(SymbolKind::Function));
1665        // `defp helper(x)` -> Function.
1666        assert_eq!(named("helper"), Some(SymbolKind::Function));
1667        // `def run` (no parens) -> Function (name is a bare identifier head).
1668        assert_eq!(named("run"), Some(SymbolKind::Function));
1669    }
1670
1671    #[test]
1672    fn elixir_records_calls() {
1673        let g = build(Language::Elixir, ELIXIR_SRC);
1674        // `helper(a)` -> reference to `helper` from inside `add`; the def head
1675        // `add(a, b)` is not itself recorded as a call.
1676        assert!(
1677            g.references
1678                .iter()
1679                .any(|r| r.name == "helper" && r.from.as_deref() == Some("add")),
1680            "helper() call from add"
1681        );
1682        // Remote call `Remote.compute(1)` -> reference to `compute` from `run`
1683        // (the target's trailing identifier).
1684        assert!(
1685            g.references
1686                .iter()
1687                .any(|r| r.name == "compute" && r.from.as_deref() == Some("run")),
1688            "Remote.compute() call from run"
1689        );
1690        // The definition heads must not leak in as self-calls.
1691        assert!(
1692            !g.references.iter().any(|r| r.name == "add"),
1693            "def head add(a, b) not recorded as a call"
1694        );
1695    }
1696
1697    // #136: JS/TS arrow-function and function-expression bindings are named
1698    // functions; a `variable_declarator`/`public_field_definition` whose value
1699    // is an `arrow_function`/`function_expression`.
1700    #[test]
1701    fn javascript_extracts_arrow_and_function_expression_bindings() {
1702        let src = r#"
1703const foo = () => { bar(); };
1704const baz = function () { qux(); };
1705"#;
1706        let g = build(Language::JavaScript, src);
1707        let named = |n: &str| g.symbols.iter().find(|s| s.name == n).map(|s| s.kind);
1708        assert_eq!(named("foo"), Some(SymbolKind::Function));
1709        assert_eq!(named("baz"), Some(SymbolKind::Function));
1710        // Calls inside are attributed to the binding name (`walk` sets enclosing).
1711        assert!(
1712            g.references
1713                .iter()
1714                .any(|r| r.name == "bar" && r.from.as_deref() == Some("foo")),
1715            "bar() call attributed to foo"
1716        );
1717        assert!(
1718            g.references
1719                .iter()
1720                .any(|r| r.name == "qux" && r.from.as_deref() == Some("baz")),
1721            "qux() call attributed to baz"
1722        );
1723    }
1724
1725    #[test]
1726    fn typescript_extracts_arrow_bindings_and_class_fields() {
1727        // A `const` arrow binding and a class-field arrow (`public_field_definition`).
1728        let src = r#"
1729const foo = (): void => { bar(); };
1730class C { handler = (): void => { onClick(); }; }
1731"#;
1732        let g = build(Language::TypeScript, src);
1733        let named = |n: &str| g.symbols.iter().find(|s| s.name == n).map(|s| s.kind);
1734        assert_eq!(named("foo"), Some(SymbolKind::Function));
1735        assert_eq!(named("handler"), Some(SymbolKind::Function));
1736        assert!(
1737            g.references
1738                .iter()
1739                .any(|r| r.name == "bar" && r.from.as_deref() == Some("foo")),
1740            "bar() call attributed to foo"
1741        );
1742        assert!(
1743            g.references
1744                .iter()
1745                .any(|r| r.name == "onClick" && r.from.as_deref() == Some("handler")),
1746            "onClick() call attributed to handler"
1747        );
1748    }
1749
1750    // #137: PHP method (`$this->m()`, `member_call_expression`) and static
1751    // (`A::b()`, `scoped_call_expression`) calls are recorded.
1752    #[test]
1753    fn php_records_method_and_static_calls() {
1754        let src = r#"<?php
1755class A {
1756    function run() {
1757        $this->other();
1758        self::x();
1759        B::stat();
1760    }
1761}
1762"#;
1763        let g = build(Language::Php, src);
1764        let called = |n: &str| {
1765            g.references
1766                .iter()
1767                .any(|r| r.name == n && r.from.as_deref() == Some("run"))
1768        };
1769        assert!(called("other"), "$this->other() recorded from run");
1770        assert!(called("x"), "self::x() recorded from run");
1771        assert!(called("stat"), "B::stat() recorded from run");
1772    }
1773
1774    // #151: Swift struct/enum/actor and Kotlin interface/object are no longer all
1775    // mislabeled Class.
1776    #[test]
1777    fn swift_distinguishes_struct_enum_class() {
1778        let src = r#"
1779struct Point { var x: Int }
1780enum Color { case red }
1781class Widget {}
1782actor Worker {}
1783protocol Shape {}
1784"#;
1785        let g = build(Language::Swift, src);
1786        let named = |n: &str| g.symbols.iter().find(|s| s.name == n).map(|s| s.kind);
1787        assert_eq!(named("Point"), Some(SymbolKind::Struct));
1788        assert_eq!(named("Color"), Some(SymbolKind::Enum));
1789        assert_eq!(named("Widget"), Some(SymbolKind::Class));
1790        assert_eq!(named("Worker"), Some(SymbolKind::Class)); // `actor` -> Class
1791        assert_eq!(named("Shape"), Some(SymbolKind::Interface)); // `protocol`
1792    }
1793
1794    #[test]
1795    fn kotlin_distinguishes_interface_from_class() {
1796        let src = r#"
1797interface Shape { }
1798class Widget { }
1799object Config
1800"#;
1801        let g = build(Language::Kotlin, src);
1802        let named = |n: &str| g.symbols.iter().find(|s| s.name == n).map(|s| s.kind);
1803        assert_eq!(named("Shape"), Some(SymbolKind::Interface));
1804        assert_eq!(named("Widget"), Some(SymbolKind::Class));
1805        assert_eq!(named("Config"), Some(SymbolKind::Class)); // `object` singleton
1806    }
1807}