| description | Knowledge graph schema with node types, relationships, and language-specific AST mappings. |
|---|
The knowledge graph uses a unified schema across all supported languages.
A label marked opt-in belongs to a capture group that a default index leaves out, so it appears only once that group is enabled.
| Label | Properties |
|---|---|
| Project | {name: string, root_path: string?} |
| Package | {qualified_name: string, name: string, path: string, absolute_path: string} |
| Folder | {path: string, name: string, absolute_path: string} |
| File | {path: string, name: string, extension: string?, absolute_path: string} |
| Module | {qualified_name: string, name: string, path: string, absolute_path: string, docstring: string?, flow_covered: boolean?, generated: boolean?, generator: string?, start_line: int?, end_line: int?, decorators: list[string]?, rust_cfg_test_mods: list[string]?, rust_ungated_mods: list[string]?, front_matter: list[string]?, unresolved_specifiers: list[string]?, unresolved_references: list[string]?} |
| Class | {qualified_name: string, name: string, modifiers: list[string], decorators: list[string], path: string, absolute_path: string, start_col: int?, start_line: int?, end_line: int?, docstring: string?, is_exported: boolean?, anchor_hash: string?, namespace: string?} |
| Function | {qualified_name: string, name: string, modifiers: list[string], decorators: list[string], path: string, absolute_path: string, start_col: int?, name_start_line: int?, name_start_col: int?, start_line: int?, end_line: int?, docstring: string?, is_exported: boolean?, is_macro: boolean?, is_object_member: boolean?, is_body_scoped_name: boolean?, positional_params: list[string]?, return_type: string?, param_types: list[string]?, ast_fingerprint: string?, ast_fingerprint_nodes: int?, ast_branch_fingerprints: list[string]?, anchor_hash: string?} |
| Method | {qualified_name: string, name: string, modifiers: list[string], decorators: list[string], path: string, absolute_path: string, start_col: int?, name_start_line: int?, name_start_col: int?, start_line: int?, end_line: int?, docstring: string?, is_exported: boolean?, is_property: boolean?, overrides_external: boolean?, positional_params: list[string]?, return_type: string?, param_types: list[string]?, ast_fingerprint: string?, ast_fingerprint_nodes: int?, ast_branch_fingerprints: list[string]?, anchor_hash: string?} |
| Interface | {qualified_name: string, name: string, path: string, absolute_path: string, modifiers: list[string]?, decorators: list[string]?, start_col: int?, start_line: int?, end_line: int?, docstring: string?, is_exported: boolean?, anchor_hash: string?, namespace: string?} |
| Enum | {qualified_name: string, name: string, path: string, absolute_path: string, modifiers: list[string]?, decorators: list[string]?, start_col: int?, start_line: int?, end_line: int?, docstring: string?, is_exported: boolean?, anchor_hash: string?, namespace: string?} |
| Type | {qualified_name: string, name: string, path: string?, absolute_path: string?, modifiers: list[string]?, decorators: list[string]?, start_col: int?, start_line: int?, end_line: int?, docstring: string?, is_exported: boolean?, anchor_hash: string?} |
| Union | {qualified_name: string, name: string, path: string?, absolute_path: string?, modifiers: list[string]?, decorators: list[string]?, start_col: int?, start_line: int?, end_line: int?, docstring: string?, is_exported: boolean?, anchor_hash: string?} |
| ModuleInterface | {qualified_name: string, name: string, path: string, absolute_path: string, module_type: string} |
| ModuleImplementation | {qualified_name: string, name: string, path: string, absolute_path: string, implements_module: string, module_type: string} |
| ExternalPackage | {name: string} |
| ExternalModule | {qualified_name: string, name: string, path: string} |
Resource (opt-in: io) |
{qualified_name: string, name: string, kind: string} |
| Section | {qualified_name: string, name: string, heading_level: int, start_line: int, end_line: int, path: string, absolute_path: string} |
Pattern (opt-in: findings) |
{qualified_name: string, name: string, message: string, start_line: int, end_line: int, path: string, snippet: string?} |
CodeSmell (opt-in: findings) |
{qualified_name: string, name: string, message: string, start_line: int, end_line: int, path: string, snippet: string?} |
SecurityIssue (opt-in: findings) |
{qualified_name: string, name: string, message: string, start_line: int, end_line: int, path: string, snippet: string?} |
Gloss (opt-in: glosses) |
{qualified_name: string, kind: string, status: string, body: string, created_by: string, created_at: string, commit_sha: string?, target_qn: string, target_hash: string?, anchor_quote: string?, anchor_prefix: string?, anchor_suffix: string?, anchor_state: string, moved_from: string?, candidate_qns: list[string]?, project: string?, write_id: string?, mention_qns: list[string]?} |
Parameter (opt-in: parameters) |
{qualified_name: string, name: string, index: int, path: string, absolute_path: string, start_line: int?, start_col: int?, type_name: string?, is_variadic: boolean?, has_default: boolean?} |
Field (opt-in: fields) |
{qualified_name: string, name: string, path: string, absolute_path: string, start_line: int?, start_col: int?, type_name: string?, modifiers: list[string]?, is_static: boolean?, docstring: string?} |
EnumVariant (opt-in: enum_variants) |
{qualified_name: string, name: string, path: string, absolute_path: string, start_line: int?, start_col: int?, index: int, value: string?, docstring: string?} |
Constant (opt-in: constants) |
{qualified_name: string, name: string, path: string, absolute_path: string, start_line: int?, start_col: int?, type_name: string?, value: string?} |
ExternalModule stands for an imported module that lives outside the repository (a third-party or stdlib target of IMPORTS, or a positively-external base class target of INHERITS/IMPLEMENTS).
Resource is a synthetic node standing for an external I/O target (a file, environment variable, network endpoint, database, standard stream, socket). Its qualified_name has the form resource::<KIND>::<identity>, where identity is a static string literal when one is available and <dynamic> otherwise, and kind is one of FILE, NETWORK, DATABASE, STDIN, STDOUT, STDERR, ENV, SOCKET. Resource nodes are captured only when the io capture group is enabled (see below).
Section is a heading in a document (Markdown), holding the heading's text, its level (1-6) and its line span. Sections nest through CONTAINS_SECTION by heading level, so a subheading hangs off the heading above it rather than off the file; a top-level heading hangs off the document's Module. The span covers the heading and the prose beneath it, ending at the line before the next heading at the same or a shallower level (or at end of file), so a parent section's span contains its subsections.
Pattern, CodeSmell, and SecurityIssue are ast-grep finding nodes, captured only when the findings capture group is enabled.
Every relationship type belongs to exactly one capture group. A relationship marked opt-in belongs to a group that a default index leaves out, so it appears only once that group is enabled.
| Source | Relationship | Target |
|---|---|---|
| Project, Package, Folder | CONTAINS_PACKAGE | Package |
| Project, Package, Folder | CONTAINS_FOLDER | Folder |
| Project, Package, Folder | CONTAINS_FILE | File |
| Project, Package, Folder | CONTAINS_MODULE | Module |
| Module, Section | CONTAINS_SECTION | Section |
| Module, Function, Method, Class | DEFINES | Class, Function, Method, Enum, Interface, Type, Union, Module |
| Class, Interface, Enum, Type, Union | DEFINES_METHOD | Method |
| Module | IMPORTS | Module, ExternalModule |
| Module | EXPORTS | Class, Function |
| Module | EXPORTS_MODULE | ModuleInterface |
| Module | IMPLEMENTS_MODULE | ModuleImplementation |
| Class, Interface, Function | INHERITS | Class, Interface, Function, ExternalModule |
| Class, Enum | IMPLEMENTS | Interface, Class, Enum, ExternalModule |
| Method, Function | OVERRIDES | Method |
| Function, Method | RETURNS | Class, Interface, Enum, Type, Union |
| Function, Method | ACCEPTS | Class, Interface, Enum, Type, Union |
| ModuleImplementation | IMPLEMENTS | ModuleInterface |
| Project | DEPENDS_ON_EXTERNAL | ExternalPackage |
| Module | LINKS_TO | File |
| Module, Function, Method | CALLS | Function, Method, Enum, Type |
| Module, Function, Method | REFERENCES | Function, Method, Class |
| Module, Function, Method | INSTANTIATES | Class |
| Module, Function, Method | READS_FROM (opt-in: io) |
Resource |
| Module, Function, Method | WRITES_TO (opt-in: io) |
Resource |
| Module, Function, Method, Resource | FLOWS_TO (opt-in: io) |
Module, Function, Method, Resource |
| Function, Method, File | EXPOSES (opt-in: io) |
Resource |
| Resource | RESOLVES_TO (opt-in: io) |
Resource |
| Module | IMPLEMENTS_PATTERN (opt-in: findings) |
Pattern |
| Module | HAS_SMELL (opt-in: findings) |
CodeSmell |
| Module | HAS_VULNERABILITY (opt-in: findings) |
SecurityIssue |
| Gloss | ANNOTATES (opt-in: glosses) |
Module, Class, Function, Method, Interface, Enum, Type, Union |
| Gloss | MENTIONS (opt-in: glosses) |
Module, Class, Function, Method, Interface, Enum, Type, Union |
| Function, Method | HAS_PARAMETER (opt-in: parameters) |
Parameter |
| Class, Interface, Enum, Type, Union | HAS_FIELD (opt-in: fields) |
Field |
| Enum | HAS_VARIANT (opt-in: enum_variants) |
EnumVariant |
| Module | DEFINES_CONSTANT (opt-in: constants) |
Constant |
| Parameter, Field, Constant | OF_TYPE (opt-in: parameters) |
Class, Interface, Enum, Type, Union |
REFERENCES records a non-call mention of a callable or class (a function passed as a value, a callback stored in a dict, a Java method reference such as Acc::add). INSTANTIATES records a class being constructed; a Java constructor reference (Acc::new) instantiates its class and references each declared constructor. Both belong to the default calls capture group. The findings relationships (IMPLEMENTS_PATTERN, HAS_SMELL, HAS_VULNERABILITY) are opt-in with the findings capture group.
CALLS, REFERENCES and INSTANTIATES edges record where they were produced, not only that they exist, and IMPORTS edges record the statement that produced them (issue #1522):
| Edge types | Property | Meaning |
|---|---|---|
| CALLS, REFERENCES, INSTANTIATES, IMPORTS | line: int, col: int, end_line: int, end_col: int |
Span of the producing expression (the call, the referencing name, the constructor invocation) or of the import statement. Lines are 1-based, columns 0-based and the end is exclusive, matching node start_line / start_col. |
| CALLS, REFERENCES, INSTANTIATES | arg_count: int?, kwarg_names: list[string]? |
Present when the site has an argument list: the number of arguments passed (positional plus keyword) and the keyword names in source order. A Python *rest or **opts unpacking is not counted: it passes an unknown number of arguments, and star_args / star_kwargs record it instead. A reference site (a bare function value) carries neither. |
| CALLS, REFERENCES, INSTANTIATES | star_args: boolean?, star_kwargs: boolean? |
true when the Python argument list unpacks a sequence (f(*rest)) or a mapping (f(**opts)); absent otherwise (issue #2635). The arguments written beside a *rest are a lower bound on the positionals passed, which is how cgr check reads them. |
| IMPORTS | alias: string? |
The name the statement binds in the importing scope: the as name when renamed, otherwise the imported symbol or module name. Wildcard imports and Go dot-imports bind no name and carry no alias. |
| IMPORTS | imported_name: string? |
For symbol-level imports (from x import y, import { y as z }, use a::b::y, Java import a.b.C, const { y } = require(...)) the symbol's own name; * for wildcards; absent for whole-module imports. |
| CALLS, REFERENCES, INSTANTIATES | resolution: string? |
How the edge was bound (issue #1526): exact (scope, import, type or signature), overload (one edge per same-named candidate), heuristic (name-only: trie suffix, wildcard import, package member), trace_confirmed (a static edge a runtime trace observed), dynamic (a call only a trace saw). Absent on edges emitted before the label existed; they rank as exact. |
| INHERITS, IMPLEMENTS (C#) | resolution: string? |
heuristic when neither the class's namespace, an enclosing namespace, nor a using alias or namespace declares the base, and it was bound by a project-wide name match instead (issue #2534). Absent when scope resolved it. |
CALLS (dynamic only) |
dispatch_literal: boolean?, unlocatable: boolean? |
dispatch_literal: true with line/col pointing at the getattr(obj, "name") argument or the registry-key literal the call went through; unlocatable: true when the caller's own body (nested definitions excluded) holds no such literal, or more than one, since two candidates cannot be told apart statically. |
| CALLS, REFERENCES, INSTANTIATES | spread_args: boolean? |
true when a TypeScript, JavaScript, PHP or Go call passes a number of values its written arguments do not show: f(...xs), f(...$xs), f(xs...), a Go call whose lone argument is itself a call (f(pair()) passes every result of pair), or a tagged template, which passes its strings array and one value per substitution. arg_count keeps what is written; absent otherwise (issue #2517). cgr check gives such a site no definite arity verdict. |
| CALLS, REFERENCES, INSTANTIATES | call_qualifier: string? |
What a Rust or C# call is written through: the last name of a Rust path (S in S::m(s, 1), Self, <S as Trait>) or of a C# member call's left side when that name binds no local, parameter, field or property at the call (Util in Util.Ext(s, 1)), and "" when the left side is a value (every Rust s.m(1); C# s.Ext(1) for a parameter s, "x".Ext(1), this.Ext(1), s?.Ext(1)). Absent for a bare call and in other languages (issue #2517). cgr check reads it to count a Rust self or C# extension receiver only where the call passes it. |
Sites are stored as one edge per site: a function that calls g twice has two CALLS edges to g, one per call expression, and from x import a, b yields two IMPORTS edges to x (same statement span, different alias). The site properties join the write-time MERGE key (line, col; plus alias for IMPORTS), the same mechanism that keeps parallel FLOWS_TO edges apart, so re-indexing is idempotent. A query that wants callers rather than call sites should DISTINCT on the endpoint; a query that wants the sites reads r.line.
Edges emitted without a syntactic site (and trace-written edges without a dispatch literal, those marked unlocatable) carry none of these properties and keep collapsing on their endpoints: libclang macro uses and #include edges, Roslyn-only facts, inferred C# namespace imports, interprocedural callable-parameter flow edges, and edges written back by dynamic tracing. For a Go grouped import ( ... ) block the site is the individual spec line, which is the unit an import rewrite edits. cgr diff treats these properties as location, not structure: a line shift never reports as a changed relationship.
CALLS edges are otherwise created by static analysis with no further properties. Dynamic call tracing decorates them with runtime provenance (dynamic, dynamic_call_count, dynamic_workloads, dynamic_workload_count, dynamic_receiver_types) and creates runtime-only edges flagged static_missed: true when no matching static edge existed in the graph at ingest time. Dynamic dispatch, reflection, and registries are the common causes. Ingest also upgrades every observed static edge's resolution to trace_confirmed in place (on each of its sites) and tags the runtime-only edges dynamic; cgr dead-code --min-resolution and the callers/callees tools read the label. An incremental sync that re-parses an endpoint keeps these edges, re-applied by qualified name, and sets dynamic_stale: true on the ones whose caller or callee definition changed since the trace was ingested.
return_type and param_types (issue #1527) hold a definition's annotations
as written in the source. return_type is absent when the definition has no
return annotation, so "unknown" and "annotated as None" stay
distinguishable. param_types is parallel to the declared parameters in
source order, one entry per parameter and "" for an unannotated one; it is
absent, not empty, for languages the extractor does not read (Python,
TypeScript/JavaScript, Go, Java, Rust, C# and, return type only, C/C++ are
read). Receivers (self, &self) count as a parameter with "".
positional_params lists a Python definition's positional parameter names
as CPython counts them, receiver included (issue #227). TypeScript,
JavaScript, Go, Rust, PHP, Java and C# definitions list every parameter a
call fills, marked with the optionality the signature declares (issue
#2517): pad? may be left out, ...rest takes any number of trailing
arguments, and self (Rust) or this s (a C# extension method) is a
receiver one call form passes and another does not. The property is absent,
never empty, for every other language and for a bodiless TypeScript
signature, which reads as "kinds unknown". cgr check compares the lists to
report signature changes.
The names an annotation mentions are resolved after every file is parsed:
through the module's imports first, then the module and its enclosing
modules, then a unique project type of that name (two equally near
candidates stay unresolved rather than guessed). Each resolved name yields
one RETURNS (return annotation) or ACCEPTS (any parameter annotation)
edge to the Class / Interface / Enum / Type / Union node. Builtins and
third-party types produce no edge.
Which parts of the schema above an index writes is chosen per capture group. Every relationship type belongs to exactly one group; a node label a group owns is written only while at least one of that group's relationships is enabled, and a label no group owns is always written. A default index enables the groups marked ✓. The others are opt-in: until the repository is indexed with one of them, a query for its labels or relationships, such as MATCH (f)-[:HAS_PARAMETER]->(p), returns nothing.
| Group | Default | Node labels | Relationships | Description |
|---|---|---|---|---|
structure |
✓ | - | CONTAINS_PACKAGE, CONTAINS_FOLDER, CONTAINS_FILE, CONTAINS_MODULE, CONTAINS_SECTION, DEFINES, DEFINES_METHOD | The containment tree from the project down to modules and document sections, and what each module, class or function defines. |
calls |
✓ | - | CALLS, REFERENCES, INSTANTIATES | Call sites, functions and classes used as values, and class instantiations. |
types |
✓ | - | IMPLEMENTS_MODULE, INHERITS, IMPLEMENTS, OVERRIDES, RETURNS, ACCEPTS | Inheritance, interface and module implementation, method overrides, and the project types a signature returns or accepts. |
imports |
✓ | - | IMPORTS, EXPORTS, EXPORTS_MODULE, DEPENDS_ON_EXTERNAL, LINKS_TO | Imports and exports between modules, the project's external package dependencies, and document links to files. |
io |
- | Resource | READS_FROM, WRITES_TO, FLOWS_TO, EXPOSES, RESOLVES_TO | External resources code reads, writes or exposes (files, environment variables, network, databases, endpoints), value flow between them, and client calls resolved to the endpoints they reach. |
findings |
- | Pattern, CodeSmell, SecurityIssue | IMPLEMENTS_PATTERN, HAS_SMELL, HAS_VULNERABILITY | ast-grep findings on each module: design patterns, code smells and security issues. |
glosses |
- | Gloss | ANNOTATES, MENTIONS | Notes agents write about definitions with the annotate MCP tool, rather than anything parsed from source. |
parameters |
- | Parameter | HAS_PARAMETER, OF_TYPE | One node per declared parameter of a function or method, and the OF_TYPE edge from a parameter or field to the project type its annotation names. |
fields |
- | Field | HAS_FIELD | One node per field of a class, interface, enum, type or union. A field's OF_TYPE edge belongs to parameters, so field types need both. |
enum_variants |
- | EnumVariant | HAS_VARIANT | One node per enum member, with its position and value. |
constants |
- | Constant | DEFINES_CONSTANT | One node per module-level constant, with its declared type and value. A constant's OF_TYPE edge belongs to parameters, so constant types need both. |
Every indexing run reads the selection from the CGR_CAPTURE environment variable, whose tokens are separated by commas, semicolons or spaces. cgr start --update-graph and cgr index also take --capture, repeatable and comma-separated (--capture none,structure), applied after CGR_CAPTURE. Tokens apply left to right, starting from the default groups:
| Token | Effect |
|---|---|
GROUP, +GROUP |
Add the group. |
-GROUP |
Drop the group. |
TYPE, +TYPE |
Add one relationship type, such as +HAS_PARAMETER. |
-TYPE |
Drop one relationship type, such as -OVERRIDES. |
all |
Enable every group. |
none |
Disable every group, so the tokens after it build the selection from nothing. |
Names are case-insensitive. A name that is both a group and a relationship type is read as the group, so -calls and -CALLS both drop CALLS, REFERENCES and INSTANTIATES. Node labels are not tokens: +Parameter is not recognised, while the group parameters is. A bare group is added to what is already enabled, so naming a default group changes nothing and logs a warning saying so; put none first to capture only that group. A --capture token that names neither a group nor a relationship type is a usage error, and the command stops before indexing anything. In CGR_CAPTURE, which long-running servers also read, such a token is skipped with the warning Ignoring unknown capture token, and the rest of the selection still applies.
# The defaults plus Parameter and Field nodes
cgr start --repo-path . --update-graph --capture parameters --capture fields
# Every group
cgr index --repo-path . -o ./index-out --capture all
# Only the containment tree and definitions
CGR_CAPTURE=none,structure cgr start --repo-path . --update-graph
# The defaults without OVERRIDES edges
cgr start --repo-path . --update-graph --capture -OVERRIDESRecorded on pallets/itsdangerous.
The selection is part of the parser fingerprint, so enabling a group on an indexed project needs no --clean: the next --update-graph re-parses the project once and writes the group's nodes and relationships (see Document Support).
A Resource node stands for something outside the code that code reads,
writes, exposes or calls. Its kind is one of:
| Kind | Stands for |
|---|---|
| FILE | A file path an I/O call names |
| NETWORK | A URL or host a client call reaches |
| DATABASE | A database, table or query target |
| STDIN | Standard input |
| STDOUT | Standard output |
| STDERR | Standard error |
| ENV | An environment variable |
| SOCKET | A socket address |
| PROCESS | A command run as a subprocess |
| ENDPOINT | A route a handler exposes (GET /users/{id}), reached from a NETWORK resource through RESOLVES_TO |
| CONTRACT | A codegen contract operation shared by client stubs and server implementations |
| RPC | An RPC method a handler exposes; callers join it directly |
| DISPATCH | A string-keyed dispatch target (a queue name, a command key); callers join it directly, or through RESOLVES_TO from a key/deployment variant of the key |
EXPOSES joins a handler to the ENDPOINT, RPC or DISPATCH resource it
serves. RESOLVES_TO joins a client's NETWORK resource to the ENDPOINT its
literal URL matches, a client stub's RPC operation and a server's ENDPOINT
to the CONTRACT they implement, and a key/deployment DISPATCH variant to
its head key.
The io capture group (opt-in; excluded from the default capture set) adds the relationships that model how code touches external resources and how values move between them: the three below, and EXPOSES and RESOLVES_TO from Resource Kinds.
READS_FROM and WRITES_TO connect a callable to a Resource it reads from or writes to (for example os.getenv("K") reads the ENV resource, print(x) writes the STDOUT resource).
FLOWS_TO records value flow, turning provenance questions into graph reachability. It is emitted in three shapes, distinguished by a kind edge property:
- resource → resource (
kind = resource): a value read from one resource reaches a write to another within a function body, e.g.x = os.getenv("K"); print(x)yieldsResource(ENV::K) -FLOWS_TO-> Resource(STDOUT). - caller → callee (
kind = arg): a tainted local value is passed as an argument to a first-party callee. Aviaedge property names the conduit asarg:<index>orkw:<name>. - callee → caller (
kind = return,via = return): a callee whose return value is tainted flows that value back to its caller. The edge terminates at the calling function, not at the assignment that received the value.
Taint is propagated through plain x = y assignments. FLOWS_TO is intentionally conservative in this phase: flow inside a body is tracked by an intra-procedural walk, return taint composes transitively across functions and files, and argument hand-off is one level.
See I/O and Data-Flow Edges for the detailed reference: the taint model, propagation and kill rules, the kind/via edge properties, scope attribution, and example queries.
Every Module node carries the documentation for the file as a whole in its
optional docstring property, in whatever form the language uses.
The grammars do not distinguish a documentation comment from an ordinary one
-- tree-sitter reports Rust's //! and a throwaway // note both as
line_comment -- so the marker prefix decides, not the node type.
| Language | Marker | Notes |
|---|---|---|
| Python | """docstring""" |
A string literal as the first statement |
| Rust | //!, /*! |
Inner docs only; /// documents the next item, not the module |
| Go | // above package |
No marker: a blank line before package makes it a licence header instead |
| Java, Scala | /**, /*!, /// |
Javadoc/Scaladoc |
| JavaScript, TypeScript, TSX | /**, /*! |
JSDoc. /// is TypeScript's <reference/> directive, not a doc |
| C, C++ | /**, /*!, /// |
Doxygen |
| C# | ///, /**, /*! |
XML documentation comments |
| Dart | ///, /**, /*! |
Library docs |
| PHP | /**, /*!, /// |
Follows the <?php tag |
| Lua | --- |
LuaDoc/LDoc; a plain -- is an ordinary comment |
| SQL | none | No module-documentation convention, so nothing is extracted |
Consecutive line comments join into one block, and a blank line ends it. A shebang before the comment is skipped, so a CLI entry point keeps its documentation.
A comment that does not carry its language's marker is left alone: recording a
licence header or a // TODO as the file's documentation is a wrong answer
that reads like a right one. Three further kinds are excluded for the same
reason, even when they do carry the marker:
- Directives --
//go:generate,//nolint:,// Code generated ... DO NOT EDIT.-- are instructions to tooling. They are skipped rather than treated as the end of the comment, so a real doc beneath one is still found. - Separator rules --
--------,////////-- are decoration, not prose. - TypeScript's
/// <reference />is machine input, so///is not a doc marker in JavaScript, TypeScript or TSX;/**is.
Class, Function, Method, Interface, Enum, Type and Union carry
the documentation of that one definition in the same optional docstring
property. Python's is the string literal that opens the body; every other
language's is the doc comment immediately above the declaration -- or above
the statement that wraps it: an export, a Go type, a const f = () =>
assignment, a module.exports.f = function assignment. A comment that trails
the previous line (int a; ///< the a field) is that line's remark, never the
next declaration's documentation.
The markers are the ones in the module table with one exception: Rust
documents a definition with the outer forms, /// and /**, while //!
and /*! describe the enclosing module and are never attached to an item. Go
has no marker at either level, so // directly above a declaration is its doc.
Whether a comment belongs to the file or to the declaration beneath it is one
decision, made once, from the blank line: a doc comment touching a declaration
is that declaration's, a detached one is the file's. So /** Class docs */
directly above class C {} lands on the Class node and not on the Module,
and the same comment separated by a blank line does the reverse. Rust is the one
language where a detached /// belongs to neither -- it documents nothing, and
rustc warns on it.
An attribute between the comment and its declaration does not detach it
(/// doc / #[derive(Debug)] / struct S). In the other languages an
annotation is part of the declaration node itself, so the comment is already
adjacent and no skipping is needed.
The exclusions are the module table's -- separator rules, directives, ordinary
comments without the marker -- for the same reason: an // ordinary note
recorded as a function's documentation is a wrong answer that reads like a
right one.
A function or class defined inside another function or method (a closure or a function-local class) is attached by DEFINES to its enclosing scope, not flattened onto the Module. So DEFINES can originate from a Function or Method as well as a Module. A top-level function or class is still defined by its Module.
A JavaScript or TypeScript named function expression whose value is not stored under that same name (a callback argument such as app.use(function createError (req, res, next) {...}), a return value, var g = function f () {}) carries is_body_scoped_name: true. Its name is in scope inside its own body only, so a bare call by that name resolves to it from there (recursion). Anywhere else the name reaches it only as one @line variant of a same-named definition that does bind the name (see Qualified Name Uniqueness below).
Methods of classes defined inside function bodies are captured only when CGR_CAPTURE_LOCAL_DEFINITIONS is enabled, which is the default (see Configuration); function-local classes are always captured, and setting the flag to false skips their methods.
qualified_name uniquely identifies each Function, Method, and Class node. When the same qualified name is defined more than once in a module, every definition is kept as a distinct node. This happens with the if has_x(): ... else: ... import-fallback idiom, typing.overload, and try/except ImportError fallbacks.
The first definition keeps the plain dotted qualified name; each later definition is suffixed with @<start_line> (for example pkg.module.store_embedding@161) so both survive instead of one overwriting the other. The name property stays the plain name on every variant. Source order decides across labels too: a Python class Tool followed by a same-named def Tool in an if block (a docs or TYPE_CHECKING shim) keeps m.Tool for the class, and the def becomes m.Tool@<line>.
A CALLS edge to a name that has more than one definition links to every variant, since each is a runtime-possible target. When a Python name has both a class and a function variant, a call such as Tool() records INSTANTIATES to each class variant and CALLS to each function variant, and a method call on the result (Tool().run()) resolves through the class. A bare decorator @Tool runs Tool(func) and binds the same way: the module INSTANTIATES a class decorator (and CALLS its __init__), whether or not a same-named function shares its name.
A JavaScript or TypeScript function written as an object literal's property value ({retry: {delay: () => 0}}, {delay: function () {}}, {delay () {}}) is named by its key under the enclosing scope, without the object's path, and carries is_object_member: true. Only its object reaches it (options.retry.delay()), so a bare call such as delay(5) never links to it by name, and a bare call to a real delay does not fan out onto such a variant. A binding imported from a module that exports the object (const { delay } = require('./opts')) still resolves to it.
Module nodes are also identified by qualified_name (File and Folder nodes are keyed by absolute_path instead, so they stay per-checkout), but without the @<start_line> suffix mechanism: bodied modules that share one qualified name (for example mutually-exclusive #[cfg] twin mod blocks in one Rust file) merge into a single Module node whose location properties come from the last definition ingested. This is an accepted representational merge: call resolution is unaffected, because each twin's functions bind through their own module body's imports rather than a merged import map.
Macro definitions map onto the existing Function label rather than a dedicated node type, since macros are a cross-language concept (C and C++ #define, Rust macro_rules!). Macro Function nodes carry is_macro: true, macro invocations resolve to their definitions and emit CALLS edges, and dead-code analysis treats macros like any function.
Language notes:
- Rust: macros and functions live in separate namespaces, so a macro invocation (
write!) never binds a same-namedfnand a function call never binds a same-named macro.#[macro_export]setsis_exported(macros take nopub). - C/C++ (macro semantics, shared by the libclang-backed modes): compiler builtins, system-header macros, and empty-bodied object-like macros (include guards, feature flags) are not nodes. A macro use inside a function body emits
CALLSfrom that function; a use outside any function attributes to theModule. A macro whose definition body references another macro emits a macro-to-macroCALLSedge, since nested expansions are never reported as individual uses. - C/C++ hybrid mode (the default:
CPP_FRONTEND=hybrid;libclangforces the pure libclang frontend andtreesitterdisables libclang entirely; the libclang bindings ship in thecppextra,pip install "code-graph-rag[cpp]"): tree-sitter remains the backbone (every file gets its tree-sitter definitions and calls; nothing is skipped) and libclang layers on only macroFunctionnodes and#includeIMPORTSedges, whose qualified names are identical between the two schemes. Macro uses are attributed to the tightest enclosing tree-sitter definition span after the definition pass, so macroCALLSedges join the qualified-name scheme the rest of the graph uses. - C#: a namespace that mirrors the file's directory is not repeated in the qualified name:
src/Serilog/Capturing/PropertyBinder.csundernamespace Serilog.Capturingisproj.src.Serilog.Capturing.PropertyBinder.PropertyBinder, not…PropertyBinder.Serilog.Capturing.PropertyBinder. A namespace the directory does not spell stays in the qualified name, so two same-named types in one file remain distinct. The declared namespace is always on the type node asnamespace, andresolvefinds a type by<namespace>.<name>through it. - C# hybrid mode (opt-in: the default is
CSHARP_FRONTEND=treesitter; selectingautouses hybrid mode whendotnetis on PATH, whilehybrid/roslynexplicitly request Roslyn-backed analysis; unavailable toolchains fall back to tree-sitter): tree-sitter remains the backbone and a bundled Roslyn tool (requiresdotnet) layers on location-keyed semantic facts. Base lists get exactINHERITS-vs-IMPLEMENTSclassification; each invocation site gets the compiler's own overload resolution (argument types, not arity) and extension-method binding, overriding the syntactic heuristics per call;partialtypes merge by symbol identity instead of the directory heuristic; and LINQ query-syntax operators that resolve to first-party methods emitCALLSedges tree-sitter cannot see (query syntax has no invocation nodes). Source generators run inside the workspace compilation, so resolution through generated members works, but generated code has no repo file and gets no nodes. Any missing fact degrades to the tree-sitter heuristic for that site. See the security model before enabling toolchain-backed analysis on untrusted repositories. - Java (default
JAVA_FRONTEND=heuristic): a call to same-arity overloads is bound by the argument types the parser can see: literals, declared and cast types, and widening up the project's own classes and interfaces or common JDK collection, map and reflection types. Overloads a class inherits compete with the ones it declares. When the argument types cannot tell candidates apart, each tied overload gets aCALLSedge labelledoverloadinstead of the first declaration getting one labelledexact. The same happens when a candidate the parser cannot rule out could beat the pick: a type-variable parameter, or a JDK supertype outside that table (anIOExceptionargument besidef(Throwable)andf(Object)).
The function- and class-defining AST node types captured per language (auto-generated from the language specs):
- C:
enum_specifier,function_definition,struct_specifier,union_specifier - C#:
class_declaration,constructor_declaration,conversion_operator_declaration,destructor_declaration,enum_declaration,interface_declaration,local_function_statement,method_declaration,operator_declaration,property_declaration,record_declaration,struct_declaration - C++:
class_specifier,declaration,enum_specifier,field_declaration,function_definition,lambda_expression,struct_specifier,template_declaration,union_specifier - Dart:
class_definition,constant_constructor_signature,constructor_signature,enum_declaration,extension_declaration,extension_type_declaration,factory_constructor_signature,function_signature,getter_signature,mixin_declaration,setter_signature - Go:
function_declaration,method_declaration,type_alias,type_spec - Java:
annotation_type_declaration,class_declaration,constructor_declaration,enum_declaration,interface_declaration,method_declaration,record_declaration - JavaScript:
arrow_function,class,class_declaration,function_declaration,function_expression,generator_function,generator_function_declaration,method_definition - Lua:
function_declaration,function_definition - PHP:
anonymous_class,anonymous_function,arrow_function,class_declaration,enum_declaration,function_definition,interface_declaration,method_declaration,trait_declaration - Python:
class_definition,function_definition - Rust:
closure_expression,enum_item,function_item,function_signature_item,impl_item,macro_definition,struct_item,trait_item,type_item,union_item - TypeScript (TSX):
abstract_class_declaration,arrow_function,class,class_declaration,enum_declaration,function_declaration,function_expression,function_signature,generator_function,generator_function_declaration,interface_declaration,internal_module,method_definition,type_alias_declaration - TypeScript:
abstract_class_declaration,arrow_function,class,class_declaration,enum_declaration,function_declaration,function_expression,function_signature,generator_function,generator_function_declaration,interface_declaration,internal_module,method_definition,type_alias_declaration - Scala:
class_definition,function_declaration,function_definition,object_definition,trait_definition - SQL (PostgreSQL):
create_function

