Repository navigation
Conversation
…y#2204) A vendored minified script (`*.min.js`, or a script of a few enormous lines - the same detection colbymchenry#2122 uses to rank them last) keeps mangled one-letter names, so a name match from any other file is coincidence: healthchecks' 369 `$(...)` jQuery calls went to a helper inside bootstrap-native.min.js, TestBox's popper.min.js to jquery.min.js internals, flaskbb's docs script to its vendors bundle. Such files' symbols are no longer visible across files by name; calls inside a bundle still resolve. healthchecks -460, flaskbb -48, TestBox -36; bat, lodash, minimal-mistakes, netbox, express, excalidraw unchanged. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…mchenry#2205) The declared-member receiver inference colbymchenry#2171 gave C#, Java and Kotlin now reads Swift too: `var cache: ImageCache!`, `let storage: DiskStorage.Backend<Data>`, `self.x` as well as `x`, a function's own `let` shadowing the property. The dotted-call path types the receiver from it, and a chained bare call keeps only members of that type or its supertypes / protocols. Kingfisher: `cache.imageCachedType(forKey:)` -> ImageCache's (was a test subclass's override), `storage.store/value/isCached` -> DiskStorage.Backend, `manager.retrieveImage` -> KingfisherManager (was a test double), DispatchQueue `ioQueue.async` no longer a project method. realm-swift: `List::append`, `RealmCollection::sum/index` (were Objective-C RLMArray selectors); vapor: `self.storage.write` -> NIOHTTPBodyWriterStorage. Kingfisher -100 +179, realm-swift -108 +203, Alamofire -16 +54, vapor -15 +10, IceCubesApp -6 +5; Newtonsoft.Json, okhttp unchanged. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
colbymchenry#2172 made the members of every type a project function type takes as a receiver reachable from any bare call, since it could not tell which lambda a call is in. With several such types in reach, proximity chose: koin's `single { C51(get(), get()) }` and `Scope.new(...)`'s `get()` went to `Koin.get` (koin also has `Koin.() -> ...` lambdas), 2,190 times. - A lambda's receiver is read from the function it is passed to: the type before `.(` in its last parameter's function type, directly or through a typealias (`definition: Definition<T>`, `typealias Definition<T> = Scope.(ParametersHolder) -> T`), when every project function of that name agrees. The brace frames put it in the lambda's hierarchy. - Of the members a bare call can reach, those its lexical hierarchy reaches - its class, an extension's receiver, the lambda it is in - come before those only the project-wide receiver set reaches. koin: `get()` -> Scope (2,184), `viewModel` in `scope { }` -> ScopeDSL, `modules(...)` in `startKoin { }` -> KoinApplication. Exposed: `varchar(...)`, `integer`, `reference`, `datetime` in a table object -> Table's (were a test table's same-named property). kotlinx.coroutines: `launch { }` -> CoroutineScope.launch (was a test DSL's). Edge counts ~unchanged (targets move): koin, Exposed, okhttp, coroutines, okio; nowinandroid, moshi unchanged. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…colbymchenry#2207) Dart joins Rust/Go/Kotlin/C# with a standard-method list (String, List, Iterable, Map, Set: endsWith, contains, forEach, putIfAbsent, where, sublist...): a guess onto a project method of such a name needs a receiver named after its owner. And a Dart extension's owner, for that naming, is the type it is `on` - getx's `ext.endsWith(".avi")` on a String shared the word `Ext` with `extension RxStringExt on Rx<String>`, none with `Rx<String>`. getx -36 (`RxStringExt::endsWith` x23, IOHttpResponse::join/toList), riverpod -37 (`UnmodifiableMap::putIfAbsent/addAll` on plain maps), bloc -18 (`ignore.contains(...)` -> GithubCache, a Map's `forEach` -> _Emitter; bloc's untyped `emit.forEach(...)` handler lambdas lose their lucky guess too). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…colbymchenry#2208) `answers in under 100 ms` took one wall-clock sample while the rest of the suite ran in parallel, and failed under that load at 250-430 ms (three times in one day of full-suite runs) while passing alone. It now takes the fastest of five warm requests: contention is gone from at least one, and a real slowdown is slow on every one. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…olbymchenry#2209) A test suite is not linked into the program, so a name match from production code into one is coincidence - but only Strategy 3's receiver guess (colbymchenry#2171) ruled it out. Now no strategy's name match reaches a symbol in a test suite from a non-test file: a test source set (`src/test/`, `jvmTest/`), a `tests/` / `__tests__/` / `spec/` / `e2e/` directory, a `FooTest.kt` / `test_foo.py` / `foo.test.ts` / `foo_spec.rb` file. Test-support code a project ships - `testing/`, `fakes/`, `mocks/`, a `*-test` module - stays in reach, and `…Spec.java` (halo's `IndexSpecs`, okhttp's `ConnectionSpec`), `post-spec.ts` models and `useTests.ts` hooks are not test suites. typeorm -64 (`Record<K, V>` -> a test entity), okhttp -164 (117 sample `@Override` -> a test's nested `Override`), kotlinx.coroutines -318 (`import kotlinx.coroutines.*` onto one test file's package node), sttp -62, Exposed -92, halo 0, element-plus -36 (`isClient` -> a test's vueuse mock), excalidraw -10 (`actionManager.executeAction` -> the test `API` helper), tokio -30, trpc -36, AutoMapper -37, netbox -22, cats -21, mealie -10, pytest -8, Alamofire -7; zod, eShop, react-native-reusables unchanged. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…henry#2210) Framework resolvers see every reference of every language, and the Vue resolver had no language guard: its PascalCase-component rule took mealie's Python `QueryFilterBuilder(...)` (an unresolved import) to the `QueryFilterBuilder.vue` component at 0.8, and its macro / auto-import rules matched any language's `ref(...)` or `computed(...)`. It now answers only refs from vue / javascript / typescript / tsx / jsx files. mealie: 6 Python calls reroute to the Python class; halo, elk, element-plus unchanged. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…olbymchenry#2211) The React resolver's `languages` gates its route extraction, not its resolve(): framework resolvers see every language's references. Its component rule was already JSX-only, but its hook (`use...`) and context (`...Context` / `...Provider`) rules were not: in halo - a Spring app whose UI package makes React detected - 209 Java import edges (`SecurityContext`, `ApplicationContext`, `ObjectProvider`, `DirtiesContext`...) were resolved by it onto import nodes. It now answers only JavaScript / TypeScript / TSX / JSX references. halo -209 +3 (a Java test's `responseContext()` helper now resolves to itself); trpc, excalidraw, react-redux-realworld, bulletproof-react, react-native-reusables unchanged. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…it (colbymchenry#2212) A method that delegates to another overload of itself - `toInstant(instant)` returning `toInstant(instant, Instant.EPOCH)`, `DeserializeXNode(value)` returning `DeserializeXNode(value, null)` - bound to itself, since every overload shares the name: the fuller overload never listed its convenience overloads among its callers, and its impact missed them. When a name match lands on the calling method itself, in a language that overloads by arity (C#, Java, Kotlin, Swift, C++, Scala, Dart, VB.NET), the call's argument count is read and compared with the method's parameters (defaults and varargs / `params` / `...` counted); if they cannot take it, the one same-owner overload they fit is the target. Real recursion (`depth(n - 1)`) keeps its self-edge. Self-loops -> overload edges: commons-lang 637 -> 457, Newtonsoft 303 -> 196, halo 173 -> 116, okhttp 248 -> 208, Alamofire 147 -> 120, gson 74 -> 55, AutoMapper 71 -> 57 (edge counts unchanged; overloads share a qualified name, so only the target moves). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…bymchenry#2213) colbymchenry#2212 moved a method's call to its own name onto the sibling overload the arguments fit. The same holds for every caller: a match landing on an overload whose parameters cannot take the call's arguments moves to the one same-owner overload they fit - commons-lang's `HashCodeBuilder.reflectionHashCode(this)` from the three-parameter overload indexed first to the `(Object, String...)` one, Newtonsoft's `SetToken(token, value)` from `SetToken(token)`, halo's four-argument `menuItem(...)` from the one-argument `menuItem`. Overload sets and arities are read once per target node, and a target with no same-owner sibling costs one lookup, so indexing time is unchanged (commons-lang 4.67s vs 4.70s, Newtonsoft 4.04s vs 4.05s, wasm both arms). C++ keeps only the self-call case: its overload sets (templates, SFINAE priority tags, `data()` on any container) move targets unreliably. Targets moved between same-named overloads: commons-lang 2,334, Newtonsoft 697, halo 286, gson 232, okhttp 188, Alamofire 50; nlohmann json and fmt unchanged. Edge counts unchanged. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…olbymchenry#2214) The Rust scope rule (colbymchenry#2124) only checked prelude names; every other bare name could mean any same-named project item, and a name after any path (`io::Result`) could mean any at all. The Rust framework resolver's PascalCase / handler / service patterns bound names with no scope check at 0.7-0.8. tokio's 1,221 `io::Result<...>` went to a private `runtime::task::Result` alias, 642 `Context<'_>` (from `use std::task::{Context, Poll}`) to `runtime::context::Context`. - A type or function from another file is in scope only through a `use` that binds it (its leaf, `as` alias, or `{self}`) or a glob over its module; methods and fields (reached through a value), import refs and module / file targets are unaffected. - A name a file imports from outside the project - `std` / `core` / `alloc`, or a crate its Cargo manifests declare as a dependency - is that outside item. (An inline `mod support { pub mod panic; }` is the project's.) - A path names its module: `seg::Name` reaches an item of module `seg`; a path rooted at std (`std::io::Error`) or at an outside import (`io::` under `use std::io`) reaches none; `crate::` / `self::` / `super::` and a project crate's name (`clap::Command`, re-exported) look it up as before. The path is read anywhere on the line, not only at the reference's column. - The framework resolver's patterns apply the same check. tokio -3008 +610, serde -792 +650, ripgrep -602 +247, axum -453 +186, clap -342 +32, bat -38 +9. Sampled losses: std / core / futures / syn / http / anyhow imports and paths, associated types (`S::Ok`, `S::Error`, `V::Value`, `Fut::Output`); gains are same-module items the old first match hid. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…enry#2216) A bare C# type name could mean any same-named project type. Newtonsoft's `async Task` tests (`using System.Threading.Tasks;`) bound `Task` to a test class `Task` in `Newtonsoft.Json.Tests.Schema` 433 times; AutoMapper's DTOs in `OmmitedDTOModel3` extended `OmmitedDatabaseModel3`'s `BaseEntity`; Newtonsoft's `BindingFlags` went to its portable-build polyfill. A type in namespace N is now in reach of a file whose code runs in N or a namespace inside it, or that imports N: its `using N;`, any file's `global using N;`, the project files' `<Using Include="N" />`, the SDK's implicit usings when `<ImplicitUsings>` is on (serilog's own `System.TimeProvider` polyfill is seen through them), or an alias naming the type (`using License = AutoMapper.Licensing.License;`). A type in the global namespace is seen everywhere; qualified names are not judged. Newtonsoft -529, AutoMapper -72, eShop -4; serilog, CleanArchitecture unchanged. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…olbymchenry#2217) A kind-projector placeholder (`Align[Either[A, *]]`, `Functor[Map[K, ?]]`) reached the resolver as a type reference named `*`, and exact or fuzzy matching bound it to the one `*` in the project - algebra's `Signed.Sign.*` operator - 567 times in cats. A symbolic name in a Scala type now resolves only to a type or type alias of that name (cats' `F ~> G`), and otherwise to nothing, whatever strategy runs. cats -567; sttp unchanged. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…nly its owner names (colbymchenry#2218) A lone project method now stands for an untyped receiver's member value only when the receiver is named after the method's class: netbox's `device=self.parent.device` model-field reads no longer reach a GraphQL filter's `device` method. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…t is written (colbymchenry#2219) A function nested in another function is reachable only from inside it; a Python name the enclosing function binds is that local's value; a pytest fixture is a test's parameter only in its own module or under its conftest. Unreachable same-named functions still count against a lone cross-file guess. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…s scopes (colbymchenry#2220) A block namespace qualifies only its own body (a nested one is Outer.Inner), mirrored in the kernel; a using names the project's namespace, never another file's using; a global using is its own project's; a nested type is reached by bare name only from inside its owner or a type deriving from it, through any partial part; visible C# types are filtered before ranking. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…t calling itself (colbymchenry#2221) A TS/JS call written through this.<field>… or window.<x>… reaches the resolver by its bare name; it resolves to the calling method only when the field is declared as the caller's own class. A name-scored receiver guess never lands on the calling method. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…package (colbymchenry#2222) The Spring resolver's entity/service/controller name heuristics took the first same-named class under a conventional folder; they now prefer the ref's own file, then its package, and never reach another file's nested class by its bare name. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…s not the caller (colbymchenry#2223) Generalizes the collapsed-chain self veto beyond TS/JS this./window. roots: a call the extractor recorded by its bare name but the source writes as a member call on anything other than this/self/super is never the calling method; and a value's initializer never calls the value itself. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
colbymchenry#2224) The Rust resolver's handler/service/struct heuristics took the first same-named item and then checked scope; they now drop items declared inside a function body and out-of-scope names before choosing, and take the file's own item first. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…, own scope first (colbymchenry#2225) One shared pick behind the Django/FastAPI/Flask, ASP.NET, Gin, SwiftUI/Vapor, Spring and Rust name heuristics: never a function-local declaration, a type nested in another file's type, or a node the language keeps out of the reference's file; the reference's own file first, then its directory (not Rust), then a conventional folder. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…#2226) A JS/TS parameter or plain var/let/const above the reference shadows a same-named function declared elsewhere in the file, whichever strategy found it; destructured call results, member calls and followed aliases are left alone. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…wn (colbymchenry#2227) isVisibleAcrossFiles rejects a declaration in a .svelte instance <script> or a Vue <script setup> from another file; a Svelte <script module>, a plain Vue <script>, and a type a <script setup> exports stay reachable. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ns (colbymchenry#2228) A receiver that is the test's own parameter takes the type its in-reach fixture returns (`return Cls(…)` or a local assigned `Cls(…)`), so calls on it resolve on that class, or on nothing when it is a library's; fixtures a conftest.py star-imports or lists in pytest_plugins are in reach. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
An extends/implements ref that settled on the declaring type is re-chosen among the other types of its name, the one the written qualifier leads to (owner path plus file package), or none. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… or by import (colbymchenry#2230) isScalaPackageObjectMemberVisible: a member of `package object X` resolves from its package and those under it, or a file that imports through it; applied before ranking (so the visible namesake wins) and as a veto. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…bymchenry#2231) In isRustNameInScope, a type reference written `Self::Name` is in scope only as a type alias declared in the enclosing impl/trait block above it, and one written through a generic type parameter (`V::Value`) names no project item. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ember (colbymchenry#2232) In a .kts file, a bare call reaches a method/field/property only when its owner is no class the project declares (build logic's extensions on Gradle's types); the old rule let every member through. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… symbol (colbymchenry#2233) isVisibleAcrossFiles (before the same-file shortcut) rejects every Go candidate for a reference its line writes as `pkg.Name` through an import from outside the module (stdlib or third-party). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
matchReference refuses a method target for a calls ref from a C file: C has no methods and cannot call a C++ one. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… global type (colbymchenry#2568) protobuf's hpb declares `class Arena { … backend::Arena arena_; }`, where `namespace backend = hpb::internal::backend::upb;` and that namespace says `using Arena = ::upb::Arena;`. cppTypeName dropped the leading `::`, so the alias's target was looked up as `upb::Arena` from the alias's own scope, whose nearest `upb` is that very namespace: the alias named itself, the lookup ended on no class, and `arena_.Fuse(other.arena_)` fell back to the type's last name, linking `hpb::Arena::Fuse` (and `IsFused`, `RefArena`) to itself instead of `upb::Arena::Fuse`. TypeName now records a leading `::` (`global`), and a global name is looked up from the global scope alone: when an alias's target is followed (follow, and the scope expand looks the target up from and hands on), in a class's base clause (baseClassesOf), and in cppAliasedTypeWritten, which colbymchenry#2532's `auto` locals and chains walk alias by alias. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…there, and a far lowercase outside type reaches no namesake (colbymchenry#2569) The two follow-ups colbymchenry#2567 and colbymchenry#2568 left open. A receiver declared with a leading `::` lost it: the declarator regex started a type at a letter, so `::upb::Arena& a` inside `namespace hpb` was looked up from `hpb` and found a namespace-local `hpb::upb::Arena`. The declared type now keeps a leading `::` where one can stand (not after a name, a template's arguments or a `)`, so `SkipList<Key, Comparator>::Iterator iter` is not read as `::Iterator`), and resolveCppAliasedType no longer takes a function-local `using Foo = Bar;` for `::Foo`. A lowercase outside type declared outside the calling function still used the last-name typed match: colbymchenry#2413's library rule skips such declarations, and colbymchenry#2567's namesake gate took capitalized types only. So a far `absl::flat_hash_set<int> seen` linked `seen.insert(v)` to the project's `util::flat_hash_set::insert`, and a far `std::string text` linked `text.clear()` to a global project `string` class. The gate now covers lowercase types, which are decided as when no class shares their name, and cppFindsOnlyNamesakes reads a `std::` name exactly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… SkipList<Key, Comparator>::Iterator, is that template's member (colbymchenry#2573) The declarator regex read a type as a name plus one template argument list, so `SkipList<Key, Comparator>::Iterator iter(&list);` was read from its last name alone and looked up from the calling function: leveldb's skip-list tests linked `iter.Valid()` to `leveldb::Iterator::Valid`, rocksdb's to `IteratorBase::Valid`, and rocksdb's SkipListRep iterators linked their `iter_` member's calls (`InlineSkipList<…>::Iterator iter_;`) to their own methods. The type is now read whole, `Outer<Args>::Member`, and C++ lookup finds the member in the class template. A library template's member read this way is a library type only as an iterator (`std::vector<int>::iterator`, never a project class named `iterator`). Others name another type that may be the project's (`std::vector<Slice>::value_type`, gmock's `typename std::tuple_element<N - 1, MatcherTuple>::type matcher`), so their calls go on as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ent's method (colbymchenry#2576) `std::deque<Slice>::const_iterator it; it->data()` was matched by the method's name alone, so it could reach any class with a `data`, and `itr->c_str()` on a `std::vector<std::string>::const_iterator` reached gtest's `FilePath::c_str`. A `->` on a value of an iterator type now reaches the container's element, as `->` on a smart pointer reaches what it holds: the element is read from the container's template arguments (cppRangeElementOf), before the container's own `iterator` alias would lose them (protobuf's `RepeatedPtrField<T>::iterator`), and looked up as C++ does. A library element (`std::string`) or the caller's template parameter calls no project method. An element only a class template's own `begin()`/`operator*` writes with its parameter (googletest's `ParamGenerator<T>::iterator`) goes on as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s the element's method through `->` (colbymchenry#2579) `auto it = logs_.begin(); it->IsSyncing()` had no type for `it`: a call on a library container (`std::deque<LogWriterNumber>`) returned nothing the auto-local reader could use, so `->` calls on it were matched by the method's name. A standard or abseil container's `begin`/`end`/`find`/ `lower_bound`/`rbegin`/… now types as `<container>::iterator`, and a standard search (`std::lower_bound`, `std::find_if`, `std::next`, …) as the iterator it is given first; colbymchenry#2576's cppArrowTarget then reaches the container's element. The element is looked up where the container's type is written (the `auto` local's declared site, a data member's class), not where the call is: an element nested in another class (`vs->obsolete_files_.begin()`) finds that class's type, not a namesake visible at the call. A holder's type stays looked up at the call: from a pimpl member's header the lookup finds a namesake (protobuf's other `DescriptorIndex`) or nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…()`, calls the method of what it points at (colbymchenry#2598) The extractor records `(*it)->Visit()` and `(*node).Weight()` by their bare names, which the name strategies then gave to whichever class had a method of that name (0.4-0.7 guesses), and a local a dereference sets, `auto first = *nodes.begin()`, had no type. `*x` and `x->` in C++ expression typing now dereference a container's iterator to its element as well as pointers and holders (cppDereferenced, used by cppExpressionType, cppMemberObject and cppChainInnerType), and a bare-name call written on `(*name)` (read at the call's column, where the receiver starts) is resolved on the dereferenced value's class by C++ lookup before the name strategies run: the method, or no project method for a library type, a template parameter's, or a class that lacks it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, `it->second.Flush()`, calls the value's method (colbymchenry#2601) The extractor records `it->second.Flush()`, `kv.second.Flush()` and `(*it).first.Print()` by their bare names, which the name strategies gave to whichever class had a method of that name — 45 `std::string::c_str()` calls went to googletest's `FilePath::c_str` — and a local such a member initializes (`auto& v = it->second`) had no type. `x.first` / `x.second` (and `->`) on a `std::pair` now type as the pair's elements in C++ expression typing (cppPairMember), so a map's iterator, range-for element or declared pair reaches its key and value, and colbymchenry#2598's bare-name receiver reader also takes `name->second`, `name.first` and `(*name).second`. A member typed by another class template's parameter (`CoreLocalArray<T>::AccessElementAndIndex()`'s `std::pair<T*, size_t>`) goes on as before: only the calling function's own template parameter names no particular class. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ound()`, calls the element's method (colbymchenry#2602) The extractor records a call on a subscripted receiver, like `statuses[i].IsNotFound()`, `buffers[key].Flush()` or `iters[i]->Valid()`, by its bare name, which went to whichever class had a method of that name, and a local an element initializes (`auto& s = statuses[i]`) had no type. - C++ expression typing reads a subscript: an array's element, what a pointer points at, a library map's mapped value, a sequence's element, or what a class's `operator[]` returns. - Declarations record their array dimensions (locals, parameters and data members, also through an alias), so a subscript, `*arr`, `arr->` and a range-for over an array read the type the array is declared with. - The bare-name receiver reader takes subscripts after a name or `(*name)`, reading the line with its literals blanked. - Overload arguments use the same subscript typing: a map's `values[key]` is its value type, no longer a `std::pair`. A/B vs main, every changed site triaged: leveldb 12 removed, 14 added, 1 retargeted, 11 metadata-only; fmt 6/4/10/2; redis identical; rocksdb 513/292/843/807; protobuf 58/5/34/66. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…OBUF_DEFINE_ACCESSOR(FieldDescriptor, number, int)`, is indexed where the macro is invoked (colbymchenry#2605) protobuf declares its descriptor accessors in their classes and defines them below with macros, like `PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, number, int)`; its reflection pastes `Reflection::Get##TYPENAME` in another. The parser never sees those definitions (the preParse blanks the invocations) and a declaration mints no node, so the methods had none and every call to them linked to nothing. - After either extractor (the kernel or the wasm walker), a step over the original source expands the file's own function-like macros whose body defines `Class::method(...) ... {`, at each invocation at namespace scope, and mints a method node per definition: named and qualified like an out-of-line definition, contained by the file and the same-file class, with the expanded head as its signature. Constructors, destructors and variadic macros are left out. - The parse worker leaves the raw kernel fast path only for a file that may define one (a cheap text test), so the step sees it. - The C++ resolver reads such a method's return type and parameters from its signature, and never takes it for a deleted overload. A/B vs main on the kernel path, every changed site triaged: leveldb and redis identical; fmt +13 nodes and rocksdb +11 (gtest's TestEventRepeater overrides); protobuf +257 nodes, 6,203 calls and 2 references added, 252 calls retargeted, 11 metadata-only, none removed. Kernel and wasm dumps are byte-identical, protobuf's one pre-existing Swift line aside. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…::ReadPrimitive<int32_t, WireFormatLite::TYPE_INT32>`, is named after its template (colbymchenry#2609) An explicit specialization of a function or member function template kept its template arguments in its name: protobuf's `template <> bool WireFormatLite::ReadPrimitive<int32_t, WireFormatLite::TYPE_INT32>(…)` was named `TYPE_INT32>` and qualified `…::ReadPrimitive::TYPE_INT32>`, the qualified id split at the `::` inside its arguments; rocksdb's `template <> int64_t Deserialize<int64_t>(…)` was `Deserialize<int64_t>`. A call writes the bare name, so nothing linked to them. Both extractors (wasm and the native kernel) now name a specialization after its template, from the template-id's own name, and take its receiver from the text before the declarator's name. That text must end in a real `::`: tree-sitter recovers Metal's `vertex VertexOut vertexShader(…)` with a missing, zero-width one. A specialization now meets its template among a name's candidates, so the resolver chooses between them wherever it picks among C++ candidates (a class's methods, the implicit-this walk and its bases, exact, qualified, global, fuzzy and macro-namespace matching): - a call that writes template arguments reaches the specialization written with the same ones (spacing aside, or qualified further on one side), else the template itself or an ordinary overload, else nothing: fmt's `num_bits<unsigned short>()` stays on `num_bits<T>()` instead of the nearer `num_bits<uint128>()`; protobuf's `Arena::Create<T>` keeps calling `DefaultConstruct<T>`, not the tests' `DefaultConstruct< DispatcherTestProto>` hook; a dependent `ReadPrimitive<CType, DeclaredType>(…)` links nowhere rather than to the first specialization. - a call that writes none, or `<>`, reaches a specialization only when nothing else has the name. - a class's own member template, specialized but none for these arguments, ends the lookup there instead of falling to a base's namesake. The `cpp-override` pass leaves specializations out on both sides: a member template is never virtual. Validated on the cgstd kit against main (f7951a1): leveldb and redis byte-identical; fmt: 8 specializations renamed, every call target as on main; rocksdb: the cassandra `Serialize<T>`/`Deserialize<T>` calls (25 in format.cc, 75 in its tests, which main sent to `ColumnBase` methods) each reach the specialization their argument names (100 of 100 agree); protobuf: `MergeFrom<MessageLite>` (3 calls), `ReadPrimitive<int, TYPE_ENUM>` (2) and a test's `ReadPackedPrimitive<uint32_t, TYPE_FIXED32>` reach their specializations; 5 dependent calls like `GetRepeatedFieldInternal<T>`, and one `MutableRepeatedFieldInternal<int>` (the macro writes `<int32_t>`), no longer link to the first specialization a macro defines. Kernel and wasm dumps identical (but for protobuf's known Swift line). Full suite: 7179 passed, 0 failed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…seWithLimit(limit, data, result)`, reaches the overload its arguments fit (colbymchenry#2612) A C++ call to a function outside a class took whichever same-named function the name strategies ranked first, by file and line proximity, never by its arguments. Methods already picked their overload by argument count, string literals and written argument types (cppOverloadFor); functions never did. protobuf's `ParseWithLimit(limit, data, result)` on an `int32_t result` reached the `int64_t&` overload beside the call, and rocksdb's `NewLRUCache(cache_opts)` the overload taking a `size_t capacity`. Exact, qualified (global, exact, partial) and macro-namespace matching now keep their choice of function, then let the call's arguments pick among its overloads, the functions of its qualified name the call's file can see (cppFunctionOverload, with cppOverloadFor's count-then-type fit; the macro-namespace scorer breaks its ties by type). The choice stays on a tie. - An overload only another source file sees (another `.cc` file's `static`, an unnamed namespace's) never competes. When the name strategy had picked such a function, which was then rejected, the call now links to the overload its file sees: leveldb's `GetLengthPrefixedSlice(&input, &key)` reached memtable.cc's static and linked nothing. - A function hides a class of its name, so a call to a name both have reaches the function. - Only C++ calls: protobuf's Rust FFI calls lost their `extern` targets to the C definitions when every language went through it. Argument types read better too: a parameter named like the function it belongs to, protobuf's `std::string u64(uint64_t u64)`, was read as the function's own declarator, so `u64` looked like a `std::string`. Validated on the cgstd kit against main (627018e), every changed site classified by argument count against both targets, ~100 type-decided sites read by hand: - leveldb: 5 calls linked that linked nothing. - fmt: 283 retargeted (139 to an overload that takes that many arguments, 110 by type, 34 multi-line or defaulted); fmt's `fill_n(it, …)` on an iterator of unknown type now takes the `basic_appender` overload, right for fmt's usual iterator. - rocksdb: 101 linked, 1118 retargeted (720 by count, 339 by type). - protobuf: 29 linked, 941 retargeted (372 by count, 558 by type), e.g. the compiler helpers' `QualifiedClassName(enum_, …)` reaching the `EnumDescriptor*` overload and `Namespace(d->file())` no longer calling itself. - redis (C): byte-identical. Full suite: 7188 passed, 0 failed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…wn file can see, past another file's `static` copy (colbymchenry#2613) A C call now links to the function of its name the call's own file can see: its own, or one a header it includes defines, else one with external linkage, the best placed of them (cppFunctionOverload's C branch). Before, the name strategies took the function nearest the call; when that was another source file's `static`, isVisibleAcrossFiles dropped it and the call was left unlinked, so no call reached redis's `server.c` `mstime()` or `ustime()`. - A source file's `static` function is visible to a file that `#include`s that source file: hiredis's async.c includes dict.c, redis's ae.c its event-loop backend. - The coordinator's `resolveImport` hook falls back to matchByFilePath for a C/C++ `#include` the import resolver can't place, as resolveOneInner already does, so cppIncluders sees includes only the end of their path names: protobuf's `google/protobuf/descriptor.upb.h`, which two generated copies end in, and jemalloc's `jemalloc/internal/*.h`. Standard headers are left out: otherwise `#include <math.h>` makes jemalloc's test/math.h visible to its own include through isIncludedCppTestSuite. A/B on the cgstd kit against main 4a591ae: leveldb, fmt and rocksdb identical; redis +194 calls linked and 115 retargeted (114 to hiredis alloc.h's POSIX `static inline`s, 1 known miss: lua-cjson's fpconv_init to an inline only `#ifdef USE_INTERNAL_FPCONV` builds use); protobuf 412 upb calls retargeted from the unincluded php-upb.h amalgamation to the headers the calling file includes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…'s `#ifdef USE_INTERNAL_FPCONV` `fpconv_init`, gives way to the source file the build compiles (colbymchenry#2615) lua-cjson's fpconv.h defines `static inline void fpconv_init()` only under `#ifdef USE_INTERNAL_FPCONV` and otherwise declares it `extern` for fpconv.c to define. Nothing in redis's tree sets the flag, so its build calls fpconv.c's, but colbymchenry#2613's rule (the file's own or an included header's definition first) took the header's. c-feature-variants.ts decides whether a line is compiled only by a build that sets a flag nothing sets. It reads the conditionals around the line (cpp-macro-visibility's directiveLines, now exported): `#ifdef`/`#ifndef`, and `#if` with defined, !, &&, ||, parentheses, integers and bare flags, then `#elif` and `#else`. A flag is set nowhere when the line's own file doesn't define it, no build file from the project root down to the definition or the call names it, and no C/C++ file of their components (the tree under the nearest directory with a build file) defines it. Reserved and platform names stay unknown. cppFunctionOverload's C branch then takes a non-static, non-variant definition outside the file's tier when one exists, when every definition the file sees is such a variant, and when the call itself is compiled by the default build. A branch around both the definition and the call decides nothing: redis's quicklist.c tests call their own `static mstime` inside `#ifdef REDIS_TEST`. A/B against main dc4ccf6: leveldb, fmt, rocksdb and protobuf identical; redis 1 call retargeted, lua_cjson.c's fpconv_init() from fpconv.h to fpconv.c. Indexing time within noise (redis 10.5-11.9 s on main, 10.2-12.3 s here). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… holder's `get()`/`value()` returns, like rocksdb's `edits.back()->MarkAtomicGroup(…)`, calls the element's method (colbymchenry#2616) A call on what a library container hands out linked to nothing: the standard library's `front()` returns nothing the index can read, and the library-receiver gate keeps the name strategies off a `std::vector`. cppLibraryElementOf reads what such a member returns, where the container's type is written: - `front()`, `back()` and `at(i)` of a sequence (std/absl, and the project sequences cppRangeElementOf already treats as such) → its element; - a library map's `at(key)` → the mapped value; - `std::stack`/`std::priority_queue` `top()`, `std::queue` `front()`/`back()` → the element; - `get()` of `std::unique_ptr`/`std::shared_ptr`/`absl::NoDestructor` → a pointer to the held type, `value()`/`value_or()` of an optional or an `absl::StatusOr` → the held type. It is asked in cppChainInnerType (a chain `recv.front().m()`) and in cppExpressionType (an `auto` local, a range, an argument), next to cppLibraryIteratorOf. A/B on the cgstd kit against main 98127e5: leveldb, fmt and redis identical; rocksdb 299 calls added, 89 retargeted (overloads picked by a typed `.get()` argument, like `test::PerThreadDBPath(env_.get(), …)`, and inherited methods on a typed element), 10 removed (guesses on what turned out to be library containers) and 56 confidence-only; protobuf 7 added and 16 retargeted (cast helpers off their `std::shared_ptr` overloads, `Add` off a deleted one). Every site was joined with the element type an instrumented arm logged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…DynamicCastMessage(const MessageLite*)` and `DynamicCastMessage(MessageLite*)`, a call reaches the one C++ picks (colbymchenry#2618) Overload fit by argument type (cppArgumentTypeChoice) scored a `const T*` parameter and its `T*` twin alike, and the one declared first won: protobuf's `DynamicCastMessage<T>(ptr.get())` on a `std::shared_ptr<Message>` took the `const MessageLite*` overload. - cppConstTwinChoice: of the best-fitting overload's twins, alike but for the `const` of a pointer or reference parameter, the one that adds `const` to the fewest arguments. - cppWrittenTypeFit: a pointer or reference to a non-const type never takes a const object, nor a reference a value a call returns (cppNamesObject tells a named object from a call's result); an object is no pointer, nor a pointer to characters a pointer to another type (or the reverse). None of that for an argument of a template parameter's type, and the pointer and `const` rules not for a parameter the function's own template parameter types (it deduces the `const`). - The argument's type keeps a `const` its declaration writes in front (`const Buffer* b`, `constexpr char key[]`), which the declarator regex leaves out; an array is a pointer to its element; `&ref` is a pointer to the referee; cppLibraryElementOf keeps a `const` in a holder's template argument (`std::shared_ptr<const Message>`). - Template headers are read 8 lines up, not 3 (fmt's `join`). A/B on the cgstd kit against main 0893606: leveldb and redis identical; fmt 14, rocksdb 25 and protobuf 194 calls retargeted, each triaged against C++'s choice (const twins, objects and arrays off the wrong pointer overloads, temporaries to `T&&`). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every full test run left about 40 directories in the temp dir, even when it passed. On the Windows dev machine about 16,000 had piled up by mid-October. A full run with TEMP pointed at an empty folder showed where each one came from: - function-ref (20) and ts-field-classification (3): each case's `finally` cleared `tmpDir` right after `cg.destroy()`, so the `afterEach` that removes the dir found nothing to remove. - npm-sdk (3) and liveness-watchdog (3) never removed the dirs they made. - extraction's colbymchenry#1502 suite (3) made a dir in `beforeEach` and had no `afterEach`; pr19-improvements' schema-version case (1) made one inline. - setup-home-sandbox (6 on Windows): Vitest runs no hooks for a file whose every test is skipped, so each platform-gated file left its home sandbox. Each sandbox now records itself in a per-run registry, and a global teardown removes whatever is still listed and names any dir still in use. With the fix, the same full run leaves nothing behind but Node's own reused compile cache. test-home-sandbox gains two tests: a file's home is listed in the run's registry, and the teardown removes every listed home, then the registry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mchenry#2624) Two MCP test files sometimes failed under full-suite load on Windows with EBUSY in their afterEach, and then left their temp dir behind: - mcp-status-freshness: every case leaves an index-freshness measurement worker still ending, and the deadline case's worker is terminated in the middle of its measurement. Until it exits, the worker can hold the index or the project dir, and the removal raced that exit (EBUSY on codegraph.db or on the dir, ENOTEMPTY on .codegraph). The afterEach now awaits endFreshnessMeasurements(), the server's own shutdown wait, before it closes the index and removes the dir. - mcp-initialize: after the killed server's 'close', the dir itself can stay busy a moment longer (EBUSY rmdir). It freed within about 80 ms, with no process left under the server. A bounded `maxRetries` doesn't help: on Node 22, fs.rmSync never retries EBUSY on a directory and throws at once. So removeWhenReleased() retries the removal itself, for up to 5 s, and a Windows-only test holds a dir as a child's cwd to prove the helper waits. Under CPU load (14 of 16 cores busy, two runs at once), the status teardown failed 20 times in 240 without the wait and 0 times in 240 with it. Alternating real runs of both files failed in 5 of 18 on main and 0 of 10 with the fix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ions.cf_paths.emplace_back(…)` on a `std::vector`, no longer links to a project method of that name (colbymchenry#2626) The extractor records a call on a member chain (`a.b.m()`, `a->b->m()`, `this->p_.m()`) by its bare method name, so it went to whichever project method shared the name. A census of the cgstd kit found most such calls linked by name alone, and most of those links wrong. Above all, they were on members of standard-library types, whose methods (`size`, `reset`, `push_back`, `get`, `empty`) a large codebase has namesakes of. matchCppOutsideMemberChainCall reads the chain where the call is written (cppMemberChainReceiver) and types it with the existing cppMemberObject: - a data member is typed by its declared type in its class (cppDataMemberType); - `->` goes through a pointer, smart pointer, optional or iterator. It gives no edge when the object the method is called on is a library type (isCppLibraryType) or a type under a namespace the project declares nothing in (isCppOutsideQualifiedType). These are the call-chain resolver's rules. Unlike a call chain, the operator is read as written, so `.` on a `std::vector<T>` or a `std::unique_ptr<T>` is that type's own member, whatever `T` is. Anything else, a member of a project class included, goes to the name strategies as before. Kit A/B against main a257069 (only removals; nothing added or retargeted): | repo | calls removed | |---|---| | rocksdb | 2,001 | | protobuf | 234 | | leveldb | 15 | | fmt | 12 | | redis | 0 (identical) | Every removed link was a bare-name guess. Of the 2,235 removed sites in rocksdb and protobuf, a judge reading the members' declarations across each repo rated: - 2,044 as wrong links (a library type's namesake, or an unrelated class); - 165 unknown; - 10 not counted as chains by its pattern (after a cast, like `(int)data->entries.size()`); - 16 right. These turned out to be name collisions on the judge's side, checked in the source (`std::deque logs_`, `std::shared_ptr decompressor`, `std::vector ids`). 24 random removals were also read by hand, and all were correct. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ead->rand.Next()` on a `Random64`, links to the method of the member's class (colbymchenry#2627) Step 2 of member chains; colbymchenry#2626 was step 1. matchCppMemberChainCall (formerly matchCppOutsideMemberChainCall) now also resolves a chain whose member is of a project class, in the same steps matchCppUnspelledReceiverCall takes for `it->second.Flush()`: - the class C++ lookup finds for the member's type, where that type is written; - then its method, or one it inherits (cppMethodOf, with overloads picked by argument types); - or no edge when the class surely lacks it (cppLacksMethod). Before, such a call went by its bare name to whichever class had a method of that name. Inside a method it could also be taken for an implicit `this` call on the method's own class. A member is now read from the class node C++ lookup found (cppDataMemberIn), not from the first class of that qualified name that declares it (cppDataMemberType, removed). rocksdb's db_bench_tool.cc and db_stress_shared_state.h each declare a ROCKSDB_NAMESPACE::ThreadState, whose `rand` is a Random64 in one and a Random in the other. Kit A/B against main 6e884d7: | repo | added | retargeted | same target, now typed | removed | |---|---|---|---|---| | rocksdb | 571 | 2,008 | 1,738 | 2 | | protobuf | 42 | 95 | 142 | 0 | | leveldb | 42 | 30 | 141 | 0 | | fmt | 0 | 56 | 20 | 0 | | redis | 0 | 0 | 0 | 0 (identical) | The most common rocksdb retargets are DBImpl -> DB (calls on a `DB*`), BlobContents -> Slice, BlobFileReader -> FileDescriptor and EnvWrapper -> Env. Hand-reading random samples found one wrong retarget (db_bench's `thread->rand.Next()`, the ThreadState namesake), which led to the member-lookup fix above. After that fix, all 76 sites sampled across the four repos were correct: - 30 retargets and additions, plus both removals, in rocksdb; - 20 in protobuf, 14 in leveldb and 10 in fmt. The two removed calls are `task_reader->rep_->GetCoroutine(…)` on a `SstFileReader::Rep`, which has no such method. They had linked to `CoroDB`'s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… `options.statistics->getTickerCount(…)` on a RocksDB `Options`, links to the member's class (colbymchenry#2628) Step 3 of member chains, after colbymchenry#2626 and colbymchenry#2627. The type of `x.member` was read from x's class alone, so a member that class inherits was never found and the call fell back to its bare name. A temporary diagnostic on main measured why chain calls still fell through: 2,801 of 4,884 were a member declared in a base class. Nearly all were rocksdb's `Options`, which declares no members of its own and inherits `statistics`, `table_factory`, `listeners`, `prefix_extractor` and the rest from `DBOptions` and `ColumnFamilyOptions`. The other causes are left for later: - a chain whose base can't be typed: 875; - a class the index lacks, e.g. protobuf's generated `_impl_`: 626; - a member found nowhere: 337. cppExpressionType's member branch now uses cppInheritedDataMember, the lookup the calling class's own members already use (colbymchenry#2533): - the class's own declaration first; - then the classes it derives from, through their base edges, to depth 4; - a base that depends on the class's template parameters is skipped unless a using-declaration brings the name in. Kit A/B against main e8ec7fd: | repo | removed | added | retargeted | same target, now typed | |---|---|---|---|---| | rocksdb | 1,410 | 634 | 392 | 184 | | protobuf | 0 | 2 | 149 | 55 | | leveldb, fmt, redis | identical | | | | - Removed: the bare-name guesses on inherited std::shared_ptr and std::vector members, such as `options.table_factory.reset(…)` → FSWritableFilePtr::reset (654) and `options_.db_paths.emplace_back(…)` → autovector::emplace_back (256). - Added: `options.statistics->getTickerCount(…)` → Statistics (432), `rate_limiter` → RateLimiter (51), `comparator` → Comparator (26). - Retargeted: DummyOldStats -> Statistics, DB -> Env, ClockCacheShard -> Cache. - protobuf's generated `this_._internal_metadata_.Clear<…>()`, `MergeFrom<…>` and `unknown_fields<…>` now reach InternalMetadata, a member every message inherits from MessageLite. Before, they reached the message's own `Clear` or `Any::MergeFrom`. All 50 random sites read by hand (36 rocksdb, 14 protobuf) were correct. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…colbymchenry#2629) A full suite run on Windows left a `serve --mcp --path %TEMP%\cg-rebuild-…` process behind. Its parent was gone, and its one thread was suspended with 0 CPU used. It would never exit, and it held the checkout's dist. libuv creates every detached child suspended on Windows and resumes it only at the end of `uv_spawn`. A launcher killed inside that call never resumes its daemon. The daemon never runs, so it writes no daemon.pid, and nothing can find or stop it. The rebuild test's teardown killed its launcher at once. The test stops the daemon on purpose, so by then the launcher is serving calls in-process with a retry timer running. When the retry comes due during the last call, the launcher spawns its daemon as soon as that call answers, just as the teardown begins. Under load the two coincide. The teardown now: - closes each launcher's stdin and waits for it to exit. A graceful exit can't land inside the synchronous spawn. - records the daemons each launcher spawns (colbymchenry#1773's recorder) and stops them until all have exited. One spawned as its launcher exited may still be starting, and stopDaemonAt leaves a daemon running until it answers. The recorder's comment now says that a launcher killed mid-spawn records nothing. CODEGRAPH_DAEMON_RETRY_MS=2500 puts the retry inside the last call on a quiet box. Under it, main's test left a daemon in 6 of 12 runs: 2 suspended ones, plus 4 live ones whose fixture then failed to delete. This test left nothing in 12 runs. Of the mutants, killing launchers again, a single stopDaemonAt and no spawn recorder each leaked. The other daemon test files left nothing in 2 runs each. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ule when the module changes (colbymchenry#2630) `NODE_PATH_EXCLUDELIST.includes(p)` after `import { NODE_PATH_EXCLUDELIST } from './constants'`, `store.get(k)` through a default import or `api.get()` through a namespace import is linked by a fresh index through the import. When the module was missing or lacked the export, the failed reference was parked under its member's name (`includes`, `get`), which the module seldom declares and which the retry skips past its 500 ceiling. A sync that gave the module the export never relinked it, nor one that added the module where no import edge reached its file. A direct member reference through an import binding of a project module is now parked under the module's key, as whole names through a binding are since colbymchenry#2450, and keeps two more triggers. Its member's name, through a new expression index on the tail its name gives (schema v16), under the same ceiling as the references still parked under that name: a namespace import's barrel forwards the member from another file, and a guess by name links a receiver whose module never resolves. Its receiver's name, through a prefix range on the module-name index of colbymchenry#2450: a barrel's import of the declaring file that no walk finds. The router navigation retry and the dead-code check read module-parked calls by their member through the new index. A chain or a longer path keeps its member's tail: on vscode, parking them too tripled the refs an edit of a hot module retries, and none of them resolved. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…odule's file (colbymchenry#2633) In a project with a `vue` dependency or a `.vue` file, a script's module import through `@/` or `~/` went through the Vue resolver's own mapping to `src/`, which linked the first node of the file it found: a constant the module declares, or a component. A tsconfig alias that starts like a Nuxt virtual module (`#app/where`) linked the importing file itself. Without Vue the import resolver links the module's file node, following tsconfig paths. So the walks a sync makes from a module or barrel that appears or goes away to the files importing it (colbymchenry#2512, colbymchenry#2560) never found these importers. Which node the edge hit also depended on the order a file's nodes were stored, so a sync and a fresh index could disagree. The Vue resolver now leaves a module import to the import resolver whenever that finds the file, and only adds what a Vue app's bundler finds from a plain script: a component named without `.vue`, or a folder's `index.vue` (`import Layout from '@/layout'`), linked to the file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…when the class's module changes (colbymchenry#2637) `cache.get(k)` after `const cache = new LRUCache(10)` (or an annotated local, a typed parameter, a default or namespace import of the class) is linked by a fresh index through the local's declaration. When the class's module was missing or empty, the failed call was parked under its member's name, `get`, which more failed refs share than the retry's ceiling, so a sync that added, restored or filled in the module never retried it. Such a call now waits under the key of the module the type's binding imports, as a member of the binding itself does since the previous change (2630), and keeps that change's member and receiver triggers. Only calls are parked this way: in TypeScript a typed local's member is only ever a call, and a method of `this` passed as a value is looked up in the enclosing class. A local declared as an array of the class (`results: URI[]`) keeps its member's tail. Resolution reads the element type there too, but `results.push` is the array's method, which no edit of the class's module links. On vscode those calls were 60% of what the parking added and nearly doubled what an edit of `uri.ts` retried. Inference now reports the line it read the declaration from. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…glob leaves members out (colbymchenry#2638) pnpm's `packages/**/*` (folo) and npm's `libs/**/*` (bitwarden) list member packages at any depth, `packages/plugin-*` and `apps/*/web` name them by part of a folder name or by a folder below the wildcard, and a negated glob (`!**/example`, `!examples/with-nx`) leaves some out. The workspace loader listed only the folders directly under the text before the first `*` and took a negation for a folder name, so folo's `@follow/utils`, two levels under `packages/`, was no member and every import of it counted as an outside package's. Globs now match folder by folder: `*`, `?` and `{a,b}` within a name, `**` across folders, breadth-first so a member nearer the root keeps a name a deeper copy also declares. A wildcard never enters a dot-folder, `node_modules` or `bower_components`, and a `**` skips `test` and `tests` folders as pnpm's default ignores do; a folder the glob names literally is always entered. What a negated glob matches is no member. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…links to nothing, not the class's method (colbymchenry#2639) `items: Item[]` was read as an `Item`: the annotation's capture stopped at `[`, so `items.find(…)` linked to `Item.find` whenever the class declares a method of that name. On vscode that was 16 wrong edges, like `distinctElements.find(…)` on an `ExplorerItem[]` reaching `ExplorerItem.find` and `ranges.map(…)` on an `OffsetRange[]` reaching `OffsetRange.map`. The annotation pattern now captures a bracket suffix with the type. On an array of a type, a method arrays have links to nothing and isn't matched by its name either. Any other method can't be called on the array, so it is called on an element that shares its name, as in `d.forEach(d => d.dispose())`, and keeps the element's type as before. An indexed type (`Element["groupIds"]`) is still read as the type it indexes. The sync parking that came with the previous change now reads arrays from the type inference gives instead of re-reading the declaration line: an array method on an array keeps its member's tail, and an element's or an indexed type's call waits for that type's module. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ts (colbymchenry#2641) When several classes share a name, a TypeScript or JavaScript call on a typed value broke the tie only by the call site's own file, then took the first declaration indexed, and a `this.<field>` call by folder nearness. On vscode, about 25,000 calls on classes vscode imports from its own `vs/base` and `vs/platform` reached the copies the Copilot extension vendors, 907 `Emitter` calls a copilot test fixture's copy, and 952 calls whose first match was a test fixture's class linked to nothing. The file declaring the class the call's file imports under the written name, read through re-exports, `import type`, namespace imports, default imports and `require`, is now passed to resolveMethodOnType, which picks that file's method among several, through the supertype walk too. `this.<field>` calls check the import of the call's own file before folder nearness. Java, Kotlin and every other language are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Moves the base from e63fe2e to upstream 10e316d (main, 2026-10-19), 363 commits on, past v1.6.2. The four fork patches merge back without a conflict, line for line as they were. Conflicts were only the version marker, the lockfile, release.yml (the fork keeps its npm-free workflow) and install.ps1, where upstream's new Install-CodeGraphFiles swap now names the fork in its reinstall hint, as does its copy in src/upgrade/index.ts.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4dc197c813
ℹ️ 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".
| const std = (n.language === 'go' ? GO_STD_METHODS : RUST_STD_METHODS).has(n.name); | ||
| return !std || /^(?:self|Self)$/.test(shape.receiver) || (shape.receiver !== '' && sharesReceiverWord(shape.receiver, n)); |
There was a problem hiding this comment.
Preserve indexed Rust receiver calls
When a project Rust type has a standard-library-named method and it is called through an indexed receiver, e.g. items[0].map(), this filter drops the real Item::map target: the new shape parser reduces the receiver to items, which neither equals nor shares a word with Item. That leaves the call unresolved even though the indexed expression has a concrete project element type; retain candidate methods for indexed/wrapped receivers unless their type is known to be a standard-library type.
Useful? React with 👍 / 👎.
| // Require statements — each declarator of a list too (`var express = require('../'), | ||
| // request = require('supertest')`), and a member of the module (`require('./utils').methods`). | ||
| const requireRegex = /(?:\b(?:const|let|var)\s+|,\s*)(?:([A-Za-z_$][\w$]*)|{([^}]+)})\s*=\s*require\(\s*['"]([^'"]+)['"]\s*\)(?:\s*\.\s*([A-Za-z_$][\w$]*))?/g; |
There was a problem hiding this comment.
Keep CommonJS bindings from claiming
this calls
With valid whitespace inside a destructured CommonJS require, such as const { matches } = require( "./esquery" ), this broadened regex now records matches as an import binding. Because TS/JS extraction represents this.matches() by the bare name and import resolution runs before method matching, the call is resolved to the required function instead of the enclosing class's own matches method, creating a false cross-module edge. Exclude calls written through this from the import-binding path.
Useful? React with 👍 / 👎.
Moves the base from
e63fe2eto upstream10e316d(main, 2026-10-19), 363commits on, none of them merges, and releases as
1.6.2-fmagent.1.Why
main, notv1.6.2Upstream tagged
v1.6.1(09-29) andv1.6.2(10-03) since our base, but what thissync is for landed after
v1.6.2:usingaliases, smart pointers, iterators, range-for,auto, member chains,overloads picked by their arguments, gtest fixtures, macro-defined methods.
Out-of-line nested classes are no longer named
<anonymous>, and annotationmacros such as
LOCKS_EXCLUDEDare no longer read as function names.export const— plain consts,window.X = {…}and IIFE-scoped objects are invisible colbymchenry/codegraph#2300):const api = { load() {…} }inside a function or IIFE now gives
api::load. CommonJSmodule.exports = {…},call-argument literals and returned literals still do not.
v1.6.2itself slows indexing of JavaScript and Python projects; the fixes(perf(resolution): remove the JS indexing regressions since 1.6.2; the summary time covers the whole run (#2334) colbymchenry/codegraph#2362, perf(resolution): remove the Python indexing regressions since 1.6.2 — graph byte-identical (#2332) colbymchenry/codegraph#2344) are on
mainonly.What happens to each patch
All four merge back without a conflict, and every line they add or remove is the
same as in
.6:fm_agentwork-directory exclusion — unchanged.name-matcher.tsby about 15k lines in this span, none of it overlapping.do not list
generator_function, on either extraction path.Conflicts were only outside them:
package.json,ui/package.json,package-lock.json: the version marker.release.yml: the fork keeps its workflow without the npm steps.install.ps1: upstream addedInstall-CodeGraphFiles, a Windows swap thatrenames files aside and puts them back on failure. Its "reinstall" hint names
upstream's installer; it now names the fork's, in
install.ps1and in its copyWINDOWS_SWAP_FUNCTIONinsrc/upgrade/index.ts(a test keeps the two identical).install.sh's hint for Windows shells still names upstream'sinstall.ps1, as in.6:installer-targets.test.tspins that string.FORK.mdand the fork'sCHANGELOGentries are updated for the new base.Verification
Tests (winbox, Ubuntu 24.04, Node 22, locally built kernel,
GIT_CONFIG_GLOBAL=/dev/null):10e316dbundle-launcher.test.ts(2) fails on both: the machine has nozip.daemon-older-version.test.ts(3) fails on the fork only, every run. Upstream'sDaemon outlives a CLI upgrade, loads parsers from the deleted package and zeroes every re-indexed file colbymchenry/codegraph#2335 stops a daemon left by an older install, but
isOlderReleasecomparesplain
X.Y.Zonly, so a1.6.2-fmagent.Nbuild neither stops an older daemonnor is stopped by a newer one. FM-Agent runs
init/indexand starts no daemon.Written up in
FORK.md; this repo's CI does not run these.fm-agent-*files are collected and pass.codegraph-benchmark (
make check, with 11 new cases infmagent-project/codegraph-benchmark — one per extraction bug FM-Agent has hit:
upstream colbymchenry#1211, colbymchenry#1373, colbymchenry#1585, colbymchenry#1588, colbymchenry#1727, colbymchenry#1741, colbymchenry#1747, colbymchenry#1861, and the four
patches here):
1.6.0-fmagent.610e316d, upstreamv1.6.2v1.6.0v1.5.0What FM-Agent reads, on 12 projects, through FM-Agent's own
CodeGraphExtractor:.6→ this.6/ upstream / thisserver/etcdserverlibcrateslibsrcEleven projects come out the same as upstream; on opencode the patches bring back
407 edges. Reading samples of what changed against
.6, by the call site's sourceline: the edges that go are mostly calls on library values landing on a
same-named project method (
.clone(),.map(),.get(),zap.Error(),super.x()on itself); the ones that come are mostly real — jsoup's enumconstants now have their own method bodies (+111 functions, the parser's state
machine), and etcd's and leveldb's field and base-class calls resolve.
Not fixed here (upstream, to report)
Optionor aBoxfield (errs[0].io_error(),prev.choose(seq)) is no longer resolved.About 5 in 25 of the edges ripgrep loses are real ones like these.
v1.6.2: in a class whose file doesconst { parse, matches } = require("./esquery"),this.matches(...)resolvesto the required function instead of the class's own
matchesmethod(ESLint
lib/linter/source-code-traverser.js)..6got this right.Both are recorded for a separate check before reporting.
Release
1.6.2-fmagent.1, as a prerelease first so the build can be checked againstcodegraph-benchmark before FM-Agent's pin moves (a separate PR there). Projects
FM-Agent analysed with
.6need one full run before--incremental: theextraction version goes from 27 to 28, and C++ function names change.