mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-10 05:39:12 +00:00
Rename ruff_python_semantic's Context
struct to SemanticModel
(#4565)
This commit is contained in:
parent
3238743a7b
commit
19c4b7bee6
304 changed files with 1253 additions and 1142 deletions
|
@ -3,7 +3,7 @@ use rustpython_parser::ast::Expr;
|
|||
use ruff_python_ast::call_path::from_qualified_name;
|
||||
use ruff_python_ast::helpers::map_callable;
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::model::SemanticModel;
|
||||
use crate::scope::{Scope, ScopeKind};
|
||||
|
||||
const CLASS_METHODS: [&str; 3] = ["__new__", "__init_subclass__", "__class_getitem__"];
|
||||
|
@ -19,7 +19,7 @@ pub enum FunctionType {
|
|||
|
||||
/// Classify a function based on its scope, name, and decorators.
|
||||
pub fn classify(
|
||||
ctx: &Context,
|
||||
model: &SemanticModel,
|
||||
scope: &Scope,
|
||||
name: &str,
|
||||
decorator_list: &[Expr],
|
||||
|
@ -32,7 +32,8 @@ pub fn classify(
|
|||
if decorator_list.iter().any(|expr| {
|
||||
// The method is decorated with a static method decorator (like
|
||||
// `@staticmethod`).
|
||||
ctx.resolve_call_path(map_callable(expr))
|
||||
model
|
||||
.resolve_call_path(map_callable(expr))
|
||||
.map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["", "staticmethod"]
|
||||
|| staticmethod_decorators
|
||||
|
@ -45,7 +46,7 @@ pub fn classify(
|
|||
// Special-case class method, like `__new__`.
|
||||
|| scope.bases.iter().any(|expr| {
|
||||
// The class itself extends a known metaclass, so all methods are class methods.
|
||||
ctx.resolve_call_path(map_callable(expr)).map_or(false, |call_path| {
|
||||
model.resolve_call_path(map_callable(expr)).map_or(false, |call_path| {
|
||||
METACLASS_BASES
|
||||
.iter()
|
||||
.any(|(module, member)| call_path.as_slice() == [*module, *member])
|
||||
|
@ -53,7 +54,7 @@ pub fn classify(
|
|||
})
|
||||
|| decorator_list.iter().any(|expr| {
|
||||
// The method is decorated with a class method decorator (like `@classmethod`).
|
||||
ctx.resolve_call_path(map_callable(expr)).map_or(false, |call_path| {
|
||||
model.resolve_call_path(map_callable(expr)).map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["", "classmethod"] ||
|
||||
classmethod_decorators
|
||||
.iter()
|
||||
|
|
|
@ -2,7 +2,7 @@ use rustpython_parser::ast::{self, Expr};
|
|||
|
||||
use ruff_python_ast::call_path::collect_call_path;
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::model::SemanticModel;
|
||||
|
||||
/// Return `true` if the given `Expr` is a potential logging call. Matches
|
||||
/// `logging.error`, `logger.error`, `self.logger.error`, etc., but not
|
||||
|
@ -16,9 +16,9 @@ use crate::context::Context;
|
|||
/// # This is detected to be a logger candidate
|
||||
/// bar.error()
|
||||
/// ```
|
||||
pub fn is_logger_candidate(context: &Context, func: &Expr) -> bool {
|
||||
pub fn is_logger_candidate(model: &SemanticModel, func: &Expr) -> bool {
|
||||
if let Expr::Attribute(ast::ExprAttribute { value, .. }) = func {
|
||||
let Some(call_path) = (if let Some(call_path) = context.resolve_call_path(value) {
|
||||
let Some(call_path) = (if let Some(call_path) = model.resolve_call_path(value) {
|
||||
if call_path.first().map_or(false, |module| *module == "logging") || call_path.as_slice() == ["flask", "current_app", "logger"] {
|
||||
Some(call_path)
|
||||
} else {
|
||||
|
|
|
@ -5,7 +5,7 @@ use ruff_python_stdlib::typing::{
|
|||
IMMUTABLE_GENERIC_TYPES, IMMUTABLE_TYPES, PEP_585_GENERICS, PEP_593_SUBSCRIPTS, SUBSCRIPTS,
|
||||
};
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::model::SemanticModel;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Callable {
|
||||
|
@ -26,14 +26,14 @@ pub enum SubscriptKind {
|
|||
|
||||
pub fn match_annotated_subscript<'a>(
|
||||
expr: &Expr,
|
||||
context: &Context,
|
||||
model: &SemanticModel,
|
||||
typing_modules: impl Iterator<Item = &'a str>,
|
||||
) -> Option<SubscriptKind> {
|
||||
if !matches!(expr, Expr::Name(_) | Expr::Attribute(_)) {
|
||||
return None;
|
||||
}
|
||||
|
||||
context.resolve_call_path(expr).and_then(|call_path| {
|
||||
model.resolve_call_path(expr).and_then(|call_path| {
|
||||
if SUBSCRIPTS.contains(&call_path.as_slice()) {
|
||||
return Some(SubscriptKind::AnnotatedSubscript);
|
||||
}
|
||||
|
@ -80,8 +80,8 @@ impl std::fmt::Display for ModuleMember {
|
|||
|
||||
/// Returns the PEP 585 standard library generic variant for a `typing` module reference, if such
|
||||
/// a variant exists.
|
||||
pub fn to_pep585_generic(expr: &Expr, context: &Context) -> Option<ModuleMember> {
|
||||
context.resolve_call_path(expr).and_then(|call_path| {
|
||||
pub fn to_pep585_generic(expr: &Expr, model: &SemanticModel) -> Option<ModuleMember> {
|
||||
model.resolve_call_path(expr).and_then(|call_path| {
|
||||
let [module, name] = call_path.as_slice() else {
|
||||
return None;
|
||||
};
|
||||
|
@ -110,7 +110,11 @@ pub enum Pep604Operator {
|
|||
}
|
||||
|
||||
/// Return the PEP 604 operator variant to which the given subscript [`Expr`] corresponds, if any.
|
||||
pub fn to_pep604_operator(value: &Expr, slice: &Expr, context: &Context) -> Option<Pep604Operator> {
|
||||
pub fn to_pep604_operator(
|
||||
value: &Expr,
|
||||
slice: &Expr,
|
||||
model: &SemanticModel,
|
||||
) -> Option<Pep604Operator> {
|
||||
/// Returns `true` if any argument in the slice is a string.
|
||||
fn any_arg_is_str(slice: &Expr) -> bool {
|
||||
match slice {
|
||||
|
@ -129,13 +133,13 @@ pub fn to_pep604_operator(value: &Expr, slice: &Expr, context: &Context) -> Opti
|
|||
return None;
|
||||
}
|
||||
|
||||
context
|
||||
model
|
||||
.resolve_call_path(value)
|
||||
.as_ref()
|
||||
.and_then(|call_path| {
|
||||
if context.match_typing_call_path(call_path, "Optional") {
|
||||
if model.match_typing_call_path(call_path, "Optional") {
|
||||
Some(Pep604Operator::Optional)
|
||||
} else if context.match_typing_call_path(call_path, "Union") {
|
||||
} else if model.match_typing_call_path(call_path, "Union") {
|
||||
Some(Pep604Operator::Union)
|
||||
} else {
|
||||
None
|
||||
|
@ -145,10 +149,10 @@ pub fn to_pep604_operator(value: &Expr, slice: &Expr, context: &Context) -> Opti
|
|||
|
||||
/// Return `true` if `Expr` represents a reference to a type annotation that resolves to an
|
||||
/// immutable type.
|
||||
pub fn is_immutable_annotation(context: &Context, expr: &Expr) -> bool {
|
||||
pub fn is_immutable_annotation(model: &SemanticModel, expr: &Expr) -> bool {
|
||||
match expr {
|
||||
Expr::Name(_) | Expr::Attribute(_) => {
|
||||
context.resolve_call_path(expr).map_or(false, |call_path| {
|
||||
model.resolve_call_path(expr).map_or(false, |call_path| {
|
||||
IMMUTABLE_TYPES
|
||||
.iter()
|
||||
.chain(IMMUTABLE_GENERIC_TYPES)
|
||||
|
@ -156,7 +160,7 @@ pub fn is_immutable_annotation(context: &Context, expr: &Expr) -> bool {
|
|||
})
|
||||
}
|
||||
Expr::Subscript(ast::ExprSubscript { value, slice, .. }) => {
|
||||
context.resolve_call_path(value).map_or(false, |call_path| {
|
||||
model.resolve_call_path(value).map_or(false, |call_path| {
|
||||
if IMMUTABLE_GENERIC_TYPES
|
||||
.iter()
|
||||
.any(|target| call_path.as_slice() == *target)
|
||||
|
@ -164,16 +168,16 @@ pub fn is_immutable_annotation(context: &Context, expr: &Expr) -> bool {
|
|||
true
|
||||
} else if call_path.as_slice() == ["typing", "Union"] {
|
||||
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = slice.as_ref() {
|
||||
elts.iter().all(|elt| is_immutable_annotation(context, elt))
|
||||
elts.iter().all(|elt| is_immutable_annotation(model, elt))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else if call_path.as_slice() == ["typing", "Optional"] {
|
||||
is_immutable_annotation(context, slice)
|
||||
is_immutable_annotation(model, slice)
|
||||
} else if call_path.as_slice() == ["typing", "Annotated"] {
|
||||
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = slice.as_ref() {
|
||||
elts.first()
|
||||
.map_or(false, |elt| is_immutable_annotation(context, elt))
|
||||
.map_or(false, |elt| is_immutable_annotation(model, elt))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -187,7 +191,7 @@ pub fn is_immutable_annotation(context: &Context, expr: &Expr) -> bool {
|
|||
op: Operator::BitOr,
|
||||
right,
|
||||
range: _range,
|
||||
}) => is_immutable_annotation(context, left) && is_immutable_annotation(context, right),
|
||||
}) => is_immutable_annotation(model, left) && is_immutable_annotation(model, right),
|
||||
Expr::Constant(ast::ExprConstant {
|
||||
value: Constant::None,
|
||||
..
|
||||
|
@ -213,11 +217,11 @@ const IMMUTABLE_FUNCS: &[&[&str]] = &[
|
|||
|
||||
/// Return `true` if `func` is a function that returns an immutable object.
|
||||
pub fn is_immutable_func(
|
||||
context: &Context,
|
||||
model: &SemanticModel,
|
||||
func: &Expr,
|
||||
extend_immutable_calls: &[CallPath],
|
||||
) -> bool {
|
||||
context.resolve_call_path(func).map_or(false, |call_path| {
|
||||
model.resolve_call_path(func).map_or(false, |call_path| {
|
||||
IMMUTABLE_FUNCS
|
||||
.iter()
|
||||
.any(|target| call_path.as_slice() == *target)
|
||||
|
|
|
@ -5,7 +5,7 @@ use rustpython_parser::ast::{self, Expr, Stmt};
|
|||
use ruff_python_ast::call_path::{collect_call_path, CallPath};
|
||||
use ruff_python_ast::helpers::map_callable;
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::model::SemanticModel;
|
||||
|
||||
#[derive(Debug, Clone, Copy, is_macro::Is)]
|
||||
pub enum Visibility {
|
||||
|
@ -14,9 +14,10 @@ pub enum Visibility {
|
|||
}
|
||||
|
||||
/// Returns `true` if a function is a "static method".
|
||||
pub fn is_staticmethod(ctx: &Context, decorator_list: &[Expr]) -> bool {
|
||||
pub fn is_staticmethod(model: &SemanticModel, decorator_list: &[Expr]) -> bool {
|
||||
decorator_list.iter().any(|expr| {
|
||||
ctx.resolve_call_path(map_callable(expr))
|
||||
model
|
||||
.resolve_call_path(map_callable(expr))
|
||||
.map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["", "staticmethod"]
|
||||
})
|
||||
|
@ -24,9 +25,10 @@ pub fn is_staticmethod(ctx: &Context, decorator_list: &[Expr]) -> bool {
|
|||
}
|
||||
|
||||
/// Returns `true` if a function is a "class method".
|
||||
pub fn is_classmethod(ctx: &Context, decorator_list: &[Expr]) -> bool {
|
||||
pub fn is_classmethod(model: &SemanticModel, decorator_list: &[Expr]) -> bool {
|
||||
decorator_list.iter().any(|expr| {
|
||||
ctx.resolve_call_path(map_callable(expr))
|
||||
model
|
||||
.resolve_call_path(map_callable(expr))
|
||||
.map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["", "classmethod"]
|
||||
})
|
||||
|
@ -34,23 +36,24 @@ pub fn is_classmethod(ctx: &Context, decorator_list: &[Expr]) -> bool {
|
|||
}
|
||||
|
||||
/// Returns `true` if a function definition is an `@overload`.
|
||||
pub fn is_overload(ctx: &Context, decorator_list: &[Expr]) -> bool {
|
||||
pub fn is_overload(model: &SemanticModel, decorator_list: &[Expr]) -> bool {
|
||||
decorator_list
|
||||
.iter()
|
||||
.any(|expr| ctx.match_typing_expr(map_callable(expr), "overload"))
|
||||
.any(|expr| model.match_typing_expr(map_callable(expr), "overload"))
|
||||
}
|
||||
|
||||
/// Returns `true` if a function definition is an `@override` (PEP 698).
|
||||
pub fn is_override(ctx: &Context, decorator_list: &[Expr]) -> bool {
|
||||
pub fn is_override(model: &SemanticModel, decorator_list: &[Expr]) -> bool {
|
||||
decorator_list
|
||||
.iter()
|
||||
.any(|expr| ctx.match_typing_expr(map_callable(expr), "override"))
|
||||
.any(|expr| model.match_typing_expr(map_callable(expr), "override"))
|
||||
}
|
||||
|
||||
/// Returns `true` if a function definition is an abstract method based on its decorators.
|
||||
pub fn is_abstract(ctx: &Context, decorator_list: &[Expr]) -> bool {
|
||||
pub fn is_abstract(model: &SemanticModel, decorator_list: &[Expr]) -> bool {
|
||||
decorator_list.iter().any(|expr| {
|
||||
ctx.resolve_call_path(map_callable(expr))
|
||||
model
|
||||
.resolve_call_path(map_callable(expr))
|
||||
.map_or(false, |call_path| {
|
||||
matches!(
|
||||
call_path.as_slice(),
|
||||
|
@ -69,9 +72,14 @@ pub fn is_abstract(ctx: &Context, decorator_list: &[Expr]) -> bool {
|
|||
/// Returns `true` if a function definition is a `@property`.
|
||||
/// `extra_properties` can be used to check additional non-standard
|
||||
/// `@property`-like decorators.
|
||||
pub fn is_property(ctx: &Context, decorator_list: &[Expr], extra_properties: &[CallPath]) -> bool {
|
||||
pub fn is_property(
|
||||
model: &SemanticModel,
|
||||
decorator_list: &[Expr],
|
||||
extra_properties: &[CallPath],
|
||||
) -> bool {
|
||||
decorator_list.iter().any(|expr| {
|
||||
ctx.resolve_call_path(map_callable(expr))
|
||||
model
|
||||
.resolve_call_path(map_callable(expr))
|
||||
.map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["", "property"]
|
||||
|| call_path.as_slice() == ["functools", "cached_property"]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
pub mod analyze;
|
||||
pub mod binding;
|
||||
pub mod context;
|
||||
pub mod definition;
|
||||
pub mod model;
|
||||
pub mod node;
|
||||
pub mod scope;
|
||||
|
|
|
@ -20,8 +20,8 @@ use crate::definition::{Definition, DefinitionId, Definitions, Member, Module};
|
|||
use crate::node::{NodeId, Nodes};
|
||||
use crate::scope::{Scope, ScopeId, ScopeKind, Scopes};
|
||||
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct Context<'a> {
|
||||
/// A semantic model for a Python module, to enable querying the module's semantic information.
|
||||
pub struct SemanticModel<'a> {
|
||||
pub typing_modules: &'a [String],
|
||||
pub module_path: Option<&'a [String]>,
|
||||
// Stack of all visited statements, along with the identifier of the current statement.
|
||||
|
@ -49,7 +49,7 @@ pub struct Context<'a> {
|
|||
pub handled_exceptions: Vec<Exceptions>,
|
||||
}
|
||||
|
||||
impl<'a> Context<'a> {
|
||||
impl<'a> SemanticModel<'a> {
|
||||
pub fn new(typing_modules: &'a [String], path: &'a Path, module: Module<'a>) -> Self {
|
||||
Self {
|
||||
typing_modules,
|
||||
|
@ -836,7 +836,7 @@ impl ContextFlags {
|
|||
}
|
||||
}
|
||||
|
||||
/// A snapshot of the [`Context`] at a given point in the AST traversal.
|
||||
/// A snapshot of the [`SemanticModel`] at a given point in the AST traversal.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Snapshot {
|
||||
scope_id: ScopeId,
|
Loading…
Add table
Add a link
Reference in a new issue