Skip to content

feat(upstream): reconciliation batch 3a — Ruby, JS, Vue, and Rust resolution fixes - #601

Open
bompus wants to merge 9 commits into
fork/consolidatedfrom
merge/upstream-cd04b21a-b3a
Open

bompus wants to merge 9 commits into
fork/consolidatedfrom
merge/upstream-cd04b21a-b3a

Conversation

@bompus

@bompus bompus commented Oct 31, 2026

Copy link
Copy Markdown
Owner

Summary

Batch 3a of the upstream reconciliation line (upstream colbymchenry/codegraph through 69af19ac), eight ports into the fork's Rust-kernel resolution path. Continues #598 (batch 2) and #600 (batch 3c); batch 3b follows on its own branch.

upstream fork commit port
(colbymchenry#3046) 01acfc4d a parameter or local named like an imported module is not that module
cec50372 a base's type argument is no supertype, so Repo.save() is not User.save
(colbymchenry#3049) e24528d9 calls to !/? methods and calls on what .new made link their method
(colbymchenry#3053) 21397fd7 a call on an instance variable that initialize types links to its class's method
(colbymchenry#3070) f57fd223 a self-closing <script /> no longer swallows the next script
(colbymchenry#3068) 7a7a2ab8 an export default line inside a template literal no longer names the default
(colbymchenry#3025) 96166c33 calls through a type a path or use names reach that type's own function
(colbymchenry#3064) 0e85f46d a module a #[path] attribute loads is the module that declares it

Fork adaptations (deliberate deviations from upstream)

  • f57fd223: consolidated shared SFC_SCRIPT_ELEMENT / SFC_SCRIPT_OR_STYLE_ELEMENT / sfcBlockLineRanges exports in sfc-script.ts, used by the Vue, Svelte, and Astro extractors plus syntax-token and vue-template masking — upstream patches each extractor separately.
  • 7a7a2ab8: upstream scans template-literal source text; the kernel parses defaults from AST bindings so the template-text skip is structural. The real port added upstream's defaultFnClass expression-default fallback, gated on anonymous-vs-expression head classification.
  • 96166c33 / 0e85f46d: #[path]/cfg_attr module claims and use-bound type paths implemented in rust_paths.rs/rust_modules.rs/rust_types.rs against the fork's resolved-edge model rather than upstream's source-scan model; rust_super_module stops above target roots and the 2015 crate-root fallback is blocked by path modules.

Validation

  • Full suite: 963 files, 11,402 passed / 46 skipped / 1 todo / 0 failed (66 s)
  • Fail-before/pass-after demonstrated per commit (e.g. SFC 4/4 red on parent, Rust #[path] 5/8 red on 7a7a2ab8, type-member paths red on parent)
  • Golden dumps: 8/8, zero .dump diff (20-lang torture corpus)
  • tsc --noEmit clean; kernel cargo check + release build clean
  • Precision: eval:precision on vite held (6/6 + 2/2); no Rust corpus exists in the eval set — noted upstream A/B link deltas (fast-mlsirm, dioxus, tokio) not reproduced locally

Not verified

  • Golden corpora contain no #[path] modules — coverage is via the 8 regression tests, not dump diffs.

…t that module (colbymchenry#3046)

A Python call through a name the file imports as a module, like
other.update(...) after `from pkg import other`, linked to the module's
function whatever the calling function bound `other` to: a with/except/loop
target, a walrus, a case capture, a lambda parameter or a comprehension
variable. Parameters and plain assignments were already scoped.

The Python walker now emits a function-scoped local binding row for loop,
`with ... as`, `except ... as`, walrus and `case` capture targets, and for
tuple and augmented assignment targets. A lambda parameter or comprehension
variable binds only inside its own expression, narrower than the line-wide
scope a row carries, so the resolver reads those from the syntax tree.

Ported from upstream b700a3e
…is not User.save

The fork's extractor emits an extends edge only for a plain or dotted base
name, so `class Repo(Base[User])` never reached User.save. Adds upstream's
regression test for that; the check that Base's own method is still reached
through a parameterized base stays a todo, since the fork emits no edge for it.

Ported from upstream cd04b21 (test only)
…k their method (colbymchenry#3049)

A call to a method whose name ends in `!` or `?` on a receiver
(`writer.flush!`, `writer.ready?`, `CacheWriter.create!(1)`) failed the
method-call pattern. A method called straight on a new object
(`CacheWriter.new(key).write_cache`, `Billing::Ledger.new.charge!`) was
recorded with the receiver's raw text, arguments and blocks included, which
no strategy matched.

The Ruby walker now records a call on `Const.new(...)`, `A::B.new` or
`::Foo.new` as `new Const().m`, the constructed-receiver shape other
languages use. The resolver claims that shape ahead of name matching: the
class by Ruby's constant lookup from the namespaces around the call, the
method as the class's own instance method or the first its ancestry
declares, and nothing when either is missing. The Ruby dot pattern takes a
trailing `!` or `?`, the receiver-word scoring among several project methods
of the name stays off for such names on a value receiver, and a module a call
returns (`include RouteSet.new.tap {...}.url_helpers`) is no longer read as a
mixin of the constant the call starts at.

Ported from upstream 3a4cd3b
@bompus bompus added the author:cognition Pull request written by a cognition model label Oct 31, 2026

@bompus-ai-review bompus-ai-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bompus AI Review (GPT-6.1 Sol, xhigh reasoning): 5 finding(s).

Comment thread codegraph-kernel/src/resolve/query.rs Resolved
Comment thread codegraph-kernel/src/python.rs Resolved
- A multiline `export default (\n  v\n) => v` head is anonymous too: the
  classifier now reads from the marker's line to the end of the file, as
  upstream's readDefaultExport scans the whole statement, so an
  unfinished parenthesized head no longer counts as an expression
  default and wrongly enables the first-exported-function fallback.
- A receiver proven to be an instance (`new C().m`, an `initialize`-typed
  `@x`) can only reach instance methods: an own class method no longer
  stands in for one an ancestor declares, so `Child.new.flush` reaches
  `Parent#flush` rather than `Child.flush` the class method.
- A `Const.new` tail sliced at byte 200 could land inside a multibyte
  character and panic (caught per-ref, leaving the call unresolved); the
  regexes now read a char-boundary-safe prefix.

@bompus-ai-review bompus-ai-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bompus AI Review (GPT-6.1 Sol, xhigh reasoning): 4 finding(s).

.next()
.filter(|n| n.kind == "class");
let method = match class {
Some(class) => self.ruby_method_of(&class.qualified_name, &c[3], r, "instance")?,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high: Preserve Ruby ancestor order for constructed receivers

The new receiver path calls ruby_method_of(..., "instance"), which collects ancestors, runs ancestors.sort(), and returns the first matching method. Alphabetical order replaces Ruby's inheritance order.

Failure: With class A; def run; end; end, class Z < A; def run; end; end, and class Child < Z; end, Child.new.run links to A#run instead of the override Z#run. Typed instance-variable receivers have the same failure.

Suggested fix: Use an ordered Ruby ancestry walk that checks the nearest superclass before its ancestors; add a regression with both parent and grandparent defining the method.

"method" | "function" | "variable" | "constant" | "enum_member"
)
&& n.qualified_name == qualified
&& Self::rust_crate_dir_of(&n.file_path, &graph) == own

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium: Verify the fallback member belongs to the resolved Rust type

The fallback accepts n.qualified_name == qualified and equal rust_crate_dir_of(...), then selects a candidate without resolving its impl's owner. Same-named types in different modules satisfy both checks.

Failure: Let a.rs declare Handle, a separate impls.rs implement a trait with a default current() for that Handle, and b.rs declare another Handle with an inherent current(). A valid crate::a::Handle::current() call misses rust_method_on because its provided-method scan only reads the declaration file. This new fallback then links the call to b::Handle::current.

Suggested fix: Resolve each candidate impl's owner and compare declaration IDs with ty, as rust_method_on already does. Leave the call unresolved when no candidate belongs to that type.

if re!(r"^[A-Za-z_$][\w$]*[ \t]*=>").is_match(rest) {
return true;
}
if rest.starts_with('(') {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium: Classify anonymous defaults from syntax rather than their text prefix

Only an identifier followed by => or text starting with ( is recognized as an arrow. export default /* comment */ () => 2 reaches the final false, enabling default_fn_class.

Failure: A module containing export function helper() { return 1; } followed on another line by export default /* comment */ () => 2; makes a consumer's default-import call link to helper. The default is an unrelated anonymous arrow. Generic TypeScript arrows such as export default <T>(x: T) => x are also misclassified.

Suggested fix: Inspect the marked export statement's AST value and exclude anonymous arrow/function/class expressions before enabling the fallback.

target_names(alias, &mut targets);
}
}
"named_expression" => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium: Keep lambda-body walrus bindings inside the lambda

named_expression targets are emitted with self.enclosing_scope(), while the walker descends into lambdas without creating a lambda scope. New runtime evidence contradicts the earlier reply: Python compiles the lambda's assignment as STORE_FAST other, while the enclosing function reads other with LOAD_GLOBAL.

Failure: After from pkg import other, a function containing thunk = lambda: (other := {}) followed by other.persist() receives a false function-local binding for other. The import resolver rejects the actual module binding and loses the module call edge. Invoking the lambda does not change that module binding either.

Suggested fix: Scope walrus targets in lambda bodies to the lambda, while retaining the containing-scope rule for comprehension walruses.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author:cognition Pull request written by a cognition model

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant