Skip to content

Latest commit

 

History

History
195 lines (164 loc) · 309 KB

languages.md

File metadata and controls

195 lines (164 loc) · 309 KB

Supported Languages

CodeGraph extracts code structure deterministically using tree-sitter grammars and custom embedded extractors. No AI, vectors, or embeddings are involved. The output is byte-stable across runs.

38 code and template language IDs are supported, grouped into three extraction tiers. Three additional Godot project/resource format IDs are tracked for file/framework analysis. Internally this is 41 concrete IDs plus the unknown fallback (42 entries in LANGUAGE_STRINGS).

Note on TypeScript/JavaScript variants: typescript and tsx, and javascript and jsx, are distinct grammar variants internally (separate Language enum entries). They share grammars but handle different file-extension sets. The table lists each variant separately so the extension mapping is unambiguous.


Tier 1 — Full symbol extraction (29 languages)

Tree-sitter parses the file and extracts all symbols (functions, classes, structs, methods, variables, imports, etc.) plus call and dependency edges. This is the richest extraction level. Grammar error nodes are common and tolerated when useful symbols survive. If the parse tree has errors and collapses to only the synthetic file node, indexing still succeeds but emits and stores parse produced no symbols (tree has errors) so an empty graph is never presented as a clean parse.

A read through a type, a static member or an enum value (JsonScope.EMPTY_DOCUMENT, Color::RED), references the type it reads through in Java, C#, Kotlin, Swift, Scala, Dart, PHP and C++, and a Rust variant path (Mode::A, mode::Mode::B, a Mode::C(x) pattern, Self::A in an impl) the enum that declares the variant, nothing else: a Rust associated const read the same way (Limits::MAX) references no type. The receiver must be one capitalized name, and an access that a call is made through stays the call's. tree-sitter-cpp writes every C++ scope as a namespace name, so a capitalized scope (Color::RED, Limits::kMax) is read as the type it names; a lowercase namespace (std::) and a global-scope read (::g) name none. A symbol also references the constants and variables of its own file that it reads (value references, see data-model.md).

PHP, Pascal and CFML names ignore case: a reference written in another case than its declaration (formatprice() for FormatPrice) passes the name pre-filter and is matched by its name in any case, still only when one node has that name in any case.

In TypeScript, TSX and ArkTS, what a declare module 'x' { … } or declare global { … } body declares is exported, as TypeScript's binder exports it: it merges into the module's or the global scope's type (interface Window, a library's PluginOptionsByType), so it is reachable from other files and never dead code. A body holding an export declaration of its own (export {…}, export =, export default x) exports only what that names; a namespace nested in an ambient body is ambient too, and declare namespace X stays the file's.

In Python, a call written as a bare name (get(1)) never means a method, as Python has no implicit self. It takes another file's function or class only when its own file neither imports the name from a module outside the project (from django.shortcuts import render) nor binds it itself (view = UserView.as_view(), a parameter), unless that symbol is a pytest fixture the test can take by name. A call through a receiver the graph keeps no type for (User.objects.get(), self.client.login()) means only a method of a class the chain's last link names (objects, client; underscores and case aside) or a function or class of a module of that name. In every language but C and C++, a function or type declared inside a function, or a method of such a type, is in scope only in that function.

A Python name imported from a package (from users.models import User), or used through an imported module (generic.ObjectEditView), is the module's own top-level definition, else the one it re-exports by name (from .tokens import Token) or through a star import (from .users import *), up to three modules deep. Two imports rebinding the name (try: from .fast import Parser / except ImportError: from .slow import Parser), or star-imported modules offering different definitions, bind nothing. A parenthesized from-import binds every name it lists, one per line or not; a comment or docstring binds none. In a method value, pkg.mod.Cls is the class import pkg.mod names, unless another import shares the module's last segment. A call written on self (self.get_ip(request)) is the class's own or inherited method even when the file imports a function of that name.

A method value on a Python module global with no static type (pool.submit(settings.conn.fetch), where conn = None is rebound by global conn; conn = Store()) is a method of the classes the global can hold: what its module assigns it, at module scope or in a function that declares it global, and what other production modules write to it (settings.conn = Store()). One class gives its own method; several give the nearest declaration they all inherit. Any binding that is not a whole constructor call (a factory, a conditional, a tuple target, for, with, an import, a star import, a namespace-dict write, an annotation the value contradicts), a write whose import could name another module, or the caller binding the name itself, leaves the value unresolved; a test that installs a double (settings.conn = MagicMock(), any test file or conftest.py) sees no production type, and a __main__ block is script code, not module state.

A pytest test's parameter is the class its fixture returns (return Cls(…), or a local assigned Cls(…) and returned), so a call on it is that class's method, and none of the project's when the class comes from outside (click's CliRunner()). The fixture is the test module's own, else the nearest conftest.py above the test's, or one a module such a conftest star-imports or lists in pytest_plugins defines. A local bound to a Django manager call that returns one instance (site = Site.objects.create(…), also get, first, last, latest, earliest, get_by_natural_key) is that model; filter and all return a QuerySet and type nothing. A keyword argument inside a call (prefix=IPNetwork(…),) does not bind the receiver it names.

Language Extensions Extraction Notes
TypeScript .ts .mts .cts Full tree-sitter Generator callables, interface members, declarator handlers, supported React hooks, and curried wrappers own body calls. Pure callable aliases forward; receiver-qualified method values emit callable-use References edges (not immediate Calls); this.field.method() and awaited Promise<T> receivers require declared, visible project types; untyped call-result/bare-call guesses are refused. Import-only modules with no export are sealed. Config aliases follow bounded inherited tsconfig/jsconfig chains; an alias makes a package import, and a supertype named through it, local only when it maps the import to an indexed file, so a catch-all "*" alias or one into node_modules does not. A module specifier written as a path (./x, ../x, @/lib/x; a side-effect import './x' too) is an Imports edge to the file it resolves to, found the way the runtime finds it; one that names no file stays unresolved, never an edge to its own import statement. A bare call never reaches a property, field or enum case. A name bound from a package outside the repository (the repository's own package name excepted) names no project symbol, whatever the reference kind, and a member call on it, or on a namespace import, takes no method guessed by name; a member call on any import never binds a method of the calling file. import type and an inline type modifier bind only the names they import.
TSX .tsx Full tree-sitter TypeScript grammar, JSX syntax; a capitalized JSX tag is a References edge from the component that renders it; a tag whose name that component binds itself, a parameter or a plain const/let/var by the tag's line, renders only a component, function or class it declares inside itself, else nothing (a destructured name and a tag above the local keep their link).
JavaScript .js .mjs .cjs .xsjs .xsjslib Full tree-sitter Generator/declarator/hook/wrapper ownership and fail-closed receiver/alias rules follow TypeScript; exports.x = function and module.exports.x = function create exported callables. Classic scripts and real CommonJS/later exports remain visible; an import-only module with no export is sealed. .xsjs / .xsjslib follow ordinary JS candidates. A JavaScript or JSX file whose leading comments carry Flow's @flow pragma (not @noflow) is read as TSX, and its file row records tsx; in a .js, .jsx, .mjs or .cjs file Flow-only syntax ({| |}, a maybe type's ?, an inexact object's ..., import typeof, opaque type) is first blanked to spaces, keeping every offset and line. A named object literal owns its function members, exported or not, declared in a function or IIFE, or hung on a path by assignment (window.App = {…}, App.utils = {…}): api::load, window.App::init, and the calls in a member are its own. A member is reached through its object (App.init(), window.App.init(), App.utils.pad(), a sibling's this.init(), a { init } = App binding), never by its bare name; a window. namespace or a classic script's top-level one is a global another script reaches. CommonJS export objects, prototypes, call arguments and minified scripts keep their shape.
JSX .jsx Full tree-sitter JavaScript grammar, JSX syntax; a Flow-typed .jsx is read as TSX (see JavaScript); as in TSX, a capitalized JSX tag is a References edge from the component that renders it; a tag whose name that component binds itself, a parameter or a plain const/let/var by the tag's line, renders only a component, function or class it declares inside itself, else nothing (a destructured name and a tag above the local keep their link).
ArkTS .ets Full tree-sitter HarmonyOS / OpenHarmony; tree-sitter-arkts grammar. @Component struct → struct symbol; calls inside its methods, such as build(), are extracted. ArkUI dynamic-dispatch bridges deferred. Plain .ts stays TypeScript
Python .py .pyw Full tree-sitter Bare class names used as values resolve to References edges in return, assignment, pair, argument, and list positions. import x as y binds y to module x; from p import x as y prefers module p.x when that file exists, otherwise it remains a member import. Receiver-qualified method values emit callable-use References edges rather than immediate calls. Missing, duplicate, or ambiguous aliases stay unresolved and never fall through to a global bare-name guess. Tuple returns are not recursively traversed, and bare names never resolve to methods. A class's identifier and dotted bases are Extends references; metaclass= and subscripted generics are not.
Go .go Full tree-sitter Lowercase declarations are package-local (directory-scoped) and cannot become cross-package name targets; local callable parameters/variables also block same-named method guesses. Exported identifiers remain cross-package candidates. A composite literal of a named type is an Instantiates reference that keeps its package qualifier (pkga.Widget{}); slice, map and array literals are not. Parameter and result types are References. An embedded struct field (*Head, pkg.Base, Box[T]) and an interface's embedded interface are Extends references. A package is a directory: an import path names a package of one of the project's modules (every go.mod above an indexed Go file; the longest module path wins, the importing file's own module breaks a tie, a module under testdata/ or a _/. directory answers only its own files), and a name written through one (store.Manager) is that package's. A receiver or parameter typed by its declaration, an unexported type included (func (s *server)), reaches its declaring package's methods, then those of the types its declaration embeds; a two-hop field chain (s.db.CreateItem()) reaches the method on the type the field's declaration names, in the package declaring it, or nothing. A type alias (type A = B, grouped or generic) is a node of the kind a declaration of its type gets — a type_alias, or a struct / interface for a literal — and a defined type (type A B) a type_alias; each references the types its right-hand side names, on the name, but not Go's predeclared types, its own type parameters, or its own name written bare. A method called on an alias is the aliased type's, through further aliases too; a defined type has none of its underlying type's methods. A type declared on its own (type Foo struct{}) takes the doc comment above type; a group's leading comment is the group's, unless the group holds one type without a comment of its own; a comment that begins after code on its line, and one that begins on the line such a comment ends, is that line's and never the next declaration's doc. A name written through an import from outside the project's modules (context.Context, http.Handler, a third-party gin.H) is never a project symbol, and an embedding or an alias written through a package none of its file's imports is named (clientv3.KV under an unaliased …/client/v3, recorded as v3) links nothing. A dotted call is written through its operand, the only part that can be a package, whatever else its line spells (p.id.String() beside zap.String(). An unaliased import is known by its path's last element and by the name goimports assumes for it (go.yaml.in/yaml/v3 → yaml, github.com/mattn/go-sqlite3 → sqlite3), unless another import holds that name or two assume it; only the import section is read, without its comments. A parameter or local named like an import (jwt, err := newTokenProviderJWT(…), func f(cmp Comparer)) is that variable where it is in scope, and the type it is declared as decides whose methods it has. A type position — a parameter or result type, a composite literal's type, an alias's or a defined type's right-hand side — names a type of the package Go reads it from, never a method or a function of its name, and a call made through an expression never lands on a type. A call on an expression (c.Flags().String(…)) is read by its shape: a bare call never reaches a method, a chained one never a package function, and a standard-library method name (String, Error, Lock, …) only a method of a type the receiver is named after; Get, Set, Add, … on w.Header() are net/http's. A call through a type assertion (srv.(KVServer).Range(…)) reaches the asserted type's method — its own, its interface's, or one embedding promotes — in the call's package, a dot-imported one, or the package its qualifier names; a type from outside the project or a type literal links nothing.
Rust .rs Full tree-sitter Methods in generic, lifetime, reference, and qualified impl blocks belong to the implementing type; only trait impls emit Implements. Exact self.method() binds only to the enclosing impl owner, including split/trait impls. Exact self.field.method() resolves through the owning struct field's declared project type. Private items are visible only to their module subtree (trait-impl methods follow trait visibility); absent members, containers, generics, external types, and ambiguity remain unresolved. A struct expression (Widget { n: 1 }) is an Instantiates reference. Parameter and return types are References. A module-level const or static, an impl's associated const included, is a file-level variable node. A bare name reaches only what is in scope: an item of its own file, one a use binds or a glob over its module brings in, or the prelude's, so Ok(x), Some(x), Result<T> and drop(v) reach a project item only through a project use that spells the name or a glob over the item's module. An enum variant is in scope through a use of it and its enum or a glob over the enum, and never names a type. A name the file imports from std, core, alloc or a crate a Cargo.toml at or above an indexed .rs file depends on is that outside item; a name written through a path (io::Result under use std::io, crate::error::Result) is looked up in that path's module, and a project crate's name (clap::Command) reads as crate:: does. Self::A names the impl's type, whatever path its line writes elsewhere. A type reference written Self::Name is only an alias the enclosing impl or trait block declares above it, and one written through a generic type parameter (V::Value, F::Output) names no project item. A CamelCase path receiver the project declares no type, trait or alias of (Vec::new(), String::from(…), not Self and not a SCREAMING_CASE static) is an outside type, and a lowercase module path (task::spawn(…), io::stdout()) never reaches a method.
Java .java Full tree-sitter Object creation is an Instantiates reference; an anonymous class new T() { ... } is a class <T$anon@line> that extends T and owns its body. Parameter, return and field types are References. A static final field is a constant; any other field stays a field. An enum constant with a body of its own (PLUS { int apply(…) { … } }) owns the members that body declares (Op::PLUS::apply). A class's Lombok-generated members are nodes of the class, marked with a lombok decorator and a docstring naming the annotation, at the field's or the class's name: getters and setters for @Getter / @Setter (on the class or a field), @Data (setters only for non-final fields) and @Value (getters), builder() for @Builder / @SuperBuilder, toString, equals and hashCode for @Data, @Value, @ToString and @EqualsAndHashCode, and the log field of the @Log* family. A static field gets no accessor, a member the class declares is never generated again, and constructors, fluent builder setters and @Accessors(fluent = true) names are not generated. A bare call (helper(), this.x(), super.x()) reaches a method of a class around it, of a supertype the heads of those classes name (read from source by name, transitively, at most 40 types; an anonymous class's supertype is what it instantiates), or of a class the file imports statically. A name bound by a single-type or static import from a package no Java or Kotlin file of the project declares (import java.lang.reflect.Field;) names nothing in the project; Scala files declare no package node, so a package only Scala files declare counts as outside. A bare type name means a type of the reference's own package or one a single-type or on-demand import names; a nested type is reached written through its owner, from inside its owner or a subtype of it (an anonymous class counts as a subtype of what it instantiates), or through an import. A constructor follows its type, and an enum constant is reached inside its enum, in a case label, through a static import or written through its enum. A receiver's declared type is read through generics and arrays (Pair<L, R> pair, String[] args) and enhanced-for variables. A call through a field or property (_innerWriter.WriteValue(…), writer.writeValue(…), sink.write(…)) is a call on the type the class around it declares the member with, or a class it inherits does (with the type arguments the subclass gives; a member typed by a type parameter takes the argument, else its bound), read from the classes' member-level lines; a member of a built-in type has no project method, and a type parameter with no bound reaches nothing.
C .c .h Full tree-sitter .h may be promoted to C++ or Objective-C, and its file row then records that language. C++ detection scans masked full source for ordinary class/struct Derived : Base forms while rejecting comments, strings, character literals, preprocessor text, bitfields, labels, and ternaries. A function-like #define is a constant; a call it shadows binds nothing when the macro is definitely visible at that line, with directives, #if/#ifdef branches, guards and #pragma once followed in include order, or when only macros bear the name. A header re-entered with nothing changed since a visit that changed nothing is not walked again, and a walk stops after a million directive events; at and after the line where it stopped, no call is taken for a macro. A file-scope initialized, pointer or array declaration is a value: a const one a constant, the rest variables; a prototype is none. A struct, union or enum defined in a declaration's type (struct Foo { … } foo;) is a node beside those values, documented by the comment above the declaration; an unnamed one takes the name of the first variable the declaration declares (static struct { … } SPT; is SPT), as a typedef's takes the typedef name, and one whose declaration declares no variable stays <anonymous>. In a function body an enum is named the same way; a struct or union defined there is not a node. A typedef naming no struct, union or enum is a type_alias whose signature is its declaration as written (typedef unsigned int UINT32). A call from a C file never reaches a method: C has no methods and cannot call a C++ one. Before parsing, kernel-style shapes the grammar cannot read are rewritten, every offset kept: a lowercase annotation between a storage class and a declaration (static notrace void f(…)), a C23 auto local, a parameterized annotation with its arguments (__free(fput), __printf(4, 0), a whole-line __bpf_md_ptr(…); field with its ;), a bare struct/union/enum macro argument (kzalloc_obj(struct T), stars too), a multi-token va_arg type, a static/extern all-caps declaration macro line, the two-argument initialized form rewritten into the declaration it expands to (static DEFINE_PER_CPU(struct T, name) = { keeps name's column), and the dots of a GNU named-variadic #define parameter; every other directive line stays as written.
C++ .cpp .cc .cxx .hpp .hxx .metal .cu .cuh Full tree-sitter Class/struct inheritance supports access modifiers, virtual, qualified bases, and templated-base stripping; pure virtual declarations are abstract owned methods; local object initialization calls one constructor only with unique namespace/arity proof: an in-class prototype is a method whose defaults reach its out-of-line definition, array elements are constructed one by one, extern, pointer and reference declarations construct nothing, and an initializer_list or parameter-pack overload declines the call. A free static function in another translation unit is not a target, while header static inline remains usable. Raw-string contents are opaque to macro blankers. Before parsing, macros the grammar cannot read are blanked to spaces (offsets kept): export and attribute macros between class/struct and the name of a class it defines, known inline-specifier macros, attribute macros between a pointer and the declared name, after a parameter list, after a declared name and opening a declaration, a lone macro line, and the C leading attribute macro only where the file #defines it as one; an access specifier spelled as a macro becomes the keyword, a preprocessor conditional inside a declaration keeps its first branch, and every directive line stays as written. A class or struct annotated with a macro none of that reads (class my_export Foo : public Base { … }) mints no function, as tree-sitter would read it. A C++ file whose tree still has errors is walked in the scopes its braces open: each declaration takes its namespaces, and at declaration level its enclosing classes, from the source's braces (comments, literals, raw strings, digit separators and preprocessor lines skipped, each #if branch read from the braces open at the #if), a class-like node ends at its body's }, and a class the tree glued into a declaration's type is still a class; a file whose braces do not balance, and every file that parses cleanly, keeps the tree's scopes. A class, struct, union or enum defined in a declaration's type is a node, named and documented as in C, except in a file whose tree has errors and whose braces do not balance; a namespace-scope variable is not a node. A typedef or using alias is a type_alias whose signature is its declaration as written, an alias template's with its template <…> head. A receiver declared through an alias calls the type the alias names, the name looked up as C++ looks it up (aliases declared earlier in the calling function, the caller's class and its bases, the enclosing classes and namespaces), a qualified name a segment at a time; an alias of a template parameter, a typename or a decltype names no type, a . call or a pointer's call is not guessed by the receiver's name when the aliased type lacks the method, and a local object of an alias type constructs the aliased type. A receiver's declared type drops its template arguments, nested ones with them (autovector<std::pair<int, Meta*>> is autovector), and the end of a declaration begun on an earlier line names no type; comments are never read as declarations. A . call, or a raw pointer's ->, on a receiver the calling function or its class declares as a qualified lowercase type the project does not declare (std::string, absl::flat_hash_set<int>; a std:: name only when the project declares none in std) is that type's own member, never a project method guessed by name; a _t name, a declaration the call contradicts (. on a pointer), and one an unreadable re-declaration hides (auto, a range-for variable, a structured binding) do not count. A base class is the class C++ name lookup finds from where the deriving class is declared: its enclosing scopes outwards (a class scope with its bases, a qualified name a segment at a time, an alias followed to the class it names, ::Base from the global scope only), then the namespace aliases, using-declarations and using-directives the file writes before the class; a class a source file defines is only that file's, a template parameter names no class, and when nothing finds it the only class of that name the file can see is taken, several being no edge. A qualified call keeps its scope: ns::f() calls ns::f, Base::m() calls Base::m, and a call from the global scope, ::f(), is recorded as ::f. A name from the global scope is the declaration of exactly that qualified name, never a namesake nested in another namespace or class nor one inside a namespace a macro opens; ::std:: names and ::memset count as the built-ins they name. A qualified name no strategy resolves reaches a declaration in a namespace the project opens with a macro (fmt::format inside FMT_BEGIN_NAMESPACE, #define FMT_BEGIN_NAMESPACE namespace fmt { inline namespace v12 {; single-parameter openers such as PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE), through identifier macro aliases, inline namespaces transparent), or through a namespace alias the project writes (py::print under namespace py = pybind11;); among an overload set the call's arguments pick the one they fit (count, string literals against string parameters, narrow against wide). A base class reads the project's namespace aliases and macro-opened namespaces too. When an alias or a macro is defined in several files, the first by path wins.
C# .cs Full tree-sitter Every entry of a base_list is an Extends reference (type arguments and global:: dropped); the resolver turns one that binds an interface into Implements. new T(...) is an Instantiates reference; a target-typed new() names no type, except as a field's or property's initializer, where it instantiates the declared type (List<Foo> _items = new(); instantiates List). A property's accessor bodies, expression body and = initializer, and each field declarator's initializer, are walked as that member: their calls, instantiations, static reads, lambdas and function values are its; attributes are not walked. Type positions (property, return, field and parameter types) are References; a qualified name by its last segment. A const or static readonly field is a constant; any other field stays a field. A block namespace X { … } qualifies only its body: a file's second block namespace, or one written inside another (Outer.Inner), is a namespace node of its own, and a using outside every block is the file's. A type position (Type name, List<int>, Type?, Type[], a generic argument, new T) names only a type, never a property, method (a constructor), enum case, field or constant of that name. A bare type name is one its file can see: in its namespace or one around it, or imported by the file's using, by the global using of any file of its project (the project is the outermost directory holding a .csproj; outside one, any file's), by the <Using Include> of a .csproj or .props file above it, by the SDK's implicit usings under <ImplicitUsings>enable</ImplicitUsings>, or by an alias naming it; a nested type is reached by bare name only from inside its owner or a type deriving from it, through any partial part. A using X; names the project's namespace X or stays unresolved, never another file's using. A bare name whose line shows no receiver (TestContext.Current, Helper()) is a member only of the types around it, of the bases their heads list (every partial declaration, by name, up to 40 types), or of a type a static using brings in: the file's using static, any file's global using static, or a <Using … Static="true"/> of a .csproj or .props file above it; outside every type it is not judged. A call through a field or property (_innerWriter.WriteValue(…), writer.writeValue(…), sink.write(…)) is a call on the type the class around it declares the member with, or a class it inherits does (with the type arguments the subclass gives; a member typed by a type parameter takes the argument, else its bound), read from the classes' member-level lines; a member of a built-in type has no project method, and a type parameter with no bound reaches nothing. Without an access modifier an interface's member is public, a type in a namespace or the file internal, any other member private. A using alias names its type as a receiver (Assert.AreEqual under using Assert = …XUnitAssert;), a keyword type (string.Equals) is no project type, and a standard .NET method name (table.Columns.Add, reader.Value.ToString()) on an untyped receiver reaches a project method only when the receiver names its owner. A local, parameter or member of a keyword type (this string format) reaches only the project's extension method for it (KEEP-RUST), never a standard .NET name.
PHP .php .module .install .theme .inc Full tree-sitter new T(...) is an Instantiates reference named by the class name's last segment. Parameter, return and property type hints are References (primitives and self/static/parent are not); upstream reads no property type. extends and implements (and an interface's extends list) are supertypes by each name's last \ segment. A file-scope const is a constant, as a class const is; a class property is a field, one per $name. A call through $this-> (or ?->), self::, static:: or parent:: reaches only a method of the class it is written in, of a class its extends chain names or a trait any of them uses (each name read through its file's namespace and use imports, at most 80 types), or, except through parent::, of a subclass; inside a trait it is not judged, and past an ancestor outside the repository a repository trait's method still counts. A call through a receiver nothing types takes the one project method of its name only when the receiver is named after its class ($avatars->assignToUser() → UserAvatars) or after a class whose ancestry has it ($page->save() → Entity, Page extending it). A call written without a receiver (redirect(…), 'size' => filesize($zip), or \helper() from the global namespace) is a function call and reaches only a function. An unqualified class name is the class of the file's namespace or the one a use imports, never another namespace's; a class name written with a namespace (new Field\FirstName() through use App\Fields as Field;, extends Sub\Base, \Ns\X::make()) is read the way PHP reads it (a leading \ is absolute, an imported first segment is replaced by what it imports, anything else is relative to the current namespace) and resolves to that one project class, or stays unresolved when the project has none, never to another node of its last segment's name; such a name, which the reference records by its last segment, is read back from the source at the reference's position.
Ruby .rb .rake Full tree-sitter A call with a receiver is named receiver.method (self/super leave the bare name; a constant receiver is also a References; Foo.new is an Instantiates). A constant assignment at the top level or in a class or module (MAX = 3) is a variable node. A bare call written inside a class is a call on self: it reaches only a method of the class, of its superclasses, or of a module any of them includes, extends or prepends, read from the declarations' source by qualified name (the superclass from a declaration's first line, the mixins from every line of its body; each constant resolved against the lexical nesting to a class or module the project declares, else taken as written; at most 60 classes and modules, declarations of one name in path order); a bare call in a module body, at the top of a file or in a script is not judged. A call through a receiver other than self / self.class takes the one method of its name only when the receiver is named after the method's owner (web_push_request.legacy_encrypt reaches WebPushRequest, user_name.name no lone Person::name).
Swift .swift Full tree-sitter An extension is a class node, but a reference to a type never lands on one: it moves to the type's declaration (the one in the reference's file, else the nearest by directory, none on a tie) or, when the project only extends the type (SwiftUI's View), stays unresolved; a conformance to such a type is read from the conformer's head, so the extensions' members still reach it. A nested type is reached bare only from inside its parent, the type itself or a type whose head inherits from the parent, and a qualified name (Build.Id, read back from the line; generic arguments dropped) only on its path or under a module qualifier. A call through a type path (API.PackageController.GetRoute.query(on:), two or more capitalized segments) keeps the path and reaches only the member on the type it names (an extension's written path counts, a path shortened inside its namespace or under a module qualifier fits; a TCA @Reducer enum's Path.State.case), else nothing. A bare, self. or super. call reaches only a member of the types around it and what they inherit or conform to (their heads, UIKit / AppKit ancestry, the standard collection protocols), the nearest first (super. the inherited only); down a longer chain a standard-library name (removeAll, contains, …) needs a receiver naming the owner, an owner the project only extends, or a first argument label only the project's method takes; a member of what a constructor before the call builds, or of a property's declared type (var cache: ImageCache!, self.x, unless a function's own let shadows it), is that type's; a playground's or test target's globals are no other file's call target. Parameter and return types are References; a protocol composition continued on an & line inside a type body is joined before parsing. A stored property is a node: at the top level a let is a constant and a var a variable, in a type a static let/static var likewise and an instance one a field; a computed property is a property.
Kotlin .kt .kts Full tree-sitter Top-level functions, class methods, and extension functions include raw parameter-list signatures with optional declared return types; constructors, accessors, lambdas, and anonymous functions are not synthesized as callable nodes. Private definitions never become cross-file targets. Parameter and return types are References (an extension receiver is not). A property at the top level or in an object or companion object is a constant (val) or variable (var); a class property is a field; a local is none. A property's declared type is a References from it (an extension property's receiver is not); upstream reads no property type. An enum entry with a body of its own (NewBuffer { override fun pipe() … }) owns the methods and fields that body declares (Factory::NewBuffer::pipe). An infix call (id eq 3, single { … } bind I::class) is a call of the infix function, id.eq when the left operand is a plain name and bind otherwise; a literal left operand records none. A when guard (is T if cond ->) and a multi-dollar string ($$"…"), syntax newer than the grammar, are rewritten to same-length equivalents before the parse, so the class around them survives. Imports are read without a ;, and import a.b.C as D binds D to a.b.C (D.create() reaches C's member). A top-level declaration (a function, an extension function indexed under its receiver type, a property) is reached from its own package, an import of it, a star import of its package, or a package every Kotlin file imports. A bare call reaches a member of a type around it (a class, an anonymous object : Base(), an extension function's receiver, a lambda's receiver read from the last parameter type of the function it is passed to, through up to two typealiases), of what those inherit (class heads read from source by name, and Android's platform ancestry), of a type some project function type takes as a receiver or an outside type the project writes extensions on, or of an object the file imports; the code around the call wins over the project-wide receivers. require(…) / check(…) / assert(…) with a condition is Kotlin's precondition. A standard-library name on an untyped chain link (.apply { }, .map { }) or in an infix call on an expression (alias(x) apply false) reaches only a member its receiver is named after. A project's bitwise extension on a number type (infix fun Byte.and(mask: Int)) is never a call's target, by name, typed or guessed: x and 0xff is the standard library's. A Gradle script's bare call never reaches a member of a class the project declares. A name bound by an import from outside the project names nothing in it, unless the file declares it itself. A call through a field or property (_innerWriter.WriteValue(…), writer.writeValue(…), sink.write(…)) is a call on the type the class around it declares the member with, or a class it inherits does (with the type arguments the subclass gives; a member typed by a type parameter takes the argument, else its bound), read from the classes' member-level lines; a member of a built-in type has no project method, and a type parameter with no bound reaches nothing.
Dart .dart Full tree-sitter Dart 3 extension type declarations are class-like owners; getters and methods qualify under the extension type rather than leaking to file scope. A function's or method's signature types, the type an extension is on, a field's type (its class's), a top-level variable's (the file's) and every type a body or an initializer names (a local's, a generic argument, a cast, a type test, a catch clause) are References; a new/const constructor call's class, an import prefix and a lowercase name are not, and a class whose field holds its own type gets no self-edge (#2360). A class extends its base and implements each with mixin and implements interface (an enum's and a mixin's too; a mixin's on constraint is none); a mixin application class A = B with M; is named A. A member keeps the dartdoc written above its annotations, a member with no body included, and its annotations are Decorates. An annotation is a constant expression and links what it is written as, read from its @ (#2390): a constant or, called, a class (no extension) its library can see, through the import prefix it is written with (@riverpod, @Riverpod(keepAlive: true), @meta.immutable); a static constant, an enum value or, called, a named constructor of the type it is written through (@Riverpod.scoped, @Retry.always, @Riverpod.forever()); on a member, a static constant or enum value of the type around it, written bare; never a method, a getter or a function, so riverpod's @riverpod is riverpod_annotation's const riverpod = Riverpod();, not an extension getter of that name. KEEP-RUST: this grammar writes a dotted annotation's name as a qualified node, recorded by its last name as upstream records it. A generic call ref.read<Repo>() calls ref.read. A top-level const/final or a class static const/static final is a constant, whose initializer is code it runs: its calls, instantiations, reads, types and function values are the constant's, and a field's or a var's initializer is its class's or the file's (#2367). A named const constructor and a redirecting factory (const BlocProvider.value(…), const factory Foo.bar() = _Bar;) are methods named by the constructor that return their class, with a parameters-only signature (an unnamed one is no node), and a redirecting factory references the class it constructs, an UpperCamel name of its target (#2380). A call through a type's name (BlocProvider.value(…), Provider.autoDispose(…), BlocProvider<T>.value(…)) reaches that type's own constructor, static member or static constant (#2367), the call's own file's first, then the nearest, or nothing in the project; a named constructor is never reached by a bare name, by a function value (#2380) or through a value of its type: a typed parameter or local, or a chain's last link, calls an instance member or an extension's of that name, or nothing (KEEP-RUST: upstream's member lookup takes the constructor for an instance member), and by an annotation only as one that calls it through its type (#2390). KEEP-RUST: the annotations of a member with a body sit inside its node in this grammar, so whether it is a constructor, a getter or a setter is read from its signature past them. A member read through a plain name (x.area, s?.label, Config.instance) calls the getter its receiver's type reaches, declared on it or on a type it extends, mixes in or implements, else added by an extension on one of those (an unnamed one only in its own library), or links nothing: a field read, an outside type's getter, an untyped receiver; the receiver is a type named directly, a parameter or local the function declares, or a field of the class around it or of one it extends (#2360). A call on a typed receiver whose type declares no such member reaches the one an extension on it adds, an enum's too (#2360). A later link of a call chain (r.load().save(), context.read<Repo>().fetch(), BlocProvider.of<CounterCubit>(context).increment()) calls the member of what the chain before it evaluates to (#2376, #2383): its head is what the local or parameter it names is declared as or initialized with, else a field or getter of the class around it, or a constant's declared type or initializer; what the function or method it calls is declared to return; the class a constructor call or a cast names; the type this or super is written in. Each link is its member's declared return type, read or called: a member returning its own type parameter returns the type argument the call gives, one returning its class's type parameter nothing; a lookup nothing in the project declares (read<T>(), watch<T>(), get<T>(), of<T>(), and the like) hands back its one type argument, and toString() a String. A static member is looked up on the type the head names, an instance member through the type's lineage, else on an extension on one of its types, an extension on any type ranking last and applying to a project type only; an initializer is read at most three deep, and a part nothing types links nothing. KEEP-RUST: the extractor records a later link where the chain starts, by its member alone after a call, a parenthesis, an index, !, this or super, or past the last segment of a member chain (s.m, #1496); the link is read back after that place in the same encoding. A function's or method's return type is the last name of the type its signature writes (Future<Todo> is Future, p.Todo is Todo), a method is static as its signature says, x?.m() is recorded as x.m() is, and self and cls are plain names. const Foo.named() and new Foo() call the class and constructor they name (KEEP-RUST: this grammar writes them in the call's type, a named constructor as a second type_identifier, told from an import prefix by its case). The grammar ends an arrow function in front of the call or member access its body ends with ((ref) => createDio() reads as a call of (ref) => createDio); that call or access is the tail's, written where the tail starts (KEEP-RUST; upstream #2375 reads a receiver past the arrow the same way). A call written without a receiver (not a chain's later link, whose receiver the extractor drops) reaches a member of the class it is written in, of what that class extends, mixes in or implements, or of an extension on one of those types, the nearest one winning; outside any class, no member (upstream #2145). A function declared in a body is no member (KEEP-RUST: it is qualified by the function around it, outer::make), and a call in there reaches it. A call written without a receiver, or a function passed by name, whose name a parameter or local around it binds (a function's, method's, closure's, catch clause's or constructor's parameter, its initializer list included; a for loop's variable; a local a block declares, from there to the block's end: final x = …, Foo? x;, a destructuring, a pattern's final x) calls that parameter or local and links nothing, a local function declared nearer keeping its link (#2379). A String, List, Iterable, Map or Set method name on an untyped receiver reaches a project method only through a receiver named after its owner, an extension's owner being the type it is on (#2207). An unnamed extension on X, named <anonymous>, applies only in its own library, the file and its parts (#2237, #2386). A call, constructor call or type written without a receiver reaches only a top-level declaration its library can see: its own (the file and its parts; one of them shadows an imported namesake) or one a library it imports without a prefix exports, through export chains, show, hide and every alternative of a conditional import; a type written through an import prefix (p.Report) is what that import exports, and a bare call never reaches another type's enum constant or static constant (#2386). A call or constructor call through an import prefix (fmt.describe(…), http.get(…), kit.Widget()) reaches the one top-level declaration the libraries imported with that prefix export, export chains, show and hide followed and a part taking its library's prefixes, or nothing in the project: a package outside it, a name not shown or hidden, a deferred library's loadLibrary, two declarations of the name; never a member found by the receiver's name (#2389). A parameter, a local or a member of the type around it named like the prefix hides it, and a null-aware p?.m() is written on no prefix. A chain headed by an import prefix (kit.Box.create(), fmt.make().save(), fmt.config.save()) starts at what the prefix brings in: the type of that name, a function's declared return type, a constant's type. KEEP-RUST: a prefixed call is recorded at the prefix, where its chain starts, and a call through a type written behind a prefix (p.Box.create(), p.Box<int>.named()) is that chain's link. A file with no pubspec.yaml at or above it, or whose part of names no library the index holds, sees every file. An import, export or part links the one indexed file its URI names: package:<name>/<path> is <root>/lib/<path> of the package whose pubspec.yaml says name: <name> (same-named packages told apart by the importer's own package, then the one enclosing it, then its path: dependency in pubspec_overrides.yaml or pubspec.yaml), any other URI a path from the directive's file; a dart: library, a package outside the project, a path out of it or a file the index lacks links nothing (#2388). A part 'x.dart'; is an import node whose reference links the library to the part's file; part of records nothing (#2391). Only an indexed file's directives are read (KEEP-RUST).
Scala .scala .sc Full tree-sitter Scala grammar 0.26.2; multiple constructor parameter lists and every extends/with parent are retained. Companion object nodes are Modules, while same-named class/trait types win inheritance resolution. new T(...) and new T { ... } are Instantiates references. Parameter types of every parameter list, the return type and type-parameter bounds are References. An object's val is a constant and its var a variable, as at the top level; a class, trait, enum or given value is a field. A bare call reaches a member only where it is in reach: a later link of a chain (List(1).map(…), and a.b.c(), recorded as b.c) only a member of what its receiver is named after, a type or object only through what holds it (its outer type, or for a top-level one its file's package: app.client.Response(200)), and a package object's function only when an extension line marks it; a name the enclosing definition binds only that local; otherwise a member of the types around the call, of an anonymous class's bases, of what those extend (heads read from source by name, at most 60 types), of an imported object or the package object of the file's package and what they extend, or anything when the file imports from a value (import builder._). A val written in a block of a class body (test("a") { val f = … }) is that block's, and a val or constant declared in any language's function body that body's. A member of package object X is in scope only in its package and those under it, or through an import naming it. A symbolic name in a type (F ~> G, a kind-projector *) is the one type or alias of that name, or nothing.
Lua .lua Full tree-sitter local f = function emits one Function (not a duplicate Variable); M.f = function, string-key table assignments, and nested table constructor functions emit qualified Methods. Body calls belong to the synthesized callable; f(), M.f(), and M:f() resolve, while computed keys stay dynamic. A require is an Imports reference to the module file it names, or unresolved; it never binds the local it is assigned to. A local declaration is invisible outside its file, except to a bare call through a require alias that names it (upstream #2143). A bare call never exactly matches a table's method (function M.x, function M:x), and one named like a Lua global function (ipairs, error, busted's it, …) is the standard library's unless its file defines a function of that name (#2202). A bare call through a local alias follows it (#2188): require "m" and its members reach the module's function of the name, or what the module hands out under it (return { check = check_phase }, M.x = y), its own alias or its global, up to three modules deep; an alias of a global function (local type = type) or of an outside module's member is no edge; a library table's member (ngx.sleep) is the project's own patch of it when there is exactly one, and a host table's (kong.response.exit) the one Lua method of a table named after its holder.
Luau .luau Full tree-sitter Roblox Luau dialect
Objective-C .m .mm Full tree-sitter A message send is a call named by its full selector ([c a:x b:y] → c.a:b:, a self/super receiver leaves the bare selector); @interface X : S <P> extends S and implements P, and a class conforming to a protocol of its own name implements the protocol, its own header's first. A name in C call syntax (completionBlock()) calls a function, a block or a function pointer, never a method, property or field. A message to self reaches a member of the sender's hierarchy, read from the @interface heads it passes and, for a category on a system class, from UIKit and AppKit ancestry; one to super goes past the sender's own class. An untyped receiver reaches the one method of its name only when it is named after the method's owner, or names a class or ends in a property declared with a type that has the owner in its hierarchy; a local's or a parameter's declaration (FMResultSet *rs) types its messages.
R .r Full tree-sitter A plain call (range(x), no $, @ or :: before it) is a function call, never an object's method (upstream #2177). A call named like a base function data often shadows (c, t, df, range, …) never reaches a variable or constant of that name unless the binding's line holds a function (<- function, = \(x); #2239). A variable or constant a file under tests/ or test/ assigns stays in that file, which testthat runs in an environment of its own, except in the helper-*.R and setup-*.R files every test sources (#2144).
Solidity .sol Full tree-sitter tree-sitter-solidity grammar; contracts/libraries/interfaces, structs, enums, modifiers, events, errors; is-inheritance → Extends (resolver promotes to Implements for interfaces); emit/revert/modifier-guard call edges A call written without a receiver reaches a function of the contract, interface or library around it or of one it inherits, its is clause read from each declaration's head with constructor arguments dropped and a dotted base by its last name, every declaration of a name counted, or a free function; never another contract's override, and outside any contract no member (upstream #2178): OpenZeppelin's _msgSender() in a Context-derived Governor is Context's, not ERC2771Context's. KEEP-RUST: a // comment in a head drops only the rest of its own line.
Nix .nix Full tree-sitter tree-sitter-nix grammar; let/attrset bindings, curried lambdas, inherit; import/callPackage/imports-list file imports; module-system option synthesizer deferred
Terraform .tf .tfvars .tofu Full tree-sitter tree-sitter-hcl grammar (HCL/Terraform/OpenTofu); resource/data→class, module→module, variable/output→variable, provider→namespace, locals→constant; qualified names + var/local/module/data/resource traversal refs; module-boundary framework resolver deferred
Erlang .erl .hrl Full tree-sitter Functions keep a bare display name and qualify as module::function/arity; clauses, exports, specs, local/remote calls, fun, static gen_server targets, and spawn/apply-style MFA lists are arity-aware. Binary-literal commas do not inflate arity. Missing or ambiguous arities remain unresolved; dynamic behaviour/resource wiring is still deferred. A call written without its module reaches the function of its own module, else another module's only through -import(Mod, [f/N]) (nothing when Mod declares no f/N), else one a .hrl declares; with neither, nothing the project declares (upstream #2178): cowboy's -import(req_SUITE, [do_get/3]) reaches req_SUITE's do_get/3, not compress_SUITE's. A call written in a .hrl keeps reaching any module's function of its name and arity.
GDScript .gd Full tree-sitter Godot scripting; extracts functions, classes, enums, variables, signals, extends, preload. Dynamic dispatch edges (connect/get_node/$/%/call/group) added by the Godot resolver — see godot.md
Pascal .pas .dpr .dpk .lpr .dfm .fmx Full tree-sitter / custom .dfm/.fmx form files use a custom path A unit or class const is a constant; a routine's own const section is local to it. An .inc include with no PHP open tag and a Pascal-only line shape is Pascal (grammar manifest).
CFML .cfc .cfm .cfs Full tree-sitter ColdFusion; dual-grammar tree-sitter-cfml (cfscript + cfml tag), dialect chosen by first-token sniff. Bare-script component→class (name from file) + extends/implements→refs (each at its attribute value); tag <cfcomponent>→class (name attr, spanning to its close tag) + <cffunction>→method + extends/implements→refs; <cfinterface>→interface (its functions are methods, an extends per comma-separated parent). The tag walk follows scope: a <cffunction> at component, interface or template scope is extracted at any depth (inside <cfprocessingdirective> or <cfsilent>), never through an ERROR node and never inside another function; the expressions of <cfset>, <cfif>/<cfelseif>, <cfreturn>, a #hash# anywhere in markup and a <cfloop condition> are the code of the function, component or file around them. A <cfscript> body is read with the cfscript grammar and a <cfquery> body's #hash# expressions with the cfquery grammar, both in place over the file's bytes (positions and node ids are the file's): at component scope a script's top-level functions are the component's methods, scoped Component::name, and a var at a script's top level is a local of the scope around it. A call through variables./this./local./arguments. keeps that receiver (variables.svc.save). A receiver is typed by its declaration: new X() (a dotted path by its last segment), createObject("component", "a.X") or createObject("a.X"), a typed parameter (required X svc), a <cfargument type>, or a property with type/inject; a variables./this. field is looked for in the whole file, the nearest declaration above the call first. When components of the type's name in several files declare the method, a dotted path picks the one whose directories match the most segments right to left (any case) and a bare name the one in the call's own directory; the call is no edge when neither tells them apart, two tie, or the method is a supertype's whose name several components share. An extends/implements written as a component path resolves on its own: a relative path (../base) against the referencing file, a dotted one (coldbox.system.web.Controller) to the same-named component whose directories match the most segments right to left (any case), none when no directory matches or two tie. A bare call in a component (.cfc) whose line shows no receiver reaches only a method of the component's own file or of a component file its extends chain names: each extends= read from the text before the component's first function (a text with none, or opening with one, is read whole), the written path matched against the .cfc files declaring a class of its last segment's name, any case (a bare name: the same directory's first; a dotted one: the longest matching path suffix), at most 30 files; a bare call in a .cfm template is not judged. A call through a receiver other than this/super takes the one method of its name only when the receiver is named after its component (userService.list), its whole name spelling it (cbsecurity -> CBSecurity) or a word of it with a trailing number dropped (executor1 -> Executor); server.keyExists is the struct member function

Tier 2 — Embedded / template extraction (6 languages)

These languages wrap or embed code in another language (or use a custom extractor). The host file gets its own node; inner code is delegated to the appropriate Tier-1 grammar and merged back into the parent result.

Language Extensions Extraction Notes
Vue .vue Embedded (delegates to TS/JS) <script> and <script setup> blocks delegated; lang="ts" selects TypeScript grammar Each <script> block is extracted by the TypeScript or JavaScript extractor at its file lines.
Svelte .svelte Embedded (delegates to TS/JS) Script blocks extracted and delegated; component node created for the file
Astro .astro Embedded Detected via embedded pre-pass only (not in the built-in extension map)
Razor .razor .cshtml Embedded (custom) Detected via embedded pre-pass only; C# snippets extracted from .cshtml/.razor files
Liquid .liquid, templates/*.json, sections/*.json Custom regex extractor Shopify templates; .json only inside a Shopify theme, whose section and snippet links stay in it (Liquid)
XML (MyBatis) .xml Custom (MyBatis mapper) Extracts SQL-mapper nodes from MyBatis XML files; generic XML gets a file node only

Tier 3 — File/framework-level IDs

YAML, Twig, and Properties complete the 38 public code/template language IDs. The three Godot rows are additional internal resource-format IDs. All six receive file nodes at the language-extraction layer; the Godot resolver adds project/resource semantics.

Language Extensions Extraction Notes
YAML .yml .yaml File node only No symbol extraction
Twig .twig File node only No symbol extraction
Properties .properties File node only No symbol extraction
GodotScene .tscn File node only Semantic graph (node tree, signals, scripts, groups, sub-scenes) built by the Godot framework resolver — see godot.md
GodotResource .tres File node only Resource→script/resource references built by the Godot framework resolver — see godot.md
GodotProject project.godot File node only Autoload singletons, input actions, plugins parsed by the Godot framework resolver — see godot.md

The three Godot file types carry file nodes only at the language-extraction level. All Godot-specific symbols, edges, and honesty signals are emitted by the GodotResolver (a FrameworkResolver that activates when project.godot is present). For the full extraction inventory and static-vs-runtime boundary, see godot.md.


Adding custom extension mappings

Non-standard extensions can be mapped to any supported language via .codegraph/codegraph.json:

{
  "extensions": {
    ".x": "lua",
    ".blade": "php",
  },
}

Keys are dot-stripped and lowercased before matching. Unknown language names are silently skipped. Exactly one file is read — the resolved project's own codegraph.json under the selected index root; there is no directory-tree walk or cross-project inheritance.


See also

  • godot.md — full Godot static-analysis reference: what gets extracted from .tscn/.tres/project.godot/.gd, the static-vs-runtime boundary, honesty signals, and the optional resource DSL hook.
  • docs/grammar-manifest.md — engineering ABI manifest: per-language grammar crate, tier policy, and ABI smoke status (for contributors and grammar maintainers).
  • docs/embedded-extraction.md — detailed description of the embedded extraction pipeline: region detection, line-number remapping, and node merging.