Skip to content

Fix imports whose spelling does not match the file on disk - #188

Open
shellygr wants to merge 2 commits into
masterfrom
shelly/import-filename-mismatch
Open

Fix imports whose spelling does not match the file on disk#188
shellygr wants to merge 2 commits into
masterfrom
shelly/import-filename-mismatch

Conversation

@shellygr

Copy link
Copy Markdown
Contributor

What this fixes

A Solidity project written on macOS or Windows can import a file by a path that does not match
the real filename, usually differing only in letter case. Those filesystems are case-insensitive,
so the project compiles for its authors and cannot compile anywhere case-sensitive. Compilation
analysis used to stop there, with a source-not-found error and nothing to try next.

Approach

New module certora_autosetup/setup/import_spelling_fix.py. It checks each component of an import
path against the real directory entries with os.scandir instead of asking whether the path
exists. An existence check on a case-insensitive filesystem resolves the misspelled path happily,
which would make the defect invisible on the machine where the code is written and leave the fixer
firing only in production.

Handled: letter case in the basename, in any directory component, and in the extension; Windows
separators and doubled slashes; and the right basename in the wrong directory, but only when
exactly one file on disk carries that basename.

Not handled, each one an explicit bail rather than a silent miss:

  • Fuzzy or edit-distance matching. A near-miss name is a different contract, and pointing an
    import at it gives you a project that compiles while verifying code nobody asked about.
  • Guessing at the shape of a name.
  • Inventing or changing an extension. Only its letter case can change.
  • Renaming files on disk.
  • Imports that resolve through a remapping or package prefix. Those belong to the packages
    workaround.
  • More than one candidate. Two files differing only in case are legal on a case-sensitive
    filesystem, so an ambiguous match rewrites nothing.

Integration

Runs in run_compilation_analysis ahead of the existing import patcher. The order matters: that
patcher resolves each relative import against its map of real files and skips the ones that miss,
so an import with a wrong spelling is exactly the case it cannot help with. Either fix applying is
reason enough to compile again. If the retry still fails, both revert in reverse order of
application; if it succeeds, the fix stays.

Rewrites are scoped to the quoted path literal, checked against the recorded bytes before anything
is written, kept single-line so the patcher's line-indexed revert stays correct, never applied
inside a dependency tree, and logged at WARNING with the old and new spelling.

Also adds a small package_prefixes() helper to utils/import_diagnostics.py so the fixer can
leave remapped prefixes alone.

Testing

24 unit tests. They assert on planned and applied rewrites built from one real file plus a
wrong-cased import string, rather than creating two files that differ only in case. The latter is
impossible on a case-insensitive filesystem and would make the suite behave differently per
developer machine.

Full suite: 1142 passed, 10 skipped, 11 deselected. pyright: 0 errors.

Known limitation

extract_imports_multiline accumulates lines until the next ;, so a commented-out import with
no semicolon can make the scan land on an ordinary string constant further down the file and
rewrite it. Closing this properly wants comment-aware scanning or a real parser; the typed solc
AST is not an option at this point because it only exists after a successful build, and here the
build has failed. Left open deliberately rather than papered over with more string handling.

🤖 Generated with Claude Code

shellygr and others added 2 commits August 24, 2026 22:43
A project written on a case-insensitive filesystem can import a path that
differs from the real filename, usually only in letter case. It compiles for
its authors and fails on Linux. Compilation analysis now checks each component
of an import path against the actual directory entries and rewrites the quoted
literal to the spelling the filesystem holds.

This runs before the existing import patcher. That patcher resolves relative
imports against a map of real files and skips the ones that miss, so a
misspelled import is precisely what it cannot canonicalize. As with the
patcher, a rewrite survives if the retry compiles and is reverted if it does
not.

Nothing is resolved by guessing. Two candidates, a name that matches only by
edit distance, or an import that goes through a package prefix all leave the
file alone and log the reason.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The import scan accumulates lines until the next semicolon, so a commented-out
import without one ran on to whatever semicolon came next and could land the
rewrite on an ordinary string constant further down the file.

Scanning now runs over a copy of the source with comment bytes replaced by
spaces. Each byte is replaced one for one, so columns still address the real
file and the rewrite is applied to it unchanged.

Comment starts are found by scanning characters in context rather than by
matching // or /* directly. A block comment spans lines, so recognising it at
all needs state carried between them, and the same pass keeps string literals
out of it for free.

Co-Authored-By: Claude Opus 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