/** * #1005 (Mechanism 3, Phase 1): recover REAL dependents for a Java/Kotlin * file when the import graph finds none, because a same-package reference * needs NO import statement at all -- JLS §6.5.5.1 / Kotlin's identical * same-package visibility rule both grant every top-level type in a package * unqualified access to every other top-level type in that SAME package, * with nothing in `chunk.metadata.imports` ever naming the reference. * Mechanism 1 (#1046, dotted-FQN import resolution, `jvm-source-root.ts`) * and Mechanism 2 (the `dependentAttributionIncomplete` honesty caveat, * `sameUnitAccessWithoutImport`/`samePackageTestConvention`) both already * shipped; this is the first mechanism that RESOLVES the same-package case * instead of only caveating it. * * Modeled directly on `csharp-type-reference-signals.ts` (build/resolve * split, `identifierBoundaryRe` word-boundary matching, a brute-force * reference implementation kept alongside the pruned one) -- see that * module's doc comment for the shared reasoning this one doesn't repeat. * The one structural difference: C# has no language-level "same package" * scope narrower than the whole corpus, so it needs a project-wide * uniqueness gate (tier 1) plus a namespace-scoping tier (tier 2) to safely * narrow candidates. Java/Kotlin's package IS that narrower scope already -- * G2 below restricts every candidate to `targetFile`'s own exact package * string, so there is no second tier here. * * ## The six resolution gates * * For target file `targetFile`, for each TOP-LEVEL `class`/`interface` type * `T` it declares: * * G1' (package-local uniqueness): exactly one file in T's OWN package * declares a top-level T. This is the REVISED gate from #1005's plan * review -- the ORIGINALLY planned gate was corpus-wide uniqueness * (mirroring C#'s tier 1), but that was measured to be simultaneously * INERT (0% effect on javapoet/klaxon, the corpora that motivated it) * and PERMEABLE (it passes exactly in the fabrication case it was * meant to catch, because a same-named competing declaration living in * an unindexed third-party jar is invisible to any corpus-wide count * by construction). Package-local uniqueness IS the real JLS * name-resolution scope, not a statistical proxy for it -- there is no * tunable dial between "corpus-wide" and "package-local" worth * keeping. * G2 (exact package match): candidate's derived package string EQUALS * target's, exactly (string equality). Deliberately NOT * `csharp-type-reference-signals.ts`'s `enclosingNamespaceChain` * walk -- Java/Kotlin have no enclosing-PACKAGE visibility rule (a * nested namespace sees its enclosing namespace in C#; a sub-package * is NOT visible to its parent package in Java/Kotlin), so copying * that mechanism would fabricate sibling/enclosing-package edges that * don't exist. * G3 (package must be derivable): a file whose package can't be derived * (`derivePackage` returns `undefined`) never participates as a * target OR a candidate -- see that function's doc comment. * G4 (candidate doesn't declare its own T): a candidate file with ANY * class/interface declaration named T -- NESTED-INCLUSIVE, no * `parentClass` filter -- is excluded entirely, mirroring * `fileDeclaresTypeName` in the C# module exactly. This must stay * nested-inclusive: narrowing it to top-level-only would reopen the * same fabrication `fileDeclaresTypeName`'s own doc comment describes * for C#/serilog -- a candidate with an unrelated NESTED type named T * would stay eligible, and a genuine reference to ITS OWN nested T * would be misattributed as a reference to the target's top-level T. * G5 (type declarations only): `symbolType` must be `'class'` or * `'interface'` -- Kotlin's extractor already maps `object`/`enum` * declarations onto `'class'`, so both fall out of this for free; see * `KotlinSymbolExtractor`'s own doc comment. * G6 (import-shadowing exclusion): exclude a candidate whose OWN * single-type or single-static import declares a DIFFERENT binding * for the simple name T. This is the fix for the fabrication class * the original plan review's corpus-wide gate couldn't see (a * same-named type reachable ONLY via an explicit import to somewhere * else). Computed from a per-file regex scan of raw `import` lines -- * see `collectShadowBindings`'s doc comment for why this reads source * text directly rather than `chunk.metadata.imports` (whose entries * get REWRITTEN by `jvm-source-root.ts`'s resolution and collapse a * real distinction this gate depends on). The exact rule, per JLS * §6.4.1/§7.5.1/§7.5.3 and Kotlin's import-alias semantics: * - A single-type import (`import a.b.Foo;`) or single-static * import (`import static a.B.C;`) SHADOWS a same-package * declaration of the identical simple name -- excluded. * - An import-on-demand (`import a.b.*;`, `import static a.B.*;`) * does NOT shadow (JLS §6.4.1: on-demand imports never shadow a * same-package type) -- never excluded on this basis. This falls * out for free: `SINGLE_IMPORT_LINE_RE` cannot match a line * containing a literal `*`. * - Kotlin's `import a.b.Foo as Bar` binds the ALIAS `Bar`, not * `Foo` -- a same-package `Foo` stays fully visible. The bound * name is keyed on the alias when present, never the FQN's last * segment, so this is automatic. * G7 (source-set direction, cheap insurance only): a non-test candidate * is never credited as a dependent of a test target (Gradle/Maven put * main sources on a test source set's classpath, never the reverse). * Measured to catch ZERO edges G6 doesn't already catch, independently, * across all four corpora used to validate this module (javapoet, * klaxon, retrofit, okhttp) -- kept because it's free, NOT because it's * load-bearing. Do not read its presence as evidence it's doing work. * * ## What this deliberately does NOT do (Phase 1 scope) * * - Does not touch `NO_DIRECTORY_NAMESPACE_LANGS` (`graph/dependency-graph.ts`) * -- that feeds the call-graph consumer (`buildCallerEdges`), a separate * Phase 2 concern with its own test evidence, not `findDependents`. * - Does not resolve Kotlin `typealias` or Java annotation declarations, and * does not add Kotlin top-level `fun`/`val` as resolution targets -- * deferred to Phase 3. Extending to bare top-level functions specifically * would re-import the exact problem class * `csharp-type-reference-signals.ts`'s own doc comment (and * `swift-symbol-usage-signals.ts`/`go-root-package-signals.ts`'s * distinctive-name gates) exist to guard against: a bare METHOD/function * name collides with unrelated same-named callables far more often than a * bare TYPE name collides with unrelated same-named types. * - Does not touch Swift at all. * * Verified against real clones (javapoet, klaxon, retrofit, okhttp) during * the design review that preceded this implementation -- see the PR * description for the real (not filesystem-spike) before/after edge counts * measured with THIS module against this repo's own parser/chunker. */ import type { CodeChunk } from './types.js'; interface JvmTypeDeclaration { file: string; typeName: string; package: string | undefined; } /** * Everything `resolveJvmSamePackageDependents` needs to resolve any number * of target files against ONE project-wide scan -- built once by * `buildJvmSamePackageIndex` and reused per target, mirroring * `CSharpTypeReferenceIndex`/`GoRootPackageIndex`'s "build once, resolve * many" discipline. */ export interface JvmSamePackageIndex { chunksByFile: Map; packageByFile: Map; /** Top-level class/interface declarations only -- see `collectTopLevelDeclarations`. */ declarations: JvmTypeDeclaration[]; /** `${package}::${typeName}` -> declaring files (G1'). */ pkgLocalOwners: Map>; /** package -> every JVM file with that derived package (G2 candidate set). */ filesByPackage: Map; /** file -> its own content with `import`/`package` lines and comments stripped (the G6/text-match corpus). */ nonImportContentByFile: Map; /** file -> its own single-type/single-static import bindings (G6). */ shadowBindingsByFile: Map>; /** file -> `isTestFile(file)` (G7). */ isTestByFile: Map; } /** * Build the project-wide index `resolveJvmSamePackageDependents` needs from * `chunks` once. `chunks` should be the FULL project chunk set -- G1' * package-local uniqueness is scoped to a package, not to any one target * file, so every file sharing a package must be visible to compute it * correctly. */ export declare function buildJvmSamePackageIndex(chunks: CodeChunk[]): JvmSamePackageIndex; /** * Find Java/Kotlin files (any directory, production or test) that reference * one of `targetFile`'s declared top-level type names via same-package * visibility -- see the module doc for the full six-gate rule -- against an * already-built `index` (see `buildJvmSamePackageIndex`). Excludes * `targetFile` itself. Returns a sorted, deduplicated list of filepaths -- * empty when `targetFile` isn't Java/Kotlin, has no derivable package, * declares no package-locally-unique top-level type, or genuinely has no * textual referrers in the index. * * `targetFile` must be the exact `chunk.metadata.file` string used by * `targetFile`'s own chunks within the chunks `index` was built from -- * mirrors `resolveCSharpTypeReferenceDependents`'s same no-normalization * discipline. */ export declare function resolveJvmSamePackageDependents(targetFile: string, index: JvmSamePackageIndex): string[]; /** * BRUTE-FORCE reference implementation of `resolveJvmSamePackageDependents`: * identical gate logic (`resolvesJvmSamePackageReference`), but candidates * are every file in `index.chunksByFile` with G2 checked explicitly, rather * than the pre-narrowed `filesByPackage` lookup the fast path uses. Because * G2 (exact package-string equality) is checked EXACTLY, not approximated, * this is not merely a safe superset the way C#'s tokenizing candidate index * is (see that module's own doc comment) -- the fast and brute-force paths * must agree EXACTLY on every corpus, not just "brute-force never drops a * match the fast path found." `jvm-same-package-signals.test.ts` asserts * this equality directly (the P1 property). * * Never call this in production: it revisits every file in the corpus for * every target's every declared type name, exactly the cost * `filesByPackage` exists to avoid. */ export declare function resolveJvmSamePackageDependentsBruteForce(targetFile: string, index: JvmSamePackageIndex): string[]; /** * Single-target convenience wrapper around `buildJvmSamePackageIndex` + * `resolveJvmSamePackageDependents`, for callers resolving just ONE target * file (`get_dependents`'s file-level recovery). Callers resolving MANY * target files against the same chunk set should build the index once * themselves instead of calling this in a loop -- see * `JvmSamePackageIndex`'s doc comment. */ export declare function findJvmSamePackageDependents(targetFile: string, chunks: CodeChunk[]): string[]; /** * #1005 Phase 2: the PER-TYPE twin of `resolveJvmSamePackageDependents`, for * `graph/dependency-graph.ts`'s call-graph tier (`getCallers(filepath, * symbolName)` is always scoped to ONE symbol, never "every dependent of this * file"). Wraps the same `collectDependentsForName` the file-level resolver * already unions across all of `targetFile`'s declared types -- this is an * ADDITION, not a modification: the file-level resolver's contract and * callers (`findDependents`'s recovery tier) are untouched. * * Scoping matters: a JVM file can top-level-declare MORE than one * class/interface (idiomatic for sealed hierarchies or grouped data * classes), and the file-level resolver's union means "the set of files that * reference ANY of this file's declared types" -- correct for "who depends * on this FILE", but wrong for "who calls THIS type": a reference to a * sibling type declared in the same file would be misattributed as a * reference to `typeName`. This function applies the exact same G3 * (package-derivable) and target-is-test gates the file-level resolver does, * but resolves candidates for `typeName` alone -- returning `[]` immediately * when `typeName` isn't one of `targetFile`'s own TOP-LEVEL declared * class/interface names (G5's type-only restriction still applies; a bare * method/function seed is never resolvable here, by construction). */ export declare function resolveJvmSamePackageDependentsForType(targetFile: string, typeName: string, index: JvmSamePackageIndex): string[]; export {}; //# sourceMappingURL=jvm-same-package-signals.d.ts.map