Skip to content

fix: make five upstream-parity resolution rules stricter or more exact - #295

Merged
sunerpy merged 8 commits into
mainfrom
fix/keep-rust-strictness
Oct 1, 2026
Merged

sunerpy merged 8 commits into
mainfrom
fix/keep-rust-strictness

Conversation

@sunerpy

@sunerpy sunerpy commented Oct 1, 2026

Copy link
Copy Markdown
Owner

Summary

The retroactive kirocodex review of the v1.6.1 port (see #291) left five findings at upstream behavior. The owner asked on 2026-10-02 for all five to be fixed beyond upstream v1.6.1 (f4ddf50). The design passed the kirocodex plan review in round 3 (blocking items 6 → 1 → 0). Each fix is a KEEP-RUST divergence, stricter than upstream or more exact, and each was red first.

Commit Finding Change
7f9cc86 #278: method values An unknowable Python or Go receiver (self.store.fetch with an unannotated store, or obj.Fetch with an external type) leaves the value unresolved. Upstream falls back to the only project method of that name.
3cafab6 #277: store actions get(), X.getState(), a destructured getState() and selectors bind only inside a store a Zustand factory built. The factory must sit on the initializer's call path to the action function. A new extractor helper reads that path with the parser, following the same search that finds the actions, so a comment, a string, a type argument or an argument off the path never proves provenance. A wrapped hook (createSelectors(create(...))) now binds its selectors.
e69f39d #282: #if A C preprocessor expression evaluator decides the whole #if/#elif three-valued, with C precedence. &&/`
405ab71 #282: guards Only a whole-file include guard forces its branch: an empty #define X, and an #endif that is the last code line. A feature-flag default and a fallback #ifndef MIN / #define MIN(a, b) stay undecided, because a prior MIN may call the function, so the call keeps its edge.
8e44db7 #282: constructors T x(<literal>, ...), with no { in the arguments, drops the constructors whose first parameter is the initializer_list, then resolves by arity. A variable argument, braces and every other form still decline.
545dd7b — Extraction version 18 → 19: the persisted edges change.

Verification

  • Red first. /tmp/evidence-p10/red-{A,B,C,D,E}-*.log record each new test failing on its base for the stated reason.
  • Goldens. The release-build drift check re-indexed all 19 corpora, and every one is identical.
  • make pre-ci at 545dd7b, clean tree, rustc 1.98.0: 3981 passed, 0 failed, all checks passed.
  • Acceptance dry run. /tmp/verify-0.52.2.sh, the plan's acceptance table, ran against this head's release build and the official v0.52.1. Every row matches:
    • A: fake::reset is gone and useStore::reset is kept;
    • B: 1 edge becomes 0;
    • C: the HOOK edge is gone;
    • D: the HOOK2 edge appears;
    • E: the L::L edge appears;
    • an 18 index reads outdated, then current after sync.

BEGIN_COMMIT_OVERRIDE
fix(resolve): leave a method value with an unknowable receiver unresolved
fix(resolve): bind store actions only inside stores a Zustand factory built
fix(resolve): evaluate whole #if expressions three-valued
fix(resolve): treat only a whole-file #ifndef as an include guard
fix(resolve): construct from a literal past initializer_list overloads
END_COMMIT_OVERRIDE

🤖 Generated with Claude Code

CodeGraph Test added 7 commits October 2, 2026 06:33
…lved

A Python or Go method value whose receiver type the source cannot prove,
such as `self.store.fetch` with an unannotated `store` or `obj.Fetch`
with an external type, fell back to the only project method of that name.
Upstream keeps that unique-or-drop discipline. The receiver may be a
library object, so the port now returns no edge. Every receiver-scoped
path is unchanged: imports, self and cls, typed fields, locals and
annotations, class names, typed-base descendants, and Go local types and
method expressions.
… built

`get().m()`, `X.getState().m()` and a destructured `X.getState()` bound to
an action of ANY exported call-initializer that holds an inline action
object. So `export const fake = otherFactory(() => ({ reset() {} }))` made
`fake.getState().reset()` a call into `fake::reset`. Upstream checks
Zustand provenance for selectors only.

Every form now needs a Zustand factory on the initializer's call path to
the action function. The factories are `create`, `createStore` and
`createWithEqualityFn` from zustand, zustand/vanilla or
zustand/traditional, the default zustand import, and a namespace import's
member. A selector additionally needs a hook factory. A new extractor
helper reads that path with the parser, following the same search that
finds the actions, so a comment, a string, a type argument or an argument
off the path never proves provenance. A wrapped hook such as
`createSelectors(create(...))` now binds its selectors too.
An #if or #elif condition was decided only when it was a literal, a
single defined test or a bare name; upstream reads the same forms. So
under `#if 1 || FLAG` a macro was never definitely visible and its calls
bound to a same-named function.

A small C preprocessor expression evaluator now decides the whole
expression, with C precedence:
- `&&` and `||` decide whenever one side does;
- `?:` with an unknown condition is known only when both branches agree;
- every other operator needs known operands;
- a definitely undefined name reads 0.

Unseen names, call-like `__has_include(...)`, character literals,
overflow, division by zero and malformed text stay unknown. #if, #elif
and object-like #define values are first spliced across backslash
continuations, with each continuation line's comments removed.
Any `#ifndef X` whose next directive was `#define X`, or a fallback
`#define X(`, forced its undecided branch active, as upstream's
guardsItself does. So a feature-flag default (`#ifndef FEATURE` /
`#define FEATURE` / `#define HOOK(x) ...` among other code) suppressed a
real HOOK call that a build with -DFEATURE makes. A fallback
`#ifndef MIN` / `#define MIN(a, b)` did the same, although a prior MIN
from an unseen header or -D may be a wrapper that calls the function.

A guard now has to be the whole file:
- the test is the first code line;
- the next directive is an empty `#define X`;
- the matching `#endif`, with no `#else` or `#elif` at its depth, is the
  last code line.

Lines that hold only comments do not count as code. Every other
`#ifndef` stays undecided, and the call keeps its function.
Any constructor mentioning initializer_list made the whole overload set
decline, whatever the construction's form, as upstream does. So
`struct L { L(std::initializer_list<int>); L(int); }; L value(1);` had no
edge, although C++ calls L(int).

The resolver now reads the construction at the reference's anchor, the
initializer's own `(`, and accepts one form: `T x(<literal>, ...)`, where
the balanced argument text holds no `{` and the first argument is a
numeric, character or string literal, or true, false or nullptr. Such an
argument is never and never converts to an initializer_list, so
constructors whose first parameter is the list are dropped, and the rest
compete by arity as before. Everything else still declines: an
identifier such as an initializer_list variable, a braced argument or
lambda, brace or default initialization, or a list past the first
parameter.
These resolution changes alter persisted edges, so an index built at version 18 is reported outdated and rebuilt, and sync stays equal to index --force across the upgrade.
The 2026-10-02 entry records how #295 makes each finding the retroactive review kept at upstream behavior stricter or more exact, with upstream's line for comparison. The Current alignment KEEP-RUST bullet points to it.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 545dd7b3f5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Some(if state.defined == Some(false) {
Value::Int(0)
} else {
Value::Truth(state.value)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve integer macro values in compound conditions

For #define VERSION 2 followed by #if VERSION == 2, record stores the replacement only as Some(true), this arm reconstructs it as Value::Truth, and Value::int() converts that truth to 1; the comparison therefore becomes definitively false instead of true or unknown. This can skip active macro definitions/includes and retain fabricated function-call edges, so preserve the numeric value or remain unresolved when its identity has been lost.

AGENTS.md reference: AGENTS.md:L59-L61

Useful? React with 👍 / 👎.

@codecov

codecov Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.12069% with 27 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/codegraph-resolve/src/c_macro_visibility.rs 97.63% 11 Missing ⚠️
crates/codegraph-resolve/src/resolver.rs 93.02% 9 Missing ⚠️
crates/codegraph-extract/src/walker.rs 91.07% 5 Missing ⚠️
crates/codegraph-resolve/src/js_store.rs 95.45% 2 Missing ⚠️

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #295      +/-   ##
==========================================
+ Coverage   95.30%   95.31%   +0.01%     
==========================================
  Files         161      161              
  Lines       99130    99752     +622     
==========================================
+ Hits        94478    95083     +605     
- Misses       4652     4669      +17     
Files with missing lines Coverage Δ
crates/codegraph-resolve/src/member_value.rs 89.06% <100.00%> (-0.27%) ⬇️
crates/codegraph-store/src/index_state.rs 83.33% <ø> (ø)
crates/codegraph-resolve/src/js_store.rs 90.51% <95.45%> (+0.87%) ⬆️
crates/codegraph-extract/src/walker.rs 93.39% <91.07%> (-0.02%) ⬇️
crates/codegraph-resolve/src/resolver.rs 97.09% <93.02%> (-0.15%) ⬇️
crates/codegraph-resolve/src/c_macro_visibility.rs 94.72% <97.63%> (+2.21%) ⬆️

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Round 1 of the review found two #if defects.

A macro's value is recorded only as a truth, yet it was read as the
number 1, so `#define N 2` made `#if N == 1` true. A name's value is now
a truth with no number: arithmetic and comparison on it are unknown,
while a logical result (`!`, `&&`, `||`, `defined`) is still a real 0
or 1.

Continuations were spliced after comment removal, so in
`#if 1 || /* comment \` the backslash vanished with the comment.
Translation phase 2 now runs on the raw-string-masked source before
comments are removed, for directives and the guard's code-line scan
alike. Each joined line moves to the line that started it and leaves an
empty line behind, so line numbers stay put. That replaces the post-hoc
continuation splicing.
@sunerpy
sunerpy merged commit ff52e5c into main Oct 1, 2026
10 checks passed
@sunerpy
sunerpy deleted the fix/keep-rust-strictness branch October 1, 2026 23:21
sunerpy added a commit that referenced this pull request Oct 2, 2026
The release record for #295: the release PR merge and its tag SHA, the workflow run, the published digest, and the black-box acceptance against v0.52.1.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant