Commit Graph

286 Commits

Author SHA1 Message Date
Charlie Marsh 56476dfd61
Use `matches!` for `CallPath` comparisons (#5099)
## Summary

This PR consistently uses `matches! for static `CallPath` comparisons.
In some cases, we can significantly reduce the number of cases or
checks.

## Test Plan

`cargo test `
2023-06-14 17:06:34 -04:00
Charlie Marsh bae183b823
Rename `semantic_model` and `model` usages to `semantic` (#5097)
## Summary

As discussed in Discord, and similar to oxc, we're going to refer to
this as `.semantic()` everywhere.

While I was auditing usages of `model: &SemanticModel`, I also changed
as many function signatures as I could find to consistently take the
model as the _last_ argument, rather than the first.
2023-06-14 15:01:51 -04:00
Charlie Marsh 65dbfd2556
Improve names and documentation on scope API (#5095)
## Summary

Just minor improvements to improve consistency of method names and
availability.
2023-06-14 18:28:55 +00:00
Charlie Marsh 86ff1febea
Re-export `ruff_python_semantic` members (#5094)
## Summary

This PR adds a more unified public API to `ruff_python_semantic`, so
that we don't need to do deeply nested imports all over the place.
2023-06-14 18:23:38 +00:00
Charlie Marsh a33bbe6335
Track "delayed" annotations in the semantic model (#5070)
## Summary

This PR tackles a corner case that we'll need to support local symbol
renaming. It relates to a nuance in how we want handle annotations
(i.e., `AnnAssign` statements with no value, like `x: int` in a function
body).

When we see a statement like:

```python
x: int
```

We create a `BindingKind::Annotation` for `x`. This is a special
`BindingKind` that the resolver isn't allowed to return. For example,
given:

```python
x: int
print(x)
```

The second line will yield an `undefined-name` error.

So why does this `BindingKind` exist at all? In Pyflakes, to support the
`unused-annotation` lint:

```python
def f():
    x: int  # unused-annotation
```

If we don't track `BindingKind::Annotation`, we can't lint for unused
variables that are only "defined" via annotations.

There are a few other wrinkles to `BindingKind::Annotation`. One is
that, if a binding already exists in the scope, we actually just discard
the `BindingKind`. So in this case:

```python
x = 1
x: int
```

When we go to create the `BindingKind::Annotation` for the second
statement, we notice that (1) we're creating an annotation but (2) the
scope already has binding for the name -- so we just drop the binding on
the floor. This has the nice property that annotations aren't considered
to "shadow" another binding, which is important in a bunch of places
(e.g., if we have `import os; os: int`, we still consider `os` to be an
import, as we should). But it also means that these "delayed"
annotations are one of the few remaining references that we don't track
anywhere in the semantic model.

This PR adds explicit support for these via a new `delayed_annotations`
attribute on the semantic model. These should be extremely rare, but we
do need to track them if we want to support local symbol renaming.

### This isn't the right way to model this

This isn't the right way to model this.

Here's an alternative:

- Remove `BindingKind::Annotation`, and treat annotations as their own,
separate concept.
- Instead of storing a map from name to `BindingId` on each `Scope`,
store a map from name to... `SymbolId`.
- Introduce a `Symbol` abstraction, where a symbol can point to a
current binding, and a list of annotations, like:

```rust
pub struct Symbol {
  binding: Option<BindingId>,
  annotations: Vec<AnnotationId>
}
```

If we did this, we could appropriately model the semantics described
above. When we go to resolve a binding, we ignore annotations (always).
When we try to find unused variables, we look through the list of
symbols, and have sufficient information to discriminate between
annotations and bound variables. Etc.

The main downside of this `Symbol`-based approach is that it's going to
take a lot more work to implement, and it'll be less performant (we'll
be storing more data per symbol, and our binding lookups will have an
added layer of indirection).
2023-06-14 17:54:35 +00:00
Charlie Marsh c992cfa76e
Make some of `ruff_python_semantic` `pub(crate)` (#5093) 2023-06-14 17:49:37 +00:00
Charlie Marsh 6f10aeebaa
Remove unused `Scope#delete` method (#5085)
## Summary

This is now intentionally unused and is now made impossible (via this
PR).
2023-06-14 14:15:14 +00:00
Charlie Marsh c74ef77e85
Move binding accesses into `SemanticModel` method (#5084) 2023-06-14 14:07:46 +00:00
Charlie Marsh 1e497162d1
Add a dedicated read result for unbound locals (#5083)
## Summary

Small follow-up to #4888 to add a dedicated `ResolvedRead` case for
unbound locals, mostly for clarity and documentation purposes (no
behavior changes).

## Test Plan

`cargo test`
2023-06-14 09:58:48 -04:00
Charlie Marsh aa41ffcfde
Add `BindingKind` variants to represent deleted bindings (#5071)
## Summary

Our current mechanism for handling deletions (e.g., `del x`) is to
remove the symbol from the scope's `bindings` table. This "does the
right thing", in that if we then reference a deleted symbol, we're able
to determine that it's unbound -- but it causes a variety of problems,
mostly in that it makes certain bindings and references unreachable
after-the-fact.

Consider:

```python
x = 1
print(x)
del x
```

If we analyze this code _after_ running the semantic model over the AST,
we'll have no way of knowing that `x` was ever introduced in the scope,
much less that it was bound to a value, read, and then deleted --
because we effectively erased `x` from the model entirely when we hit
the deletion.

In practice, this will make it impossible for us to support local symbol
renames. It also means that certain rules that we want to move out of
the model-building phase and into the "check dead scopes" phase wouldn't
work today, since we'll have lost important information about the source
code.

This PR introduces two new `BindingKind` variants to model deletions:

- `BindingKind::Deletion`, which represents `x = 1; del x`.
- `BindingKind::UnboundException`, which represents:

```python
try:
  1 / 0
except Exception as e:
  pass
```

In the latter case, `e` gets unbound after the exception handler
(assuming it's triggered), so we want to handle it similarly to a
deletion.

The main challenge here is auditing all of our existing `Binding` and
`Scope` usages to understand whether they need to accommodate deletions
or otherwise behave differently. If you look one commit back on this
branch, you'll see that the code is littered with `NOTE(charlie)`
comments that describe the reasoning behind changing (or not) each of
those call sites. I've also augmented our test suite in preparation for
this change over a few prior PRs.

### Alternatives

As an alternative, I considered introducing a flag to `BindingFlags`,
like `BindingFlags::UNBOUND`, and setting that at the appropriate time.

This turned out to be a much more difficult change, because we tend to
match on `BindingKind` all over the place (e.g., we have a bunch of code
blocks that only run when a `BindingKind` is
`BindingKind::Importation`). As a result, introducing these new
`BindingKind` variants requires only a few changes at the client sites.
Adding a flag would've required a much wider-reaching change.
2023-06-14 09:27:24 -04:00
Charlie Marsh 1895011ac2
Document some attributes on the semantic model (#5064) 2023-06-13 20:45:24 +00:00
Charlie Marsh 364bd82aee
Don't treat annotations as resolved in forward references (#5060)
## Summary

This behavior dates back to a Pyflakes commit (5fc37cbd), which was used
to allow this test to pass:

```py
from __future__ import annotations
T: object
def f(t: T): pass
def g(t: 'T'): pass
```

But, I think this is an error. Mypy and Pyright don't accept it -- you
can only use variables as type annotations if they're type aliases
(i.e., annotated with `TypeAlias`), in which case, there has to be an
assignment on the right-hand side (see: [PEP
613](https://peps.python.org/pep-0613/)).
2023-06-13 14:47:29 -04:00
Charlie Marsh 19f972a305
Use `Scope#has` in lieu of `Scope#get` (#5051)
## Summary

These usages don't actually need the `BindingId`.
2023-06-13 15:59:53 +00:00
Charlie Marsh 68b6d30c46
Use consistent `Cargo.toml` metadata in all crates (#5015) 2023-06-12 00:02:40 +00:00
Charlie Marsh e86f12a1ec
Rename some methods on `SemanticModel` (#4990) 2023-06-09 19:36:59 +00:00
Charlie Marsh 5c502a3320
Add documentation for `BindingKind` variants (#4989) 2023-06-09 18:32:50 +00:00
Davide Canton 63fdcea29e
Handled dict and set inside f-string (#4249) (#4563) 2023-06-09 04:53:13 +00:00
Micha Reiser 39a1f3980f
Upgrade RustPython (#4900) 2023-06-08 05:53:14 +00:00
Charlie Marsh ae75b303f0
Avoid attributing runtime references to module-level imports (#4942) 2023-06-07 21:56:03 +00:00
Charlie Marsh 780d153ae8
Replace one-off locals property with `ScopeFlags` (#4912) 2023-06-06 21:22:21 -04:00
Charlie Marsh 8c048b463c
Track symbol deletions separately from bindings (#4888) 2023-06-06 18:49:36 +00:00
Charlie Marsh 7b0fb1a3b4
Respect noqa directives on `ImportFrom` parents for type-checking rules (#4889) 2023-06-06 02:37:07 +00:00
Charlie Marsh 8938b2d555
Use `qualified_name` terminology in more structs for consistency (#4873) 2023-06-05 19:06:48 +00:00
Charlie Marsh d31eb87877
Extract shared simple AST node inference utility (#4871) 2023-06-05 18:23:37 +00:00
Charlie Marsh a0721912a4
Invert structure of Scope#shadowed_bindings (#4855) 2023-06-05 02:03:21 +00:00
Charlie Marsh 466719247b
Invert parent-shadowed bindings map (#4847) 2023-06-04 00:18:46 -04:00
Charlie Marsh 3fa4440d87
Modify semantic model API to push bindings upon creation (#4846) 2023-06-04 02:28:25 +00:00
Charlie Marsh c14896b42c
Move `Binding` initialization into `SemanticModel` (#4819) 2023-06-03 15:26:55 -04:00
Charlie Marsh 935094c2ff
Move import-name matching into methods on `BindingKind` (#4818) 2023-06-03 15:01:27 -04:00
Charlie Marsh 26b1dd0ca2
Remove `name` field from import binding kinds (#4817) 2023-06-02 23:02:47 -04:00
Charlie Marsh 211d8e170d
Ignore error calls with `exc_info` in TRY400 (#4797) 2023-06-02 04:59:45 +00:00
Charlie Marsh ea3cbcc362
Avoid enforcing native-literals rule within nested f-strings (#4488) 2023-06-02 04:00:31 +00:00
qdegraaf fcbf5c3fae
Add PYI034 for `flake8-pyi` plugin (#4764) 2023-06-02 02:15:57 +00:00
Jonathan Plasse edadd7814f
Add `pyflakes.extend-generics` setting (#4677) 2023-06-01 22:19:37 +00:00
Charlie Marsh ea31229be0
Track `TYPE_CHECKING` blocks in `Importer` (#4593) 2023-05-30 16:18:10 +00:00
Charlie Marsh 80fa3f2bfa
Add a convenience method to check if a name is bound (#4718) 2023-05-30 01:52:41 +00:00
Aarni Koskela 0106bce02f
[`flake8-future-annotations`] Implement `FA102` (#4702) 2023-05-29 22:41:45 +00:00
Charlie Marsh 9741f788c7
Remove globals table from `Scope` (#4686) 2023-05-27 22:35:20 -04:00
Charlie Marsh af433ac14d
Avoid using typing-imported symbols for runtime edits (#4649) 2023-05-26 20:36:37 -04:00
qdegraaf ccca11839a
Allow more immutable funcs for RUF009 (#4660) 2023-05-26 15:18:52 -04:00
Charlie Marsh 0f610f2cf7
Remove dedicated ScopeKind structs in favor of AST nodes (#4648) 2023-05-25 19:31:02 +00:00
Charlie Marsh f0e173d9fd
Use `BindingId` copies in lieu of `&BindingId` in semantic model methods (#4633) 2023-05-24 15:55:45 +00:00
Charlie Marsh fcdc7bdd33
Remove separate `ReferenceContext` enum (#4631) 2023-05-24 15:12:38 +00:00
Charlie Marsh 5cedf0f724
Remove `ReferenceContext::Synthetic` (#4612) 2023-05-24 14:30:35 +00:00
Charlie Marsh 8961d8eb6f
Track all read references in semantic model (#4610) 2023-05-24 14:14:27 +00:00
Charlie Marsh ba4c0a21fa
Rename `ContextFlags` to `SemanticModelFlags` (#4611) 2023-05-23 17:47:07 -04:00
Micha Reiser 652c644c2a
Introduce `ruff_index` crate (#4597) 2023-05-23 17:40:35 +02:00
Charlie Marsh 74effb40b9
Rename `index` to `binding_id` in a few iterators (#4594) 2023-05-23 03:56:00 +00:00
Micha Reiser daadd24bde
Include decorators in `Function` and `Class` definition ranges (#4467) 2023-05-22 17:50:42 +02:00
Micha Reiser cbe344f4d5
Rename `Checker::model` to `semantic_model` (#4573) 2023-05-22 15:14:30 +02:00
Charlie Marsh d70f899f71
Use `SemanticModel` in lieu of `Checker` in more methods (#4568) 2023-05-22 02:58:47 +00:00
Charlie Marsh 19c4b7bee6
Rename ruff_python_semantic's `Context` struct to `SemanticModel` (#4565) 2023-05-22 02:35:03 +00:00
Charlie Marsh fe7f2e2e4d
Move submodule alias resolution into `Context` (#4543) 2023-05-20 16:34:10 +00:00
Charlie Marsh 6aa9900c03
Improve handling of `__qualname__`, `__module__`, and `__class__` (#4512) 2023-05-20 03:03:45 +00:00
Charlie Marsh 9e21414294
Improve reference resolution for deferred-annotations-within-classes (#4509) 2023-05-20 02:54:18 +00:00
Charlie Marsh bb4e674415
Move reference-resolution into Context (#4510) 2023-05-20 02:47:15 +00:00
Charlie Marsh 73efbeb581
Invert quote-style when generating code within f-strings (#4487) 2023-05-18 14:33:33 +00:00
Micha Reiser fa26860296
Refactor range from `Attributed` to `Node`s (#4422) 2023-05-16 06:36:32 +00:00
Charlie Marsh 2414469ac3
Enable automatic rewrites of `typing.Deque` and `typing.DefaultDict` (#4420) 2023-05-15 22:33:24 +00:00
Tyler Yep 01b372a75c
Implement `flake8-future-annotations` FA100 (#3979) 2023-05-14 03:00:06 +00:00
Charlie Marsh 7e3ba7f32a
Use `bitflags` for tracking `Context` flags (#4381) 2023-05-12 16:07:26 +00:00
Charlie Marsh 9158f13ee6
Respect `__all__` imports when determining definition visibility (#4357) 2023-05-11 17:43:51 +00:00
Charlie Marsh 72e0ffc1ac
Delay computation of `Definition` visibility (#4339) 2023-05-11 17:14:29 +00:00
Jeong, YunWon be6e00ef6e
Re-integrate RustPython parser repository (#4359)
Co-authored-by: Micha Reiser <micha@reiser.io>
2023-05-11 07:47:17 +00:00
Charlie Marsh f4f88308ae
Remove `Copy` and destructure `Snapshot` (#4358) 2023-05-10 19:46:18 +00:00
Charlie Marsh ea3d3a655d
Add a `Snapshot` abstraction for deferring and restoring visitor context (#4353) 2023-05-10 16:50:47 +00:00
Charlie Marsh 6d6d7abf70
Use short-import for `HashMap` (#4351) 2023-05-10 15:46:55 +00:00
Charlie Marsh 7b91a162c6
Remove `current_` prefix from some Context methods (#4325) 2023-05-09 19:40:12 +00:00
Charlie Marsh d365dab904
Include static and class methods in in abstract decorator list (#4298) 2023-05-08 21:54:02 -04:00
Charlie Marsh 539af34f58
Add a utility method to detect top-level state (#4259) 2023-05-06 20:24:27 +00:00
Charlie Marsh 983bb31577
Remove `RefEquality` usages from `Context` (#4257) 2023-05-06 15:55:14 -04:00
Charlie Marsh cd27b39aff
Re-order some code in scope.rs (#4255) 2023-05-06 16:36:20 +00:00
Charlie Marsh a9fc648faf
Use `NodeId` for `Binding` source (#4234) 2023-05-06 16:20:08 +00:00
Charlie Marsh c1f0661225
Replace `parents` statement stack with a `Nodes` abstraction (#4233) 2023-05-06 16:12:41 +00:00
Dhruv Manilawala 2c91412321
Consider Flask app logger as logger candidate (#4253) 2023-05-06 11:31:10 -04:00
Charlie Marsh 64b7280eb8
Respect parent-scoping rules for `NamedExpr` assignments (#4145) 2023-04-29 22:45:30 +00:00
Charlie Marsh 2115d99c43
Remove `ScopeStack` in favor of child-parent `ScopeId` pointers (#4138) 2023-04-29 18:23:51 -04:00
Moritz Sauter ee6d8f7467
Add bugbear immutable functions as allowed in dataclasses (#4122) 2023-04-27 21:23:06 -04:00
Micha Reiser cab65b25da
Replace row/column based `Location` with byte-offsets. (#3931) 2023-04-26 18:11:02 +00:00
Jonathan Plasse 5e91211e6d
Add `in_boolean_test` to `Context` (#4072) 2023-04-23 23:18:23 -06:00
Micha Reiser ba4f4f4672
Upgrade dependencies (#4064) 2023-04-22 18:04:01 +01:00
Dhruv Manilawala ba98149022
Avoid `RUF008` if field annotation is immutable (#4039) 2023-04-20 16:02:12 -04:00
Charlie Marsh 10d5415bcb
Ignore certain flake8-pyi errors within function bodies (#4029) 2023-04-19 15:10:29 -04:00
Charlie Marsh 7fa1da20fb
Support relative imports in `banned-api` enforcement (#4025) 2023-04-19 14:30:13 -04:00
Dhruv Manilawala b6155232ac
Consider logger candidate from `logging` module only (#3878) 2023-04-04 19:52:57 +00:00
Charlie Marsh d919adc13c
Introduce a `ruff_python_semantic` crate (#3865) 2023-04-04 16:50:47 +00:00