Skip to content

Commit

Permalink
Track top-level module imports in the semantic model
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Feb 2, 2024
1 parent 92d99a7 commit 747f8e0
Show file tree
Hide file tree
Showing 55 changed files with 379 additions and 85 deletions.
3 changes: 1 addition & 2 deletions crates/ruff_linter/src/checkers/ast/analyze/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
numpy::rules::numpy_2_0_deprecation(checker, expr);
}
if checker.enabled(Rule::DeprecatedMockImport) {
pyupgrade::rules::deprecated_mock_attribute(checker, expr);
pyupgrade::rules::deprecated_mock_attribute(checker, attribute);
}
if checker.enabled(Rule::SixPY3) {
flake8_2020::rules::name_or_attribute(checker, expr);
Expand Down Expand Up @@ -976,7 +976,6 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
if checker.enabled(Rule::UnsortedDunderAll) {
ruff::rules::sort_dunder_all_extend_call(checker, call);
}

if checker.enabled(Rule::DefaultFactoryKwarg) {
ruff::rules::default_factory_kwarg(checker, call);
}
Expand Down
37 changes: 25 additions & 12 deletions crates/ruff_linter/src/checkers/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,13 +349,6 @@ where
}
}

// Track each top-level import, to guide import insertions.
if matches!(stmt, Stmt::Import(_) | Stmt::ImportFrom(_)) {
if self.semantic.at_top_level() {
self.importer.visit_import(stmt);
}
}

// Store the flags prior to any further descent, so that we can restore them after visiting
// the node.
let flags_snapshot = self.semantic.flags;
Expand All @@ -371,14 +364,22 @@ where
self.handle_node_load(target);
}
Stmt::Import(ast::StmtImport { names, range: _ }) => {
if self.semantic.at_top_level() {
self.importer.visit_import(stmt);
}

for alias in names {
if alias.name.contains('.') && alias.asname.is_none() {
// Given `import foo.bar`, `name` would be "foo", and `qualified_name` would be
// "foo.bar".
let name = alias.name.split('.').next().unwrap();
// Given `import foo.bar`, `module` would be "foo", and `call_path` would be
// `["foo", "bar"]`.
let module = alias.name.split('.').next().unwrap();

// Mark the top-level module as "seen" by the semantic model.
self.semantic.see(module);

if alias.asname.is_none() && alias.name.contains('.') {
let call_path: Box<[&str]> = alias.name.split('.').collect();
self.add_binding(
name,
module,
alias.identifier(),
BindingKind::SubmoduleImport(SubmoduleImport { call_path }),
BindingFlags::EXTERNAL,
Expand Down Expand Up @@ -413,8 +414,20 @@ where
level,
range: _,
}) => {
if self.semantic.at_top_level() {
self.importer.visit_import(stmt);
}

let module = module.as_deref();
let level = *level;

// Mark the top-level module as "seen" by the semantic model.
if level.map_or(true, |level| level == 0) {
if let Some(module) = module.and_then(|module| module.split('.').next()) {
self.semantic.see(module);
}
}

for alias in names {
if let Some("__future__") = module {
let name = alias.asname.as_ref().unwrap_or(&alias.name);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use ruff_python_ast::Expr;

use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::Expr;
use ruff_python_semantic::Modules;
use ruff_text_size::Ranged;

use crate::checkers::ast::Checker;
Expand Down Expand Up @@ -47,6 +47,10 @@ impl Violation for SixPY3 {

/// YTT202
pub(crate) fn name_or_attribute(checker: &mut Checker, expr: &Expr) {
if !checker.semantic().seen(Modules::SIX) {
return;
}

if checker
.semantic()
.resolve_call_path(expr)
Expand Down
25 changes: 14 additions & 11 deletions crates/ruff_linter/src/rules/flake8_async/rules/blocking_os_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use ruff_python_ast::ExprCall;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::call_path::CallPath;
use ruff_python_semantic::Modules;
use ruff_text_size::Ranged;

use crate::checkers::ast::Checker;
Expand Down Expand Up @@ -42,17 +43,19 @@ impl Violation for BlockingOsCallInAsyncFunction {

/// ASYNC102
pub(crate) fn blocking_os_call(checker: &mut Checker, call: &ExprCall) {
if checker.semantic().in_async_context() {
if checker
.semantic()
.resolve_call_path(call.func.as_ref())
.as_ref()
.is_some_and(is_unsafe_os_method)
{
checker.diagnostics.push(Diagnostic::new(
BlockingOsCallInAsyncFunction,
call.func.range(),
));
if checker.semantic().seen(Modules::OS) {
if checker.semantic().in_async_context() {
if checker
.semantic()
.resolve_call_path(call.func.as_ref())
.as_ref()
.is_some_and(is_unsafe_os_method)
{
checker.diagnostics.push(Diagnostic::new(
BlockingOsCallInAsyncFunction,
call.func.range(),
));
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::call_path::CallPath;
use ruff_python_ast::{self as ast, Expr, Operator};
use ruff_python_semantic::SemanticModel;
use ruff_python_semantic::{Modules, SemanticModel};
use ruff_text_size::Ranged;

use crate::checkers::ast::Checker;
Expand Down Expand Up @@ -60,6 +60,10 @@ enum Reason {

/// S103
pub(crate) fn bad_file_permissions(checker: &mut Checker, call: &ast::ExprCall) {
if !checker.semantic().seen(Modules::OS) {
return;
}

if checker
.semantic()
.resolve_call_path(&call.func)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast, Expr};
use ruff_python_semantic::Modules;
use ruff_text_size::Ranged;

use crate::checkers::ast::Checker;
Expand Down Expand Up @@ -35,6 +36,10 @@ impl Violation for DjangoRawSql {

/// S611
pub(crate) fn django_raw_sql(checker: &mut Checker, call: &ast::ExprCall) {
if !checker.semantic().seen(Modules::DJANGO) {
return;
}

if checker
.semantic()
.resolve_call_path(&call.func)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast};
use ruff_python_semantic::Modules;
use ruff_text_size::Ranged;

use crate::checkers::ast::Checker;
Expand Down Expand Up @@ -35,6 +36,10 @@ impl Violation for LoggingConfigInsecureListen {

/// S612
pub(crate) fn logging_config_insecure_listen(checker: &mut Checker, call: &ast::ExprCall) {
if !checker.semantic().seen(Modules::LOGGING) {
return;
}

if checker
.semantic()
.resolve_call_path(&call.func)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use ruff_diagnostics::Diagnostic;
use ruff_diagnostics::Violation;
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast};
use ruff_python_semantic::Modules;
use ruff_text_size::Ranged;

/// ## What it does
Expand Down Expand Up @@ -48,6 +49,10 @@ impl Violation for TarfileUnsafeMembers {

/// S202
pub(crate) fn tarfile_unsafe_members(checker: &mut Checker, call: &ast::ExprCall) {
if !checker.semantic().seen(Modules::TARFILE) {
return;
}

if !call
.func
.as_attribute_expr()
Expand All @@ -65,10 +70,6 @@ pub(crate) fn tarfile_unsafe_members(checker: &mut Checker, call: &ast::ExprCall
return;
}

if !checker.semantic().seen(&["tarfile"]) {
return;
}

checker
.diagnostics
.push(Diagnostic::new(TarfileUnsafeMembers, call.func.range()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use ruff_python_ast::{self as ast};

use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_semantic::Modules;
use ruff_text_size::Ranged;

use crate::checkers::ast::Checker;
Expand Down Expand Up @@ -56,6 +57,10 @@ impl Violation for ReSubPositionalArgs {

/// B034
pub(crate) fn re_sub_positional_args(checker: &mut Checker, call: &ast::ExprCall) {
if !checker.semantic().seen(Modules::RE) {
return;
}

let Some(method) = checker
.semantic()
.resolve_call_path(&call.func)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use ruff_text_size::TextRange;

use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_semantic::Modules;

use crate::checkers::ast::Checker;

Expand Down Expand Up @@ -57,6 +58,10 @@ impl Violation for CallDateFromtimestamp {
}

pub(crate) fn call_date_fromtimestamp(checker: &mut Checker, func: &Expr, location: TextRange) {
if !checker.semantic().seen(Modules::DATETIME) {
return;
}

if checker
.semantic()
.resolve_call_path(func)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use ruff_text_size::TextRange;

use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_semantic::Modules;

use crate::checkers::ast::Checker;

Expand Down Expand Up @@ -56,6 +57,10 @@ impl Violation for CallDateToday {
}

pub(crate) fn call_date_today(checker: &mut Checker, func: &Expr, location: TextRange) {
if !checker.semantic().seen(Modules::DATETIME) {
return;
}

if checker
.semantic()
.resolve_call_path(func)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};

use ruff_python_ast::{self as ast};
use ruff_python_semantic::Modules;
use ruff_text_size::Ranged;

use crate::checkers::ast::Checker;
Expand Down Expand Up @@ -60,6 +61,10 @@ impl Violation for CallDatetimeFromtimestamp {
}

pub(crate) fn call_datetime_fromtimestamp(checker: &mut Checker, call: &ast::ExprCall) {
if !checker.semantic().seen(Modules::DATETIME) {
return;
}

if !checker
.semantic()
.resolve_call_path(&call.func)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};

use ruff_python_ast::{self as ast, Expr};
use ruff_python_semantic::Modules;
use ruff_text_size::Ranged;

use crate::checkers::ast::Checker;
Expand Down Expand Up @@ -56,6 +57,10 @@ impl Violation for CallDatetimeNowWithoutTzinfo {
}

pub(crate) fn call_datetime_now_without_tzinfo(checker: &mut Checker, call: &ast::ExprCall) {
if !checker.semantic().seen(Modules::DATETIME) {
return;
}

if !checker
.semantic()
.resolve_call_path(&call.func)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast, Expr};
use ruff_python_semantic::Modules;
use ruff_text_size::Ranged;

use crate::checkers::ast::Checker;
Expand Down Expand Up @@ -64,6 +65,10 @@ impl Violation for CallDatetimeStrptimeWithoutZone {

/// DTZ007
pub(crate) fn call_datetime_strptime_without_zone(checker: &mut Checker, call: &ast::ExprCall) {
if !checker.semantic().seen(Modules::DATETIME) {
return;
}

if !checker
.semantic()
.resolve_call_path(&call.func)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use ruff_text_size::TextRange;

use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_semantic::Modules;

use crate::checkers::ast::Checker;

Expand Down Expand Up @@ -55,6 +56,10 @@ impl Violation for CallDatetimeToday {
}

pub(crate) fn call_datetime_today(checker: &mut Checker, func: &Expr, location: TextRange) {
if !checker.semantic().seen(Modules::DATETIME) {
return;
}

if !checker
.semantic()
.resolve_call_path(func)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use ruff_text_size::TextRange;

use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_semantic::Modules;

use crate::checkers::ast::Checker;

Expand Down Expand Up @@ -63,6 +64,10 @@ pub(crate) fn call_datetime_utcfromtimestamp(
func: &Expr,
location: TextRange,
) {
if !checker.semantic().seen(Modules::DATETIME) {
return;
}

if !checker
.semantic()
.resolve_call_path(func)
Expand Down
Loading

0 comments on commit 747f8e0

Please sign in to comment.