brick/docs/resolution.md
trvon 67d1980001
Some checks failed
build / build-linux (pull_request) Successful in 1m8s
build / build-macos (pull_request) Has been cancelled
build / release (pull_request) Has been cancelled
Merge main into fix/review-followups: move the Dart import note into docs/resolution.md
2026-09-24 21:14:26 -06:00

4.8 KiB

Resolution and limits

Brick extracts syntax, without compiler type checking or build configuration. Macro expansion, overload resolution, dynamic dispatch, generated code and full package/build-system resolution are outside the default frontend. Malformed syntax produces diagnostics; recoverable sibling facts stay visible.

Every unresolved call edge records source.unresolved-reason, resolved member calls carry source.resolution, and metrics.resolution reports per-language call-site coverage. Ambiguous targets stay external. Read the graph with that coverage in mind: on a large C++ repository only a minority of call sites resolve. File fan-out is an observation, not proof of semantic coupling; scope architecture gates to production paths and verify candidates against source and tests.

Per language

  • All grammar-based languages: resolution respects language, file, declaration ownership and lexical namespace.
  • JavaScript/TypeScript: relative imports resolve against the importing directory, and missing paths never fall back to unrelated basenames. Explicit named and default import bindings resolve across files.
  • Dart: relative imports resolve against the importing directory, and package:<own package>/ against lib/. Public top-level names from imported libraries resolve across files: show/hide are honoured, as-prefixed imports never bind bare names, and a library's own exports are followed one level.
  • Rust: module-scoped grouped and aliased imports and module-qualified public calls resolve. Types are identified by their declaring file, so same-named types in different modules resolve through the caller's use binding or stay ambiguous. Calls inside expression macros (assert!, format!, vec!, ...) are recovered by re-parsing and carry source.extraction.
  • C and C++: calls resolve through the include closure. A header prototype pairs with its definition in another translation unit, lookup walks class (including in-repository bases) and namespace scopes outward, and static/anonymous definitions stay file-local. Declarations in #elif/#else branches stay in the graph (source.preprocessor-branch: alternative) but are never call targets, because Brick does not evaluate conditions. Several using directives that nominate different declarations make a call ambiguous. Virtual calls resolve to the declared type's method only (overrides are not fanned out), and Catch2 TEST_CASE bodies are not yet recognized as functions. .c files use the C grammar.
  • C++ and Rust member calls resolve only through an explicitly declared receiver type: a parameter, local, field, this/self, smart pointer, or a Self-returning constructor.
  • Python (lexical preview): names resolve in the calling file, then in files it imports.
  • Swift (lexical preview): names resolve in the calling file, then by a module-wide unique name, since Swift files of one module see each other without imports.

Not yet handled, and needing further adapters: bare package specifiers, tsconfig aliases, Rust wildcard and block-local binding expansion, Cargo workspace module paths, compile_commands.json include paths, and compiler-grade dispatch.

P4

P4 extraction is a conservative P4_16 lexical preview, not a p4c frontend. Implicit terminal states are modelled only when referenced. Parser transitions and control/action calls use scoped lexical resolution; unresolved targets stay external. The optional Tree-sitter pilot does not preprocess macros and rejects all pilot facts from a file whose parse has error or missing nodes. P4_14, macro expansion, architecture type checking, compiler IR and formal-verifier evidence need separate adapters; P4V, p4-symbolic, bf4 and P4K adapters stay disabled until a real versioned machine-readable result contract and explicit bounded-model semantics are qualified.

Evidence for these rules

The source-language regression corpus checks exact declarations, owners and dependencies across six language strata, including clean-negative files; its three-repeat benchmark runs in the quality gate. These labelled cases establish the covered behaviours, not language-wide accuracy. See language correctness, and C/C++ and Rust resolution for that resolver's research, validation and the agent-facing caller-status contract.

Schema

Schema version 4 stores scan scope (including the selected P4 frontend), parallel relationship evidence and a nested metrics.p4 contract. CLI consumers reject version 1-3 JSON rather than guess; rebuild old artifacts before rendering or checking them. Consuming compiler, YAMS and Fold graph facts through adapters, while keeping the lexical lane as a fast preview, is on the roadmap; see also the graph model.