Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[flake8-type-checking] Adds implementation for TC007 and TC008 #12927

Merged
merged 42 commits into from
Nov 27, 2024

Conversation

Daverball
Copy link
Contributor

@Daverball Daverball commented Aug 16, 2024

This is part of a series of pull requests fulfilling my promise in #9573 to port some of the enhancements and new rules from flake8-type-checking to ruff.

Summary

This adds the missing flake8-type-checking rules TC007 and TC008 including auto fixes.

There is some overlap between TC007 and TC004, currently the existing rule takes precedence, although ideally we would still emit both violations and share a fix for overlaps based on the selected strategy (if it is possible for violations of two different rules to share the same fix). We could probably move the analysis for TC007 into the same function as TC004 in order to accomplish that. But we could defer that to a future pull request.

There is also some potential overlap between TC008 and TC010. Currently TC010 is prioritized, but since it currently has no fix it might make more sense to either prioritize TC008 or add a fix for TC010, although that could be covered by a future pull request.

Test Plan

cargo nextest run

The implementation for TCH007 is incomplete and also requires some
changes to TCH004 in order to avoid conflicts between TCH004 and TCH007
Copy link
Contributor

github-actions bot commented Aug 16, 2024

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

ℹ️ ecosystem check detected linter changes. (+6 -0 violations, +0 -0 fixes in 2 projects; 53 projects unchanged)

latchbio/latch (+1 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --ignore RUF9 --output-format concise --preview

+ src/latch_cli/snakemake/config/utils.py:13:33: TC008 [*] Remove quotes from type alias

zulip/zulip (+5 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --ignore RUF9 --output-format concise --preview --select ALL

+ analytics/views/stats.py:342:14: TC008 [*] Remove quotes from type alias
+ analytics/views/stats.py:344:16: TC008 [*] Remove quotes from type alias
+ confirmation/models.py:76:5: TC008 [*] Remove quotes from type alias
+ confirmation/models.py:77:5: TC008 [*] Remove quotes from type alias
+ zerver/lib/push_notifications.py:75:49: TC008 [*] Remove quotes from type alias

Changes by rule (1 rules affected)

code total + violation - violation + fix - fix
TC008 6 6 0 0 0

@Daverball

This comment was marked as resolved.

Copy link

codspeed-hq bot commented Aug 16, 2024

CodSpeed Performance Report

Merging #12927 will not alter performance

Comparing Daverball:feat/tch007-tch008 (8cf8f27) with main (e0f3eaf)

Summary

✅ 32 untouched benchmarks

@Daverball

This comment was marked as resolved.

@Daverball

This comment was marked as resolved.

@Daverball

This comment was marked as resolved.

@Daverball
Copy link
Contributor Author

@charliermarsh It might be worth to move the TCH010 logic to where the TCH008 logic currently sits, so we can actually autofix TCH010 in the same cases where we would otherwise emit a TCH008 by removing the quotes when we know that the quoted expression should be entirely available at runtime.

Concretely we would pass it at the stage where we parse deferred string annotations and check if the current parent expression is a | BinOp. Rather than walk the AST of type definitions to collect the string literals (which doesn't appear to currently properly handle deeply nested cases like eg. list["Foo" | None]).

The drawback would be that it may be more difficult to create an autofix in the opposite case that expands the quotes to the entire type expression, since we would need to walk the parent nodes in order to determine the root node of the type expression.

@Daverball Daverball marked this pull request as ready for review August 20, 2024 16:01
@MichaReiser MichaReiser added the rule Implementing or modifying a lint rule label Sep 2, 2024
@charliermarsh
Copy link
Member

Thanks @Daverball, sorry that I haven't had a chance to review this yet.

@Daverball
Copy link
Contributor Author

@charliermarsh Just a friendly reminder in case this PR slipped through the cracks somehow

@@ -662,12 +662,117 @@ impl<'a> SemanticModel<'a> {
}
}

// FIXME: Shouldn't this happen above where `class_variables_visible` is set?
Copy link
Member

Choose a reason for hiding this comment

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

Why? Do you plan to follow-up on this? It seems unrelated to the PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because for BindingKind::Annotation this assignment is skipped. Maybe there's something subtle at play here, like BindingKind::Annotation never occurring inside a function scope, but otherwise this seems like a bug, albeit one very unlikely to affect anyone in any real way.

I noted it because simulate_runtime_load has a similar structure but moved the update of this variable above to where class_variable_visible is updated.

If you don't think there's a bug here I'm happy to remove the comment, otherwise I'll either leave it in and follow up or fix it as part of this PR, whichever you'd prefer.

Copy link
Member

Choose a reason for hiding this comment

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

Let's address this in a follow up PR

Copy link
Member

@charliermarsh charliermarsh left a comment

Choose a reason for hiding this comment

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

The changes to the semantic model seem fine to me.

// things from the outer class(es) that have been defined before the inner
// class.
if seen_function && !scope.kind.is_type() {
source_order_sensitive_lookup = false;
Copy link
Member

Choose a reason for hiding this comment

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

Do you think you could include some examples in the comments here (like, Python cod examples) to explain what this source-sensitive lookup is doing, and why?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, this prompted me to improve the function, since it currently takes some shortcuts under the assumption, that it will only be called for type definitions, but if that ever becomes untrue, or type definitions can start showing up in some of the other problematic places, then there are other kinds of bindings than AnnAssign and ClassDef where the target can occur before a child expression that doesn't yet have access to the binding. The semantic model handles this by walking the nodes in the correct order if I'm not mistaken.

I.e. things like regular assignments and named assignments. Comprehensions have the opposite problem, where the bindings can occur after use, especially with named expressions inside one of the conditions. In the flake8 plugin I'm handling all of these corner-cases by carefully choosing the position of when the binding starts being available.

There is one case I can't handle with pure source position and accept being wrong on, and that would be a named expression referring to itself within a condition of a comprehension. But everything else works. Backtracking the AST to look for bindings seems more robust, but also a lot more expensive.

I think for now I will handle the few additional easy cases and add documentation about when this method will fail to produce the correct result.

@@ -662,12 +662,117 @@ impl<'a> SemanticModel<'a> {
}
}

// FIXME: Shouldn't this happen above where `class_variables_visible` is set?
Copy link
Member

Choose a reason for hiding this comment

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

Let's address this in a follow up PR

@MichaReiser MichaReiser enabled auto-merge (squash) November 27, 2024 08:12
@Daverball
Copy link
Contributor Author

@MichaReiser The tests should still be in their own test function, since both rules are enabled in that test function. That's how we made sure there were no circularity issues.

@Daverball
Copy link
Contributor Author

@MichaReiser Also the fix safety comment is only correct for one of the rules, the other rule is always marked as unsafe but I still don't know why, I'm just relying on the fix safety of the other rule that uses the same fix.

Either way I'm going to force push my local branch and include some of your changes, I already made some of the same changes locally, but I also elaborated on the documentation of simulate_runtime_load.

@MichaReiser
Copy link
Member

Oh sorry. I think it would be good to add a comment to that test explaining why it is a separate tests or it's likely that we'll merge the tests again when we promote the rule to preview.

Could you also extend/correct the fix safety comments because that's the only remaining thing that needs resolving before merging this PR

@Daverball
Copy link
Contributor Author

@MichaReiser I think this should cover everything, but feel free to give it another cursory glance.

@MichaReiser MichaReiser merged commit 6fd10e2 into astral-sh:main Nov 27, 2024
21 checks passed
@MichaReiser
Copy link
Member

Congrats @Daverball on implementing this rather complex rules!

charliermarsh pushed a commit that referenced this pull request Nov 27, 2024
## Summary

This came up as part of #12927 when implementing
`SemanticModel::simulate_runtime_load`.

Should be fairly self-explanatory, if the scope returns a binding with
`BindingKind::Annotation` the bottom part of the loop gets skipped, so
there's no chance for `seen_function` to have been updated. So unless
there's something subtle going on here, like function scopes never
containing bindings with `BindingKind::Annotation`, this seems like a
bug.

## Test Plan

`cargo nextest run`
@Daverball Daverball deleted the feat/tch007-tch008 branch November 28, 2024 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
preview Related to preview mode features rule Implementing or modifying a lint rule
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants