Refine SemanticModel lifetime bounds (#10221)

## Summary

Corrects/refines some semantic model and related lifetime bounds.

## Test Plan

`cargo check`
This commit is contained in:
Micha Reiser 2024-03-04 09:21:13 +01:00 committed by GitHub
parent 4eac9baf43
commit 64f66cd8fe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 46 additions and 40 deletions

View file

@ -4,7 +4,7 @@ use ruff_text_size::{Ranged, TextRange};
/// An import with its surrounding context. /// An import with its surrounding context.
pub(crate) struct ImportBinding<'a> { pub(crate) struct ImportBinding<'a> {
/// The qualified name of the import (e.g., `typing.List` for `from typing import List`). /// The qualified name of the import (e.g., `typing.List` for `from typing import List`).
pub(crate) import: AnyImport<'a>, pub(crate) import: AnyImport<'a, 'a>,
/// The binding for the imported symbol. /// The binding for the imported symbol.
pub(crate) binding: &'a Binding<'a>, pub(crate) binding: &'a Binding<'a>,
/// The first reference to the imported symbol. /// The first reference to the imported symbol.

View file

@ -229,7 +229,7 @@ pub(crate) fn unused_import(checker: &Checker, scope: &Scope, diagnostics: &mut
#[derive(Debug)] #[derive(Debug)]
struct ImportBinding<'a> { struct ImportBinding<'a> {
/// The qualified name of the import (e.g., `typing.List` for `from typing import List`). /// The qualified name of the import (e.g., `typing.List` for `from typing import List`).
import: AnyImport<'a>, import: AnyImport<'a, 'a>,
/// The trimmed range of the import (e.g., `List` in `from typing import List`). /// The trimmed range of the import (e.g., `List` in `from typing import List`).
range: TextRange, range: TextRange,
/// The range of the import's parent statement. /// The range of the import's parent statement.

View file

@ -110,7 +110,7 @@ impl<'a> Binding<'a> {
/// Return `true` if this binding "redefines" the given binding, as per Pyflake's definition of /// Return `true` if this binding "redefines" the given binding, as per Pyflake's definition of
/// redefinition. /// redefinition.
pub fn redefines(&self, existing: &'a Binding) -> bool { pub fn redefines(&self, existing: &Binding) -> bool {
match &self.kind { match &self.kind {
// Submodule imports are only considered redefinitions if they import the same // Submodule imports are only considered redefinitions if they import the same
// submodule. For example, this is a redefinition: // submodule. For example, this is a redefinition:
@ -184,12 +184,12 @@ impl<'a> Binding<'a> {
} }
/// Returns the name of the binding (e.g., `x` in `x = 1`). /// Returns the name of the binding (e.g., `x` in `x = 1`).
pub fn name<'b>(&self, locator: &'b Locator) -> &'b str { pub fn name<'b>(&self, locator: &Locator<'b>) -> &'b str {
locator.slice(self.range) locator.slice(self.range)
} }
/// Returns the statement in which the binding was defined. /// Returns the statement in which the binding was defined.
pub fn statement<'b>(&self, semantic: &'b SemanticModel) -> Option<&'b Stmt> { pub fn statement<'b>(&self, semantic: &SemanticModel<'b>) -> Option<&'b Stmt> {
self.source self.source
.map(|statement_id| semantic.statement(statement_id)) .map(|statement_id| semantic.statement(statement_id))
} }
@ -205,7 +205,7 @@ impl<'a> Binding<'a> {
}) })
} }
pub fn as_any_import(&'a self) -> Option<AnyImport<'a>> { pub fn as_any_import(&self) -> Option<AnyImport<'_, 'a>> {
match &self.kind { match &self.kind {
BindingKind::Import(import) => Some(AnyImport::Import(import)), BindingKind::Import(import) => Some(AnyImport::Import(import)),
BindingKind::SubmoduleImport(import) => Some(AnyImport::SubmoduleImport(import)), BindingKind::SubmoduleImport(import) => Some(AnyImport::SubmoduleImport(import)),
@ -549,10 +549,10 @@ bitflags! {
/// A trait for imported symbols. /// A trait for imported symbols.
pub trait Imported<'a> { pub trait Imported<'a> {
/// Returns the call path to the imported symbol. /// Returns the call path to the imported symbol.
fn call_path(&self) -> &[&str]; fn call_path(&self) -> &[&'a str];
/// Returns the module name of the imported symbol. /// Returns the module name of the imported symbol.
fn module_name(&self) -> &[&str]; fn module_name(&self) -> &[&'a str];
/// Returns the member name of the imported symbol. For a straight import, this is equivalent /// Returns the member name of the imported symbol. For a straight import, this is equivalent
/// to the qualified name; for a `from` import, this is the name of the imported symbol. /// to the qualified name; for a `from` import, this is the name of the imported symbol.
@ -568,12 +568,12 @@ pub trait Imported<'a> {
impl<'a> Imported<'a> for Import<'a> { impl<'a> Imported<'a> for Import<'a> {
/// For example, given `import foo`, returns `["foo"]`. /// For example, given `import foo`, returns `["foo"]`.
fn call_path(&self) -> &[&str] { fn call_path(&self) -> &[&'a str] {
self.call_path.as_ref() self.call_path.as_ref()
} }
/// For example, given `import foo`, returns `["foo"]`. /// For example, given `import foo`, returns `["foo"]`.
fn module_name(&self) -> &[&str] { fn module_name(&self) -> &[&'a str] {
&self.call_path[..1] &self.call_path[..1]
} }
@ -585,12 +585,12 @@ impl<'a> Imported<'a> for Import<'a> {
impl<'a> Imported<'a> for SubmoduleImport<'a> { impl<'a> Imported<'a> for SubmoduleImport<'a> {
/// For example, given `import foo.bar`, returns `["foo", "bar"]`. /// For example, given `import foo.bar`, returns `["foo", "bar"]`.
fn call_path(&self) -> &[&str] { fn call_path(&self) -> &[&'a str] {
self.call_path.as_ref() self.call_path.as_ref()
} }
/// For example, given `import foo.bar`, returns `["foo"]`. /// For example, given `import foo.bar`, returns `["foo"]`.
fn module_name(&self) -> &[&str] { fn module_name(&self) -> &[&'a str] {
&self.call_path[..1] &self.call_path[..1]
} }
@ -602,12 +602,12 @@ impl<'a> Imported<'a> for SubmoduleImport<'a> {
impl<'a> Imported<'a> for FromImport<'a> { impl<'a> Imported<'a> for FromImport<'a> {
/// For example, given `from foo import bar`, returns `["foo", "bar"]`. /// For example, given `from foo import bar`, returns `["foo", "bar"]`.
fn call_path(&self) -> &[&str] { fn call_path(&self) -> &[&'a str] {
&self.call_path &self.call_path
} }
/// For example, given `from foo import bar`, returns `["foo"]`. /// For example, given `from foo import bar`, returns `["foo"]`.
fn module_name(&self) -> &[&str] { fn module_name(&self) -> &[&'a str] {
&self.call_path[..self.call_path.len() - 1] &self.call_path[..self.call_path.len() - 1]
} }
@ -619,14 +619,14 @@ impl<'a> Imported<'a> for FromImport<'a> {
/// A wrapper around an import [`BindingKind`] that can be any of the three types of imports. /// A wrapper around an import [`BindingKind`] that can be any of the three types of imports.
#[derive(Debug, Clone, is_macro::Is)] #[derive(Debug, Clone, is_macro::Is)]
pub enum AnyImport<'a> { pub enum AnyImport<'a, 'ast> {
Import(&'a Import<'a>), Import(&'a Import<'ast>),
SubmoduleImport(&'a SubmoduleImport<'a>), SubmoduleImport(&'a SubmoduleImport<'ast>),
FromImport(&'a FromImport<'a>), FromImport(&'a FromImport<'ast>),
} }
impl<'a> Imported<'a> for AnyImport<'a> { impl<'a, 'ast> Imported<'ast> for AnyImport<'a, 'ast> {
fn call_path(&self) -> &[&str] { fn call_path(&self) -> &[&'ast str] {
match self { match self {
Self::Import(import) => import.call_path(), Self::Import(import) => import.call_path(),
Self::SubmoduleImport(import) => import.call_path(), Self::SubmoduleImport(import) => import.call_path(),
@ -634,7 +634,7 @@ impl<'a> Imported<'a> for AnyImport<'a> {
} }
} }
fn module_name(&self) -> &[&str] { fn module_name(&self) -> &[&'ast str] {
match self { match self {
Self::Import(import) => import.module_name(), Self::Import(import) => import.module_name(),
Self::SubmoduleImport(import) => import.module_name(), Self::SubmoduleImport(import) => import.module_name(),
@ -642,7 +642,7 @@ impl<'a> Imported<'a> for AnyImport<'a> {
} }
} }
fn member_name(&self) -> Cow<'a, str> { fn member_name(&self) -> Cow<'ast, str> {
match self { match self {
Self::Import(import) => import.member_name(), Self::Import(import) => import.member_name(),
Self::SubmoduleImport(import) => import.member_name(), Self::SubmoduleImport(import) => import.member_name(),

View file

@ -81,7 +81,7 @@ pub struct Member<'a> {
impl<'a> Member<'a> { impl<'a> Member<'a> {
/// Return the name of the member. /// Return the name of the member.
pub fn name(&self) -> &str { pub fn name(&self) -> &'a str {
match self.kind { match self.kind {
MemberKind::Class(class) => &class.name, MemberKind::Class(class) => &class.name,
MemberKind::NestedClass(class) => &class.name, MemberKind::NestedClass(class) => &class.name,
@ -92,7 +92,7 @@ impl<'a> Member<'a> {
} }
/// Return the body of the member. /// Return the body of the member.
pub fn body(&self) -> &[Stmt] { pub fn body(&self) -> &'a [Stmt] {
match self.kind { match self.kind {
MemberKind::Class(class) => &class.body, MemberKind::Class(class) => &class.body,
MemberKind::NestedClass(class) => &class.body, MemberKind::NestedClass(class) => &class.body,
@ -123,7 +123,7 @@ pub enum Definition<'a> {
Member(Member<'a>), Member(Member<'a>),
} }
impl Definition<'_> { impl<'a> Definition<'a> {
/// Returns `true` if the [`Definition`] is a method definition. /// Returns `true` if the [`Definition`] is a method definition.
pub const fn is_method(&self) -> bool { pub const fn is_method(&self) -> bool {
matches!( matches!(
@ -136,7 +136,7 @@ impl Definition<'_> {
} }
/// Return the name of the definition. /// Return the name of the definition.
pub fn name(&self) -> Option<&str> { pub fn name(&self) -> Option<&'a str> {
match self { match self {
Definition::Module(module) => module.name(), Definition::Module(module) => module.name(),
Definition::Member(member) => Some(member.name()), Definition::Member(member) => Some(member.name()),
@ -144,7 +144,7 @@ impl Definition<'_> {
} }
/// Return the [`ast::StmtFunctionDef`] of the definition, if it's a function definition. /// Return the [`ast::StmtFunctionDef`] of the definition, if it's a function definition.
pub fn as_function_def(&self) -> Option<&ast::StmtFunctionDef> { pub fn as_function_def(&self) -> Option<&'a ast::StmtFunctionDef> {
match self { match self {
Definition::Member(Member { Definition::Member(Member {
kind: kind:
@ -158,7 +158,7 @@ impl Definition<'_> {
} }
/// Return the [`ast::StmtClassDef`] of the definition, if it's a class definition. /// Return the [`ast::StmtClassDef`] of the definition, if it's a class definition.
pub fn as_class_def(&self) -> Option<&ast::StmtClassDef> { pub fn as_class_def(&self) -> Option<&'a ast::StmtClassDef> {
match self { match self {
Definition::Member(Member { Definition::Member(Member {
kind: MemberKind::Class(class) | MemberKind::NestedClass(class), kind: MemberKind::Class(class) | MemberKind::NestedClass(class),

View file

@ -48,8 +48,8 @@ impl<'a> Globals<'a> {
builder.finish() builder.finish()
} }
pub(crate) fn get(&self, name: &str) -> Option<&TextRange> { pub(crate) fn get(&self, name: &str) -> Option<TextRange> {
self.0.get(name) self.0.get(name).copied()
} }
pub(crate) fn iter(&self) -> impl Iterator<Item = (&&'a str, &TextRange)> + '_ { pub(crate) fn iter(&self) -> impl Iterator<Item = (&&'a str, &TextRange)> + '_ {

View file

@ -131,7 +131,7 @@ pub struct SemanticModel<'a> {
} }
impl<'a> SemanticModel<'a> { impl<'a> SemanticModel<'a> {
pub fn new(typing_modules: &'a [String], path: &'a Path, module: Module<'a>) -> Self { pub fn new(typing_modules: &'a [String], path: &Path, module: Module<'a>) -> Self {
Self { Self {
typing_modules, typing_modules,
module_path: module.path(), module_path: module.path(),
@ -159,7 +159,7 @@ impl<'a> SemanticModel<'a> {
/// Return the [`Binding`] for the given [`BindingId`]. /// Return the [`Binding`] for the given [`BindingId`].
#[inline] #[inline]
pub fn binding(&self, id: BindingId) -> &Binding { pub fn binding(&self, id: BindingId) -> &Binding<'a> {
&self.bindings[id] &self.bindings[id]
} }
@ -200,7 +200,7 @@ impl<'a> SemanticModel<'a> {
} }
/// Return an iterator over the set of `typing` modules allowed in the semantic model. /// Return an iterator over the set of `typing` modules allowed in the semantic model.
pub fn typing_modules(&self) -> impl Iterator<Item = &str> { pub fn typing_modules(&self) -> impl Iterator<Item = &'a str> {
["typing", "_typeshed", "typing_extensions"] ["typing", "_typeshed", "typing_extensions"]
.iter() .iter()
.copied() .copied()
@ -567,7 +567,7 @@ impl<'a> SemanticModel<'a> {
/// For example, given `["Class", "method"`], resolve the `BindingKind::ClassDefinition` /// For example, given `["Class", "method"`], resolve the `BindingKind::ClassDefinition`
/// associated with `Class`, then the `BindingKind::FunctionDefinition` associated with /// associated with `Class`, then the `BindingKind::FunctionDefinition` associated with
/// `Class.method`. /// `Class.method`.
pub fn lookup_attribute(&'a self, value: &'a Expr) -> Option<BindingId> { pub fn lookup_attribute(&self, value: &Expr) -> Option<BindingId> {
let call_path = CallPath::from_expr(value)?; let call_path = CallPath::from_expr(value)?;
// Find the symbol in the current scope. // Find the symbol in the current scope.
@ -659,7 +659,13 @@ impl<'a> SemanticModel<'a> {
/// ``` /// ```
/// ///
/// ...then `resolve_call_path(${python_version})` will resolve to `sys.version_info`. /// ...then `resolve_call_path(${python_version})` will resolve to `sys.version_info`.
pub fn resolve_call_path(&'a self, value: &'a Expr) -> Option<CallPath<'a>> { pub fn resolve_call_path<'name, 'expr: 'name>(
&self,
value: &'expr Expr,
) -> Option<CallPath<'name>>
where
'a: 'name,
{
/// Return the [`ast::ExprName`] at the head of the expression, if any. /// Return the [`ast::ExprName`] at the head of the expression, if any.
const fn match_head(value: &Expr) -> Option<&ast::ExprName> { const fn match_head(value: &Expr) -> Option<&ast::ExprName> {
match value { match value {
@ -974,7 +980,7 @@ impl<'a> SemanticModel<'a> {
} }
/// Returns an iterator over all scopes, starting from the current [`Scope`]. /// Returns an iterator over all scopes, starting from the current [`Scope`].
pub fn current_scopes(&self) -> impl Iterator<Item = &Scope> { pub fn current_scopes(&self) -> impl Iterator<Item = &Scope<'a>> {
self.scopes.ancestors(self.scope_id) self.scopes.ancestors(self.scope_id)
} }
@ -1154,7 +1160,7 @@ impl<'a> SemanticModel<'a> {
/// Return the [`TextRange`] at which a name is declared as global in the current [`Scope`]. /// Return the [`TextRange`] at which a name is declared as global in the current [`Scope`].
pub fn global(&self, name: &str) -> Option<TextRange> { pub fn global(&self, name: &str) -> Option<TextRange> {
let global_id = self.scopes[self.scope_id].globals_id()?; let global_id = self.scopes[self.scope_id].globals_id()?;
self.globals[global_id].get(name).copied() self.globals[global_id].get(name)
} }
/// Given a `name` that has been declared `nonlocal`, return the [`ScopeId`] and [`BindingId`] /// Given a `name` that has been declared `nonlocal`, return the [`ScopeId`] and [`BindingId`]
@ -1998,7 +2004,7 @@ impl ImportedName {
self.context self.context
} }
pub fn statement<'a>(&self, semantic: &'a SemanticModel) -> &'a Stmt { pub fn statement<'a>(&self, semantic: &SemanticModel<'a>) -> &'a Stmt {
semantic.statement(self.source) semantic.statement(self.source)
} }
} }

View file

@ -95,7 +95,7 @@ impl<'a> Scope<'a> {
} }
/// Returns a tuple of the name and ID of all bindings defined in this scope. /// Returns a tuple of the name and ID of all bindings defined in this scope.
pub fn bindings(&self) -> impl Iterator<Item = (&str, BindingId)> + '_ { pub fn bindings(&self) -> impl Iterator<Item = (&'a str, BindingId)> + '_ {
self.bindings.iter().map(|(&name, &id)| (name, id)) self.bindings.iter().map(|(&name, &id)| (name, id))
} }
@ -238,7 +238,7 @@ impl<'a> Scopes<'a> {
} }
/// Returns an iterator over all [`Scope`] ancestors, starting from the given [`ScopeId`]. /// Returns an iterator over all [`Scope`] ancestors, starting from the given [`ScopeId`].
pub fn ancestors(&self, scope_id: ScopeId) -> impl Iterator<Item = &Scope> + '_ { pub fn ancestors(&self, scope_id: ScopeId) -> impl Iterator<Item = &Scope<'a>> + '_ {
std::iter::successors(Some(&self[scope_id]), |&scope| { std::iter::successors(Some(&self[scope_id]), |&scope| {
scope.parent.map(|scope_id| &self[scope_id]) scope.parent.map(|scope_id| &self[scope_id])
}) })