Skip to content

[ty] Add more autofixes - #28029

Merged
AlexWaygood merged 3 commits into
mainfrom
alex/more-autofixes
Aug 25, 2026
Merged

[ty] Add more autofixes#28029
AlexWaygood merged 3 commits into
mainfrom
alex/more-autofixes

Conversation

@AlexWaygood

Copy link
Copy Markdown
Member

Summary

Add a new SemanticMode::definitely_has_builtin_binding() that allows us to cheaply and easily query whether a builtin name such as list or int has been shadowed by a variable in an enclosing scope when viewed from a given node in the AST. Once this method is in place, it becomes much more trivial to add autofixes to various diagnostics, which this PR therefore does.

The new method does not attempt to fully model Python's name-lookup semantics. It doesn't need to -- it's okay to be conservative when offering autofixes. The method therefore errs on the side of caution and only returns true if it can be certain that the name passed in was definitely not shadowed in an enclosing scope.

The new method is similar to the SemanticModel::has_builtin_binding() method that Ruff has had for a long time.

The new SemanticModel method is pulled out of #27634, where I'm using it for several more diagnostic autofixes that make it much easier to understand how ty is trying to tell you to rewrite your code (in my opinion).

Test Plan

mdtests and snapshots

@AlexWaygood
AlexWaygood requested a review from a team as a code owner August 25, 2026 11:49
@AlexWaygood AlexWaygood added ty Multi-file analysis & type inference diagnostics Related to reporting of diagnostics. labels Aug 25, 2026
@astral-sh-bot
astral-sh-bot Bot requested a review from carljm August 25, 2026 11:49
@astral-sh-bot

astral-sh-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown

Typing conformance results

No changes detected ✅

Current numbers
The percentage of diagnostics emitted that were expected errors held steady at 97.69%. The percentage of expected errors that received a diagnostic held steady at 93.71%. The number of fully passing files held steady at 110/136.

@astral-sh-bot

astral-sh-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown

Memory usage report

Memory usage unchanged ✅

@astral-sh-bot

astral-sh-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown

ecosystem-analyzer results

No diagnostic changes detected ✅

Large timing changes:

Project Old Time New Time Change
dd-trace-py 3.36s 0.99s -71%

Full report with detailed diff (timing results)

Comment thread crates/ty_python_semantic/src/semantic_model.rs
@AlexWaygood AlexWaygood added the fixes Related to suggested fixes for violations label Aug 25, 2026
Comment thread crates/ty_python_semantic/src/types/infer/builder/type_expression.rs Outdated
Comment thread crates/ty_python_semantic/src/semantic_model.rs
diagnostic.annotate(
Annotation::secondary(span).message("Did you mean `NotImplementedError`?"),
);
autofix_with_notimplementederror(context, &mut diagnostic, sub_node);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is a super unlikely case, but given:

try:
    raise Exception()
except (NotImplemented, NotImplemented):
    pass

Both elements share one diagnostic, and each helper call replaces its existing fix via set_fix. The resulting quick fix only produces except (NotImplemented, NotImplementedError):, leaving the diagnostic unresolved, and the preview includes the same help text twice.

This is so unlikely to occur that it probably doesn't matter, though it could be a risk if we add more invalid-exception-caught autofixes in the future (e.g. an autofix for except ValueError() to except ValueError).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is a super unlikely case

yeah, my codex kept on telling me off about this too, and I kept on telling it I didn't care 😆

index
.place_table(scope)
.symbol_by_name(name)
.is_some_and(|symbol| symbol.is_bound() || symbol.is_declared())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Here's another case we currently miss:

class C:
    global list
    list = 42

value: [int]

But we also currently error on that pattern with unresolved-global, so I think it's fine.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

if a user does this then they deserve an incorrect autofix 😆

Comment thread crates/ty_python_semantic/resources/mdtest/annotations/invalid.md Outdated
@AlexWaygood
AlexWaygood merged commit 7da3281 into main Aug 25, 2026
65 checks passed
@AlexWaygood
AlexWaygood deleted the alex/more-autofixes branch August 25, 2026 18:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

diagnostics Related to reporting of diagnostics. fixes Related to suggested fixes for violations ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants