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`
This commit is contained in:
Charlie Marsh 2023-06-14 09:58:48 -04:00 committed by GitHub
parent aa41ffcfde
commit 1e497162d1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 65 additions and 12 deletions

View file

@ -4355,10 +4355,10 @@ impl<'a> Checker<'a> {
return; return;
}; };
match self.semantic_model.resolve_read(id, expr.range()) { match self.semantic_model.resolve_read(id, expr.range()) {
ResolvedRead::Resolved(..) | ResolvedRead::ImplicitGlobal => { ResolvedRead::Resolved(_) | ResolvedRead::ImplicitGlobal => {
// Nothing to do. // Nothing to do.
} }
ResolvedRead::StarImport => { ResolvedRead::WildcardImport => {
// F405 // F405
if self.enabled(Rule::UndefinedLocalWithImportStarUsage) { if self.enabled(Rule::UndefinedLocalWithImportStarUsage) {
let sources: Vec<String> = self let sources: Vec<String> = self
@ -4381,7 +4381,7 @@ impl<'a> Checker<'a> {
)); ));
} }
} }
ResolvedRead::NotFound => { ResolvedRead::NotFound | ResolvedRead::UnboundLocal(_) => {
// F821 // F821
if self.enabled(Rule::UndefinedName) { if self.enabled(Rule::UndefinedName) {
// Allow __path__. // Allow __path__.

View file

@ -59,7 +59,7 @@ pub struct SemanticModel<'a> {
/// Map from binding ID to binding ID that it shadows (in another scope). /// Map from binding ID to binding ID that it shadows (in another scope).
/// ///
/// For example: /// For example, given:
/// ```python /// ```python
/// import x /// import x
/// ///
@ -266,6 +266,7 @@ impl<'a> SemanticModel<'a> {
// The `name` in `print(name)` should be treated as unresolved, but the `name` in // The `name` in `print(name)` should be treated as unresolved, but the `name` in
// `name: str` should be treated as used. // `name: str` should be treated as used.
BindingKind::Annotation => continue, BindingKind::Annotation => continue,
// If it's a deletion, don't treat it as resolved, since the name is now // If it's a deletion, don't treat it as resolved, since the name is now
// unbound. For example, given: // unbound. For example, given:
// //
@ -276,11 +277,15 @@ impl<'a> SemanticModel<'a> {
// ``` // ```
// //
// The `x` in `print(x)` should be treated as unresolved. // The `x` in `print(x)` should be treated as unresolved.
BindingKind::Deletion | BindingKind::UnboundException => break, BindingKind::Deletion | BindingKind::UnboundException => {
_ => {} return ResolvedRead::UnboundLocal(binding_id)
} }
return ResolvedRead::Resolved(binding_id); // Otherwise, treat it as resolved.
_ => {
return ResolvedRead::Resolved(binding_id);
}
}
} }
// Allow usages of `__module__` and `__qualname__` within class scopes, e.g.: // Allow usages of `__module__` and `__qualname__` within class scopes, e.g.:
@ -309,7 +314,7 @@ impl<'a> SemanticModel<'a> {
} }
if import_starred { if import_starred {
ResolvedRead::StarImport ResolvedRead::WildcardImport
} else { } else {
ResolvedRead::NotFound ResolvedRead::NotFound
} }
@ -1065,14 +1070,62 @@ pub struct Snapshot {
#[derive(Debug)] #[derive(Debug)]
pub enum ResolvedRead { pub enum ResolvedRead {
/// The read reference is resolved to a specific binding. /// The read reference is resolved to a specific binding.
///
/// For example, given:
/// ```python
/// x = 1
/// print(x)
/// ```
///
/// The `x` in `print(x)` is resolved to the binding of `x` in `x = 1`.
Resolved(BindingId), Resolved(BindingId),
/// The read reference is resolved to a context-specific, implicit global (e.g., `__class__` /// The read reference is resolved to a context-specific, implicit global (e.g., `__class__`
/// within a class scope). /// within a class scope).
///
/// For example, given:
/// ```python
/// class C:
/// print(__class__)
/// ```
///
/// The `__class__` in `print(__class__)` is resolved to the implicit global `__class__`.
ImplicitGlobal, ImplicitGlobal,
/// The read reference is unresolved, but at least one of the containing scopes contains a star
/// import. /// The read reference is unresolved, but at least one of the containing scopes contains a
StarImport, /// wildcard import.
///
/// For example, given:
/// ```python
/// from x import *
///
/// print(y)
/// ```
///
/// The `y` in `print(y)` is unresolved, but the containing scope contains a wildcard import,
/// so `y` _may_ be resolved to a symbol imported by the wildcard import.
WildcardImport,
/// The read reference is resolved, but to an unbound local variable.
///
/// For example, given:
/// ```python
/// x = 1
/// del x
/// print(x)
/// ```
///
/// The `x` in `print(x)` is an unbound local.
UnboundLocal(BindingId),
/// The read reference is definitively unresolved. /// The read reference is definitively unresolved.
///
/// For example, given:
/// ```python
/// print(x)
/// ```
///
/// The `x` in `print(x)` is definitively unresolved.
NotFound, NotFound,
} }