Skip to content

fix(analysis): assign AstNode to the scope a node introduces - #235

Open
keshav9926 wants to merge 1 commit into
DeepSourceCorp:masterfrom
keshav9926:fix-scope-astnode
Open

fix(analysis): assign AstNode to the scope a node introduces#235
keshav9926 wants to merge 1 commit into
DeepSourceCorp:masterfrom
keshav9926:fix-scope-astnode

Conversation

@keshav9926

Copy link
Copy Markdown

Description

Scope.AstNode is documented as "the AST node that introduces this scope into the scope tree", but buildScopeTree assigned it on the parent scope rather than on the scope the node actually introduces:

nextScope = NewScope(scope)
scopeOfNode[node] = nextScope
if scope != nil {
    scope.Children = append(scope.Children, nextScope)
    scope.AstNode = node   // <- parent's field, not nextScope's
}

Two things follow from that:

  • a newly created scope's own AstNode was never assigned, so every scope in the tree reported nil except the synthetic root
  • a parent's AstNode was reassigned by each scope-creating child in turn, so it ended up holding whichever child was visited last

Reproduction

For this source:

let x = 1
function f() {
    let y = 2
}
{
    let z = 3
}

walking ScopeOfNode on master gives:

scope introduced by AstNode reported expected
program statement_block program
function_declaration statement_block (its own body) function_declaration
statement_block nil statement_block
statement_block nil statement_block

Fix

Assign nextScope.AstNode = node. ScopeOfNode already mapped each node to the correct scope, so GetScope was never affected and nothing that relies on it changes behaviour — this only makes the field match its documented meaning.

I found this while reading ScopeBuilder to understand how scope resolution works before writing a checker. The field is exported on an exported type, so checker authors reaching for scope.AstNode currently get nil or the wrong node; that seemed worth fixing even though nothing in-tree reads it today.

Type of change

  • Bug fix
  • New feature/enhancement
  • New checker
  • Documentation update
  • Other (please describe)

Checklist

  • I have updated the documentation, if applicable
  • All new checkers are in the checkers directory
  • I have added tests, if applicable
  • I have tested my changes (run make testall)
  • Checker YAML files follow the required format
  • CI checks are passing

Test_ScopeAstNode walks ScopeOfNode and asserts every scope points at its own introducing node, with explicit subtests for the parent-overwrite and function-vs-body cases. It fails on master with:

scope introduced by "program" points at "statement_block" instead
scope introduced by "function_declaration" points at "statement_block" instead
scope introduced by "statement_block" has a nil AstNode

and passes with the fix.

On my machine (Windows) TestFindYamlTestFiles fails both before and after this change, so it looks unrelated and pre-existing rather than something this PR introduces — happy to look into it separately if that's useful.

Related Issues

None.

Additional Notes

This PR was developed with AI assistance (Claude); the bug was found by reading the scope builder, and the fix and tests were reviewed and verified locally before submission.

Scope.AstNode is documented as "the AST node that introduces this scope
into the scope tree", but buildScopeTree assigned it on the *parent*
scope instead of the newly created one.

Two consequences:

  - a new scope's own AstNode was never set, so every scope in the tree
    reported nil except the synthetic root
  - a parent's AstNode was overwritten by each scope-creating child in
    turn, so it ended up holding whichever child was visited last

Concretely, for:

    let x = 1
    function f() { let y = 2 }
    { let z = 3 }

the program scope pointed at the trailing statement_block, the function
scope pointed at its own body, and both block scopes were nil.

Assign nextScope.AstNode instead. ScopeOfNode already mapped each node
to the correct scope, so GetScope was unaffected and nothing that
relied on it changes.

Adds Test_ScopeAstNode, which walks ScopeOfNode and asserts every scope
points at its own introducing node, plus explicit cases for the
parent-overwrite and function-vs-body confusions.

Signed-off-by: keshav9926 <kkakani160@gmail.com>
@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

@keshav9926 is attempting to deploy a commit to the DeepSource Team on Vercel.

A member of the Team first needs to authorize it.

keshav9926 added a commit to keshav9926/wright that referenced this pull request Aug 7, 2026
docs/wright-index-readout.html shows what the index actually extracts:
the three passes, the proof ladder over all 2,933 of globstar's call
sites, four real queries with real answers, and the scope bug the recon
surfaced. Every number comes from indexing DeepSourceCorp/globstar cold
(105 files, 359 symbols, 137 co-change pairs).

Self-contained: no network, no build step, works offline, follows the
viewer light/dark theme.

That recon produced an upstream contribution:
DeepSourceCorp/globstar#235 fixes Scope.AstNode being assigned to the
parent scope instead of the scope the node introduces.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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