Rename ruff_python_semantic's Context struct to SemanticModel (#4565)

This commit is contained in:
Charlie Marsh 2023-05-21 22:35:03 -04:00 committed by GitHub
parent 3238743a7b
commit 19c4b7bee6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
304 changed files with 1253 additions and 1142 deletions

View file

@ -13,7 +13,7 @@ use ruff_python_ast::helpers;
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 ruff_python_semantic::model::SemanticModel;
use crate::cst::helpers::compose_module_path;
use crate::cst::matchers::match_module;
@ -435,11 +435,11 @@ pub(crate) fn get_or_import_symbol(
module: &str,
member: &str,
at: TextSize,
context: &Context,
model: &SemanticModel,
importer: &Importer,
locator: &Locator,
) -> Result<(Edit, String)> {
if let Some((source, binding)) = context.resolve_qualified_import_name(module, member) {
if let Some((source, binding)) = model.resolve_qualified_import_name(module, member) {
// If the symbol is already available in the current scope, use it.
// The exception: the symbol source (i.e., the import statement) comes after the current
@ -477,7 +477,7 @@ pub(crate) fn get_or_import_symbol(
// Case 1: `from functools import lru_cache` is in scope, and we're trying to reference
// `functools.cache`; thus, we add `cache` to the import, and return `"cache"` as the
// bound name.
if context
if model
.find_binding(member)
.map_or(true, |binding| binding.kind.is_builtin())
{
@ -489,7 +489,7 @@ pub(crate) fn get_or_import_symbol(
} else {
// Case 2: No `functools` import is in scope; thus, we add `import functools`, and
// return `"functools.cache"` as the bound name.
if context
if model
.find_binding(module)
.map_or(true, |binding| binding.kind.is_builtin())
{

View file

@ -1,7 +1,7 @@
use ruff_text_size::TextRange;
use rustpython_parser::ast::Expr;
use ruff_python_semantic::context::Snapshot;
use ruff_python_semantic::model::Snapshot;
/// A collection of AST nodes that are deferred for later analysis.
/// Used to, e.g., store functions, whose bodies shouldn't be analyzed until all

File diff suppressed because it is too large Load diff

View file

@ -1,12 +1,14 @@
use ruff_python_ast::newlines::{StrExt, UniversalNewlineIterator};
use ruff_text_size::{TextLen, TextRange, TextSize};
use std::fmt::{Debug, Formatter};
use std::iter::FusedIterator;
use ruff_text_size::{TextLen, TextRange, TextSize};
use strum_macros::EnumIter;
use ruff_python_ast::newlines::{StrExt, UniversalNewlineIterator};
use ruff_python_ast::whitespace;
use crate::docstrings::styles::SectionStyle;
use crate::docstrings::{Docstring, DocstringBody};
use ruff_python_ast::whitespace;
#[derive(EnumIter, PartialEq, Eq, Debug, Clone, Copy)]
pub enum SectionKind {

View file

@ -3,9 +3,6 @@ use std::collections::{HashMap, HashSet};
use anyhow::Result;
use itertools::Itertools;
use super::external_config::ExternalConfig;
use super::plugin::Plugin;
use super::{parser, plugin};
use crate::registry::Linter;
use crate::rule_selector::RuleSelector;
use crate::rules::flake8_pytest_style::types::{
@ -23,6 +20,10 @@ use crate::settings::pyproject::Pyproject;
use crate::settings::types::PythonVersion;
use crate::warn_user;
use super::external_config::ExternalConfig;
use super::plugin::Plugin;
use super::{parser, plugin};
const DEFAULT_SELECTORS: &[RuleSelector] = &[
RuleSelector::Linter(Linter::Pyflakes),
RuleSelector::Linter(Linter::Pycodestyle),
@ -456,8 +457,6 @@ mod tests {
use pep440_rs::VersionSpecifiers;
use pretty_assertions::assert_eq;
use super::super::plugin::Plugin;
use super::convert;
use crate::flake8_to_ruff::converter::DEFAULT_SELECTORS;
use crate::flake8_to_ruff::pep621::Project;
use crate::flake8_to_ruff::ExternalConfig;
@ -469,6 +468,9 @@ mod tests {
use crate::settings::pyproject::Pyproject;
use crate::settings::types::PythonVersion;
use super::super::plugin::Plugin;
use super::convert;
fn default_options(plugins: impl IntoIterator<Item = RuleSelector>) -> Options {
Options {
ignore: Some(vec![]),

View file

@ -1,3 +1,8 @@
pub use converter::convert;
pub use external_config::ExternalConfig;
pub use plugin::Plugin;
pub use pyproject::parse;
mod black;
mod converter;
mod external_config;
@ -6,8 +11,3 @@ mod parser;
pub mod pep621;
mod plugin;
mod pyproject;
pub use converter::convert;
pub use external_config::ExternalConfig;
pub use plugin::Plugin;
pub use pyproject::parse;

View file

@ -195,12 +195,13 @@ pub(crate) fn collect_per_file_ignores(
mod tests {
use anyhow::Result;
use super::{parse_files_to_codes_mapping, parse_prefix_codes, parse_strings};
use crate::codes;
use crate::registry::Linter;
use crate::rule_selector::RuleSelector;
use crate::settings::types::PatternPrefixPair;
use super::{parse_files_to_codes_mapping, parse_prefix_codes, parse_strings};
#[test]
fn it_parses_prefix_codes() {
let actual = parse_prefix_codes("");

View file

@ -1,8 +1,8 @@
use ruff_diagnostics::Edit;
use ruff_text_size::TextSize;
use rustpython_parser::ast::{Ranged, Stmt};
use rustpython_parser::{lexer, Mode, Tok};
use ruff_diagnostics::Edit;
use ruff_python_ast::helpers::is_docstring_stmt;
use ruff_python_ast::source_code::{Locator, Stylist};

View file

@ -1,7 +1,7 @@
//! Utils for reading and writing jupyter notebooks
mod notebook;
mod schema;
pub use notebook::*;
pub use schema::*;
mod notebook;
mod schema;

View file

@ -1,9 +1,9 @@
use ruff_text_size::TextRange;
use std::fs::File;
use std::io::{BufReader, BufWriter};
use std::iter;
use std::path::Path;
use ruff_text_size::TextRange;
use serde::Serialize;
use serde_json::error::Category;

View file

@ -2,15 +2,17 @@ use std::fmt::{Display, Formatter, Write};
use std::path::Path;
use std::sync::Mutex;
use crate::fs;
use anyhow::Result;
use colored::Colorize;
use fern;
use log::Level;
use once_cell::sync::Lazy;
use ruff_python_ast::source_code::SourceCode;
use rustpython_parser::{ParseError, ParseErrorType};
use ruff_python_ast::source_code::SourceCode;
use crate::fs;
pub(crate) static WARNINGS: Lazy<Mutex<Vec<&'static str>>> = Lazy::new(Mutex::default);
/// Warn a user once, with uniqueness determined by the given ID.

View file

@ -1,7 +1,9 @@
use std::io::Write;
use ruff_python_ast::source_code::SourceLocation;
use crate::message::{Emitter, EmitterContext, Message};
use crate::registry::AsRule;
use ruff_python_ast::source_code::SourceLocation;
use std::io::Write;
/// Generate error logging commands for Azure Pipelines format.
/// See [documentation](https://learn.microsoft.com/en-us/azure/devops/pipelines/scripts/logging-commands?view=azure-devops&tabs=bash#logissue-log-an-error-or-warning)
@ -42,9 +44,10 @@ impl Emitter for AzureEmitter {
#[cfg(test)]
mod tests {
use insta::assert_snapshot;
use crate::message::tests::{capture_emitter_output, create_messages};
use crate::message::AzureEmitter;
use insta::assert_snapshot;
#[test]
fn output() {

View file

@ -1,12 +1,15 @@
use crate::message::Message;
use colored::{Color, ColoredString, Colorize, Styles};
use ruff_diagnostics::{Applicability, Fix};
use ruff_python_ast::source_code::{OneIndexed, SourceFile};
use ruff_text_size::{TextRange, TextSize};
use similar::{ChangeTag, TextDiff};
use std::fmt::{Display, Formatter};
use std::num::NonZeroUsize;
use colored::{Color, ColoredString, Colorize, Styles};
use ruff_text_size::{TextRange, TextSize};
use similar::{ChangeTag, TextDiff};
use ruff_diagnostics::{Applicability, Fix};
use ruff_python_ast::source_code::{OneIndexed, SourceFile};
use crate::message::Message;
/// Renders a diff that shows the code fixes.
///
/// The implementation isn't fully fledged out and only used by tests. Before using in production, try

View file

@ -1,8 +1,10 @@
use std::io::Write;
use ruff_python_ast::source_code::SourceLocation;
use crate::fs::relativize_path;
use crate::message::{Emitter, EmitterContext, Message};
use crate::registry::AsRule;
use ruff_python_ast::source_code::SourceLocation;
use std::io::Write;
/// Generate error workflow command in GitHub Actions format.
/// See: [GitHub documentation](https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message)
@ -57,9 +59,10 @@ impl Emitter for GithubEmitter {
#[cfg(test)]
mod tests {
use insta::assert_snapshot;
use crate::message::tests::{capture_emitter_output, create_messages};
use crate::message::GithubEmitter;
use insta::assert_snapshot;
#[test]
fn output() {

View file

@ -1,14 +1,17 @@
use crate::fs::{relativize_path, relativize_path_to};
use crate::message::{Emitter, EmitterContext, Message};
use crate::registry::AsRule;
use ruff_python_ast::source_code::SourceLocation;
use serde::ser::SerializeSeq;
use serde::{Serialize, Serializer};
use serde_json::json;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::io::Write;
use serde::ser::SerializeSeq;
use serde::{Serialize, Serializer};
use serde_json::json;
use ruff_python_ast::source_code::SourceLocation;
use crate::fs::{relativize_path, relativize_path_to};
use crate::message::{Emitter, EmitterContext, Message};
use crate::registry::AsRule;
/// Generate JSON with violations in GitLab CI format
// https://docs.gitlab.com/ee/ci/testing/code_quality.html#implement-a-custom-tool
pub struct GitlabEmitter {
@ -122,9 +125,10 @@ fn fingerprint(
#[cfg(test)]
mod tests {
use insta::assert_snapshot;
use crate::message::tests::{capture_emitter_output, create_messages};
use crate::message::GitlabEmitter;
use insta::assert_snapshot;
#[test]
fn output() {

View file

@ -1,3 +1,11 @@
use std::fmt::{Display, Formatter};
use std::io::Write;
use std::num::NonZeroUsize;
use colored::Colorize;
use ruff_python_ast::source_code::OneIndexed;
use crate::fs::relativize_path;
use crate::jupyter::JupyterIndex;
use crate::message::diff::calculate_print_width;
@ -5,11 +13,6 @@ use crate::message::text::{MessageCodeFrame, RuleCodeAndBody};
use crate::message::{
group_messages_by_filename, Emitter, EmitterContext, Message, MessageWithLocation,
};
use colored::Colorize;
use ruff_python_ast::source_code::OneIndexed;
use std::fmt::{Display, Formatter};
use std::io::Write;
use std::num::NonZeroUsize;
#[derive(Default)]
pub struct GroupedEmitter {
@ -175,9 +178,10 @@ impl std::fmt::Write for PadAdapter<'_> {
#[cfg(test)]
mod tests {
use insta::assert_snapshot;
use crate::message::tests::{capture_emitter_output, create_messages};
use crate::message::GroupedEmitter;
use insta::assert_snapshot;
#[test]
fn default() {

View file

@ -1,11 +1,14 @@
use crate::message::{Emitter, EmitterContext, Message};
use crate::registry::AsRule;
use ruff_diagnostics::Edit;
use ruff_python_ast::source_code::SourceCode;
use std::io::Write;
use serde::ser::SerializeSeq;
use serde::{Serialize, Serializer};
use serde_json::json;
use std::io::Write;
use ruff_diagnostics::Edit;
use ruff_python_ast::source_code::SourceCode;
use crate::message::{Emitter, EmitterContext, Message};
use crate::registry::AsRule;
#[derive(Default)]
pub struct JsonEmitter;
@ -94,9 +97,10 @@ impl Serialize for ExpandedEdits<'_> {
#[cfg(test)]
mod tests {
use insta::assert_snapshot;
use crate::message::tests::{capture_emitter_output, create_messages};
use crate::message::JsonEmitter;
use insta::assert_snapshot;
#[test]
fn output() {

View file

@ -1,11 +1,14 @@
use std::io::Write;
use std::path::Path;
use quick_junit::{NonSuccessKind, Report, TestCase, TestCaseStatus, TestSuite};
use ruff_python_ast::source_code::SourceLocation;
use crate::message::{
group_messages_by_filename, Emitter, EmitterContext, Message, MessageWithLocation,
};
use crate::registry::AsRule;
use quick_junit::{NonSuccessKind, Report, TestCase, TestCaseStatus, TestSuite};
use ruff_python_ast::source_code::SourceLocation;
use std::io::Write;
use std::path::Path;
#[derive(Default)]
pub struct JunitEmitter;
@ -83,9 +86,10 @@ impl Emitter for JunitEmitter {
#[cfg(test)]
mod tests {
use insta::assert_snapshot;
use crate::message::tests::{capture_emitter_output, create_messages};
use crate::message::JunitEmitter;
use insta::assert_snapshot;
#[test]
fn output() {

View file

@ -1,3 +1,25 @@
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::io::Write;
use std::ops::Deref;
use ruff_text_size::{TextRange, TextSize};
use rustc_hash::FxHashMap;
pub use azure::AzureEmitter;
pub use github::GithubEmitter;
pub use gitlab::GitlabEmitter;
pub use grouped::GroupedEmitter;
pub use json::JsonEmitter;
pub use junit::JunitEmitter;
pub use pylint::PylintEmitter;
use ruff_diagnostics::{Diagnostic, DiagnosticKind, Fix};
use ruff_python_ast::source_code::{SourceFile, SourceLocation};
pub use text::TextEmitter;
use crate::jupyter::JupyterIndex;
use crate::registry::AsRule;
mod azure;
mod diff;
mod github;
@ -8,27 +30,6 @@ mod junit;
mod pylint;
mod text;
use ruff_text_size::{TextRange, TextSize};
use rustc_hash::FxHashMap;
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::io::Write;
use std::ops::Deref;
pub use azure::AzureEmitter;
pub use github::GithubEmitter;
pub use gitlab::GitlabEmitter;
pub use grouped::GroupedEmitter;
pub use json::JsonEmitter;
pub use junit::JunitEmitter;
pub use pylint::PylintEmitter;
pub use text::TextEmitter;
use crate::jupyter::JupyterIndex;
use crate::registry::AsRule;
use ruff_diagnostics::{Diagnostic, DiagnosticKind, Fix};
use ruff_python_ast::source_code::{SourceFile, SourceLocation};
#[derive(Debug, PartialEq, Eq)]
pub struct Message {
pub kind: DiagnosticKind,
@ -152,13 +153,15 @@ impl<'a> EmitterContext<'a> {
#[cfg(test)]
mod tests {
use crate::message::{Emitter, EmitterContext, Message};
use crate::rules::pyflakes::rules::{UndefinedName, UnusedImport, UnusedVariable};
use ruff_diagnostics::{Diagnostic, Edit, Fix};
use ruff_python_ast::source_code::SourceFileBuilder;
use ruff_text_size::{TextRange, TextSize};
use rustc_hash::FxHashMap;
use ruff_diagnostics::{Diagnostic, Edit, Fix};
use ruff_python_ast::source_code::SourceFileBuilder;
use crate::message::{Emitter, EmitterContext, Message};
use crate::rules::pyflakes::rules::{UndefinedName, UnusedImport, UnusedVariable};
pub(super) fn create_messages() -> Vec<Message> {
let fib = r#"import os

View file

@ -1,8 +1,10 @@
use std::io::Write;
use ruff_python_ast::source_code::OneIndexed;
use crate::fs::relativize_path;
use crate::message::{Emitter, EmitterContext, Message};
use crate::registry::AsRule;
use ruff_python_ast::source_code::OneIndexed;
use std::io::Write;
/// Generate violations in Pylint format.
/// See: [Flake8 documentation](https://flake8.pycqa.org/en/latest/internal/formatters.html#pylint-formatter)
@ -40,9 +42,10 @@ impl Emitter for PylintEmitter {
#[cfg(test)]
mod tests {
use insta::assert_snapshot;
use crate::message::tests::{capture_emitter_output, create_messages};
use crate::message::PylintEmitter;
use insta::assert_snapshot;
#[test]
fn output() {

View file

@ -1,16 +1,19 @@
use crate::fs::relativize_path;
use crate::message::diff::Diff;
use crate::message::{Emitter, EmitterContext, Message};
use crate::registry::AsRule;
use std::borrow::Cow;
use std::fmt::{Display, Formatter};
use std::io::Write;
use annotate_snippets::display_list::{DisplayList, FormatOptions};
use annotate_snippets::snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation};
use bitflags::bitflags;
use colored::Colorize;
use ruff_python_ast::source_code::{OneIndexed, SourceLocation};
use ruff_text_size::{TextRange, TextSize};
use std::borrow::Cow;
use std::fmt::{Display, Formatter};
use std::io::Write;
use ruff_python_ast::source_code::{OneIndexed, SourceLocation};
use crate::fs::relativize_path;
use crate::message::diff::Diff;
use crate::message::{Emitter, EmitterContext, Message};
use crate::registry::AsRule;
bitflags! {
#[derive(Default)]
@ -292,9 +295,10 @@ struct SourceCode<'a> {
#[cfg(test)]
mod tests {
use insta::assert_snapshot;
use crate::message::tests::{capture_emitter_output, create_messages};
use crate::message::TextEmitter;
use insta::assert_snapshot;
#[test]
fn default() {

View file

@ -1,15 +1,15 @@
//! Registry of all [`Rule`] implementations.
mod rule_set;
use strum_macros::{AsRefStr, EnumIter};
use ruff_diagnostics::Violation;
use ruff_macros::RuleNamespace;
pub use rule_set::{RuleSet, RuleSetIterator};
use crate::codes::{self, RuleCodePrefix};
use crate::rules;
pub use rule_set::{RuleSet, RuleSetIterator};
mod rule_set;
ruff_macros::register_rules!(
// pycodestyle errors
@ -1003,6 +1003,7 @@ pub const INCOMPATIBLE_CODES: &[(Rule, Rule, &str); 2] = &[
#[cfg(test)]
mod tests {
use std::mem::size_of;
use strum::IntoEnumIterator;
use super::{Linter, Rule, RuleNamespace};

View file

@ -7,7 +7,6 @@ mod tests {
use std::path::Path;
use anyhow::Result;
use test_case::test_case;
use crate::registry::Rule;

View file

@ -6,7 +6,6 @@ mod tests {
use std::path::Path;
use anyhow::Result;
use test_case::test_case;
use crate::registry::Rule;

View file

@ -115,7 +115,7 @@ impl Violation for SysVersionSlice1 {
fn is_sys(checker: &Checker, expr: &Expr, target: &str) -> bool {
checker
.ctx
.model
.resolve_call_path(expr)
.map_or(false, |call_path| call_path.as_slice() == ["sys", target])
}
@ -272,7 +272,7 @@ pub(crate) fn compare(checker: &mut Checker, left: &Expr, ops: &[Cmpop], compara
/// YTT202
pub(crate) fn name_or_attribute(checker: &mut Checker, expr: &Expr) {
if checker
.ctx
.model
.resolve_call_path(expr)
.map_or(false, |call_path| call_path.as_slice() == ["six", "PY3"])
{

View file

@ -44,7 +44,7 @@ pub(crate) fn overloaded_name(checker: &Checker, definition: &Definition) -> Opt
..
}) = definition
{
if visibility::is_overload(&checker.ctx, cast::decorator_list(stmt)) {
if visibility::is_overload(&checker.model, cast::decorator_list(stmt)) {
let (name, ..) = match_function_def(stmt);
Some(name.to_string())
} else {
@ -68,7 +68,7 @@ pub(crate) fn is_overload_impl(
..
}) = definition
{
if visibility::is_overload(&checker.ctx, cast::decorator_list(stmt)) {
if visibility::is_overload(&checker.model, cast::decorator_list(stmt)) {
false
} else {
let (name, ..) = match_function_def(stmt);

View file

@ -8,9 +8,9 @@ pub mod settings;
mod tests {
use std::path::Path;
use crate::assert_messages;
use anyhow::Result;
use crate::assert_messages;
use crate::registry::Rule;
use crate::settings::Settings;
use crate::test::test_path;

View file

@ -438,7 +438,7 @@ fn check_dynamically_typed<F>(
) where
F: FnOnce() -> String,
{
if !is_overridden && checker.ctx.match_typing_expr(annotation, "Any") {
if !is_overridden && checker.model.match_typing_expr(annotation, "Any") {
diagnostics.push(Diagnostic::new(
AnyType { name: func() },
annotation.range(),
@ -479,7 +479,7 @@ pub(crate) fn definition(
// unless configured to suppress ANN* for declarations that are fully untyped.
let mut diagnostics = Vec::new();
let is_overridden = visibility::is_override(&checker.ctx, decorator_list);
let is_overridden = visibility::is_override(&checker.model, decorator_list);
// ANN001, ANN401
for arg in args
@ -490,7 +490,8 @@ pub(crate) fn definition(
.skip(
// If this is a non-static method, skip `cls` or `self`.
usize::from(
is_method && !visibility::is_staticmethod(&checker.ctx, cast::decorator_list(stmt)),
is_method
&& !visibility::is_staticmethod(&checker.model, cast::decorator_list(stmt)),
),
)
{
@ -591,10 +592,10 @@ pub(crate) fn definition(
}
// ANN101, ANN102
if is_method && !visibility::is_staticmethod(&checker.ctx, cast::decorator_list(stmt)) {
if is_method && !visibility::is_staticmethod(&checker.model, cast::decorator_list(stmt)) {
if let Some(arg) = args.posonlyargs.first().or_else(|| args.args.first()) {
if arg.annotation.is_none() {
if visibility::is_classmethod(&checker.ctx, cast::decorator_list(stmt)) {
if visibility::is_classmethod(&checker.model, cast::decorator_list(stmt)) {
if checker.settings.rules.enabled(Rule::MissingTypeCls) {
diagnostics.push(Diagnostic::new(
MissingTypeCls {
@ -636,7 +637,7 @@ pub(crate) fn definition(
// (explicitly or implicitly).
checker.settings.flake8_annotations.suppress_none_returning && is_none_returning(body)
) {
if is_method && visibility::is_classmethod(&checker.ctx, cast::decorator_list(stmt)) {
if is_method && visibility::is_classmethod(&checker.model, cast::decorator_list(stmt)) {
if checker
.settings
.rules
@ -649,7 +650,8 @@ pub(crate) fn definition(
helpers::identifier_range(stmt, checker.locator),
));
}
} else if is_method && visibility::is_staticmethod(&checker.ctx, cast::decorator_list(stmt))
} else if is_method
&& visibility::is_staticmethod(&checker.model, cast::decorator_list(stmt))
{
if checker
.settings

View file

@ -3,7 +3,7 @@ use rustpython_parser::ast::{Expr, Ranged};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_semantic::context::Context;
use ruff_python_semantic::model::SemanticModel;
use ruff_python_semantic::scope::{FunctionDef, ScopeKind};
use crate::checkers::ast::Checker;
@ -66,9 +66,9 @@ const BLOCKING_HTTP_CALLS: &[&[&str]] = &[
/// ASYNC100
pub(crate) fn blocking_http_call(checker: &mut Checker, expr: &Expr) {
if in_async_function(&checker.ctx) {
if in_async_function(&checker.model) {
if let Expr::Call(ast::ExprCall { func, .. }) = expr {
if let Some(call_path) = checker.ctx.resolve_call_path(func) {
if let Some(call_path) = checker.model.resolve_call_path(func) {
if BLOCKING_HTTP_CALLS.contains(&call_path.as_slice()) {
checker.diagnostics.push(Diagnostic::new(
BlockingHttpCallInAsyncFunction,
@ -133,9 +133,9 @@ const OPEN_SLEEP_OR_SUBPROCESS_CALL: &[&[&str]] = &[
/// ASYNC101
pub(crate) fn open_sleep_or_subprocess_call(checker: &mut Checker, expr: &Expr) {
if in_async_function(&checker.ctx) {
if in_async_function(&checker.model) {
if let Expr::Call(ast::ExprCall { func, .. }) = expr {
if let Some(call_path) = checker.ctx.resolve_call_path(func) {
if let Some(call_path) = checker.model.resolve_call_path(func) {
if OPEN_SLEEP_OR_SUBPROCESS_CALL.contains(&call_path.as_slice()) {
checker.diagnostics.push(Diagnostic::new(
OpenSleepOrSubprocessInAsyncFunction,
@ -197,9 +197,9 @@ const UNSAFE_OS_METHODS: &[&[&str]] = &[
/// ASYNC102
pub(crate) fn blocking_os_call(checker: &mut Checker, expr: &Expr) {
if in_async_function(&checker.ctx) {
if in_async_function(&checker.model) {
if let Expr::Call(ast::ExprCall { func, .. }) = expr {
if let Some(call_path) = checker.ctx.resolve_call_path(func) {
if let Some(call_path) = checker.model.resolve_call_path(func) {
if UNSAFE_OS_METHODS.contains(&call_path.as_slice()) {
checker
.diagnostics
@ -210,9 +210,9 @@ pub(crate) fn blocking_os_call(checker: &mut Checker, expr: &Expr) {
}
}
/// Return `true` if the [`Context`] is inside an async function definition.
fn in_async_function(context: &Context) -> bool {
context
/// Return `true` if the [`SemanticModel`] is inside an async function definition.
fn in_async_function(model: &SemanticModel) -> bool {
model
.scopes()
.find_map(|scope| {
if let ScopeKind::Function(FunctionDef { async_, .. }) = &scope.kind {

View file

@ -27,7 +27,7 @@ pub(crate) fn is_untyped_exception(type_: Option<&Expr>, checker: &Checker) -> b
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = &type_ {
elts.iter().any(|type_| {
checker
.ctx
.model
.resolve_call_path(type_)
.map_or(false, |call_path| {
call_path.as_slice() == ["", "Exception"]
@ -36,7 +36,7 @@ pub(crate) fn is_untyped_exception(type_: Option<&Expr>, checker: &Checker) -> b
})
} else {
checker
.ctx
.model
.resolve_call_path(type_)
.map_or(false, |call_path| {
call_path.as_slice() == ["", "Exception"]

View file

@ -7,11 +7,10 @@ pub mod settings;
mod tests {
use std::path::Path;
use crate::assert_messages;
use anyhow::Result;
use test_case::test_case;
use crate::assert_messages;
use crate::registry::Rule;
use crate::settings::Settings;
use crate::test::test_path;

View file

@ -108,7 +108,7 @@ pub(crate) fn bad_file_permissions(
keywords: &[Keyword],
) {
if checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| call_path.as_slice() == ["os", "chmod"])
{

View file

@ -1,6 +1,7 @@
use ruff_text_size::TextRange;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_text_size::TextRange;
#[violation]
pub struct HardcodedBindAllInterfaces;

View file

@ -60,7 +60,7 @@ fn unparse_string_format_expression(checker: &mut Checker, expr: &Expr) -> Optio
op: Operator::Add | Operator::Mod,
..
}) => {
let Some(parent) = checker.ctx.expr_parent() else {
let Some(parent) = checker.model.expr_parent() else {
if any_over_expr(expr, &has_string_literal) {
return Some(checker.generator().expr(expr));
}

View file

@ -48,7 +48,7 @@ pub(crate) fn hashlib_insecure_hash_functions(
args: &[Expr],
keywords: &[Keyword],
) {
if let Some(hashlib_call) = checker.ctx.resolve_call_path(func).and_then(|call_path| {
if let Some(hashlib_call) = checker.model.resolve_call_path(func).and_then(|call_path| {
if call_path.as_slice() == ["hashlib", "new"] {
Some(HashlibCall::New)
} else {

View file

@ -37,7 +37,7 @@ pub(crate) fn jinja2_autoescape_false(
keywords: &[Keyword],
) {
if checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["jinja2", "Environment"]

View file

@ -24,7 +24,7 @@ pub(crate) fn logging_config_insecure_listen(
keywords: &[Keyword],
) {
if checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["logging", "config", "listen"]

View file

@ -18,7 +18,7 @@ impl Violation for ParamikoCall {
/// S601
pub(crate) fn paramiko_call(checker: &mut Checker, func: &Expr) {
if checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["paramiko", "exec_command"]

View file

@ -43,7 +43,7 @@ pub(crate) fn request_with_no_cert_validation(
args: &[Expr],
keywords: &[Keyword],
) {
if let Some(target) = checker.ctx.resolve_call_path(func).and_then(|call_path| {
if let Some(target) = checker.model.resolve_call_path(func).and_then(|call_path| {
if call_path.len() == 2 {
if call_path[0] == "requests" && REQUESTS_HTTP_VERBS.contains(&call_path[1]) {
return Some("requests");

View file

@ -34,7 +34,7 @@ pub(crate) fn request_without_timeout(
keywords: &[Keyword],
) {
if checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| {
HTTP_VERBS

View file

@ -7,7 +7,7 @@ use rustpython_parser::ast::{self, Constant, Expr, Keyword, Ranged};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::Truthiness;
use ruff_python_semantic::context::Context;
use ruff_python_semantic::model::SemanticModel;
use crate::{
checkers::ast::Checker, registry::Rule, rules::flake8_bandit::helpers::string_literal,
@ -97,8 +97,8 @@ enum CallKind {
}
/// Return the [`CallKind`] of the given function call.
fn get_call_kind(func: &Expr, context: &Context) -> Option<CallKind> {
context
fn get_call_kind(func: &Expr, model: &SemanticModel) -> Option<CallKind> {
model
.resolve_call_path(func)
.and_then(|call_path| match call_path.as_slice() {
&[module, submodule] => match module {
@ -138,12 +138,15 @@ struct ShellKeyword<'a> {
}
/// Return the `shell` keyword argument to the given function call, if any.
fn find_shell_keyword<'a>(ctx: &Context, keywords: &'a [Keyword]) -> Option<ShellKeyword<'a>> {
fn find_shell_keyword<'a>(
model: &SemanticModel,
keywords: &'a [Keyword],
) -> Option<ShellKeyword<'a>> {
keywords
.iter()
.find(|keyword| keyword.arg.as_ref().map_or(false, |arg| arg == "shell"))
.map(|keyword| ShellKeyword {
truthiness: Truthiness::from_expr(&keyword.value, |id| ctx.is_builtin(id)),
truthiness: Truthiness::from_expr(&keyword.value, |id| model.is_builtin(id)),
keyword,
})
}
@ -181,11 +184,11 @@ pub(crate) fn shell_injection(
args: &[Expr],
keywords: &[Keyword],
) {
let call_kind = get_call_kind(func, &checker.ctx);
let call_kind = get_call_kind(func, &checker.model);
if matches!(call_kind, Some(CallKind::Subprocess)) {
if let Some(arg) = args.first() {
match find_shell_keyword(&checker.ctx, keywords) {
match find_shell_keyword(&checker.model, keywords) {
// S602
Some(ShellKeyword {
truthiness: Truthiness::Truthy,
@ -238,7 +241,7 @@ pub(crate) fn shell_injection(
} else if let Some(ShellKeyword {
truthiness: Truthiness::Truthy,
keyword,
}) = find_shell_keyword(&checker.ctx, keywords)
}) = find_shell_keyword(&checker.model, keywords)
{
// S604
if checker

View file

@ -25,7 +25,7 @@ pub(crate) fn snmp_insecure_version(
keywords: &[Keyword],
) {
if checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["pysnmp", "hlapi", "CommunityData"]

View file

@ -27,7 +27,7 @@ pub(crate) fn snmp_weak_cryptography(
keywords: &[Keyword],
) {
if checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["pysnmp", "hlapi", "UsmUserData"]

View file

@ -470,7 +470,7 @@ pub(crate) fn suspicious_function_call(checker: &mut Checker, expr: &Expr) {
return;
};
let Some(reason) = checker.ctx.resolve_call_path(func).and_then(|call_path| {
let Some(reason) = checker.model.resolve_call_path(func).and_then(|call_path| {
for module in SUSPICIOUS_MEMBERS {
for member in module.members {
if call_path.as_slice() == *member {

View file

@ -38,14 +38,14 @@ pub(crate) fn unsafe_yaml_load(
keywords: &[Keyword],
) {
if checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| call_path.as_slice() == ["yaml", "load"])
{
let call_args = SimpleCallArgs::new(args, keywords);
if let Some(loader_arg) = call_args.argument("Loader", 1) {
if !checker
.ctx
.model
.resolve_call_path(loader_arg)
.map_or(false, |call_path| {
call_path.as_slice() == ["yaml", "SafeLoader"]

View file

@ -6,7 +6,6 @@ mod tests {
use std::path::Path;
use anyhow::Result;
use test_case::test_case;
use crate::registry::Rule;

View file

@ -34,7 +34,7 @@ pub(crate) fn blind_except(
return;
};
for exception in ["BaseException", "Exception"] {
if id == exception && checker.ctx.is_builtin(exception) {
if id == exception && checker.model.is_builtin(exception) {
// If the exception is re-raised, don't flag an error.
if body.iter().any(|stmt| {
if let Stmt::Raise(ast::StmtRaise { exc, .. }) = stmt {
@ -58,7 +58,7 @@ pub(crate) fn blind_except(
if body.iter().any(|stmt| {
if let Stmt::Expr(ast::StmtExpr { value, range: _ }) = stmt {
if let Expr::Call(ast::ExprCall { func, keywords, .. }) = value.as_ref() {
if logging::is_logger_candidate(&checker.ctx, func) {
if logging::is_logger_candidate(&checker.model, func) {
if let Some(attribute) = func.as_attribute_expr() {
let attr = attribute.attr.as_str();
if attr == "exception" {

View file

@ -6,7 +6,6 @@ mod tests {
use std::path::Path;
use anyhow::Result;
use test_case::test_case;
use crate::registry::Rule;

View file

@ -6,11 +6,10 @@ pub mod settings;
mod tests {
use std::path::Path;
use crate::assert_messages;
use anyhow::Result;
use test_case::test_case;
use crate::assert_messages;
use crate::registry::Rule;
use crate::settings::Settings;
use crate::test::test_path;

View file

@ -3,7 +3,7 @@ use rustpython_parser::ast::{self, Constant, Expr, Keyword, Ranged, Stmt};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_semantic::analyze::visibility::{is_abstract, is_overload};
use ruff_python_semantic::context::Context;
use ruff_python_semantic::model::SemanticModel;
use crate::checkers::ast::Checker;
use crate::registry::Rule;
@ -35,16 +35,16 @@ impl Violation for EmptyMethodWithoutAbstractDecorator {
}
}
fn is_abc_class(context: &Context, bases: &[Expr], keywords: &[Keyword]) -> bool {
fn is_abc_class(model: &SemanticModel, bases: &[Expr], keywords: &[Keyword]) -> bool {
keywords.iter().any(|keyword| {
keyword.arg.as_ref().map_or(false, |arg| arg == "metaclass")
&& context
&& model
.resolve_call_path(&keyword.value)
.map_or(false, |call_path| {
call_path.as_slice() == ["abc", "ABCMeta"]
})
}) || bases.iter().any(|base| {
context
model
.resolve_call_path(base)
.map_or(false, |call_path| call_path.as_slice() == ["abc", "ABC"])
})
@ -79,7 +79,7 @@ pub(crate) fn abstract_base_class(
if bases.len() + keywords.len() != 1 {
return;
}
if !is_abc_class(&checker.ctx, bases, keywords) {
if !is_abc_class(&checker.model, bases, keywords) {
return;
}
@ -108,7 +108,7 @@ pub(crate) fn abstract_base_class(
continue;
};
let has_abstract_decorator = is_abstract(&checker.ctx, decorator_list);
let has_abstract_decorator = is_abstract(&checker.model, decorator_list);
has_abstract_method |= has_abstract_decorator;
if !checker
@ -121,7 +121,7 @@ pub(crate) fn abstract_base_class(
if !has_abstract_decorator
&& is_empty_body(body)
&& !is_overload(&checker.ctx, decorator_list)
&& !is_overload(&checker.model, decorator_list)
{
checker.diagnostics.push(Diagnostic::new(
EmptyMethodWithoutAbstractDecorator {

View file

@ -66,7 +66,7 @@ pub(crate) fn assert_raises_exception(checker: &mut Checker, stmt: &Stmt, items:
}
if !checker
.ctx
.model
.resolve_call_path(args.first().unwrap())
.map_or(false, |call_path| call_path.as_slice() == ["", "Exception"])
{
@ -78,7 +78,7 @@ pub(crate) fn assert_raises_exception(checker: &mut Checker, stmt: &Stmt, items:
{
AssertionKind::AssertRaises
} else if checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["pytest", "raises"]

View file

@ -20,7 +20,7 @@ impl Violation for CachedInstanceMethod {
fn is_cache_func(checker: &Checker, expr: &Expr) -> bool {
checker
.ctx
.model
.resolve_call_path(expr)
.map_or(false, |call_path| {
call_path.as_slice() == ["functools", "lru_cache"]
@ -30,7 +30,7 @@ fn is_cache_func(checker: &Checker, expr: &Expr) -> bool {
/// B019
pub(crate) fn cached_instance_method(checker: &mut Checker, decorator_list: &[Expr]) {
if !matches!(checker.ctx.scope().kind, ScopeKind::Class(_)) {
if !matches!(checker.model.scope().kind, ScopeKind::Class(_)) {
return;
}
for decorator in decorator_list {

View file

@ -86,7 +86,7 @@ where
match expr {
Expr::Call(ast::ExprCall { func, args, .. }) => {
if !is_mutable_func(self.checker, func)
&& !is_immutable_func(&self.checker.ctx, func, &self.extend_immutable_calls)
&& !is_immutable_func(&self.checker.model, func, &self.extend_immutable_calls)
&& !is_nan_or_infinity(func, args)
{
self.diagnostics.push((

View file

@ -3,7 +3,6 @@ use rustpython_parser::ast::{self, Constant, Expr, ExprContext, Ranged};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_stdlib::identifiers::{is_identifier, is_mangled_private};
use crate::checkers::ast::Checker;

View file

@ -27,7 +27,7 @@ const MUTABLE_FUNCS: &[&[&str]] = &[
pub(crate) fn is_mutable_func(checker: &Checker, func: &Expr) -> bool {
checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| {
MUTABLE_FUNCS
@ -70,7 +70,7 @@ pub(crate) fn mutable_argument_default(checker: &mut Checker, arguments: &Argume
&& !arg
.annotation
.as_ref()
.map_or(false, |expr| is_immutable_annotation(&checker.ctx, expr))
.map_or(false, |expr| is_immutable_annotation(&checker.model, expr))
{
checker
.diagnostics

View file

@ -45,7 +45,7 @@ pub(crate) fn no_explicit_stacklevel(
keywords: &[Keyword],
) {
if !checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["warnings", "warn"]

View file

@ -342,7 +342,7 @@ pub(crate) fn reuse_of_groupby_generator(
};
// Check if the function call is `itertools.groupby`
if !checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["itertools", "groupby"]

View file

@ -3,7 +3,6 @@ use rustpython_parser::ast::{self, Constant, Expr, ExprContext, Ranged, Stmt};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::source_code::Generator;
use ruff_python_stdlib::identifiers::{is_identifier, is_mangled_private};
@ -76,7 +75,7 @@ pub(crate) fn setattr_with_constant(
if let Stmt::Expr(ast::StmtExpr {
value: child,
range: _,
}) = &checker.ctx.stmt()
}) = &checker.model.stmt()
{
if expr == child.as_ref() {
let mut diagnostic = Diagnostic::new(SetAttrWithConstant, expr.range());

View file

@ -129,7 +129,7 @@ pub(crate) fn unused_loop_control_variable(checker: &mut Checker, target: &Expr,
// Avoid fixing any variables that _may_ be used, but undetectably so.
let certainty = Certainty::from(!helpers::uses_magic_variable_access(body, |id| {
checker.ctx.is_builtin(id)
checker.model.is_builtin(id)
}));
// Attempt to rename the variable by prepending an underscore, but avoid
@ -153,12 +153,12 @@ pub(crate) fn unused_loop_control_variable(checker: &mut Checker, target: &Expr,
if let Some(rename) = rename {
if certainty.into() && checker.patch(diagnostic.kind.rule()) {
// Find the `BindingKind::LoopVar` corresponding to the name.
let scope = checker.ctx.scope();
let scope = checker.model.scope();
let binding = scope.bindings_for_name(name).find_map(|index| {
let binding = &checker.ctx.bindings[*index];
binding
.source
.and_then(|source| (Some(source) == checker.ctx.stmt_id).then_some(binding))
let binding = &checker.model.bindings[*index];
binding.source.and_then(|source| {
(Some(source) == checker.model.stmt_id).then_some(binding)
})
});
if let Some(binding) = binding {
if binding.kind.is_loop_var() {

View file

@ -1,9 +1,10 @@
use rustpython_parser::ast::{Expr, Ranged};
use crate::checkers::ast::Checker;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use crate::checkers::ast::Checker;
#[violation]
pub struct UselessContextlibSuppress;
@ -26,7 +27,7 @@ pub(crate) fn useless_contextlib_suppress(
) {
if args.is_empty()
&& checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["contextlib", "suppress"]

View file

@ -53,7 +53,7 @@ pub(crate) fn useless_expression(checker: &mut Checker, value: &Expr) {
}
// Ignore statements that have side effects.
if contains_effect(value, |id| checker.ctx.is_builtin(id)) {
if contains_effect(value, |id| checker.model.is_builtin(id)) {
// Flag attributes as useless expressions, even if they're attached to calls or other
// expressions.
if matches!(value, Expr::Attribute(_)) {

View file

@ -1,9 +1,10 @@
use rustpython_parser::ast::{self, Expr, Keyword, Ranged};
use crate::checkers::ast::Checker;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use crate::checkers::ast::Checker;
#[violation]
pub struct ZipWithoutExplicitStrict;
@ -23,7 +24,7 @@ pub(crate) fn zip_without_explicit_strict(
) {
if let Expr::Name(ast::ExprName { id, .. }) = func {
if id == "zip"
&& checker.ctx.is_builtin("zip")
&& checker.model.is_builtin("zip")
&& !kwargs
.iter()
.any(|keyword| keyword.arg.as_ref().map_or(false, |name| name == "strict"))

View file

@ -1,8 +1,9 @@
use rustpython_parser::ast::Ranged;
use ruff_diagnostics::Diagnostic;
use ruff_diagnostics::Violation;
use ruff_macros::{derive_message_formats, violation};
use ruff_python_stdlib::builtins::BUILTINS;
use rustpython_parser::ast::Ranged;
use crate::checkers::ast::Checker;

View file

@ -6,7 +6,6 @@ mod tests {
use std::path::Path;
use anyhow::Result;
use test_case::test_case;
use crate::registry::Rule;

View file

@ -7,11 +7,10 @@ pub mod settings;
mod tests {
use std::path::Path;
use crate::assert_messages;
use anyhow::Result;
use test_case::test_case;
use crate::assert_messages;
use crate::registry::Rule;
use crate::settings::Settings;
use crate::test::test_path;

View file

@ -1,10 +1,11 @@
use rustpython_parser::ast::{self, Expr, Ranged};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation};
use crate::checkers::ast::Checker;
use crate::registry::AsRule;
use crate::rules::flake8_comprehensions::fixes;
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation};
use super::helpers;
@ -74,7 +75,7 @@ pub(crate) fn unnecessary_call_around_sorted(
if inner != "sorted" {
return;
}
if !checker.ctx.is_builtin(inner) || !checker.ctx.is_builtin(outer) {
if !checker.model.is_builtin(inner) || !checker.model.is_builtin(outer) {
return;
}
let mut diagnostic = Diagnostic::new(

View file

@ -79,7 +79,7 @@ pub(crate) fn unnecessary_collection_call(
}
_ => return,
};
if !checker.ctx.is_builtin(id) {
if !checker.model.is_builtin(id) {
return;
}
let mut diagnostic = Diagnostic::new(

View file

@ -56,7 +56,7 @@ fn add_diagnostic(checker: &mut Checker, expr: &Expr) {
Expr::DictComp(_) => "dict",
_ => return,
};
if !checker.ctx.is_builtin(id) {
if !checker.model.is_builtin(id) {
return;
}
let mut diagnostic = Diagnostic::new(

View file

@ -76,7 +76,7 @@ pub(crate) fn unnecessary_comprehension_any_all(
if is_async_generator(elt) {
return;
}
if !checker.ctx.is_builtin(id) {
if !checker.model.is_builtin(id) {
return;
}
let mut diagnostic = Diagnostic::new(UnnecessaryComprehensionAnyAll, args[0].range());

View file

@ -90,7 +90,7 @@ pub(crate) fn unnecessary_double_cast_or_process(
let Some(inner) = helpers::expr_name(func) else {
return;
};
if !checker.ctx.is_builtin(inner) || !checker.ctx.is_builtin(outer) {
if !checker.model.is_builtin(inner) || !checker.model.is_builtin(outer) {
return;
}

View file

@ -52,7 +52,7 @@ pub(crate) fn unnecessary_generator_list(
let Some(argument) = helpers::exactly_one_argument_with_matching_function("list", func, args, keywords) else {
return;
};
if !checker.ctx.is_builtin("list") {
if !checker.model.is_builtin("list") {
return;
}
if let Expr::GeneratorExp(_) = argument {

View file

@ -53,7 +53,7 @@ pub(crate) fn unnecessary_generator_set(
let Some(argument) = helpers::exactly_one_argument_with_matching_function("set", func, args, keywords) else {
return;
};
if !checker.ctx.is_builtin("set") {
if !checker.model.is_builtin("set") {
return;
}
if let Expr::GeneratorExp(_) = argument {

View file

@ -1,10 +1,11 @@
use rustpython_parser::ast::{Expr, Ranged};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation};
use crate::checkers::ast::Checker;
use crate::registry::AsRule;
use crate::rules::flake8_comprehensions::fixes;
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation};
use super::helpers;
@ -47,7 +48,7 @@ pub(crate) fn unnecessary_list_call(
let Some(argument) = helpers::first_argument_with_matching_function("list", func, args) else {
return;
};
if !checker.ctx.is_builtin("list") {
if !checker.model.is_builtin("list") {
return;
}
if !argument.is_list_comp_expr() {

View file

@ -1,10 +1,11 @@
use rustpython_parser::ast::{self, Expr, Keyword, Ranged};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation};
use crate::checkers::ast::Checker;
use crate::registry::AsRule;
use crate::rules::flake8_comprehensions::fixes;
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation};
use super::helpers;
@ -49,7 +50,7 @@ pub(crate) fn unnecessary_list_comprehension_dict(
let Some(argument) = helpers::exactly_one_argument_with_matching_function("dict", func, args, keywords) else {
return;
};
if !checker.ctx.is_builtin("dict") {
if !checker.model.is_builtin("dict") {
return;
}
let Expr::ListComp(ast::ExprListComp { elt, .. }) = argument else {

View file

@ -50,7 +50,7 @@ pub(crate) fn unnecessary_list_comprehension_set(
let Some(argument) = helpers::exactly_one_argument_with_matching_function("set", func, args, keywords) else {
return;
};
if !checker.ctx.is_builtin("set") {
if !checker.model.is_builtin("set") {
return;
}
if argument.is_list_comp_expr() {

View file

@ -1,10 +1,11 @@
use rustpython_parser::ast::{self, Expr, Keyword, Ranged};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation};
use crate::checkers::ast::Checker;
use crate::registry::AsRule;
use crate::rules::flake8_comprehensions::fixes;
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation};
use super::helpers;
@ -56,7 +57,7 @@ pub(crate) fn unnecessary_literal_dict(
let Some(argument) = helpers::exactly_one_argument_with_matching_function("dict", func, args, keywords) else {
return;
};
if !checker.ctx.is_builtin("dict") {
if !checker.model.is_builtin("dict") {
return;
}
let (kind, elts) = match argument {

View file

@ -1,10 +1,11 @@
use rustpython_parser::ast::{Expr, Keyword, Ranged};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation};
use crate::checkers::ast::Checker;
use crate::registry::AsRule;
use crate::rules::flake8_comprehensions::fixes;
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation};
use super::helpers;
@ -57,7 +58,7 @@ pub(crate) fn unnecessary_literal_set(
let Some(argument) = helpers::exactly_one_argument_with_matching_function("set", func, args, keywords) else {
return;
};
if !checker.ctx.is_builtin("set") {
if !checker.model.is_builtin("set") {
return;
}
let kind = match argument {

View file

@ -1,6 +1,7 @@
use rustpython_parser::ast::{Expr, Keyword, Ranged};
use std::fmt;
use rustpython_parser::ast::{Expr, Keyword, Ranged};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation};
@ -75,7 +76,7 @@ pub(crate) fn unnecessary_literal_within_dict_call(
let Some(argument) = helpers::first_argument_with_matching_function("dict", func, args) else {
return;
};
if !checker.ctx.is_builtin("dict") {
if !checker.model.is_builtin("dict") {
return;
}
let argument_kind = match argument {

View file

@ -1,10 +1,11 @@
use rustpython_parser::ast::{Expr, Keyword, Ranged};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation};
use crate::checkers::ast::Checker;
use crate::registry::AsRule;
use crate::rules::flake8_comprehensions::fixes;
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation};
use super::helpers;
@ -78,7 +79,7 @@ pub(crate) fn unnecessary_literal_within_list_call(
let Some(argument) = helpers::first_argument_with_matching_function("list", func, args) else {
return;
};
if !checker.ctx.is_builtin("list") {
if !checker.model.is_builtin("list") {
return;
}
let argument_kind = match argument {

View file

@ -1,10 +1,11 @@
use rustpython_parser::ast::{Expr, Keyword, Ranged};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation};
use crate::checkers::ast::Checker;
use crate::registry::AsRule;
use crate::rules::flake8_comprehensions::fixes;
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation};
use super::helpers;
@ -79,7 +80,7 @@ pub(crate) fn unnecessary_literal_within_tuple_call(
let Some(argument) = helpers::first_argument_with_matching_function("tuple", func, args) else {
return;
};
if !checker.ctx.is_builtin("tuple") {
if !checker.model.is_builtin("tuple") {
return;
}
let argument_kind = match argument {

View file

@ -88,7 +88,7 @@ pub(crate) fn unnecessary_map(
};
match id {
"map" => {
if !checker.ctx.is_builtin(id) {
if !checker.model.is_builtin(id) {
return;
}
@ -119,7 +119,7 @@ pub(crate) fn unnecessary_map(
}
}
"list" | "set" => {
if !checker.ctx.is_builtin(id) {
if !checker.model.is_builtin(id) {
return;
}
@ -149,7 +149,7 @@ pub(crate) fn unnecessary_map(
}
}
"dict" => {
if !checker.ctx.is_builtin(id) {
if !checker.model.is_builtin(id) {
return;
}

View file

@ -1,10 +1,11 @@
use num_bigint::BigInt;
use rustpython_parser::ast::{self, Constant, Expr, Ranged, Unaryop};
use crate::checkers::ast::Checker;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use crate::checkers::ast::Checker;
use super::helpers;
/// ## What it does
@ -57,7 +58,7 @@ pub(crate) fn unnecessary_subscript_reversal(
if !(id == "set" || id == "sorted" || id == "reversed") {
return;
}
if !checker.ctx.is_builtin(id) {
if !checker.model.is_builtin(id) {
return;
}
let Expr::Subscript(ast::ExprSubscript { slice, .. }) = first_arg else {

View file

@ -6,7 +6,6 @@ mod tests {
use std::path::Path;
use anyhow::Result;
use test_case::test_case;
use crate::registry::Rule;

View file

@ -125,7 +125,7 @@ pub(crate) fn call_datetime_without_tzinfo(
location: TextRange,
) {
if !checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "datetime"]
@ -158,7 +158,7 @@ pub(crate) fn call_datetime_without_tzinfo(
/// Use `datetime.datetime.now(tz=)` instead.
pub(crate) fn call_datetime_today(checker: &mut Checker, func: &Expr, location: TextRange) {
if checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "datetime", "today"]
@ -180,7 +180,7 @@ pub(crate) fn call_datetime_today(checker: &mut Checker, func: &Expr, location:
/// current time in UTC is by calling `datetime.now(timezone.utc)`.
pub(crate) fn call_datetime_utcnow(checker: &mut Checker, func: &Expr, location: TextRange) {
if checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "datetime", "utcnow"]
@ -207,7 +207,7 @@ pub(crate) fn call_datetime_utcfromtimestamp(
location: TextRange,
) {
if checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "datetime", "utcfromtimestamp"]
@ -228,7 +228,7 @@ pub(crate) fn call_datetime_now_without_tzinfo(
location: TextRange,
) {
if !checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "datetime", "now"]
@ -270,7 +270,7 @@ pub(crate) fn call_datetime_fromtimestamp(
location: TextRange,
) {
if !checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "datetime", "fromtimestamp"]
@ -311,7 +311,7 @@ pub(crate) fn call_datetime_strptime_without_zone(
location: TextRange,
) {
if !checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "datetime", "strptime"]
@ -332,7 +332,7 @@ pub(crate) fn call_datetime_strptime_without_zone(
}
};
let (Some(grandparent), Some(parent)) = (checker.ctx.expr_grandparent(), checker.ctx.expr_parent()) else {
let (Some(grandparent), Some(parent)) = (checker.model.expr_grandparent(), checker.model.expr_parent()) else {
checker.diagnostics.push(Diagnostic::new(
CallDatetimeStrptimeWithoutZone,
location,
@ -370,7 +370,7 @@ pub(crate) fn call_datetime_strptime_without_zone(
/// Use `datetime.datetime.now(tz=).date()` instead.
pub(crate) fn call_date_today(checker: &mut Checker, func: &Expr, location: TextRange) {
if checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "date", "today"]
@ -390,7 +390,7 @@ pub(crate) fn call_date_today(checker: &mut Checker, func: &Expr, location: Text
/// Use `datetime.datetime.fromtimestamp(, tz=).date()` instead.
pub(crate) fn call_date_fromtimestamp(checker: &mut Checker, func: &Expr, location: TextRange) {
if checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "date", "fromtimestamp"]

View file

@ -7,7 +7,6 @@ mod tests {
use std::path::Path;
use anyhow::Result;
use test_case::test_case;
use crate::registry::Rule;

View file

@ -1,11 +1,12 @@
use rustpython_parser::ast::{Expr, Ranged, Stmt};
use crate::checkers::ast::Checker;
use crate::rules::flake8_debugger::types::DebuggerUsingType;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::call_path::{format_call_path, from_unqualified_name, CallPath};
use crate::checkers::ast::Checker;
use crate::rules::flake8_debugger::types::DebuggerUsingType;
#[violation]
pub struct Debugger {
using_type: DebuggerUsingType,
@ -42,7 +43,7 @@ const DEBUGGERS: &[&[&str]] = &[
/// Checks for the presence of a debugger call.
pub(crate) fn debugger_call(checker: &mut Checker, expr: &Expr, func: &Expr) {
if let Some(target) = checker.ctx.resolve_call_path(func).and_then(|call_path| {
if let Some(target) = checker.model.resolve_call_path(func).and_then(|call_path| {
DEBUGGERS
.iter()
.find(|target| call_path.as_slice() == **target)

View file

@ -6,7 +6,6 @@ mod tests {
use std::path::Path;
use anyhow::Result;
use test_case::test_case;
use crate::registry::Rule;

View file

@ -52,7 +52,7 @@ pub(crate) fn all_with_model_form(
bases: &[Expr],
body: &[Stmt],
) -> Option<Diagnostic> {
if !bases.iter().any(|base| is_model_form(&checker.ctx, base)) {
if !bases.iter().any(|base| is_model_form(&checker.model, base)) {
return None;
}
for element in body.iter() {

View file

@ -50,7 +50,7 @@ pub(crate) fn exclude_with_model_form(
bases: &[Expr],
body: &[Stmt],
) -> Option<Diagnostic> {
if !bases.iter().any(|base| is_model_form(&checker.ctx, base)) {
if !bases.iter().any(|base| is_model_form(&checker.model, base)) {
return None;
}
for element in body.iter() {

View file

@ -1,25 +1,25 @@
use rustpython_parser::ast::Expr;
use ruff_python_semantic::context::Context;
use ruff_python_semantic::model::SemanticModel;
/// Return `true` if a Python class appears to be a Django model, based on its base classes.
pub(crate) fn is_model(context: &Context, base: &Expr) -> bool {
context.resolve_call_path(base).map_or(false, |call_path| {
pub(crate) fn is_model(model: &SemanticModel, base: &Expr) -> bool {
model.resolve_call_path(base).map_or(false, |call_path| {
call_path.as_slice() == ["django", "db", "models", "Model"]
})
}
/// Return `true` if a Python class appears to be a Django model form, based on its base classes.
pub(crate) fn is_model_form(context: &Context, base: &Expr) -> bool {
context.resolve_call_path(base).map_or(false, |call_path| {
pub(crate) fn is_model_form(model: &SemanticModel, base: &Expr) -> bool {
model.resolve_call_path(base).map_or(false, |call_path| {
call_path.as_slice() == ["django", "forms", "ModelForm"]
|| call_path.as_slice() == ["django", "forms", "models", "ModelForm"]
})
}
/// Return `true` if the expression is constructor for a Django model field.
pub(crate) fn is_model_field(context: &Context, expr: &Expr) -> bool {
context.resolve_call_path(expr).map_or(false, |call_path| {
pub(crate) fn is_model_field(model: &SemanticModel, expr: &Expr) -> bool {
model.resolve_call_path(expr).map_or(false, |call_path| {
call_path
.as_slice()
.starts_with(&["django", "db", "models"])
@ -27,8 +27,11 @@ pub(crate) fn is_model_field(context: &Context, expr: &Expr) -> bool {
}
/// Return the name of the field type, if the expression is constructor for a Django model field.
pub(crate) fn get_model_field_name<'a>(context: &'a Context, expr: &'a Expr) -> Option<&'a str> {
context.resolve_call_path(expr).and_then(|call_path| {
pub(crate) fn get_model_field_name<'a>(
model: &'a SemanticModel,
expr: &'a Expr,
) -> Option<&'a str> {
model.resolve_call_path(expr).and_then(|call_path| {
let call_path = call_path.as_slice();
if !call_path.starts_with(&["django", "db", "models"]) {
return None;

View file

@ -50,7 +50,7 @@ pub(crate) fn locals_in_render_function(
keywords: &[Keyword],
) {
if !checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["django", "shortcuts", "render"]
@ -87,7 +87,7 @@ fn is_locals_call(checker: &Checker, expr: &Expr) -> bool {
return false
};
checker
.ctx
.model
.resolve_call_path(func)
.map_or(false, |call_path| call_path.as_slice() == ["", "locals"])
}

View file

@ -84,7 +84,7 @@ fn checker_applies(checker: &Checker, bases: &[Expr], body: &[Stmt]) -> bool {
if is_model_abstract(body) {
continue;
}
if helpers::is_model(&checker.ctx, base) {
if helpers::is_model(&checker.model, base) {
return true;
}
}

View file

@ -84,7 +84,7 @@ fn is_nullable_field<'a>(checker: &'a Checker, value: &'a Expr) -> Option<&'a st
return None;
};
let Some(valid_field_name) = helpers::get_model_field_name(&checker.ctx, func) else {
let Some(valid_field_name) = helpers::get_model_field_name(&checker.model, func) else {
return None;
};

View file

@ -103,7 +103,7 @@ fn get_element_type(checker: &Checker, element: &Stmt) -> Option<ContentType> {
match element {
Stmt::Assign(ast::StmtAssign { targets, value, .. }) => {
if let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() {
if helpers::is_model_field(&checker.ctx, func) {
if helpers::is_model_field(&checker.model, func) {
return Some(ContentType::FieldDeclaration);
}
}
@ -144,7 +144,7 @@ pub(crate) fn unordered_body_content_in_model(
) {
if !bases
.iter()
.any(|base| helpers::is_model(&checker.ctx, base))
.any(|base| helpers::is_model(&checker.model, base))
{
return;
}

View file

@ -3,7 +3,6 @@ use rustpython_parser::ast::{self, Constant, Expr, ExprContext, Ranged, Stmt};
use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::source_code::{Generator, Stylist};
use ruff_python_ast::whitespace;
@ -234,7 +233,7 @@ pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr
if string.len() > checker.settings.flake8_errmsg.max_string_length {
let indentation = whitespace::indentation(checker.locator, stmt)
.and_then(|indentation| {
if checker.ctx.find_binding("msg").is_none() {
if checker.model.find_binding("msg").is_none() {
Some(indentation)
} else {
None
@ -262,7 +261,7 @@ pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr
if checker.settings.rules.enabled(Rule::FStringInException) {
let indentation = whitespace::indentation(checker.locator, stmt).and_then(
|indentation| {
if checker.ctx.find_binding("msg").is_none() {
if checker.model.find_binding("msg").is_none() {
Some(indentation)
} else {
None
@ -293,7 +292,7 @@ pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr
if attr == "format" && value.is_constant_expr() {
let indentation = whitespace::indentation(checker.locator, stmt)
.and_then(|indentation| {
if checker.ctx.find_binding("msg").is_none() {
if checker.model.find_binding("msg").is_none() {
Some(indentation)
} else {
None

View file

@ -51,12 +51,12 @@ pub(crate) fn is_executable(filepath: &Path) -> Result<bool> {
#[cfg(test)]
mod tests {
use ruff_text_size::TextSize;
use crate::rules::flake8_executable::helpers::{
extract_shebang, ShebangDirective, SHEBANG_REGEX,
};
use ruff_text_size::TextSize;
#[test]
fn shebang_regex() {
// Positive cases

View file

@ -8,7 +8,6 @@ mod tests {
use std::path::Path;
use anyhow::Result;
use test_case::test_case;
use crate::registry::Rule;

View file

@ -1,8 +1,9 @@
#![allow(unused_imports)]
use ruff_text_size::TextRange;
use std::path::Path;
use ruff_text_size::TextRange;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};

View file

@ -1,8 +1,9 @@
#![allow(unused_imports)]
use ruff_text_size::{TextLen, TextRange, TextSize};
use std::path::Path;
use ruff_text_size::{TextLen, TextRange, TextSize};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};

View file

@ -67,7 +67,7 @@ impl Violation for MissingFutureAnnotationsImport {
/// FA100
pub(crate) fn missing_future_annotations(checker: &mut Checker, expr: &Expr) {
if let Some(binding) = checker.ctx.resolve_call_path(expr) {
if let Some(binding) = checker.model.resolve_call_path(expr) {
checker.diagnostics.push(Diagnostic::new(
MissingFutureAnnotationsImport {
name: format_call_path(&binding),

Some files were not shown because too many files have changed in this diff Show more