Commit graph

84 commits

Author SHA1 Message Date
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