From 64f66cd8feed66c368c23d5f62b562f036d3de6e Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Mon, 4 Mar 2024 09:21:13 +0100 Subject: [PATCH] Refine SemanticModel lifetime bounds (#10221) ## Summary Corrects/refines some semantic model and related lifetime bounds. ## Test Plan `cargo check` --- .../src/rules/flake8_type_checking/imports.rs | 2 +- .../src/rules/pyflakes/rules/unused_import.rs | 2 +- crates/ruff_python_semantic/src/binding.rs | 40 +++++++++---------- crates/ruff_python_semantic/src/definition.rs | 12 +++--- crates/ruff_python_semantic/src/globals.rs | 4 +- crates/ruff_python_semantic/src/model.rs | 22 ++++++---- crates/ruff_python_semantic/src/scope.rs | 4 +- 7 files changed, 46 insertions(+), 40 deletions(-) diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/imports.rs b/crates/ruff_linter/src/rules/flake8_type_checking/imports.rs index 92c65cf275..3cd8f8f227 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/imports.rs +++ b/crates/ruff_linter/src/rules/flake8_type_checking/imports.rs @@ -4,7 +4,7 @@ use ruff_text_size::{Ranged, TextRange}; /// An import with its surrounding context. pub(crate) struct ImportBinding<'a> { /// 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. pub(crate) binding: &'a Binding<'a>, /// The first reference to the imported symbol. diff --git a/crates/ruff_linter/src/rules/pyflakes/rules/unused_import.rs b/crates/ruff_linter/src/rules/pyflakes/rules/unused_import.rs index 5fe0234e8c..dfbeee5778 100644 --- a/crates/ruff_linter/src/rules/pyflakes/rules/unused_import.rs +++ b/crates/ruff_linter/src/rules/pyflakes/rules/unused_import.rs @@ -229,7 +229,7 @@ pub(crate) fn unused_import(checker: &Checker, scope: &Scope, diagnostics: &mut #[derive(Debug)] struct ImportBinding<'a> { /// 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`). range: TextRange, /// The range of the import's parent statement. diff --git a/crates/ruff_python_semantic/src/binding.rs b/crates/ruff_python_semantic/src/binding.rs index 3ab5c76794..4e2028907b 100644 --- a/crates/ruff_python_semantic/src/binding.rs +++ b/crates/ruff_python_semantic/src/binding.rs @@ -110,7 +110,7 @@ impl<'a> Binding<'a> { /// Return `true` if this binding "redefines" the given binding, as per Pyflake's definition of /// redefinition. - pub fn redefines(&self, existing: &'a Binding) -> bool { + pub fn redefines(&self, existing: &Binding) -> bool { match &self.kind { // Submodule imports are only considered redefinitions if they import the same // 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`). - pub fn name<'b>(&self, locator: &'b Locator) -> &'b str { + pub fn name<'b>(&self, locator: &Locator<'b>) -> &'b str { locator.slice(self.range) } /// 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 .map(|statement_id| semantic.statement(statement_id)) } @@ -205,7 +205,7 @@ impl<'a> Binding<'a> { }) } - pub fn as_any_import(&'a self) -> Option> { + pub fn as_any_import(&self) -> Option> { match &self.kind { BindingKind::Import(import) => Some(AnyImport::Import(import)), BindingKind::SubmoduleImport(import) => Some(AnyImport::SubmoduleImport(import)), @@ -549,10 +549,10 @@ bitflags! { /// A trait for imported symbols. pub trait Imported<'a> { /// 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. - 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 /// 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> { /// For example, given `import foo`, returns `["foo"]`. - fn call_path(&self) -> &[&str] { + fn call_path(&self) -> &[&'a str] { self.call_path.as_ref() } /// For example, given `import foo`, returns `["foo"]`. - fn module_name(&self) -> &[&str] { + fn module_name(&self) -> &[&'a str] { &self.call_path[..1] } @@ -585,12 +585,12 @@ impl<'a> Imported<'a> for Import<'a> { impl<'a> Imported<'a> for SubmoduleImport<'a> { /// 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() } /// For example, given `import foo.bar`, returns `["foo"]`. - fn module_name(&self) -> &[&str] { + fn module_name(&self) -> &[&'a str] { &self.call_path[..1] } @@ -602,12 +602,12 @@ impl<'a> Imported<'a> for SubmoduleImport<'a> { impl<'a> Imported<'a> for FromImport<'a> { /// For example, given `from foo import bar`, returns `["foo", "bar"]`. - fn call_path(&self) -> &[&str] { + fn call_path(&self) -> &[&'a str] { &self.call_path } /// 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] } @@ -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. #[derive(Debug, Clone, is_macro::Is)] -pub enum AnyImport<'a> { - Import(&'a Import<'a>), - SubmoduleImport(&'a SubmoduleImport<'a>), - FromImport(&'a FromImport<'a>), +pub enum AnyImport<'a, 'ast> { + Import(&'a Import<'ast>), + SubmoduleImport(&'a SubmoduleImport<'ast>), + FromImport(&'a FromImport<'ast>), } -impl<'a> Imported<'a> for AnyImport<'a> { - fn call_path(&self) -> &[&str] { +impl<'a, 'ast> Imported<'ast> for AnyImport<'a, 'ast> { + fn call_path(&self) -> &[&'ast str] { match self { Self::Import(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 { Self::Import(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 { Self::Import(import) => import.member_name(), Self::SubmoduleImport(import) => import.member_name(), diff --git a/crates/ruff_python_semantic/src/definition.rs b/crates/ruff_python_semantic/src/definition.rs index 6c58d5787b..19f7bb8904 100644 --- a/crates/ruff_python_semantic/src/definition.rs +++ b/crates/ruff_python_semantic/src/definition.rs @@ -81,7 +81,7 @@ pub struct Member<'a> { impl<'a> Member<'a> { /// Return the name of the member. - pub fn name(&self) -> &str { + pub fn name(&self) -> &'a str { match self.kind { MemberKind::Class(class) => &class.name, MemberKind::NestedClass(class) => &class.name, @@ -92,7 +92,7 @@ impl<'a> Member<'a> { } /// Return the body of the member. - pub fn body(&self) -> &[Stmt] { + pub fn body(&self) -> &'a [Stmt] { match self.kind { MemberKind::Class(class) => &class.body, MemberKind::NestedClass(class) => &class.body, @@ -123,7 +123,7 @@ pub enum Definition<'a> { Member(Member<'a>), } -impl Definition<'_> { +impl<'a> Definition<'a> { /// Returns `true` if the [`Definition`] is a method definition. pub const fn is_method(&self) -> bool { matches!( @@ -136,7 +136,7 @@ impl Definition<'_> { } /// Return the name of the definition. - pub fn name(&self) -> Option<&str> { + pub fn name(&self) -> Option<&'a str> { match self { Definition::Module(module) => module.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. - pub fn as_function_def(&self) -> Option<&ast::StmtFunctionDef> { + pub fn as_function_def(&self) -> Option<&'a ast::StmtFunctionDef> { match self { Definition::Member(Member { kind: @@ -158,7 +158,7 @@ impl 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 { Definition::Member(Member { kind: MemberKind::Class(class) | MemberKind::NestedClass(class), diff --git a/crates/ruff_python_semantic/src/globals.rs b/crates/ruff_python_semantic/src/globals.rs index 5b3ff8baf0..a3c27db8a3 100644 --- a/crates/ruff_python_semantic/src/globals.rs +++ b/crates/ruff_python_semantic/src/globals.rs @@ -48,8 +48,8 @@ impl<'a> Globals<'a> { builder.finish() } - pub(crate) fn get(&self, name: &str) -> Option<&TextRange> { - self.0.get(name) + pub(crate) fn get(&self, name: &str) -> Option { + self.0.get(name).copied() } pub(crate) fn iter(&self) -> impl Iterator + '_ { diff --git a/crates/ruff_python_semantic/src/model.rs b/crates/ruff_python_semantic/src/model.rs index 4eed5b5ecd..6608a4aa85 100644 --- a/crates/ruff_python_semantic/src/model.rs +++ b/crates/ruff_python_semantic/src/model.rs @@ -131,7 +131,7 @@ pub struct 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 { typing_modules, module_path: module.path(), @@ -159,7 +159,7 @@ impl<'a> SemanticModel<'a> { /// Return the [`Binding`] for the given [`BindingId`]. #[inline] - pub fn binding(&self, id: BindingId) -> &Binding { + pub fn binding(&self, id: BindingId) -> &Binding<'a> { &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. - pub fn typing_modules(&self) -> impl Iterator { + pub fn typing_modules(&self) -> impl Iterator { ["typing", "_typeshed", "typing_extensions"] .iter() .copied() @@ -567,7 +567,7 @@ impl<'a> SemanticModel<'a> { /// For example, given `["Class", "method"`], resolve the `BindingKind::ClassDefinition` /// associated with `Class`, then the `BindingKind::FunctionDefinition` associated with /// `Class.method`. - pub fn lookup_attribute(&'a self, value: &'a Expr) -> Option { + pub fn lookup_attribute(&self, value: &Expr) -> Option { let call_path = CallPath::from_expr(value)?; // 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`. - pub fn resolve_call_path(&'a self, value: &'a Expr) -> Option> { + pub fn resolve_call_path<'name, 'expr: 'name>( + &self, + value: &'expr Expr, + ) -> Option> + where + 'a: 'name, + { /// Return the [`ast::ExprName`] at the head of the expression, if any. const fn match_head(value: &Expr) -> Option<&ast::ExprName> { match value { @@ -974,7 +980,7 @@ impl<'a> SemanticModel<'a> { } /// Returns an iterator over all scopes, starting from the current [`Scope`]. - pub fn current_scopes(&self) -> impl Iterator { + pub fn current_scopes(&self) -> impl Iterator> { 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`]. pub fn global(&self, name: &str) -> Option { 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`] @@ -1998,7 +2004,7 @@ impl ImportedName { 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) } } diff --git a/crates/ruff_python_semantic/src/scope.rs b/crates/ruff_python_semantic/src/scope.rs index 0699d160b0..0b2637258f 100644 --- a/crates/ruff_python_semantic/src/scope.rs +++ b/crates/ruff_python_semantic/src/scope.rs @@ -95,7 +95,7 @@ impl<'a> Scope<'a> { } /// Returns a tuple of the name and ID of all bindings defined in this scope. - pub fn bindings(&self) -> impl Iterator + '_ { + pub fn bindings(&self) -> impl Iterator + '_ { 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`]. - pub fn ancestors(&self, scope_id: ScopeId) -> impl Iterator + '_ { + pub fn ancestors(&self, scope_id: ScopeId) -> impl Iterator> + '_ { std::iter::successors(Some(&self[scope_id]), |&scope| { scope.parent.map(|scope_id| &self[scope_id]) })