What
docs/plans/issue-2088.md's findTopLevelFunctionNodeByName (condition 4's identifier-resolution chain, WU-2) recognizes a top-level function-declaration statement via an exact string match, stmt.type === 'function_declaration'. tree-sitter-javascript gives a generator function declaration (function* name() {}) a DIFFERENT node type, generator_function_declaration — confirmed by this repo's own already-shipped FUNCTION_SCOPE_NODE_TYPES constant (src/extractors/javascript.ts), which lists function_declaration and generator_function_declaration as two separate members precisely because they are distinct grammar kinds (the same pairing exists for the expression forms, function_expression vs generator_function).
So a top-level function* name() { ... } is invisible to declarationCount entirely, both for the ORIGINAL round-14 direct-children loop and for round 15's new countHoistedVarScopeDeclarations walk (which uses the identical === 'function_declaration' test, since it targets the same statement shape one hoisting hop further). Concretely:
function* run() { return 0; }
function* run() { return this.alpha(); }
const T = { alpha: fnA, run };
T.run();
Neither declaration of run is ever counted — declarationCount stays at 0 for this name via the function_declaration branch — so findTopLevelFunctionNodeByName returns null (the same "no module-level declaration found" fallback as if run didn't exist at all), and the caller fails safe. This happens to still be SAFE (fail-safe, not confidently wrong), because the function-shape branch never sets result for a generator_function_declaration either, so nothing is ever returned with false confidence for this shape — but it is a silent detection gap in a resolution chain that has otherwise been careful to enumerate every declaration statement shape recognized as a REDECLARATION CANDIDATE, and it applies identically to a hoisted (round-15) generator declaration nested in a block.
Note this is narrower than it might first appear: the DECLARATOR path (const run = function*() {}, under lexical_declaration/variable_declaration) is unaffected — declarationCount++ there fires on the declarator's OWN name match, regardless of what shape the value is, so only the standalone generator-declaration STATEMENT form is invisible, not a generator function assigned to a const/let/var.
Why this wasn't fixed inline
It is a narrow (fail-safe-already, not confidently-wrong) gap in a pre-existing, already-verified-correct part of round 14's own code (findTopLevelFunctionNodeByName's function_declaration string match), discovered while verifying round 15's own extension reuses the identical test — fixing it would mean touching round 14's already-settled work, which round 15 is scoped not to disturb, for an idiom (a dispatch-table handler declared as a generator function, redeclared or hoisted) with no observed instance in this codebase.
Suggested fix shape (not binding — decide at execute/fix time)
Wherever this chain tests stmt.type === 'function_declaration' for a candidate redeclaration (both the direct-children loop and countHoistedVarScopeDeclarations), also accept 'generator_function_declaration' — mirrored in the Rust engine's find_top_level_function_node_by_name / count_hoisted_var_scope_declarations. Add a WU-10 escape-fallback case proving a duplicate top-level (or hoisted) generator-function declaration correlates once implemented.
Where
What
docs/plans/issue-2088.md'sfindTopLevelFunctionNodeByName(condition 4's identifier-resolution chain, WU-2) recognizes a top-level function-declaration statement via an exact string match,stmt.type === 'function_declaration'. tree-sitter-javascript gives a generator function declaration (function* name() {}) a DIFFERENT node type,generator_function_declaration— confirmed by this repo's own already-shippedFUNCTION_SCOPE_NODE_TYPESconstant (src/extractors/javascript.ts), which listsfunction_declarationandgenerator_function_declarationas two separate members precisely because they are distinct grammar kinds (the same pairing exists for the expression forms,function_expressionvsgenerator_function).So a top-level
function* name() { ... }is invisible todeclarationCountentirely, both for the ORIGINAL round-14 direct-children loop and for round 15's newcountHoistedVarScopeDeclarationswalk (which uses the identical=== 'function_declaration'test, since it targets the same statement shape one hoisting hop further). Concretely:Neither declaration of
runis ever counted —declarationCountstays at 0 for this name via thefunction_declarationbranch — sofindTopLevelFunctionNodeByNamereturnsnull(the same "no module-level declaration found" fallback as ifrundidn't exist at all), and the caller fails safe. This happens to still be SAFE (fail-safe, not confidently wrong), because the function-shape branch never setsresultfor agenerator_function_declarationeither, so nothing is ever returned with false confidence for this shape — but it is a silent detection gap in a resolution chain that has otherwise been careful to enumerate every declaration statement shape recognized as a REDECLARATION CANDIDATE, and it applies identically to a hoisted (round-15) generator declaration nested in a block.Note this is narrower than it might first appear: the DECLARATOR path (
const run = function*() {}, underlexical_declaration/variable_declaration) is unaffected —declarationCount++there fires on the declarator's OWN name match, regardless of what shape the value is, so only the standalone generator-declaration STATEMENT form is invisible, not a generator function assigned to aconst/let/var.Why this wasn't fixed inline
It is a narrow (fail-safe-already, not confidently-wrong) gap in a pre-existing, already-verified-correct part of round 14's own code (
findTopLevelFunctionNodeByName'sfunction_declarationstring match), discovered while verifying round 15's own extension reuses the identical test — fixing it would mean touching round 14's already-settled work, which round 15 is scoped not to disturb, for an idiom (a dispatch-table handler declared as a generator function, redeclared or hoisted) with no observed instance in this codebase.Suggested fix shape (not binding — decide at execute/fix time)
Wherever this chain tests
stmt.type === 'function_declaration'for a candidate redeclaration (both the direct-children loop andcountHoistedVarScopeDeclarations), also accept'generator_function_declaration'— mirrored in the Rust engine'sfind_top_level_function_node_by_name/count_hoisted_var_scope_declarations. Add a WU-10 escape-fallback case proving a duplicate top-level (or hoisted) generator-function declaration correlates once implemented.Where
docs/plans/issue-2088.md— WU-2'sfindTopLevelFunctionNodeByNameandcountHoistedVarScopeDeclarations(condition 4's identifier-resolution chain); WU-7's Rust mirror.