Skip to content

feat(parser): broaden typo hints for staticcall / extcall#713

Draft
augmentcode[bot] wants to merge 1 commit into
mainfrom
michaelos443/vyper-typo-hint-levenshtein
Draft

feat(parser): broaden typo hints for staticcall / extcall#713
augmentcode[bot] wants to merge 1 commit into
mainfrom
michaelos443/vyper-typo-hint-levenshtein

Conversation

@augmentcode
Copy link
Copy Markdown

@augmentcode augmentcode Bot commented May 26, 2026

Why the existing likely_errors tuple is so narrow

vyper/ast/parse.py currently does this when the python parser raises a SyntaxError on vyper source:

likely_errors = ("staticall", "staticcal")
tmp = str(new_e)
for s in likely_errors:
    if s in tmp:
        new_e._hint = "did you mean `staticcall`?"
        break

A few observations:

  1. Only two literal substrings are checked. They cover exactly the case where one character is deleted from staticcall (one c, or the final l). Anything else — staticcakl (insertion), statiocall (transposition), staticcll (substitution) — gets no hint.
  2. extcall is missing entirely. It is just as much a vyper keyword and just as easy to mis-type, but there is no entry for any of its typos (extcal, extcll, etcall, exctall, ...).
  3. Substring matching is fragile. It works against the rendered exception string rather than identifier tokens, so any expansion of the literal list quickly runs into substring-of-a-real-keyword collisions (e.g. "staticcal" in "staticcall" is True).

What this PR does

Replaces the literal substring scan with a Levenshtein-distance check over identifier-like tokens in the error message:

  • Tokens are extracted with re.compile(r"[A-Za-z_][A-Za-z0-9_]*").
  • Tokens shorter than 4 chars and tokens that already match a keyword are skipped (avoids spurious ifextcall suggestions and avoids triggering on correct usage).
  • Each remaining token is compared against both staticcall and extcall using the existing levenshtein helper from vyper.semantics.analysis.levenshtein_utils.
  • A distance between 1 and 2 (inclusive) yields a "did you mean ?" hint. Distance 2 was chosen because both keywords are 7+ chars long, so 2 edits comfortably cover a single deletion, insertion, substitution, or adjacent transposition without false matches on unrelated identifiers.

The levenshtein helper is brought in via a lazy import inside the helper function — vyper.semantics imports heavily from vyper.ast, so a top-level import here would create a cycle. The slow path (a SyntaxError has already been raised) is the right place to absorb the import cost.

Tests

Added to vyper/tests/unit/ast/test_parser.py:

  • A parametrized test_call_keyword_typo_hint covering 11 misspellings across staticcall and extcall (deletions, insertions, substitutions, transpositions). All produce the expected hint.
  • test_unrelated_syntax_error_has_no_call_hint confirms that a normal syntax error (return 1 +) does not produce a spurious staticcall / extcall suggestion.

Full unit suite still passes:

tests/unit/ast/  ->  261 passed, 1 xfailed

Lint (black, flake8, isort) is clean on the touched files.

Files changed

  • vyper/vyper/ast/parse.py — replaced likely_errors block with _suggest_call_keyword(...) helper backed by Levenshtein distance.
  • vyper/tests/unit/ast/test_parser.py — added typo-hint coverage.

Note for maintainers: this PR was opened by the PR Author Agent in a manual session, but only the github-app-api tool was available in this session, so the PR shows as created by the org bot rather than by the launching user. Git commit metadata (michaelos443 / michael.o1@turing.com) and the branch name reflect the actual author.

The previous implementation only matched two literal substrings
("staticall", "staticcal") against the rendered SyntaxException, so
common misspellings such as "staticcakl", "statiocall", or any typo
of `extcall` produced no hint at all.

Replace the literal substring scan with a Levenshtein-based check
over identifier-like tokens in the error message. Any token of length
>= 4 whose Levenshtein distance to `staticcall` or `extcall` is between
1 and 2 (inclusive) now produces a "did you mean ...?" hint. This
covers single deletions, insertions, substitutions, and adjacent
transpositions for both keywords.

Reuses the existing `levenshtein` helper from
`vyper.semantics.analysis.levenshtein_utils` via a lazy import to
avoid a top-level cycle (vyper.semantics imports from vyper.ast).
@augmentcode augmentcode Bot changed the title feat(parser): broaden typo hints for staticcall / extcall feat(parser): broaden typo hints for staticcall / extcall May 26, 2026
@augmentcode
Copy link
Copy Markdown
Author

augmentcode Bot commented May 26, 2026

PR Author Agent

👋 I've got this PR — here's what I'll handle for you:

  • Review feedback — implement suggestions, answer questions, fix what comes up
  • CI failures — I get pinged when checks fail and try to fix them
  • Merge conflicts — bring the PR back up to date when it falls behind
  • Merge gates — watch CI, reviews, and verification
  • Ping you the moment it's ready to merge

Marking it ready and picking reviewers are your call — I'll leave both alone.

Drop a comment anytime!

@augmentcode
Copy link
Copy Markdown
Author

augmentcode Bot commented May 27, 2026

PR Author Agent

All blocking gates are green on 3ca0800 — ready to flip to ready-for-review whenever you are. Reply mark ready here, or use the Ready for review button on GitHub.

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