fix(go): address code review findings for scope resolution hooks

- Add GO_BUILTIN_TYPES exclusion set to prevent qualifying builtins
  like 'int', 'string', 'error' with package prefix (review H2)
- Update FinalizeStats.totalEdges JSDoc to reflect per-draft counting
  semantics after multi-file import target support (review H3)
- Add tracking issue TODO(#1239) for range-binding V1 limitation
  that uses module scope as fallback (review M3)
This commit is contained in:
Evan Wang 2026-05-01 05:21:40 +08:00
parent 705e3c733a
commit b72bfcdf92
3 changed files with 38 additions and 10 deletions

View file

@ -127,20 +127,22 @@ export interface FinalizedScc {
/**
* Counters reported by `finalize`.
*
* **Counting granularity** — all edge counters are **per-`ParsedImport`**,
* not per-materialized-`ImportEdge`. A single `wildcard` ParsedImport that
* expands to N exports counts as one linked edge in these stats; the
* materialized output (`FinalizeOutput.imports`) will have N edges for
* that input. `dynamic-unresolved` ParsedImports count as linked (they
* pass through with no `linkStatus`), so `linkedEdges` ≠ "has a
* **Counting granularity** — `totalEdges` is **per-generated-`ImportEdgeDraft`**,
* which may exceed the number of `ParsedImport` records when
* `resolveImportTarget` returns a multi-file array (e.g. Go package-scoped
* imports fan out to every `.go` file in the target directory). A single
* `wildcard` ParsedImport that expands to N exports also counts as one
* linked edge here; the materialized output (`FinalizeOutput.imports`) will
* have N edges for that input. `dynamic-unresolved` ParsedImports count as
* linked (they pass through with no `linkStatus`), so `linkedEdges` ≠ "has a
* BindingRef" — use the `bindings` map for that.
*
* In other words: `totalEdges === input.parsedImports.length` summed
* In other words: `totalEdges >= input.parsedImports.length` summed
* across files, and `linkedEdges + unresolvedEdges === totalEdges`.
*/
export interface FinalizeStats {
readonly totalFiles: number;
/** Total `ParsedImport` records seen across all files. */
/** Total `ImportEdgeDraft` records generated (≥ ParsedImport count). */
readonly totalEdges: number;
/**
* `ParsedImport`s whose finalized edge does NOT carry

View file

@ -8,6 +8,31 @@ import { synthesizeGoReceiverBinding } from './receiver-binding.js';
import { synthesizeGoTypeBindings } from './type-binding.js';
import { getTreeSitterBufferSize } from '../../constants.js';
/** Go builtin types that must not be qualified with a package prefix. */
const GO_BUILTIN_TYPES = new Set([
'bool',
'byte',
'complex128',
'complex64',
'error',
'float32',
'float64',
'int',
'int16',
'int32',
'int64',
'int8',
'rune',
'string',
'uint',
'uint16',
'uint32',
'uint64',
'uint8',
'uintptr',
'any',
]);
function inferPackageName(sourceText: string): string | null {
const match = sourceText.match(/^\s*package\s+([A-Za-z_][A-Za-z0-9_]*)/m);
return match?.[1] ?? null;
@ -155,8 +180,8 @@ export function emitGoScopeCaptures(
let raw = typeCap.text.trim();
while (raw.startsWith('*')) raw = raw.slice(1).trim();
if (raw.startsWith('[]')) raw = raw.slice(2).trim();
// Not a builtin or generic — safe to qualify.
if (raw.includes('.') || raw.startsWith('func(') || raw.startsWith('map[')) continue;
if (GO_BUILTIN_TYPES.has(raw)) continue;
const idx = raw.indexOf('[');
if (idx !== -1) raw = raw.slice(0, idx);
out[i] = {

View file

@ -96,6 +96,7 @@ function findEnclosingFunctionScope(
_node: unknown,
_scopeMap: ReadonlyMap<string, Scope>,
): Scope | null {
// V1 simplified: return null, use module scope as fallback
// V1 simplified: return null, use module scope as fallback.
// TODO(#1239): walk up tree-sitter AST to find enclosing func/method declaration.
return null;
}