Introduce a ruff_python_semantic crate (#3865)

This commit is contained in:
Charlie Marsh 2023-04-04 12:50:47 -04:00 committed by GitHub
parent 46bcb1f725
commit d919adc13c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
64 changed files with 267 additions and 225 deletions

18
Cargo.lock generated
View file

@ -2010,6 +2010,7 @@ dependencies = [
"ruff_diagnostics",
"ruff_macros",
"ruff_python_ast",
"ruff_python_semantic",
"ruff_python_stdlib",
"ruff_rustpython",
"rustc-hash",
@ -2170,12 +2171,10 @@ dependencies = [
"is-macro",
"itertools",
"log",
"nohash-hasher",
"num-bigint",
"num-traits",
"once_cell",
"regex",
"ruff_python_stdlib",
"ruff_rustpython",
"rustc-hash",
"rustpython-common",
@ -2196,7 +2195,6 @@ dependencies = [
"once_cell",
"ruff_formatter",
"ruff_python_ast",
"ruff_python_stdlib",
"ruff_rustpython",
"ruff_testing_macros",
"ruff_text_size",
@ -2207,6 +2205,20 @@ dependencies = [
"test-case",
]
[[package]]
name = "ruff_python_semantic"
version = "0.0.0"
dependencies = [
"bitflags",
"is-macro",
"nohash-hasher",
"ruff_python_ast",
"ruff_python_stdlib",
"rustc-hash",
"rustpython-parser",
"smallvec",
]
[[package]]
name = "ruff_python_stdlib"
version = "0.0.0"

View file

@ -24,6 +24,7 @@ is-macro = { version = "0.2.2" }
itertools = { version = "0.10.5" }
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "80e4c1399f95e5beb532fdd1e209ad2dbb470438" }
log = { version = "0.4.17" }
nohash-hasher = { version = "0.2.0" }
once_cell = { version = "1.17.1" }
path-absolutize = { version = "3.0.14" }
proc-macro2 = { version = "1.0.51" }
@ -40,6 +41,7 @@ serde = { version = "1.0.152", features = ["derive"] }
serde_json = { version = "1.0.93", features = ["preserve_order"] }
shellexpand = { version = "3.0.0" }
similar = { version = "2.2.1" }
smallvec = { version = "1.10.0" }
strum = { version = "0.24.1", features = ["strum_macros"] }
strum_macros = { version = "0.24.3" }
syn = { version = "1.0.109" }

View file

@ -18,6 +18,7 @@ ruff_cache = { path = "../ruff_cache" }
ruff_diagnostics = { path = "../ruff_diagnostics", features = ["serde"] }
ruff_macros = { path = "../ruff_macros" }
ruff_python_ast = { path = "../ruff_python_ast" }
ruff_python_semantic = { path = "../ruff_python_semantic" }
ruff_python_stdlib = { path = "../ruff_python_stdlib" }
ruff_rustpython = { path = "../ruff_rustpython" }
@ -37,7 +38,7 @@ itertools = { workspace = true }
libcst = { workspace = true }
log = { workspace = true }
natord = { version = "1.0.9" }
nohash-hasher = { version = "0.2.0" }
nohash-hasher = { workspace = true }
num-bigint = { version = "0.4.3" }
num-traits = { version = "0.2.15" }
once_cell = { workspace = true }
@ -57,7 +58,7 @@ semver = { version = "1.0.16" }
serde = { workspace = true }
serde_json = { workspace = true }
shellexpand = { workspace = true }
smallvec = { version = "1.10.0" }
smallvec = { workspace = true }
strum = { workspace = true }
strum_macros = { workspace = true }
textwrap = { workspace = true }

View file

@ -8,12 +8,12 @@ use rustpython_parser::ast::{ExcepthandlerKind, Expr, Keyword, Location, Stmt, S
use rustpython_parser::{lexer, Mode, Tok};
use ruff_diagnostics::Edit;
use ruff_python_ast::context::Context;
use ruff_python_ast::helpers;
use ruff_python_ast::helpers::to_absolute;
use ruff_python_ast::imports::{AnyImport, Import};
use ruff_python_ast::newlines::NewlineWithTrailingNewline;
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
use ruff_python_semantic::context::Context;
use crate::cst::helpers::compose_module_path;
use crate::cst::matchers::match_module;

View file

@ -1,9 +1,9 @@
use ruff_python_ast::scope::ScopeStack;
use ruff_python_semantic::scope::ScopeStack;
use rustpython_parser::ast::{Expr, Stmt};
use ruff_python_ast::types::Range;
use ruff_python_ast::types::RefEquality;
use ruff_python_ast::visibility::{Visibility, VisibleScope};
use ruff_python_semantic::analyze::visibility::{Visibility, VisibleScope};
use crate::checkers::ast::AnnotationContext;
use crate::docstrings::definition::Definition;

View file

@ -14,22 +14,22 @@ use rustpython_parser::ast::{
use ruff_diagnostics::Diagnostic;
use ruff_python_ast::all::{extract_all_names, AllNamesFlags};
use ruff_python_ast::binding::{
use ruff_python_ast::helpers::{extract_handled_exceptions, to_module_path};
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
use ruff_python_ast::types::{Node, Range, RefEquality};
use ruff_python_ast::typing::parse_type_annotation;
use ruff_python_ast::visitor::{walk_excepthandler, walk_pattern, Visitor};
use ruff_python_ast::{branch_detection, cast, helpers, str, visitor};
use ruff_python_semantic::analyze;
use ruff_python_semantic::analyze::typing::{Callable, SubscriptKind};
use ruff_python_semantic::binding::{
Binding, BindingId, BindingKind, Exceptions, ExecutionContext, Export, FromImportation,
Importation, StarImportation, SubmoduleImportation,
};
use ruff_python_ast::context::Context;
use ruff_python_ast::helpers::{extract_handled_exceptions, to_module_path};
use ruff_python_ast::scope::{
use ruff_python_semantic::context::Context;
use ruff_python_semantic::scope::{
ClassDef, FunctionDef, Lambda, Scope, ScopeId, ScopeKind, ScopeStack,
};
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
use ruff_python_ast::types::{Node, Range, RefEquality};
use ruff_python_ast::typing::{
match_annotated_subscript, parse_type_annotation, Callable, SubscriptKind,
};
use ruff_python_ast::visitor::{walk_excepthandler, walk_pattern, Visitor};
use ruff_python_ast::{branch_detection, cast, helpers, str, typing, visibility, visitor};
use ruff_python_stdlib::builtins::{BUILTINS, MAGIC_GLOBALS};
use ruff_python_stdlib::path::is_python_stub_file;
@ -2248,7 +2248,7 @@ where
|| (self.settings.target_version >= PythonVersion::Py37
&& self.ctx.annotations_future_enabled
&& self.ctx.in_annotation))
&& typing::is_pep585_builtin(expr, &self.ctx)
&& analyze::typing::is_pep585_builtin(expr, &self.ctx)
{
pyupgrade::rules::use_pep585_annotation(self, expr);
}
@ -2291,7 +2291,7 @@ where
|| (self.settings.target_version >= PythonVersion::Py37
&& self.ctx.annotations_future_enabled
&& self.ctx.in_annotation))
&& typing::is_pep585_builtin(expr, &self.ctx)
&& analyze::typing::is_pep585_builtin(expr, &self.ctx)
{
pyupgrade::rules::use_pep585_annotation(self, expr);
}
@ -3632,7 +3632,7 @@ where
self.ctx.in_subscript = true;
visitor::walk_expr(self, expr);
} else {
match match_annotated_subscript(
match analyze::typing::match_annotated_subscript(
value,
&self.ctx,
self.settings.typing_modules.iter().map(String::as_str),
@ -4051,7 +4051,7 @@ impl<'a> Checker<'a> {
&& binding.redefines(existing)
&& (!self.settings.dummy_variable_rgx.is_match(name) || existing_is_import)
&& !(existing.kind.is_function_definition()
&& visibility::is_overload(
&& analyze::visibility::is_overload(
&self.ctx,
cast::decorator_list(existing.source.as_ref().unwrap()),
))

View file

@ -1,7 +1,8 @@
use ruff_python_ast::visibility::{
use rustpython_parser::ast::{Expr, Stmt};
use ruff_python_semantic::analyze::visibility::{
class_visibility, function_visibility, method_visibility, Modifier, Visibility, VisibleScope,
};
use rustpython_parser::ast::{Expr, Stmt};
#[derive(Debug, Clone)]
pub enum DefinitionKind<'a> {

View file

@ -2,7 +2,7 @@
use rustpython_parser::ast::{Constant, Expr, ExprKind, Stmt, StmtKind};
use ruff_python_ast::visibility::{Modifier, VisibleScope};
use ruff_python_semantic::analyze::visibility;
use crate::docstrings::definition::{Definition, DefinitionKind, Documentable};
@ -28,7 +28,7 @@ pub fn docstring_from(suite: &[Stmt]) -> Option<&Expr> {
/// Extract a `Definition` from the AST node defined by a `Stmt`.
pub fn extract<'a>(
scope: VisibleScope,
scope: visibility::VisibleScope,
stmt: &'a Stmt,
body: &'a [Stmt],
kind: Documentable,
@ -36,22 +36,22 @@ pub fn extract<'a>(
let expr = docstring_from(body);
match kind {
Documentable::Function => match scope {
VisibleScope {
modifier: Modifier::Module,
visibility::VisibleScope {
modifier: visibility::Modifier::Module,
..
} => Definition {
kind: DefinitionKind::Function(stmt),
docstring: expr,
},
VisibleScope {
modifier: Modifier::Class,
visibility::VisibleScope {
modifier: visibility::Modifier::Class,
..
} => Definition {
kind: DefinitionKind::Method(stmt),
docstring: expr,
},
VisibleScope {
modifier: Modifier::Function,
visibility::VisibleScope {
modifier: visibility::Modifier::Function,
..
} => Definition {
kind: DefinitionKind::NestedFunction(stmt),
@ -59,22 +59,22 @@ pub fn extract<'a>(
},
},
Documentable::Class => match scope {
VisibleScope {
modifier: Modifier::Module,
visibility::VisibleScope {
modifier: visibility::Modifier::Module,
..
} => Definition {
kind: DefinitionKind::Class(stmt),
docstring: expr,
},
VisibleScope {
modifier: Modifier::Class,
visibility::VisibleScope {
modifier: visibility::Modifier::Class,
..
} => Definition {
kind: DefinitionKind::NestedClass(stmt),
docstring: expr,
},
VisibleScope {
modifier: Modifier::Function,
visibility::VisibleScope {
modifier: visibility::Modifier::Function,
..
} => Definition {
kind: DefinitionKind::NestedClass(stmt),

View file

@ -1,7 +1,7 @@
use rustpython_parser::ast::{Arguments, Expr, Stmt, StmtKind};
use ruff_python_ast::cast;
use ruff_python_ast::visibility;
use ruff_python_semantic::analyze::visibility;
use crate::checkers::ast::Checker;
use crate::docstrings::definition::{Definition, DefinitionKind};

View file

@ -4,10 +4,10 @@ use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::ReturnStatementVisitor;
use ruff_python_ast::types::Range;
use ruff_python_ast::visibility::Visibility;
use ruff_python_ast::visibility::{self};
use ruff_python_ast::visitor::Visitor;
use ruff_python_ast::{cast, helpers};
use ruff_python_semantic::analyze::visibility;
use ruff_python_semantic::analyze::visibility::Visibility;
use ruff_python_stdlib::typing::SIMPLE_MAGIC_RETURN_TYPES;
use crate::checkers::ast::Checker;

View file

@ -3,8 +3,8 @@ use rustpython_parser::ast::{Expr, ExprKind, Stmt, StmtKind};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::{find_keyword, is_const_true};
use ruff_python_ast::logging;
use ruff_python_ast::types::Range;
use ruff_python_semantic::analyze::logging;
use crate::checkers::ast::Checker;

View file

@ -3,7 +3,7 @@ use rustpython_parser::ast::{Constant, Expr, ExprKind, Keyword, Stmt, StmtKind};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::types::Range;
use ruff_python_ast::visibility::{is_abstract, is_overload};
use ruff_python_semantic::analyze::visibility::{is_abstract, is_overload};
use crate::checkers::ast::Checker;
use crate::registry::Rule;

View file

@ -2,8 +2,8 @@ use rustpython_parser::ast::{Expr, ExprKind};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::scope::ScopeKind;
use ruff_python_ast::types::Range;
use ruff_python_semantic::scope::ScopeKind;
use crate::checkers::ast::Checker;

View file

@ -1,6 +1,6 @@
use rustpython_parser::ast::Expr;
use ruff_python_ast::context::Context;
use ruff_python_semantic::context::Context;
/// Return `true` if a Python class appears to be a Django model, based on its base classes.
pub fn is_model(context: &Context, base: &Expr) -> bool {

View file

@ -2,8 +2,9 @@ use rustpython_parser::ast::{Constant, Expr, ExprKind, Keyword, Location, Operat
use ruff_diagnostics::{Diagnostic, Edit};
use ruff_python_ast::helpers::{find_keyword, SimpleCallArgs};
use ruff_python_ast::logging;
use ruff_python_ast::types::Range;
use ruff_python_semantic::analyze::logging;
use ruff_python_stdlib::logging::LoggingLevel;
use crate::checkers::ast::Checker;
use crate::registry::{AsRule, Rule};
@ -128,7 +129,7 @@ fn check_log_record_attr_clash(checker: &mut Checker, extra: &Keyword) {
#[derive(Copy, Clone)]
enum LoggingCallType {
/// Logging call with a level method, e.g., `logging.info`.
LevelCall(logging::LoggingLevel),
LevelCall(LoggingLevel),
/// Logging call with an integer level as an argument, e.g., `logger.log(level, ...)`.
LogCall,
}
@ -138,7 +139,7 @@ impl LoggingCallType {
if attr == "log" {
Some(LoggingCallType::LogCall)
} else {
logging::LoggingLevel::from_attribute(attr).map(LoggingCallType::LevelCall)
LoggingLevel::from_attribute(attr).map(LoggingCallType::LevelCall)
}
}
}
@ -173,7 +174,7 @@ pub fn logging_call(checker: &mut Checker, func: &Expr, args: &[Expr], keywords:
if checker.settings.rules.enabled(Rule::LoggingWarn)
&& matches!(
logging_call_type,
LoggingCallType::LevelCall(logging::LoggingLevel::Warn)
LoggingCallType::LevelCall(LoggingLevel::Warn)
)
{
let mut diagnostic = Diagnostic::new(LoggingWarn, level_call_range);
@ -228,14 +229,14 @@ pub fn logging_call(checker: &mut Checker, func: &Expr, args: &[Expr], keywords:
if let LoggingCallType::LevelCall(logging_level) = logging_call_type {
match logging_level {
logging::LoggingLevel::Error => {
LoggingLevel::Error => {
if checker.settings.rules.enabled(Rule::LoggingExcInfo) {
checker
.diagnostics
.push(Diagnostic::new(LoggingExcInfo, level_call_range));
}
}
logging::LoggingLevel::Exception => {
LoggingLevel::Exception => {
if checker
.settings
.rules

View file

@ -3,8 +3,8 @@ use rustpython_parser::ast::{Expr, ExprKind};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::call_path::collect_call_path;
use ruff_python_ast::scope::ScopeKind;
use ruff_python_ast::types::Range;
use ruff_python_semantic::scope::ScopeKind;
use crate::checkers::ast::Checker;

View file

@ -11,9 +11,9 @@ use rustpython_parser::ast::{
use ruff_diagnostics::{AlwaysAutofixableViolation, AutofixKind, Diagnostic, Edit, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::comparable::ComparableExpr;
use ruff_python_ast::context::Context;
use ruff_python_ast::helpers::{contains_effect, create_expr, has_comments, unparse_expr};
use ruff_python_ast::types::Range;
use ruff_python_semantic::context::Context;
use crate::checkers::ast::Checker;
use crate::registry::AsRule;

View file

@ -7,11 +7,12 @@ use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::comparable::{ComparableConstant, ComparableExpr, ComparableStmt};
use ruff_python_ast::helpers::{
contains_call_path, contains_effect, create_expr, create_stmt, first_colon_range, has_comments,
any_over_expr, contains_effect, create_expr, create_stmt, first_colon_range, has_comments,
has_comments_in, unparse_expr, unparse_stmt,
};
use ruff_python_ast::newlines::StrExt;
use ruff_python_ast::types::Range;
use ruff_python_semantic::context::Context;
use crate::checkers::ast::Checker;
use crate::registry::AsRule;
@ -415,6 +416,14 @@ fn ternary(target_var: &Expr, body_value: &Expr, test: &Expr, orelse_value: &Exp
})
}
/// Return `true` if the `Expr` contains a reference to `${module}.${target}`.
fn contains_call_path(ctx: &Context, expr: &Expr, target: &[&str]) -> bool {
any_over_expr(expr, &|expr| {
ctx.resolve_call_path(expr)
.map_or(false, |call_path| call_path.as_slice() == target)
})
}
/// SIM108
pub fn use_ternary_operator(checker: &mut Checker, stmt: &Stmt, parent: Option<&Stmt>) {
let StmtKind::If { test, body, orelse } = &stmt.node else {

View file

@ -3,8 +3,8 @@ use rustpython_parser::ast::{Cmpop, Expr, ExprKind, Stmt, StmtKind, Unaryop};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::{create_expr, unparse_expr};
use ruff_python_ast::scope::ScopeKind;
use ruff_python_ast::types::Range;
use ruff_python_semantic::scope::ScopeKind;
use crate::checkers::ast::Checker;
use crate::registry::AsRule;

View file

@ -1,11 +1,11 @@
use num_traits::Zero;
use rustpython_parser::ast::{Constant, Expr, ExprKind};
use ruff_python_ast::binding::{Binding, BindingKind, ExecutionContext};
use ruff_python_ast::call_path::from_qualified_name;
use ruff_python_ast::context::Context;
use ruff_python_ast::helpers::map_callable;
use ruff_python_ast::scope::ScopeKind;
use ruff_python_semantic::binding::{Binding, BindingKind, ExecutionContext};
use ruff_python_semantic::context::Context;
use ruff_python_semantic::scope::ScopeKind;
/// Return `true` if [`Expr`] is a guard for a type-checking block.
pub fn is_type_checking_block(context: &Context, test: &Expr) -> bool {

View file

@ -1,6 +1,6 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::binding::{
use ruff_python_semantic::binding::{
Binding, BindingKind, ExecutionContext, FromImportation, Importation, SubmoduleImportation,
};

View file

@ -2,7 +2,7 @@ use std::path::Path;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::binding::{
use ruff_python_semantic::binding::{
Binding, BindingKind, ExecutionContext, FromImportation, Importation, SubmoduleImportation,
};

View file

@ -5,11 +5,11 @@ use rustpython_parser::ast::{Arg, Arguments};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::binding::Bindings;
use ruff_python_ast::function_type;
use ruff_python_ast::function_type::FunctionType;
use ruff_python_ast::scope::{FunctionDef, Lambda, Scope, ScopeKind};
use ruff_python_ast::visibility;
use ruff_python_semantic::analyze::function_type;
use ruff_python_semantic::analyze::function_type::FunctionType;
use ruff_python_semantic::analyze::visibility;
use ruff_python_semantic::binding::Bindings;
use ruff_python_semantic::scope::{FunctionDef, Lambda, Scope, ScopeKind};
use crate::checkers::ast::Checker;

View file

@ -3,8 +3,8 @@ use rustpython_parser::ast::{Expr, ExprKind};
use ruff_diagnostics::Violation;
use ruff_diagnostics::{Diagnostic, DiagnosticKind};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::binding::BindingKind;
use ruff_python_ast::types::Range;
use ruff_python_semantic::binding::BindingKind;
use crate::checkers::ast::Checker;
use crate::registry::Rule;

View file

@ -3,8 +3,8 @@ use rustpython_parser::ast::{Expr, ExprKind};
use ruff_diagnostics::Violation;
use ruff_diagnostics::{Diagnostic, DiagnosticKind};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::binding::{BindingKind, Importation};
use ruff_python_ast::types::Range;
use ruff_python_semantic::binding::{BindingKind, Importation};
use crate::checkers::ast::Checker;
use crate::registry::Rule;

View file

@ -3,8 +3,8 @@ use rustpython_parser::ast::Stmt;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::identifier_range;
use ruff_python_ast::scope::{Scope, ScopeKind};
use ruff_python_ast::source_code::Locator;
use ruff_python_semantic::scope::{Scope, ScopeKind};
/// ## What it does
/// Checks for functions with "dunder" names (that is, names with two

View file

@ -2,9 +2,9 @@ use rustpython_parser::ast::{Arguments, Expr};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::function_type;
use ruff_python_ast::scope::Scope;
use ruff_python_ast::types::Range;
use ruff_python_semantic::analyze::function_type;
use ruff_python_semantic::scope::Scope;
use crate::checkers::ast::Checker;

View file

@ -2,9 +2,9 @@ use rustpython_parser::ast::{Arguments, Expr};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::function_type;
use ruff_python_ast::scope::Scope;
use ruff_python_ast::types::Range;
use ruff_python_semantic::analyze::function_type;
use ruff_python_semantic::scope::Scope;
use crate::checkers::ast::Checker;

View file

@ -4,10 +4,10 @@ use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::{match_leading_content, match_trailing_content, unparse_stmt};
use ruff_python_ast::newlines::StrExt;
use ruff_python_ast::scope::ScopeKind;
use ruff_python_ast::source_code::Stylist;
use ruff_python_ast::types::Range;
use ruff_python_ast::whitespace::leading_space;
use ruff_python_semantic::scope::ScopeKind;
use crate::checkers::ast::Checker;
use crate::registry::AsRule;

View file

@ -2,7 +2,7 @@ use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::cast;
use ruff_python_ast::helpers::identifier_range;
use ruff_python_ast::visibility::is_overload;
use ruff_python_semantic::analyze::visibility::is_overload;
use crate::checkers::ast::Checker;
use crate::docstrings::definition::{DefinitionKind, Docstring};

View file

@ -9,7 +9,7 @@ use ruff_python_ast::call_path::{from_qualified_name, CallPath};
use ruff_python_ast::cast;
use ruff_python_ast::newlines::StrExt;
use ruff_python_ast::types::Range;
use ruff_python_ast::visibility::{is_property, is_test};
use ruff_python_semantic::analyze::visibility::{is_property, is_test};
use crate::checkers::ast::Checker;
use crate::docstrings::definition::{DefinitionKind, Docstring};

View file

@ -3,7 +3,7 @@ use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::cast;
use ruff_python_ast::helpers::identifier_range;
use ruff_python_ast::types::Range;
use ruff_python_ast::visibility::{
use ruff_python_semantic::analyze::visibility::{
is_call, is_init, is_magic, is_new, is_overload, is_override, Visibility,
};

View file

@ -10,8 +10,8 @@ use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::identifier_range;
use ruff_python_ast::newlines::NewlineWithTrailingNewline;
use ruff_python_ast::types::Range;
use ruff_python_ast::visibility::is_staticmethod;
use ruff_python_ast::{cast, whitespace};
use ruff_python_semantic::analyze::visibility::is_staticmethod;
use crate::checkers::ast::Checker;
use crate::docstrings::definition::{DefinitionKind, Docstring};

View file

@ -2,8 +2,8 @@ use rustpython_parser::ast::Stmt;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::scope::ScopeKind;
use ruff_python_ast::types::Range;
use ruff_python_semantic::scope::ScopeKind;
use crate::checkers::ast::Checker;

View file

@ -1,7 +1,7 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::scope::Scope;
use ruff_python_ast::types::Range;
use ruff_python_semantic::scope::Scope;
#[violation]
pub struct UndefinedExport {

View file

@ -2,8 +2,8 @@ use std::string::ToString;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::binding::Bindings;
use ruff_python_ast::scope::{Scope, ScopeKind};
use ruff_python_semantic::binding::Bindings;
use ruff_python_semantic::scope::{Scope, ScopeKind};
#[violation]
pub struct UndefinedLocal {

View file

@ -1,6 +1,6 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::scope::ScopeId;
use ruff_python_semantic::scope::ScopeId;
use crate::checkers::ast::Checker;

View file

@ -6,9 +6,9 @@ use rustpython_parser::{lexer, Mode, Tok};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::contains_effect;
use ruff_python_ast::scope::{ScopeId, ScopeKind};
use ruff_python_ast::source_code::Locator;
use ruff_python_ast::types::{Range, RefEquality};
use ruff_python_semantic::scope::{ScopeId, ScopeKind};
use crate::autofix::actions::delete_stmt;
use crate::checkers::ast::Checker;

View file

@ -4,8 +4,8 @@ use rustpython_parser::ast::{Expr, ExprKind};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::scope::ScopeKind;
use ruff_python_ast::types::Range;
use ruff_python_semantic::scope::ScopeKind;
use crate::checkers::ast::Checker;

View file

@ -1,6 +1,6 @@
use ruff_python_ast::function_type;
use ruff_python_ast::function_type::FunctionType;
use ruff_python_ast::scope::{FunctionDef, ScopeKind};
use ruff_python_semantic::analyze::function_type;
use ruff_python_semantic::analyze::function_type::FunctionType;
use ruff_python_semantic::scope::{FunctionDef, ScopeKind};
use crate::checkers::ast::Checker;

View file

@ -2,8 +2,8 @@ use rustpython_parser::ast::Expr;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::scope::{FunctionDef, ScopeKind};
use ruff_python_ast::types::Range;
use ruff_python_semantic::scope::{FunctionDef, ScopeKind};
use crate::checkers::ast::Checker;

View file

@ -2,8 +2,8 @@ use rustpython_parser::ast::Expr;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::scope::ScopeKind;
use ruff_python_ast::types::Range;
use ruff_python_semantic::scope::ScopeKind;
use crate::checkers::ast::Checker;

View file

@ -3,8 +3,9 @@ use rustpython_parser::ast::{Constant, Expr, ExprKind, Keyword};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::SimpleCallArgs;
use ruff_python_ast::logging;
use ruff_python_ast::types::Range;
use ruff_python_semantic::analyze::logging;
use ruff_python_stdlib::logging::LoggingLevel;
use crate::checkers::ast::Checker;
use crate::registry::Rule;
@ -105,7 +106,7 @@ pub fn logging_call(checker: &mut Checker, func: &Expr, args: &[Expr], keywords:
}
if let ExprKind::Attribute { attr, .. } = &func.node {
if logging::LoggingLevel::from_attribute(attr.as_str()).is_some() {
if LoggingLevel::from_attribute(attr.as_str()).is_some() {
let call_args = SimpleCallArgs::new(args, keywords);
if let Some(msg) = call_args.argument("msg", 0) {
if let ExprKind::Constant {

View file

@ -3,9 +3,9 @@ use rustpython_parser::ast::{Excepthandler, ExcepthandlerKind, Expr, ExprContext
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::call_path::compose_call_path;
use ruff_python_ast::context::Context;
use ruff_python_ast::helpers::{create_expr, unparse_expr};
use ruff_python_ast::types::Range;
use ruff_python_semantic::context::Context;
use crate::checkers::ast::Checker;
use crate::registry::AsRule;

View file

@ -2,8 +2,8 @@ use rustpython_parser::ast::{ArgData, Expr, ExprKind, Stmt, StmtKind};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::scope::ScopeKind;
use ruff_python_ast::types::Range;
use ruff_python_semantic::scope::ScopeKind;
use crate::checkers::ast::Checker;
use crate::registry::AsRule;

View file

@ -2,9 +2,9 @@ use rustpython_parser::ast::{Expr, ExprKind, Keyword, Stmt};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::binding::{Binding, BindingKind, Bindings};
use ruff_python_ast::scope::Scope;
use ruff_python_ast::types::Range;
use ruff_python_semantic::binding::{Binding, BindingKind, Bindings};
use ruff_python_semantic::scope::Scope;
use crate::autofix::actions::remove_argument;
use crate::checkers::ast::Checker;

View file

@ -1,8 +1,9 @@
use rustpython_parser::ast::{Expr, ExprKind};
use ruff_python_ast::context::Context;
use ruff_python_ast::visitor;
use ruff_python_ast::visitor::Visitor;
use ruff_python_ast::{logging, visitor};
use ruff_python_semantic::analyze::logging;
use ruff_python_semantic::context::Context;
/// Collect `logging`-like calls from an AST.
pub struct LoggerCandidateVisitor<'a> {

View file

@ -8,7 +8,6 @@ rust-version = { workspace = true }
[lib]
[dependencies]
ruff_python_stdlib = { path = "../ruff_python_stdlib" }
ruff_rustpython = { path = "../ruff_rustpython" }
anyhow = { workspace = true }
@ -16,7 +15,6 @@ bitflags = { workspace = true }
is-macro = { workspace = true }
itertools = { workspace = true }
log = { workspace = true }
nohash-hasher = { version = "0.2.0" }
num-bigint = { version = "0.4.3" }
num-traits = { version = "0.2.15" }
once_cell = { workspace = true }
@ -25,4 +23,4 @@ rustc-hash = { workspace = true }
rustpython-common = { workspace = true }
rustpython-parser = { workspace = true }
serde = { workspace = true }
smallvec = { version = "1.10.0" }
smallvec = { workspace = true }

View file

@ -13,7 +13,6 @@ use rustpython_parser::{lexer, Mode, Tok};
use smallvec::SmallVec;
use crate::call_path::CallPath;
use crate::context::Context;
use crate::source_code::{Generator, Indexer, Locator, Stylist};
use crate::types::Range;
use crate::visitor;
@ -50,14 +49,6 @@ pub fn unparse_constant(constant: &Constant, stylist: &Stylist) -> String {
generator.generate()
}
/// Return `true` if the `Expr` contains a reference to `${module}.${target}`.
pub fn contains_call_path(ctx: &Context, expr: &Expr, target: &[&str]) -> bool {
any_over_expr(expr, &|expr| {
ctx.resolve_call_path(expr)
.map_or(false, |call_path| call_path.as_slice() == target)
})
}
/// Return `true` if the `Expr` contains an expression that appears to include a
/// side-effect (like a function call).
///

View file

@ -1,23 +1,17 @@
pub mod all;
pub mod binding;
pub mod branch_detection;
pub mod call_path;
pub mod cast;
pub mod comparable;
pub mod context;
pub mod function_type;
pub mod hashable;
pub mod helpers;
pub mod imports;
pub mod logging;
pub mod newlines;
pub mod relocate;
pub mod scope;
pub mod source_code;
pub mod str;
pub mod token_kind;
pub mod types;
pub mod typing;
pub mod visibility;
pub mod visitor;
pub mod whitespace;

View file

@ -1,80 +1,12 @@
use anyhow::Result;
use rustpython_parser as parser;
use rustpython_parser::ast::{Expr, ExprKind, Location};
use rustpython_parser::ast::{Expr, Location};
use ruff_python_stdlib::typing::{PEP_585_BUILTINS_ELIGIBLE, PEP_593_SUBSCRIPTS, SUBSCRIPTS};
use crate::call_path::{from_unqualified_name, CallPath};
use crate::context::Context;
use crate::relocate::relocate_expr;
use crate::source_code::Locator;
use crate::str;
use crate::types::Range;
#[derive(Copy, Clone)]
pub enum Callable {
Cast,
NewType,
TypeVar,
NamedTuple,
TypedDict,
MypyExtension,
}
#[derive(Copy, Clone)]
pub enum SubscriptKind {
AnnotatedSubscript,
PEP593AnnotatedSubscript,
}
pub fn match_annotated_subscript<'a>(
expr: &Expr,
context: &Context,
typing_modules: impl Iterator<Item = &'a str>,
) -> Option<SubscriptKind> {
if !matches!(
expr.node,
ExprKind::Name { .. } | ExprKind::Attribute { .. }
) {
return None;
}
context.resolve_call_path(expr).and_then(|call_path| {
if SUBSCRIPTS.contains(&call_path.as_slice()) {
return Some(SubscriptKind::AnnotatedSubscript);
}
if PEP_593_SUBSCRIPTS.contains(&call_path.as_slice()) {
return Some(SubscriptKind::PEP593AnnotatedSubscript);
}
for module in typing_modules {
let module_call_path: CallPath = from_unqualified_name(module);
if call_path.starts_with(&module_call_path) {
for subscript in SUBSCRIPTS.iter() {
if call_path.last() == subscript.last() {
return Some(SubscriptKind::AnnotatedSubscript);
}
}
for subscript in PEP_593_SUBSCRIPTS.iter() {
if call_path.last() == subscript.last() {
return Some(SubscriptKind::PEP593AnnotatedSubscript);
}
}
}
}
None
})
}
/// Returns `true` if `Expr` represents a reference to a typing object with a
/// PEP 585 built-in.
pub fn is_pep585_builtin(expr: &Expr, context: &Context) -> bool {
context.resolve_call_path(expr).map_or(false, |call_path| {
PEP_585_BUILTINS_ELIGIBLE.contains(&call_path.as_slice())
})
}
#[derive(is_macro::Is, Copy, Clone)]
pub enum AnnotationKind {
/// The annotation is defined as part a simple string literal,

View file

@ -8,7 +8,6 @@ rust-version = { workspace = true }
[dependencies]
ruff_formatter = { path = "../ruff_formatter" }
ruff_python_ast = { path = "../ruff_python_ast" }
ruff_python_stdlib = { path = "../ruff_python_stdlib" }
ruff_rustpython = { path = "../ruff_rustpython" }
ruff_text_size = { path = "../ruff_text_size" }

View file

@ -0,0 +1,19 @@
[package]
name = "ruff_python_semantic"
version = "0.0.0"
publish = false
edition = { workspace = true }
rust-version = { workspace = true }
[lib]
[dependencies]
ruff_python_ast = { path = "../ruff_python_ast" }
ruff_python_stdlib = { path = "../ruff_python_stdlib" }
bitflags = { workspace = true }
is-macro = { workspace = true }
nohash-hasher = { workspace = true }
rustc-hash = { workspace = true }
rustpython-parser = { workspace = true }
smallvec = { workspace = true }

View file

@ -1,8 +1,9 @@
use rustpython_parser::ast::Expr;
use crate::call_path::from_qualified_name;
use ruff_python_ast::call_path::from_qualified_name;
use ruff_python_ast::helpers::map_callable;
use crate::context::Context;
use crate::helpers::map_callable;
use crate::scope::{Scope, ScopeKind};
const CLASS_METHODS: [&str; 3] = ["__new__", "__init_subclass__", "__class_getitem__"];

View file

@ -1,34 +1,9 @@
use rustpython_parser::ast::{Expr, ExprKind};
use crate::call_path::collect_call_path;
use ruff_python_ast::call_path::collect_call_path;
use crate::context::Context;
#[derive(Copy, Clone)]
pub enum LoggingLevel {
Debug,
Critical,
Error,
Exception,
Info,
Warn,
Warning,
}
impl LoggingLevel {
pub fn from_attribute(level: &str) -> Option<Self> {
match level {
"debug" => Some(LoggingLevel::Debug),
"critical" => Some(LoggingLevel::Critical),
"error" => Some(LoggingLevel::Error),
"exception" => Some(LoggingLevel::Exception),
"info" => Some(LoggingLevel::Info),
"warn" => Some(LoggingLevel::Warn),
"warning" => Some(LoggingLevel::Warning),
_ => None,
}
}
}
/// Return `true` if the given `Expr` is a potential logging call. Matches
/// `logging.error`, `logger.error`, `self.logger.error`, etc., but not
/// arbitrary `foo.error` calls.

View file

@ -0,0 +1,4 @@
pub mod function_type;
pub mod logging;
pub mod typing;
pub mod visibility;

View file

@ -0,0 +1,70 @@
use rustpython_parser::ast::{Expr, ExprKind};
use ruff_python_ast::call_path::{from_unqualified_name, CallPath};
use ruff_python_stdlib::typing::{PEP_585_BUILTINS_ELIGIBLE, PEP_593_SUBSCRIPTS, SUBSCRIPTS};
use crate::context::Context;
#[derive(Copy, Clone)]
pub enum Callable {
Cast,
NewType,
TypeVar,
NamedTuple,
TypedDict,
MypyExtension,
}
#[derive(Copy, Clone)]
pub enum SubscriptKind {
AnnotatedSubscript,
PEP593AnnotatedSubscript,
}
pub fn match_annotated_subscript<'a>(
expr: &Expr,
context: &Context,
typing_modules: impl Iterator<Item = &'a str>,
) -> Option<SubscriptKind> {
if !matches!(
expr.node,
ExprKind::Name { .. } | ExprKind::Attribute { .. }
) {
return None;
}
context.resolve_call_path(expr).and_then(|call_path| {
if SUBSCRIPTS.contains(&call_path.as_slice()) {
return Some(SubscriptKind::AnnotatedSubscript);
}
if PEP_593_SUBSCRIPTS.contains(&call_path.as_slice()) {
return Some(SubscriptKind::PEP593AnnotatedSubscript);
}
for module in typing_modules {
let module_call_path: CallPath = from_unqualified_name(module);
if call_path.starts_with(&module_call_path) {
for subscript in SUBSCRIPTS.iter() {
if call_path.last() == subscript.last() {
return Some(SubscriptKind::AnnotatedSubscript);
}
}
for subscript in PEP_593_SUBSCRIPTS.iter() {
if call_path.last() == subscript.last() {
return Some(SubscriptKind::PEP593AnnotatedSubscript);
}
}
}
}
None
})
}
/// Returns `true` if `Expr` represents a reference to a typing object with a
/// PEP 585 built-in.
pub fn is_pep585_builtin(expr: &Expr, context: &Context) -> bool {
context.resolve_call_path(expr).map_or(false, |call_path| {
PEP_585_BUILTINS_ELIGIBLE.contains(&call_path.as_slice())
})
}

View file

@ -2,10 +2,10 @@ use std::path::Path;
use rustpython_parser::ast::{Expr, Stmt, StmtKind};
use crate::call_path::collect_call_path;
use crate::call_path::CallPath;
use ruff_python_ast::call_path::{collect_call_path, CallPath};
use ruff_python_ast::helpers::map_callable;
use crate::context::Context;
use crate::helpers::map_callable;
#[derive(Debug, Clone, Copy)]
pub enum Modifier {

View file

@ -4,8 +4,9 @@ use std::ops::{Deref, Index, IndexMut};
use bitflags::bitflags;
use rustpython_parser::ast::Stmt;
use ruff_python_ast::types::{Range, RefEquality};
use crate::scope::ScopeId;
use crate::types::{Range, RefEquality};
#[derive(Debug, Clone)]
pub struct Binding<'a> {

View file

@ -1,10 +1,15 @@
use std::path::Path;
use nohash_hasher::{BuildNoHashHasher, IntMap};
use ruff_python_ast::call_path::{collect_call_path, from_unqualified_name, CallPath};
use ruff_python_ast::helpers::from_relative_import;
use ruff_python_ast::types::RefEquality;
use ruff_python_ast::typing::AnnotationKind;
use rustc_hash::FxHashMap;
use rustpython_parser::ast::{Expr, Stmt};
use smallvec::smallvec;
use crate::analyze::visibility::{module_visibility, Modifier, VisibleScope};
use ruff_python_stdlib::path::is_python_stub_file;
use ruff_python_stdlib::typing::TYPING_EXTENSIONS;
@ -12,12 +17,7 @@ use crate::binding::{
Binding, BindingId, BindingKind, Bindings, Exceptions, ExecutionContext, FromImportation,
Importation, SubmoduleImportation,
};
use crate::call_path::{collect_call_path, from_unqualified_name, CallPath};
use crate::helpers::from_relative_import;
use crate::scope::{Scope, ScopeId, ScopeKind, ScopeStack, Scopes};
use crate::types::RefEquality;
use crate::typing::AnnotationKind;
use crate::visibility::{module_visibility, Modifier, VisibleScope};
#[allow(clippy::struct_excessive_bools)]
pub struct Context<'a> {

View file

@ -0,0 +1,4 @@
pub mod analyze;
pub mod binding;
pub mod context;
pub mod scope;

View file

@ -189,7 +189,7 @@ impl<'a> Scopes<'a> {
}
/// Pushes a new scope and returns its unique id
pub(crate) fn push_scope(&mut self, kind: ScopeKind<'a>) -> ScopeId {
pub fn push_scope(&mut self, kind: ScopeKind<'a>) -> ScopeId {
let next_id = ScopeId::try_from(self.0.len()).unwrap();
self.0.push(Scope::local(next_id, kind));
next_id

View file

@ -2,6 +2,7 @@ pub mod builtins;
pub mod future;
pub mod identifiers;
pub mod keyword;
pub mod logging;
pub mod path;
pub mod str;
pub mod sys;

View file

@ -0,0 +1,25 @@
#[derive(Copy, Clone)]
pub enum LoggingLevel {
Debug,
Critical,
Error,
Exception,
Info,
Warn,
Warning,
}
impl LoggingLevel {
pub fn from_attribute(level: &str) -> Option<Self> {
match level {
"debug" => Some(LoggingLevel::Debug),
"critical" => Some(LoggingLevel::Critical),
"error" => Some(LoggingLevel::Error),
"exception" => Some(LoggingLevel::Exception),
"info" => Some(LoggingLevel::Info),
"warn" => Some(LoggingLevel::Warn),
"warning" => Some(LoggingLevel::Warning),
_ => None,
}
}
}