Skip to content

Conversation

@gakonst
Copy link
Member

@gakonst gakonst commented Jan 20, 2026

Summary

When AST output is requested via --ast flag, sparse output optimization must be disabled because AST node IDs are specific to each compilation unit.

Problem

When compiling with forge test --ast, if a test file imports a source file that hasn't changed (cache hit), the sparse output optimization would request empty output selection for that cached import. This causes:

  1. The test file gets freshly compiled with AST containing references to the imported contract
  2. The imported contract's cached AST has different node IDs (from a previous compilation with different sources)
  3. Cross-file AST references are broken because node IDs don't match

Fix

This PR adds:

  • OutputSelection::contains_ast() method to detect if AST is in the output selection
  • Logic in sparse_sources to skip sparse optimization when AST is requested

When AST is detected in the output selection, all files in the compilation unit get full output selection, ensuring consistent AST node IDs across all files.

Testing

Added unit test for contains_ast() covering:

  • Explicit AST selection (ast_output_selection())
  • Complete output selection (includes * wildcard)
  • Default output selection (no AST)
  • Empty output selection
  • Contract-only outputs

Fixes foundry-rs/foundry#11056

Reproduction

# Clone reproduction repo
git clone https://github.com/ferranbt/test-ast-update
cd test-ast-update

# Clear cache and compile first test
rm -rf out cache
forge test --match-test test_Increment --match-path test/Counter.t.sol --ast

# Compile second test
forge test --match-test test_Other --match-path test/Other.t.sol --ast

# Compare node IDs - they should match but don't without this fix
cat out/Other.sol/Other.json | jq ".ast.exportedSymbols.Other.[0]"
cat out/Other.t.sol/OtherTest.json | jq ".ast.exportedSymbols.Other.[0]"

When AST output is requested, sparse output optimization must be disabled
because AST node IDs are specific to each compilation unit. If we
selectively compile only dirty files with full output while requesting
empty output for non-dirty imports, the freshly compiled AST will
reference node IDs that don't exist in the cached (stale) AST of the
imported files.

This fix adds:
- `OutputSelection::contains_ast()` method to detect if AST is requested
- Logic in `sparse_sources` to skip optimization when AST is in output

Fixes foundry-rs/foundry#11056
Adds two tests for foundry-rs/foundry#11056:

1. test_output_selection_contains_ast() - Verifies OutputSelection::contains_ast()
   correctly detects AST in various selection configurations

2. sparse_output_preserves_selection_when_ast_requested() - Integration test
   verifying AST output is preserved when sparse output is enabled

These tests ensure the fix that disables sparse output optimization when AST
is requested continues to work correctly.
@gakonst gakonst force-pushed the fix/ast-sparse-output-optimization branch from d0e37b2 to 8f6b8eb Compare January 20, 2026 20:08
@zerosnacks zerosnacks marked this pull request as ready for review January 20, 2026 20:10
@zerosnacks zerosnacks self-requested a review January 20, 2026 20:11
Copy link
Member

@onbjerg onbjerg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rationale & fix lgtm

@zerosnacks zerosnacks enabled auto-merge (squash) January 21, 2026 08:17
@zerosnacks zerosnacks merged commit cc3b2bf into foundry-rs:main Jan 21, 2026
18 checks passed
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.

bug(forge test): AST is not updated for unchanged dependencies in forge test compilation

3 participants