Skip to content

fix(config): prioritize nested remappings#15800

Open
stevencartavia wants to merge 4 commits into
masterfrom
steven/nested-remapping-precedence
Open

fix(config): prioritize nested remappings#15800
stevencartavia wants to merge 4 commits into
masterfrom
steven/nested-remapping-precedence

Conversation

@stevencartavia

Copy link
Copy Markdown
Member

Nested dependency remappings can point to a source directory such as contracts/ or src/, but filesystem detection may incorrectly replace them with the package root.

Preserve configured nested remappings when they refine the detected package path, while keeping root and direct dependency precedence unchanged.

Fixes #6431.

@decofe

decofe commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Potential regression to cover: this change can narrow a package-root remapping globally and break imports outside the configured source directory.

Example fixture:

lib/outer/
├── foundry.toml
├── remappings.txt       # inner/=lib/inner/contracts/
├── src/Outer.sol        # imports "inner/Root.sol"
└── lib/inner/
    ├── Root.sol
    └── contracts/

Before this change, auto-detection yields inner/=lib/outer/lib/inner/, so inner/Root.sol resolves. This change refines it to inner/=lib/outer/lib/inner/contracts/, so the same import resolves to the nonexistent contracts/Root.sol.

Could we add coverage for mixed package layouts where Solidity files are imported from the package root, interfaces/, or utils/, and clarify whether globally narrowing the mapping is intended? I prepared the package-root regression test locally, but could not execute it because Cargo's foundry-rs/optimism dependency fetch returned 401.

@grandizzy grandizzy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

pls consider #15800 (comment) Projects are using remappings in many ways and our unit tests doesn't cover them so likely a fix introduce regressions. Some other scenarios to take into account for this fix:

  • Cross-dependency leakage: two parents defining the same alias differently remain uncontextualized; the globally “closest” path wins, potentially compiling one parent against the other’s dependency.
  • Dependency-controlled hijack: a transitive dependency can narrow an auto-detected alias to a deeper, unexpected directory—similar to the security concern in #9146.
  • Scoped/versioned packages: filtering self-remappings by directory basename fails when folder and import names differ, e.g. foo-1.2.0 declaring foo/ or @scope/foo/.
  • Symlinks/nonexistent targets: Path::starts_with() is lexical when canonicalization fails, so .. paths may incorrectly qualify as descendants.
  • Performance: every nested foundry.toml is loaded twice, adding configuration and filesystem work across large dependency trees.

@stevencartavia
stevencartavia requested a review from grandizzy July 17, 2026 06:14
@decofe

decofe commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Remaining contextual-remapping regressions to cover:

1. Root file-level context

project/
├── foundry.toml
├── src/A.sol             # imports "@x/X.sol"
├── src/B.sol
└── lib/x/X.sol
[profile.default]
remappings = ["src/A.sol:@x/=lib/x/"]

The context becomes src/A.sol/. Solar checks parent.starts_with(context), so parent src/A.sol no longer matches and the import fails.

2. Dependency file-level context

project/
└── lib/outer/
    ├── foundry.toml      # remappings=["src/A.sol:@x/=lib/x/"]
    ├── src/A.sol         # imports "@x/X.sol"
    └── lib/x/X.sol

The context becomes lib/outer/src/A.sol/, producing the same mismatch inside the dependency.

3. Source-prefix context without a directory

project/
├── foundry.toml
├── src/generatedA.sol    # imports "@x/X.sol"
└── lib/x/X.sol
[profile.default]
remappings = ["src/generated:@x/=lib/x/"]

Contexts use source-unit prefix matching, so this currently applies to src/generatedA.sol. The change rewrites it as src/generated/, which no longer matches. If declared by a dependency, it is dropped earlier because src/generated cannot be canonicalized.

These cases come from unconditionally treating contexts as directories. Could we preserve exact user-supplied contexts and only add directory boundaries to synthesized dependency contexts?

@grandizzy grandizzy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

ty! Please check variations in #15800 (comment) 🙏

@stevencartavia
stevencartavia requested a review from grandizzy July 17, 2026 15:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Forge remappings output wrong with multiple remappings.txt on dependencies.

3 participants