Compare commits

..

1 Commits

Author SHA1 Message Date
David Peter
ae6dd04910 [ty] Type inference for hasattr(…) 2025-05-28 09:58:52 +02:00
783 changed files with 5629 additions and 6869 deletions

View File

@@ -1,33 +1,5 @@
# Changelog
## 0.11.12
### Preview features
- \[`airflow`\] Revise fix titles (`AIR3`) ([#18215](https://github.com/astral-sh/ruff/pull/18215))
- \[`pylint`\] Implement `missing-maxsplit-arg` (`PLC0207`) ([#17454](https://github.com/astral-sh/ruff/pull/17454))
- \[`pyupgrade`\] New rule `UP050` (`useless-class-metaclass-type`) ([#18334](https://github.com/astral-sh/ruff/pull/18334))
- \[`flake8-use-pathlib`\] Replace `os.symlink` with `Path.symlink_to` (`PTH211`) ([#18337](https://github.com/astral-sh/ruff/pull/18337))
### Bug fixes
- \[`flake8-bugbear`\] Ignore `__debug__` attribute in `B010` ([#18357](https://github.com/astral-sh/ruff/pull/18357))
- \[`flake8-async`\] Fix `anyio.sleep` argument name (`ASYNC115`, `ASYNC116`) ([#18262](https://github.com/astral-sh/ruff/pull/18262))
- \[`refurb`\] Fix `FURB129` autofix generating invalid syntax ([#18235](https://github.com/astral-sh/ruff/pull/18235))
### Rule changes
- \[`flake8-implicit-str-concat`\] Add autofix for `ISC003` ([#18256](https://github.com/astral-sh/ruff/pull/18256))
- \[`pycodestyle`\] Improve the diagnostic message for `E712` ([#18328](https://github.com/astral-sh/ruff/pull/18328))
- \[`flake8-2020`\] Fix diagnostic message for `!=` comparisons (`YTT201`) ([#18293](https://github.com/astral-sh/ruff/pull/18293))
- \[`pyupgrade`\] Make fix unsafe if it deletes comments (`UP010`) ([#18291](https://github.com/astral-sh/ruff/pull/18291))
### Documentation
- Simplify rules table to improve readability ([#18297](https://github.com/astral-sh/ruff/pull/18297))
- Update editor integrations link in README ([#17977](https://github.com/astral-sh/ruff/pull/17977))
- \[`flake8-bugbear`\] Add fix safety section (`B006`) ([#17652](https://github.com/astral-sh/ruff/pull/17652))
## 0.11.11
### Preview features

9
Cargo.lock generated
View File

@@ -2485,7 +2485,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.11.12"
version = "0.11.11"
dependencies = [
"anyhow",
"argfile",
@@ -2642,6 +2642,7 @@ dependencies = [
"rayon",
"regex",
"ruff",
"ruff_diagnostics",
"ruff_formatter",
"ruff_linter",
"ruff_notebook",
@@ -2671,7 +2672,9 @@ dependencies = [
name = "ruff_diagnostics"
version = "0.0.0"
dependencies = [
"anyhow",
"is-macro",
"log",
"ruff_text_size",
"serde",
]
@@ -2722,7 +2725,7 @@ dependencies = [
[[package]]
name = "ruff_linter"
version = "0.11.12"
version = "0.11.11"
dependencies = [
"aho-corasick",
"anyhow",
@@ -3058,7 +3061,7 @@ dependencies = [
[[package]]
name = "ruff_wasm"
version = "0.11.12"
version = "0.11.11"
dependencies = [
"console_error_panic_hook",
"console_log",

View File

@@ -148,8 +148,8 @@ curl -LsSf https://astral.sh/ruff/install.sh | sh
powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
# For a specific version.
curl -LsSf https://astral.sh/ruff/0.11.12/install.sh | sh
powershell -c "irm https://astral.sh/ruff/0.11.12/install.ps1 | iex"
curl -LsSf https://astral.sh/ruff/0.11.11/install.sh | sh
powershell -c "irm https://astral.sh/ruff/0.11.11/install.ps1 | iex"
```
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
@@ -182,7 +182,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
```yaml
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.11.12
rev: v0.11.11
hooks:
# Run the linter.
- id: ruff

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff"
version = "0.11.12"
version = "0.11.11"
publish = true
authors = { workspace = true }
edition = { workspace = true }

View File

@@ -349,6 +349,7 @@ impl FileCache {
.iter()
.map(|msg| {
Message::diagnostic(
msg.rule.into(),
msg.body.clone(),
msg.suggestion.clone(),
msg.range,
@@ -356,7 +357,6 @@ impl FileCache {
msg.parent,
file.clone(),
msg.noqa_offset,
msg.rule,
)
})
.collect()

View File

@@ -12,7 +12,7 @@ use rayon::prelude::*;
use rustc_hash::FxHashMap;
use ruff_db::panic::catch_unwind;
use ruff_linter::Diagnostic;
use ruff_diagnostics::Diagnostic;
use ruff_linter::message::Message;
use ruff_linter::package::PackageRoot;
use ruff_linter::registry::Rule;

View File

@@ -6,7 +6,7 @@ use serde::ser::SerializeSeq;
use serde::{Serialize, Serializer};
use strum::IntoEnumIterator;
use ruff_linter::FixAvailability;
use ruff_diagnostics::FixAvailability;
use ruff_linter::registry::{Linter, Rule, RuleNamespace};
use crate::args::HelpFormat;

View File

@@ -12,7 +12,7 @@ use colored::Colorize;
use log::{debug, warn};
use rustc_hash::FxHashMap;
use ruff_linter::Diagnostic;
use ruff_diagnostics::Diagnostic;
use ruff_linter::codes::Rule;
use ruff_linter::linter::{FixTable, FixerResult, LinterResult, ParseSource, lint_fix, lint_only};
use ruff_linter::message::Message;

View File

@@ -596,13 +596,6 @@ impl AsRef<SystemPath> for Utf8PathBuf {
}
}
impl AsRef<SystemPath> for camino::Utf8Component<'_> {
#[inline]
fn as_ref(&self) -> &SystemPath {
SystemPath::new(self.as_str())
}
}
impl AsRef<SystemPath> for str {
#[inline]
fn as_ref(&self) -> &SystemPath {
@@ -633,22 +626,6 @@ impl Deref for SystemPathBuf {
}
}
impl<P: AsRef<SystemPath>> FromIterator<P> for SystemPathBuf {
fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> Self {
let mut buf = SystemPathBuf::new();
buf.extend(iter);
buf
}
}
impl<P: AsRef<SystemPath>> Extend<P> for SystemPathBuf {
fn extend<I: IntoIterator<Item = P>>(&mut self, iter: I) {
for path in iter {
self.push(path);
}
}
}
impl std::fmt::Debug for SystemPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)

View File

@@ -14,6 +14,7 @@ license = { workspace = true }
ty = { workspace = true }
ty_project = { workspace = true, features = ["schemars"] }
ruff = { workspace = true }
ruff_diagnostics = { workspace = true }
ruff_formatter = { workspace = true }
ruff_linter = { workspace = true, features = ["schemars"] }
ruff_notebook = { workspace = true }

View File

@@ -10,7 +10,7 @@ use itertools::Itertools;
use regex::{Captures, Regex};
use strum::IntoEnumIterator;
use ruff_linter::FixAvailability;
use ruff_diagnostics::FixAvailability;
use ruff_linter::registry::{Linter, Rule, RuleNamespace};
use ruff_options_metadata::{OptionEntry, OptionsMetadata};
use ruff_workspace::options::Options;

View File

@@ -8,7 +8,7 @@ use std::borrow::Cow;
use std::fmt::Write;
use strum::IntoEnumIterator;
use ruff_linter::FixAvailability;
use ruff_diagnostics::FixAvailability;
use ruff_linter::registry::{Linter, Rule, RuleNamespace};
use ruff_linter::upstream_categories::UpstreamCategoryAndPrefix;
use ruff_options_metadata::OptionsMetadata;

View File

@@ -16,5 +16,7 @@ doctest = false
[dependencies]
ruff_text_size = { workspace = true }
anyhow = { workspace = true }
log = { workspace = true }
is-macro = { workspace = true }
serde = { workspace = true, optional = true, features = [] }

View File

@@ -3,12 +3,12 @@ use log::debug;
use ruff_text_size::{Ranged, TextRange, TextSize};
use crate::registry::AsRule;
use crate::violation::Violation;
use crate::{Fix, codes::Rule};
use crate::{Fix, Violation};
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Diagnostic {
/// The identifier of the diagnostic, used to align the diagnostic with a rule.
pub name: &'static str,
/// The message body to display to the user, to explain the diagnostic.
pub body: String,
/// The message to display to the user, to explain the suggested fix.
@@ -16,24 +16,17 @@ pub struct Diagnostic {
pub range: TextRange,
pub fix: Option<Fix>,
pub parent: Option<TextSize>,
pub(crate) rule: Rule,
}
impl Diagnostic {
// TODO(brent) We temporarily allow this to avoid updating all of the call sites to add
// references. I expect this method to go away or change significantly with the rest of the
// diagnostic refactor, but if it still exists in this form at the end of the refactor, we
// should just update the call sites.
#[expect(clippy::needless_pass_by_value)]
pub fn new<T: Violation>(kind: T, range: TextRange) -> Self {
Self {
name: T::rule_name(),
body: Violation::message(&kind),
suggestion: Violation::fix_title(&kind),
range,
fix: None,
parent: None,
rule: T::rule(),
}
}
@@ -57,7 +50,7 @@ impl Diagnostic {
pub fn try_set_fix(&mut self, func: impl FnOnce() -> Result<Fix>) {
match func() {
Ok(fix) => self.fix = Some(fix),
Err(err) => debug!("Failed to create fix for {}: {}", self.rule, err),
Err(err) => debug!("Failed to create fix for {}: {}", self.name, err),
}
}
@@ -68,7 +61,7 @@ impl Diagnostic {
match func() {
Ok(None) => {}
Ok(Some(fix)) => self.fix = Some(fix),
Err(err) => debug!("Failed to create fix for {}: {}", self.rule, err),
Err(err) => debug!("Failed to create fix for {}: {}", self.name, err),
}
}
@@ -87,12 +80,6 @@ impl Diagnostic {
}
}
impl AsRule for Diagnostic {
fn rule(&self) -> Rule {
self.rule
}
}
impl Ranged for Diagnostic {
fn range(&self) -> TextRange {
self.range

View File

@@ -1,7 +1,11 @@
pub use diagnostic::Diagnostic;
pub use edit::Edit;
pub use fix::{Applicability, Fix, IsolationLevel};
pub use source_map::{SourceMap, SourceMarker};
pub use violation::{AlwaysFixableViolation, FixAvailability, Violation, ViolationMetadata};
mod diagnostic;
mod edit;
mod fix;
mod source_map;
mod violation;

View File

@@ -1,7 +1,5 @@
use std::fmt::{Debug, Display};
use crate::codes::Rule;
#[derive(Debug, Copy, Clone)]
pub enum FixAvailability {
Sometimes,
@@ -20,8 +18,8 @@ impl Display for FixAvailability {
}
pub trait ViolationMetadata {
/// Returns the rule for this violation
fn rule() -> Rule;
/// Returns the rule name of this violation
fn rule_name() -> &'static str;
/// Returns an explanation of what this violation catches,
/// why it's bad, and what users should do instead.

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff_linter"
version = "0.11.12"
version = "0.11.11"
publish = false
authors = { workspace = true }
edition = { workspace = true }

View File

@@ -67,6 +67,3 @@ getattr(self.
import builtins
builtins.getattr(foo, "bar")
# Regression test for: https://github.com/astral-sh/ruff/issues/18353
setattr(foo, "__debug__", 0)

View File

@@ -1,15 +0,0 @@
import os
from pathlib import Path
os.symlink("usr/bin/python", "tmp/python")
os.symlink(b"usr/bin/python", b"tmp/python")
Path("tmp/python").symlink_to("usr/bin/python") # Ok
os.symlink("usr/bin/python", "tmp/python", target_is_directory=True)
os.symlink(b"usr/bin/python", b"tmp/python", target_is_directory=True)
Path("tmp/python").symlink_to("usr/bin/python", target_is_directory=True) # Ok
fd = os.open(".", os.O_RDONLY)
os.symlink("source.txt", "link.txt", dir_fd=fd) # Ok: dir_fd is not supported by pathlib
os.close(fd)

View File

@@ -1,184 +0,0 @@
SEQ = "1,2,3"
class Foo(str):
class_str = "1,2,3"
def split(self, sep=None, maxsplit=-1) -> list[str]:
return super().split(sep, maxsplit)
class Bar():
split = "1,2,3"
# Errors
## Test split called directly on string literal
"1,2,3".split(",")[0] # [missing-maxsplit-arg]
"1,2,3".split(",")[-1] # [missing-maxsplit-arg]
"1,2,3".rsplit(",")[0] # [missing-maxsplit-arg]
"1,2,3".rsplit(",")[-1] # [missing-maxsplit-arg]
## Test split called on string variable
SEQ.split(",")[0] # [missing-maxsplit-arg]
SEQ.split(",")[-1] # [missing-maxsplit-arg]
SEQ.rsplit(",")[0] # [missing-maxsplit-arg]
SEQ.rsplit(",")[-1] # [missing-maxsplit-arg]
## Test split called on class attribute
Foo.class_str.split(",")[0] # [missing-maxsplit-arg]
Foo.class_str.split(",")[-1] # [missing-maxsplit-arg]
Foo.class_str.rsplit(",")[0] # [missing-maxsplit-arg]
Foo.class_str.rsplit(",")[-1] # [missing-maxsplit-arg]
## Test split called on sliced string
"1,2,3"[::-1].split(",")[0] # [missing-maxsplit-arg]
"1,2,3"[::-1][::-1].split(",")[0] # [missing-maxsplit-arg]
SEQ[:3].split(",")[0] # [missing-maxsplit-arg]
Foo.class_str[1:3].split(",")[-1] # [missing-maxsplit-arg]
"1,2,3"[::-1].rsplit(",")[0] # [missing-maxsplit-arg]
SEQ[:3].rsplit(",")[0] # [missing-maxsplit-arg]
Foo.class_str[1:3].rsplit(",")[-1] # [missing-maxsplit-arg]
## Test sep given as named argument
"1,2,3".split(sep=",")[0] # [missing-maxsplit-arg]
"1,2,3".split(sep=",")[-1] # [missing-maxsplit-arg]
"1,2,3".rsplit(sep=",")[0] # [missing-maxsplit-arg]
"1,2,3".rsplit(sep=",")[-1] # [missing-maxsplit-arg]
## Special cases
"1,2,3".split("\n")[0] # [missing-maxsplit-arg]
"1,2,3".split("split")[-1] # [missing-maxsplit-arg]
"1,2,3".rsplit("rsplit")[0] # [missing-maxsplit-arg]
## Test class attribute named split
Bar.split.split(",")[0] # [missing-maxsplit-arg]
Bar.split.split(",")[-1] # [missing-maxsplit-arg]
Bar.split.rsplit(",")[0] # [missing-maxsplit-arg]
Bar.split.rsplit(",")[-1] # [missing-maxsplit-arg]
## Test unpacked dict literal kwargs
"1,2,3".split(**{"sep": ","})[0] # [missing-maxsplit-arg]
# OK
## Test not accessing the first or last element
### Test split called directly on string literal
"1,2,3".split(",")[1]
"1,2,3".split(",")[-2]
"1,2,3".rsplit(",")[1]
"1,2,3".rsplit(",")[-2]
### Test split called on string variable
SEQ.split(",")[1]
SEQ.split(",")[-2]
SEQ.rsplit(",")[1]
SEQ.rsplit(",")[-2]
### Test split called on class attribute
Foo.class_str.split(",")[1]
Foo.class_str.split(",")[-2]
Foo.class_str.rsplit(",")[1]
Foo.class_str.rsplit(",")[-2]
### Test split called on sliced string
"1,2,3"[::-1].split(",")[1]
SEQ[:3].split(",")[1]
Foo.class_str[1:3].split(",")[-2]
"1,2,3"[::-1].rsplit(",")[1]
SEQ[:3].rsplit(",")[1]
Foo.class_str[1:3].rsplit(",")[-2]
### Test sep given as named argument
"1,2,3".split(sep=",")[1]
"1,2,3".split(sep=",")[-2]
"1,2,3".rsplit(sep=",")[1]
"1,2,3".rsplit(sep=",")[-2]
## Test varying maxsplit argument
### str.split() tests
"1,2,3".split(sep=",", maxsplit=1)[-1]
"1,2,3".split(sep=",", maxsplit=1)[0]
"1,2,3".split(sep=",", maxsplit=2)[-1]
"1,2,3".split(sep=",", maxsplit=2)[0]
"1,2,3".split(sep=",", maxsplit=2)[1]
### str.rsplit() tests
"1,2,3".rsplit(sep=",", maxsplit=1)[-1]
"1,2,3".rsplit(sep=",", maxsplit=1)[0]
"1,2,3".rsplit(sep=",", maxsplit=2)[-1]
"1,2,3".rsplit(sep=",", maxsplit=2)[0]
"1,2,3".rsplit(sep=",", maxsplit=2)[1]
## Test user-defined split
Foo("1,2,3").split(",")[0]
Foo("1,2,3").split(",")[-1]
Foo("1,2,3").rsplit(",")[0]
Foo("1,2,3").rsplit(",")[-1]
## Test split called on sliced list
["1", "2", "3"][::-1].split(",")[0]
## Test class attribute named split
Bar.split[0]
Bar.split[-1]
Bar.split[0]
Bar.split[-1]
## Test unpacked dict literal kwargs
"1,2,3".split(",", **{"maxsplit": 1})[0]
"1,2,3".split(**{"sep": ",", "maxsplit": 1})[0]
# TODO
## Test variable split result index
## TODO: These require the ability to resolve a variable name to a value
# Errors
result_index = 0
"1,2,3".split(",")[result_index] # TODO: [missing-maxsplit-arg]
result_index = -1
"1,2,3".split(",")[result_index] # TODO: [missing-maxsplit-arg]
# OK
result_index = 1
"1,2,3".split(",")[result_index]
result_index = -2
"1,2,3".split(",")[result_index]
## Test split result index modified in loop
## TODO: These require the ability to recognize being in a loop where:
## - the result of split called on a string is indexed by a variable
## - the variable index above is modified
# OK
result_index = 0
for j in range(3):
print(SEQ.split(",")[result_index])
result_index = result_index + 1
## Test accessor
## TODO: These require the ability to get the return type of a method
## (possibly via `typing::is_string`)
class Baz():
def __init__(self):
self.my_str = "1,2,3"
def get_string(self) -> str:
return self.my_str
# Errors
Baz().get_string().split(",")[0] # TODO: [missing-maxsplit-arg]
Baz().get_string().split(",")[-1] # TODO: [missing-maxsplit-arg]
# OK
Baz().get_string().split(",")[1]
Baz().get_string().split(",")[-2]
## Test unpacked dict instance kwargs
## TODO: These require the ability to resolve a dict variable name to a value
# Errors
kwargs_without_maxsplit = {"seq": ","}
"1,2,3".split(**kwargs_without_maxsplit)[0] # TODO: [missing-maxsplit-arg]
# OK
kwargs_with_maxsplit = {"maxsplit": 1}
"1,2,3".split(",", **kwargs_with_maxsplit)[0] # TODO: false positive
kwargs_with_maxsplit = {"sep": ",", "maxsplit": 1}
"1,2,3".split(**kwargs_with_maxsplit)[0] # TODO: false positive

View File

@@ -43,6 +43,7 @@ def func():
import builtins
with builtins.open("FURB129.py") as f:
for line in f.readlines():
pass
@@ -50,6 +51,7 @@ with builtins.open("FURB129.py") as f:
from builtins import open as o
with o("FURB129.py") as f:
for line in f.readlines():
pass
@@ -87,18 +89,3 @@ with open("FURB129.py") as f:
pass
for _not_line in f.readline():
pass
# https://github.com/astral-sh/ruff/issues/18231
with open("furb129.py") as f:
for line in (f).readlines():
pass
with open("furb129.py") as f:
[line for line in (f).readlines()]
with open("furb129.py") as f:
for line in (((f))).readlines():
pass
for line in(f).readlines():
pass

View File

@@ -1,6 +1,6 @@
use ruff_diagnostics::{Diagnostic, Fix};
use ruff_text_size::Ranged;
use crate::Fix;
use crate::checkers::ast::Checker;
use crate::codes::Rule;
use crate::rules::{
@@ -38,64 +38,92 @@ pub(crate) fn bindings(checker: &Checker) {
.dummy_variable_rgx
.is_match(binding.name(checker.source()))
{
checker
.report_diagnostic(
pyflakes::rules::UnusedVariable {
name: binding.name(checker.source()).to_string(),
},
binding.range(),
)
.try_set_fix(|| {
pyflakes::fixes::remove_exception_handler_assignment(
binding,
checker.locator,
)
let mut diagnostic = Diagnostic::new(
pyflakes::rules::UnusedVariable {
name: binding.name(checker.source()).to_string(),
},
binding.range(),
);
diagnostic.try_set_fix(|| {
pyflakes::fixes::remove_exception_handler_assignment(binding, checker.locator)
.map(Fix::safe_edit)
});
});
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::InvalidAllFormat) {
pylint::rules::invalid_all_format(checker, binding);
if let Some(diagnostic) = pylint::rules::invalid_all_format(binding) {
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::InvalidAllObject) {
pylint::rules::invalid_all_object(checker, binding);
if let Some(diagnostic) = pylint::rules::invalid_all_object(binding) {
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::NonAsciiName) {
pylint::rules::non_ascii_name(checker, binding);
if let Some(diagnostic) = pylint::rules::non_ascii_name(binding, checker.locator) {
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::UnconventionalImportAlias) {
flake8_import_conventions::rules::unconventional_import_alias(
if let Some(diagnostic) = flake8_import_conventions::rules::unconventional_import_alias(
checker,
binding,
&checker.settings.flake8_import_conventions.aliases,
);
) {
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::UnaliasedCollectionsAbcSetImport) {
flake8_pyi::rules::unaliased_collections_abc_set_import(checker, binding);
if let Some(diagnostic) =
flake8_pyi::rules::unaliased_collections_abc_set_import(checker, binding)
{
checker.report_diagnostic(diagnostic);
}
}
if !checker.source_type.is_stub() && checker.enabled(Rule::UnquotedTypeAlias) {
flake8_type_checking::rules::unquoted_type_alias(checker, binding);
}
if checker.enabled(Rule::UnsortedDunderSlots) {
ruff::rules::sort_dunder_slots(checker, binding);
if let Some(diagnostic) = ruff::rules::sort_dunder_slots(checker, binding) {
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::UsedDummyVariable) {
ruff::rules::used_dummy_variable(checker, binding, binding_id);
if let Some(diagnostic) = ruff::rules::used_dummy_variable(checker, binding, binding_id)
{
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::AssignmentInAssert) {
ruff::rules::assignment_in_assert(checker, binding);
if let Some(diagnostic) = ruff::rules::assignment_in_assert(checker, binding) {
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::PytestUnittestRaisesAssertion) {
flake8_pytest_style::rules::unittest_raises_assertion_binding(checker, binding);
if let Some(diagnostic) =
flake8_pytest_style::rules::unittest_raises_assertion_binding(checker, binding)
{
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::ForLoopWrites) {
refurb::rules::for_loop_writes_binding(checker, binding);
if let Some(diagnostic) = refurb::rules::for_loop_writes_binding(checker, binding) {
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::CustomTypeVarForSelf) {
flake8_pyi::rules::custom_type_var_instead_of_self(checker, binding);
if let Some(diagnostic) =
flake8_pyi::rules::custom_type_var_instead_of_self(checker, binding)
{
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::PrivateTypeParameter) {
pyupgrade::rules::private_type_parameter(checker, binding);
if let Some(diagnostic) = pyupgrade::rules::private_type_parameter(checker, binding) {
checker.report_diagnostic(diagnostic);
}
}
}
}

View File

@@ -1,9 +1,9 @@
use ruff_diagnostics::{Diagnostic, Fix};
use ruff_python_semantic::analyze::visibility;
use ruff_python_semantic::{Binding, BindingKind, Imported, ResolvedReference, ScopeKind};
use ruff_text_size::Ranged;
use rustc_hash::FxHashMap;
use crate::Fix;
use crate::checkers::ast::Checker;
use crate::codes::Rule;
use crate::fix;
@@ -112,12 +112,12 @@ pub(crate) fn deferred_scopes(checker: &Checker) {
.map(|id| checker.semantic.reference(*id))
.all(ResolvedReference::is_load)
{
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
pylint::rules::GlobalVariableNotAssigned {
name: (*name).to_string(),
},
binding.range(),
);
));
}
}
}
@@ -146,12 +146,12 @@ pub(crate) fn deferred_scopes(checker: &Checker) {
if scope.kind.is_generator() {
continue;
}
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
pylint::rules::RedefinedArgumentFromLocal {
name: name.to_string(),
},
binding.range(),
);
));
}
}
}
@@ -186,13 +186,13 @@ pub(crate) fn deferred_scopes(checker: &Checker) {
continue;
}
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
pyflakes::rules::ImportShadowedByLoopVar {
name: name.to_string(),
row: checker.compute_source_row(shadowed.start()),
},
binding.range(),
);
));
}
}
}
@@ -331,7 +331,7 @@ pub(crate) fn deferred_scopes(checker: &Checker) {
// Create diagnostics for each statement.
for (source, entries) in &redefinitions {
for (shadowed, binding) in entries {
let mut diagnostic = checker.report_diagnostic(
let mut diagnostic = Diagnostic::new(
pyflakes::rules::RedefinedWhileUnused {
name: binding.name(checker.source()).to_string(),
row: checker.compute_source_row(shadowed.start()),
@@ -346,6 +346,8 @@ pub(crate) fn deferred_scopes(checker: &Checker) {
if let Some(fix) = source.as_ref().and_then(|source| fixes.get(source)) {
diagnostic.set_fix(fix.clone());
}
checker.report_diagnostic(diagnostic);
}
}
}

View File

@@ -17,7 +17,14 @@ pub(crate) fn except_handler(except_handler: &ExceptHandler, checker: &Checker)
range: _,
}) => {
if checker.enabled(Rule::BareExcept) {
pycodestyle::rules::bare_except(checker, type_.as_deref(), body, except_handler);
if let Some(diagnostic) = pycodestyle::rules::bare_except(
type_.as_deref(),
body,
except_handler,
checker.locator,
) {
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::RaiseWithoutFromInsideExcept) {
flake8_bugbear::rules::raise_without_from_inside_except(

View File

@@ -1,6 +1,8 @@
use ruff_python_ast::{self as ast, Arguments, Expr, ExprContext, Operator};
use ruff_python_literal::cformat::{CFormatError, CFormatErrorType};
use ruff_diagnostics::Diagnostic;
use ruff_python_ast::types::Node;
use ruff_python_semantic::ScopeKind;
use ruff_python_semantic::analyze::typing;
@@ -176,9 +178,6 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
if checker.enabled(Rule::Airflow3Removal) {
airflow::rules::airflow_3_removal_expr(checker, expr);
}
if checker.enabled(Rule::MissingMaxsplitArg) {
pylint::rules::missing_maxsplit_arg(checker, value, slice, expr);
}
pandas_vet::rules::subscript(checker, value, expr);
}
Expr::Tuple(ast::ExprTuple {
@@ -196,13 +195,14 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
let check_too_many_expressions = checker.enabled(Rule::ExpressionsInStarAssignment);
let check_two_starred_expressions =
checker.enabled(Rule::MultipleStarredExpressions);
pyflakes::rules::starred_expressions(
checker,
if let Some(diagnostic) = pyflakes::rules::starred_expressions(
elts,
check_too_many_expressions,
check_two_starred_expressions,
expr.range(),
);
) {
checker.report_diagnostic(diagnostic);
}
}
}
Expr::Name(ast::ExprName { id, ctx, range }) => {
@@ -527,12 +527,12 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
match pyflakes::format::FormatSummary::try_from(string_value.to_str()) {
Err(e) => {
if checker.enabled(Rule::StringDotFormatInvalidFormat) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
pyflakes::rules::StringDotFormatInvalidFormat {
message: pyflakes::format::error_to_string(&e),
},
location,
);
));
}
}
Ok(summary) => {
@@ -936,7 +936,9 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
pylint::rules::repeated_keyword_argument(checker, call);
}
if checker.enabled(Rule::PytestPatchWithLambda) {
flake8_pytest_style::rules::patch_with_lambda(checker, call);
if let Some(diagnostic) = flake8_pytest_style::rules::patch_with_lambda(call) {
checker.report_diagnostic(diagnostic);
}
}
if checker.any_enabled(&[
Rule::PytestParametrizeNamesWrongType,
@@ -1039,7 +1041,6 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
Rule::OsPathGetctime,
Rule::Glob,
Rule::OsListdir,
Rule::OsSymlink,
]) {
flake8_use_pathlib::rules::replaceable_by_pathlib(checker, call);
}
@@ -1284,22 +1285,22 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
..
}) => {
if checker.enabled(Rule::PercentFormatUnsupportedFormatCharacter) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
pyflakes::rules::PercentFormatUnsupportedFormatCharacter {
char: c,
},
location,
);
));
}
}
Err(e) => {
if checker.enabled(Rule::PercentFormatInvalidFormat) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
pyflakes::rules::PercentFormatInvalidFormat {
message: e.to_string(),
},
location,
);
));
}
}
Ok(summary) => {
@@ -1363,7 +1364,10 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
op: Operator::Add, ..
}) => {
if checker.enabled(Rule::ExplicitStringConcatenation) {
flake8_implicit_str_concat::rules::explicit(checker, expr);
if let Some(diagnostic) = flake8_implicit_str_concat::rules::explicit(expr, checker)
{
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::CollectionLiteralConcatenation) {
ruff::rules::collection_literal_concatenation(checker, expr);

View File

@@ -1,3 +1,4 @@
use ruff_diagnostics::Diagnostic;
use ruff_python_ast::helpers;
use ruff_python_ast::types::Node;
use ruff_python_ast::{self as ast, Expr, Stmt};
@@ -38,12 +39,12 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
if !checker.semantic.scope_id.is_global() {
for name in names {
if checker.semantic.nonlocal(name).is_none() {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
pylint::rules::NonlocalWithoutBinding {
name: name.to_string(),
},
name.range(),
);
));
}
}
}
@@ -54,20 +55,22 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
}
Stmt::Break(_) => {
if checker.enabled(Rule::BreakOutsideLoop) {
pyflakes::rules::break_outside_loop(
checker,
if let Some(diagnostic) = pyflakes::rules::break_outside_loop(
stmt,
&mut checker.semantic.current_statements().skip(1),
);
) {
checker.report_diagnostic(diagnostic);
}
}
}
Stmt::Continue(_) => {
if checker.enabled(Rule::ContinueOutsideLoop) {
pyflakes::rules::continue_outside_loop(
checker,
if let Some(diagnostic) = pyflakes::rules::continue_outside_loop(
stmt,
&mut checker.semantic.current_statements().skip(1),
);
) {
checker.report_diagnostic(diagnostic);
}
}
}
Stmt::FunctionDef(
@@ -95,7 +98,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
fastapi::rules::fastapi_unused_path_parameter(checker, function_def);
}
if checker.enabled(Rule::AmbiguousFunctionName) {
pycodestyle::rules::ambiguous_function_name(checker, name);
if let Some(diagnostic) = pycodestyle::rules::ambiguous_function_name(name) {
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::InvalidBoolReturnType) {
pylint::rules::invalid_bool_return(checker, function_def);
@@ -116,14 +121,15 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
pylint::rules::invalid_str_return(checker, function_def);
}
if checker.enabled(Rule::InvalidFunctionName) {
pep8_naming::rules::invalid_function_name(
checker,
if let Some(diagnostic) = pep8_naming::rules::invalid_function_name(
stmt,
name,
decorator_list,
&checker.settings.pep8_naming.ignore_names,
&checker.semantic,
);
) {
checker.report_diagnostic(diagnostic);
}
}
if checker.source_type.is_stub() {
if checker.enabled(Rule::PassStatementStubBody) {
@@ -173,13 +179,14 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
flake8_pyi::rules::pep_484_positional_parameter(checker, function_def);
}
if checker.enabled(Rule::DunderFunctionName) {
pep8_naming::rules::dunder_function_name(
checker,
if let Some(diagnostic) = pep8_naming::rules::dunder_function_name(
checker.semantic.current_scope(),
stmt,
name,
&checker.settings.pep8_naming.ignore_names,
);
) {
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::GlobalStatement) {
pylint::rules::global_statement(checker, name);
@@ -224,13 +231,14 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
);
}
if checker.enabled(Rule::ComplexStructure) {
mccabe::rules::function_is_too_complex(
checker,
if let Some(diagnostic) = mccabe::rules::function_is_too_complex(
stmt,
name,
body,
checker.settings.mccabe.max_complexity,
);
) {
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::HardcodedPasswordDefault) {
flake8_bandit::rules::hardcoded_password_default(checker, parameters);
@@ -250,28 +258,31 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
pylint::rules::too_many_positional_arguments(checker, function_def);
}
if checker.enabled(Rule::TooManyReturnStatements) {
pylint::rules::too_many_return_statements(
checker,
if let Some(diagnostic) = pylint::rules::too_many_return_statements(
stmt,
body,
checker.settings.pylint.max_returns,
);
) {
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::TooManyBranches) {
pylint::rules::too_many_branches(
checker,
if let Some(diagnostic) = pylint::rules::too_many_branches(
stmt,
body,
checker.settings.pylint.max_branches,
);
) {
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::TooManyStatements) {
pylint::rules::too_many_statements(
checker,
if let Some(diagnostic) = pylint::rules::too_many_statements(
stmt,
body,
checker.settings.pylint.max_statements,
);
) {
checker.report_diagnostic(diagnostic);
}
}
if checker.any_enabled(&[
Rule::PytestFixtureIncorrectParenthesesStyle,
@@ -440,24 +451,28 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
pyupgrade::rules::unnecessary_class_parentheses(checker, class_def);
}
if checker.enabled(Rule::AmbiguousClassName) {
pycodestyle::rules::ambiguous_class_name(checker, name);
if let Some(diagnostic) = pycodestyle::rules::ambiguous_class_name(name) {
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::InvalidClassName) {
pep8_naming::rules::invalid_class_name(
checker,
if let Some(diagnostic) = pep8_naming::rules::invalid_class_name(
stmt,
name,
&checker.settings.pep8_naming.ignore_names,
);
) {
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::ErrorSuffixOnExceptionName) {
pep8_naming::rules::error_suffix_on_exception_name(
checker,
if let Some(diagnostic) = pep8_naming::rules::error_suffix_on_exception_name(
stmt,
arguments.as_deref(),
name,
&checker.settings.pep8_naming.ignore_names,
);
) {
checker.report_diagnostic(diagnostic);
}
}
if !checker.source_type.is_stub() {
if checker.any_enabled(&[
@@ -596,7 +611,11 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
}
if checker.enabled(Rule::Debugger) {
flake8_debugger::rules::debugger_import(checker, stmt, None, &alias.name);
if let Some(diagnostic) =
flake8_debugger::rules::debugger_import(stmt, None, &alias.name)
{
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::BannedApi) {
flake8_tidy_imports::rules::banned_api(
@@ -619,74 +638,94 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
pylint::rules::manual_from_import(checker, stmt, alias, names);
}
if checker.enabled(Rule::ImportSelf) {
pylint::rules::import_self(checker, alias, checker.module.qualified_name());
if let Some(diagnostic) =
pylint::rules::import_self(alias, checker.module.qualified_name())
{
checker.report_diagnostic(diagnostic);
}
}
if let Some(asname) = &alias.asname {
let name = alias.name.split('.').next_back().unwrap();
if checker.enabled(Rule::ConstantImportedAsNonConstant) {
pep8_naming::rules::constant_imported_as_non_constant(
checker,
name,
asname,
alias,
stmt,
&checker.settings.pep8_naming.ignore_names,
);
if let Some(diagnostic) =
pep8_naming::rules::constant_imported_as_non_constant(
name,
asname,
alias,
stmt,
&checker.settings.pep8_naming.ignore_names,
)
{
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::LowercaseImportedAsNonLowercase) {
pep8_naming::rules::lowercase_imported_as_non_lowercase(
checker,
name,
asname,
alias,
stmt,
&checker.settings.pep8_naming.ignore_names,
);
if let Some(diagnostic) =
pep8_naming::rules::lowercase_imported_as_non_lowercase(
name,
asname,
alias,
stmt,
&checker.settings.pep8_naming.ignore_names,
)
{
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::CamelcaseImportedAsLowercase) {
pep8_naming::rules::camelcase_imported_as_lowercase(
checker,
name,
asname,
alias,
stmt,
&checker.settings.pep8_naming.ignore_names,
);
if let Some(diagnostic) =
pep8_naming::rules::camelcase_imported_as_lowercase(
name,
asname,
alias,
stmt,
&checker.settings.pep8_naming.ignore_names,
)
{
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::CamelcaseImportedAsConstant) {
pep8_naming::rules::camelcase_imported_as_constant(
checker,
if let Some(diagnostic) = pep8_naming::rules::camelcase_imported_as_constant(
name,
asname,
alias,
stmt,
&checker.settings.pep8_naming.ignore_names,
);
) {
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::CamelcaseImportedAsAcronym) {
pep8_naming::rules::camelcase_imported_as_acronym(
if let Some(diagnostic) = pep8_naming::rules::camelcase_imported_as_acronym(
name, asname, alias, stmt, checker,
);
) {
checker.report_diagnostic(diagnostic);
}
}
}
if checker.enabled(Rule::BannedImportAlias) {
if let Some(asname) = &alias.asname {
flake8_import_conventions::rules::banned_import_alias(
checker,
stmt,
&alias.name,
asname,
&checker.settings.flake8_import_conventions.banned_aliases,
);
if let Some(diagnostic) =
flake8_import_conventions::rules::banned_import_alias(
stmt,
&alias.name,
asname,
&checker.settings.flake8_import_conventions.banned_aliases,
)
{
checker.report_diagnostic(diagnostic);
}
}
}
if checker.enabled(Rule::PytestIncorrectPytestImport) {
flake8_pytest_style::rules::import(
checker,
if let Some(diagnostic) = flake8_pytest_style::rules::import(
stmt,
&alias.name,
alias.asname.as_deref(),
);
) {
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::BuiltinImportShadowing) {
flake8_builtins::rules::builtin_import_shadowing(checker, alias);
@@ -798,7 +837,11 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
}
if checker.enabled(Rule::PytestIncorrectPytestImport) {
flake8_pytest_style::rules::import_from(checker, stmt, module, level);
if let Some(diagnostic) =
flake8_pytest_style::rules::import_from(stmt, module, level)
{
checker.report_diagnostic(diagnostic);
}
}
if checker.source_type.is_stub() {
if checker.enabled(Rule::FutureAnnotationsInStub) {
@@ -813,98 +856,119 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
} else if &alias.name == "*" {
if checker.enabled(Rule::UndefinedLocalWithNestedImportStarUsage) {
if !matches!(checker.semantic.current_scope().kind, ScopeKind::Module) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
pyflakes::rules::UndefinedLocalWithNestedImportStarUsage {
name: helpers::format_import_from(level, module).to_string(),
},
stmt.range(),
);
));
}
}
if checker.enabled(Rule::UndefinedLocalWithImportStar) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
pyflakes::rules::UndefinedLocalWithImportStar {
name: helpers::format_import_from(level, module).to_string(),
},
stmt.range(),
);
));
}
}
if checker.enabled(Rule::RelativeImports) {
flake8_tidy_imports::rules::banned_relative_import(
if let Some(diagnostic) = flake8_tidy_imports::rules::banned_relative_import(
checker,
stmt,
level,
module,
checker.module.qualified_name(),
checker.settings.flake8_tidy_imports.ban_relative_imports,
);
) {
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::Debugger) {
flake8_debugger::rules::debugger_import(checker, stmt, module, &alias.name);
if let Some(diagnostic) =
flake8_debugger::rules::debugger_import(stmt, module, &alias.name)
{
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::BannedImportAlias) {
if let Some(asname) = &alias.asname {
let qualified_name =
helpers::format_import_from_member(level, module, &alias.name);
flake8_import_conventions::rules::banned_import_alias(
checker,
stmt,
&qualified_name,
asname,
&checker.settings.flake8_import_conventions.banned_aliases,
);
if let Some(diagnostic) =
flake8_import_conventions::rules::banned_import_alias(
stmt,
&qualified_name,
asname,
&checker.settings.flake8_import_conventions.banned_aliases,
)
{
checker.report_diagnostic(diagnostic);
}
}
}
if let Some(asname) = &alias.asname {
if checker.enabled(Rule::ConstantImportedAsNonConstant) {
pep8_naming::rules::constant_imported_as_non_constant(
checker,
&alias.name,
asname,
alias,
stmt,
&checker.settings.pep8_naming.ignore_names,
);
if let Some(diagnostic) =
pep8_naming::rules::constant_imported_as_non_constant(
&alias.name,
asname,
alias,
stmt,
&checker.settings.pep8_naming.ignore_names,
)
{
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::LowercaseImportedAsNonLowercase) {
pep8_naming::rules::lowercase_imported_as_non_lowercase(
checker,
&alias.name,
asname,
alias,
stmt,
&checker.settings.pep8_naming.ignore_names,
);
if let Some(diagnostic) =
pep8_naming::rules::lowercase_imported_as_non_lowercase(
&alias.name,
asname,
alias,
stmt,
&checker.settings.pep8_naming.ignore_names,
)
{
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::CamelcaseImportedAsLowercase) {
pep8_naming::rules::camelcase_imported_as_lowercase(
checker,
&alias.name,
asname,
alias,
stmt,
&checker.settings.pep8_naming.ignore_names,
);
if let Some(diagnostic) =
pep8_naming::rules::camelcase_imported_as_lowercase(
&alias.name,
asname,
alias,
stmt,
&checker.settings.pep8_naming.ignore_names,
)
{
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::CamelcaseImportedAsConstant) {
pep8_naming::rules::camelcase_imported_as_constant(
checker,
if let Some(diagnostic) = pep8_naming::rules::camelcase_imported_as_constant(
&alias.name,
asname,
alias,
stmt,
&checker.settings.pep8_naming.ignore_names,
);
) {
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::CamelcaseImportedAsAcronym) {
pep8_naming::rules::camelcase_imported_as_acronym(
if let Some(diagnostic) = pep8_naming::rules::camelcase_imported_as_acronym(
&alias.name,
asname,
alias,
stmt,
checker,
);
) {
checker.report_diagnostic(diagnostic);
}
}
if !checker.source_type.is_stub() {
if checker.enabled(Rule::UselessImportAlias) {
@@ -917,21 +981,23 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
}
}
if checker.enabled(Rule::ImportSelf) {
pylint::rules::import_from_self(
checker,
if let Some(diagnostic) = pylint::rules::import_from_self(
level,
module,
names,
checker.module.qualified_name(),
);
) {
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::BannedImportFrom) {
flake8_import_conventions::rules::banned_import_from(
checker,
if let Some(diagnostic) = flake8_import_conventions::rules::banned_import_from(
stmt,
&helpers::format_import_from(level, module),
&checker.settings.flake8_import_conventions.banned_from,
);
) {
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::ByteStringUsage) {
flake8_pyi::rules::bytestring_import(checker, import_from);
@@ -1146,7 +1212,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
) => {
if !checker.semantic.in_type_checking_block() {
if checker.enabled(Rule::Assert) {
flake8_bandit::rules::assert_used(checker, stmt);
checker.report_diagnostic(flake8_bandit::rules::assert_used(stmt));
}
}
if checker.enabled(Rule::AssertTuple) {
@@ -1343,7 +1409,11 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
}
}
if checker.enabled(Rule::DefaultExceptNotLast) {
pyflakes::rules::default_except_not_last(checker, handlers, checker.locator);
if let Some(diagnostic) =
pyflakes::rules::default_except_not_last(handlers, checker.locator)
{
checker.report_diagnostic(diagnostic);
}
}
if checker.any_enabled(&[
Rule::DuplicateHandlerException,
@@ -1438,7 +1508,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
);
}
if checker.enabled(Rule::PandasDfVariableName) {
pandas_vet::rules::assignment_to_df(checker, targets);
if let Some(diagnostic) = pandas_vet::rules::assignment_to_df(targets) {
checker.report_diagnostic(diagnostic);
}
}
if checker
.settings
@@ -1632,7 +1704,11 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
pylint::rules::named_expr_without_context(checker, value);
}
if checker.enabled(Rule::AsyncioDanglingTask) {
ruff::rules::asyncio_dangling_task(checker, value, checker.semantic());
if let Some(diagnostic) =
ruff::rules::asyncio_dangling_task(value, checker.semantic())
{
checker.report_diagnostic(diagnostic);
}
}
if checker.enabled(Rule::RepeatedAppend) {
refurb::rules::repeated_append(checker, stmt);

View File

@@ -1,3 +1,4 @@
use ruff_diagnostics::Diagnostic;
use ruff_python_semantic::Exceptions;
use ruff_python_stdlib::builtins::version_builtin_was_added;
@@ -14,12 +15,12 @@ pub(crate) fn unresolved_references(checker: &Checker) {
for reference in checker.semantic.unresolved_references() {
if reference.is_wildcard_import() {
if checker.enabled(Rule::UndefinedLocalWithImportStarUsage) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
pyflakes::rules::UndefinedLocalWithImportStarUsage {
name: reference.name(checker.source()).to_string(),
},
reference.range(),
);
));
}
} else {
if checker.enabled(Rule::UndefinedName) {
@@ -41,13 +42,13 @@ pub(crate) fn unresolved_references(checker: &Checker) {
let symbol_name = reference.name(checker.source());
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
pyflakes::rules::UndefinedName {
name: symbol_name.to_string(),
minor_version_builtin_added: version_builtin_was_added(symbol_name),
},
reference.range(),
);
));
}
}
}

View File

@@ -26,9 +26,12 @@ use std::path::Path;
use itertools::Itertools;
use log::debug;
use ruff_python_parser::semantic_errors::{
SemanticSyntaxChecker, SemanticSyntaxContext, SemanticSyntaxError, SemanticSyntaxErrorKind,
};
use rustc_hash::{FxHashMap, FxHashSet};
use ruff_diagnostics::IsolationLevel;
use ruff_diagnostics::{Diagnostic, Edit, IsolationLevel};
use ruff_notebook::{CellOffsets, NotebookIndex};
use ruff_python_ast::helpers::{collect_import_from_member, is_docstring_stmt, to_module_path};
use ruff_python_ast::identifier::Identifier;
@@ -43,9 +46,6 @@ use ruff_python_ast::{
use ruff_python_ast::{PySourceType, helpers, str, visitor};
use ruff_python_codegen::{Generator, Stylist};
use ruff_python_index::Indexer;
use ruff_python_parser::semantic_errors::{
SemanticSyntaxChecker, SemanticSyntaxContext, SemanticSyntaxError, SemanticSyntaxErrorKind,
};
use ruff_python_parser::typing::{AnnotationKind, ParsedAnnotation, parse_type_annotation};
use ruff_python_parser::{ParseError, Parsed, Tokens};
use ruff_python_semantic::all::{DunderAllDefinition, DunderAllFlags};
@@ -66,14 +66,13 @@ use crate::importer::{ImportRequest, Importer, ResolutionError};
use crate::noqa::NoqaMapping;
use crate::package::PackageRoot;
use crate::preview::{is_semantic_errors_enabled, is_undefined_export_in_dunder_init_enabled};
use crate::registry::{AsRule, Rule};
use crate::registry::Rule;
use crate::rules::pyflakes::rules::{
LateFutureImport, ReturnOutsideFunction, YieldOutsideFunction,
};
use crate::rules::pylint::rules::{AwaitOutsideAsync, LoadBeforeGlobalDeclaration};
use crate::rules::{flake8_pyi, flake8_type_checking, pyflakes, pyupgrade};
use crate::settings::{LinterSettings, TargetVersion, flags};
use crate::{Diagnostic, Edit, Violation};
use crate::{Locator, docstrings, noqa};
mod analyze;
@@ -380,40 +379,10 @@ impl<'a> Checker<'a> {
self.indexer.comment_ranges()
}
/// Return a [`DiagnosticGuard`] for reporting a diagnostic.
///
/// The guard derefs to a [`Diagnostic`], so it can be used to further modify the diagnostic
/// before it is added to the collection in the checker on `Drop`.
pub(crate) fn report_diagnostic<'chk, T: Violation>(
&'chk self,
kind: T,
range: TextRange,
) -> DiagnosticGuard<'chk, 'a> {
DiagnosticGuard {
checker: self,
diagnostic: Some(Diagnostic::new(kind, range)),
}
}
/// Return a [`DiagnosticGuard`] for reporting a diagnostic if the corresponding rule is
/// enabled.
///
/// Prefer [`Checker::report_diagnostic`] in general because the conversion from a `Diagnostic`
/// to a `Rule` is somewhat expensive.
pub(crate) fn report_diagnostic_if_enabled<'chk, T: Violation>(
&'chk self,
kind: T,
range: TextRange,
) -> Option<DiagnosticGuard<'chk, 'a>> {
let diagnostic = Diagnostic::new(kind, range);
if self.enabled(diagnostic.rule()) {
Some(DiagnosticGuard {
checker: self,
diagnostic: Some(diagnostic),
})
} else {
None
}
/// Push a new [`Diagnostic`] to the collection in the [`Checker`]
pub(crate) fn report_diagnostic(&self, diagnostic: Diagnostic) {
let mut diagnostics = self.diagnostics.borrow_mut();
diagnostics.push(diagnostic);
}
/// Adds a [`TextRange`] to the set of ranges of variable names
@@ -539,9 +508,9 @@ impl<'a> Checker<'a> {
}
/// Push `diagnostic` if the checker is not in a `@no_type_check` context.
pub(crate) fn report_type_diagnostic<T: Violation>(&self, kind: T, range: TextRange) {
pub(crate) fn report_type_diagnostic(&self, diagnostic: Diagnostic) {
if !self.semantic.in_no_type_check() {
self.report_diagnostic(kind, range);
self.report_diagnostic(diagnostic);
}
}
@@ -626,7 +595,7 @@ impl SemanticSyntaxContext for Checker<'_> {
match error.kind {
SemanticSyntaxErrorKind::LateFutureImport => {
if self.settings.rules.enabled(Rule::LateFutureImport) {
self.report_diagnostic(LateFutureImport, error.range);
self.report_diagnostic(Diagnostic::new(LateFutureImport, error.range));
}
}
SemanticSyntaxErrorKind::LoadBeforeGlobalDeclaration { name, start } => {
@@ -635,28 +604,31 @@ impl SemanticSyntaxContext for Checker<'_> {
.rules
.enabled(Rule::LoadBeforeGlobalDeclaration)
{
self.report_diagnostic(
self.report_diagnostic(Diagnostic::new(
LoadBeforeGlobalDeclaration {
name,
row: self.compute_source_row(start),
},
error.range,
);
));
}
}
SemanticSyntaxErrorKind::YieldOutsideFunction(kind) => {
if self.settings.rules.enabled(Rule::YieldOutsideFunction) {
self.report_diagnostic(YieldOutsideFunction::new(kind), error.range);
self.report_diagnostic(Diagnostic::new(
YieldOutsideFunction::new(kind),
error.range,
));
}
}
SemanticSyntaxErrorKind::ReturnOutsideFunction => {
if self.settings.rules.enabled(Rule::ReturnOutsideFunction) {
self.report_diagnostic(ReturnOutsideFunction, error.range);
self.report_diagnostic(Diagnostic::new(ReturnOutsideFunction, error.range));
}
}
SemanticSyntaxErrorKind::AwaitOutsideAsyncFunction(_) => {
if self.settings.rules.enabled(Rule::AwaitOutsideAsync) {
self.report_diagnostic(AwaitOutsideAsync, error.range);
self.report_diagnostic(Diagnostic::new(AwaitOutsideAsync, error.range));
}
}
SemanticSyntaxErrorKind::ReboundComprehensionVariable
@@ -2745,12 +2717,12 @@ impl<'a> Checker<'a> {
self.semantic.restore(snapshot);
if self.enabled(Rule::ForwardAnnotationSyntaxError) {
self.report_type_diagnostic(
self.report_type_diagnostic(Diagnostic::new(
pyflakes::rules::ForwardAnnotationSyntaxError {
parse_error: parse_error.error.to_string(),
},
string_expr.range(),
);
));
}
}
}
@@ -3048,59 +3020,3 @@ pub(crate) fn check_ast(
(diagnostics.into_inner(), semantic_errors.into_inner())
}
/// An abstraction for mutating a diagnostic.
///
/// Callers can build this guard by starting with `Checker::report_diagnostic`.
///
/// The primary function of this guard is to add the underlying diagnostic to the `Checker`'s list
/// of diagnostics on `Drop`, while dereferencing to the underlying diagnostic for mutations like
/// adding fixes or parent ranges.
pub(crate) struct DiagnosticGuard<'a, 'b> {
/// The parent checker that will receive the diagnostic on `Drop`.
checker: &'a Checker<'b>,
/// The diagnostic that we want to report.
///
/// This is always `Some` until the `Drop` (or `defuse`) call.
diagnostic: Option<Diagnostic>,
}
impl DiagnosticGuard<'_, '_> {
/// Consume the underlying `Diagnostic` without emitting it.
///
/// In general you should avoid constructing diagnostics that may not be emitted, but this
/// method can be used where this is unavoidable.
pub(crate) fn defuse(mut self) {
self.diagnostic = None;
}
}
impl std::ops::Deref for DiagnosticGuard<'_, '_> {
type Target = Diagnostic;
fn deref(&self) -> &Diagnostic {
// OK because `self.diagnostic` is only `None` within `Drop`.
self.diagnostic.as_ref().unwrap()
}
}
/// Return a mutable borrow of the diagnostic in this guard.
impl std::ops::DerefMut for DiagnosticGuard<'_, '_> {
fn deref_mut(&mut self) -> &mut Diagnostic {
// OK because `self.diagnostic` is only `None` within `Drop`.
self.diagnostic.as_mut().unwrap()
}
}
impl Drop for DiagnosticGuard<'_, '_> {
fn drop(&mut self) {
if std::thread::panicking() {
// Don't submit diagnostics when panicking because they might be incomplete.
return;
}
if let Some(diagnostic) = self.diagnostic.take() {
self.checker.diagnostics.borrow_mut().push(diagnostic);
}
}
}

View File

@@ -1,9 +1,9 @@
use std::path::Path;
use ruff_diagnostics::Diagnostic;
use ruff_python_ast::PythonVersion;
use ruff_python_trivia::CommentRanges;
use crate::Diagnostic;
use crate::Locator;
use crate::package::PackageRoot;
use crate::preview::is_allow_nested_roots_enabled;

View File

@@ -1,5 +1,6 @@
//! Lint rules based on import analysis.
use ruff_diagnostics::Diagnostic;
use ruff_notebook::CellOffsets;
use ruff_python_ast::statement_visitor::StatementVisitor;
use ruff_python_ast::{ModModule, PySourceType, PythonVersion};
@@ -7,7 +8,6 @@ use ruff_python_codegen::Stylist;
use ruff_python_index::Indexer;
use ruff_python_parser::Parsed;
use crate::Diagnostic;
use crate::Locator;
use crate::directives::IsortDirectives;
use crate::package::PackageRoot;

View File

@@ -1,10 +1,10 @@
use ruff_diagnostics::Diagnostic;
use ruff_python_codegen::Stylist;
use ruff_python_index::Indexer;
use ruff_python_parser::{TokenKind, Tokens};
use ruff_source_file::LineRanges;
use ruff_text_size::{Ranged, TextRange};
use crate::Diagnostic;
use crate::Locator;
use crate::line_width::IndentWidth;
use crate::registry::{AsRule, Rule};

View File

@@ -5,6 +5,7 @@ use std::path::Path;
use itertools::Itertools;
use rustc_hash::FxHashSet;
use ruff_diagnostics::{Diagnostic, Edit, Fix};
use ruff_python_trivia::CommentRanges;
use ruff_text_size::Ranged;
@@ -20,7 +21,6 @@ use crate::rules::pygrep_hooks;
use crate::rules::ruff;
use crate::rules::ruff::rules::{UnusedCodes, UnusedNOQA};
use crate::settings::LinterSettings;
use crate::{Diagnostic, Edit, Fix};
#[expect(clippy::too_many_arguments)]
pub(crate) fn check_noqa(

View File

@@ -1,11 +1,11 @@
//! Lint rules based on checking physical lines.
use ruff_diagnostics::Diagnostic;
use ruff_python_codegen::Stylist;
use ruff_python_index::Indexer;
use ruff_source_file::UniversalNewlines;
use ruff_text_size::TextSize;
use crate::Diagnostic;
use crate::Locator;
use crate::registry::Rule;
use crate::rules::flake8_copyright::rules::missing_copyright_notice;

View File

@@ -2,13 +2,13 @@
use std::path::Path;
use ruff_diagnostics::Diagnostic;
use ruff_notebook::CellOffsets;
use ruff_python_ast::PySourceType;
use ruff_python_codegen::Stylist;
use ruff_python_index::Indexer;
use ruff_python_parser::Tokens;
use crate::Diagnostic;
use crate::Locator;
use crate::directives::TodoComment;
use crate::registry::{AsRule, Rule};

View File

@@ -6,7 +6,7 @@ use std::fmt::Formatter;
use strum_macros::{AsRefStr, EnumIter};
use crate::registry::Linter;
use crate::registry::{AsRule, Linter};
use crate::rule_selector::is_single_rule_selector;
use crate::rules;
@@ -198,7 +198,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Pylint, "C0132") => (RuleGroup::Stable, rules::pylint::rules::TypeParamNameMismatch),
(Pylint, "C0205") => (RuleGroup::Stable, rules::pylint::rules::SingleStringSlots),
(Pylint, "C0206") => (RuleGroup::Stable, rules::pylint::rules::DictIndexMissingItems),
(Pylint, "C0207") => (RuleGroup::Preview, rules::pylint::rules::MissingMaxsplitArg),
(Pylint, "C0208") => (RuleGroup::Stable, rules::pylint::rules::IterationOverSet),
(Pylint, "C0414") => (RuleGroup::Stable, rules::pylint::rules::UselessImportAlias),
(Pylint, "C0415") => (RuleGroup::Preview, rules::pylint::rules::ImportOutsideTopLevel),
@@ -935,7 +934,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Flake8UsePathlib, "207") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::Glob),
(Flake8UsePathlib, "208") => (RuleGroup::Stable, rules::flake8_use_pathlib::violations::OsListdir),
(Flake8UsePathlib, "210") => (RuleGroup::Stable, rules::flake8_use_pathlib::rules::InvalidPathlibWithSuffix),
(Flake8UsePathlib, "211") => (RuleGroup::Preview, rules::flake8_use_pathlib::violations::OsSymlink),
// flake8-logging-format
(Flake8LoggingFormat, "001") => (RuleGroup::Stable, rules::flake8_logging_format::violations::LoggingStringFormat),
@@ -1157,9 +1155,3 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
_ => return None,
})
}
impl std::fmt::Display for Rule {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
f.write_str(self.into())
}
}

View File

@@ -2,6 +2,7 @@
use anyhow::{Context, Result};
use ruff_diagnostics::Edit;
use ruff_python_ast::parenthesize::parenthesized_range;
use ruff_python_ast::{self as ast, Arguments, ExceptHandler, Expr, ExprList, Parameters, Stmt};
use ruff_python_ast::{AnyNodeRef, ArgOrKeyword};
@@ -15,7 +16,6 @@ use ruff_python_trivia::{
use ruff_source_file::{LineRanges, NewlineWithTrailingNewline, UniversalNewlines};
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
use crate::Edit;
use crate::Locator;
use crate::cst::matchers::{match_function_def, match_indented_block, match_statement};
use crate::fix::codemods;
@@ -595,19 +595,18 @@ mod tests {
use ruff_source_file::SourceFileBuilder;
use test_case::test_case;
use ruff_diagnostics::{Diagnostic, Edit, Fix};
use ruff_python_ast::Stmt;
use ruff_python_codegen::Stylist;
use ruff_python_parser::{parse_expression, parse_module};
use ruff_text_size::{Ranged, TextRange, TextSize};
use crate::Locator;
use crate::codes::Rule;
use crate::fix::apply_fixes;
use crate::fix::edits::{
add_to_dunder_all, make_redundant_alias, next_stmt_break, trailing_semicolon,
};
use crate::message::Message;
use crate::{Diagnostic, Edit, Fix};
/// Parse the given source using [`Mode::Module`] and return the first statement.
fn parse_first_stmt(source: &str) -> Result<Stmt> {
@@ -747,6 +746,7 @@ x = 1 \
iter,
));
Message::diagnostic(
diag.name,
diag.body,
diag.suggestion,
diag.range,
@@ -754,7 +754,6 @@ x = 1 \
diag.parent,
SourceFileBuilder::new("<filename>", "<code>").finish(),
None,
Rule::MissingNewlineAtEndOfFile,
)
};
assert_eq!(apply_fixes([diag].iter(), &locator).code, expect);

View File

@@ -3,7 +3,7 @@ use std::collections::BTreeSet;
use itertools::Itertools;
use rustc_hash::{FxHashMap, FxHashSet};
use ruff_diagnostics::{IsolationLevel, SourceMap};
use ruff_diagnostics::{Edit, Fix, IsolationLevel, SourceMap};
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
use crate::Locator;
@@ -11,7 +11,6 @@ use crate::linter::FixTable;
use crate::message::Message;
use crate::registry::Rule;
use crate::settings::types::UnsafeFixes;
use crate::{Edit, Fix};
pub(crate) mod codemods;
pub(crate) mod edits;
@@ -158,16 +157,14 @@ fn cmp_fix(rule1: Rule, rule2: Rule, fix1: &Fix, fix2: &Fix) -> std::cmp::Orderi
#[cfg(test)]
mod tests {
use ruff_diagnostics::SourceMarker;
use ruff_diagnostics::{Diagnostic, Edit, Fix, SourceMarker};
use ruff_source_file::SourceFileBuilder;
use ruff_text_size::{Ranged, TextSize};
use crate::Locator;
use crate::diagnostic::Diagnostic;
use crate::fix::{FixResult, apply_fixes};
use crate::message::Message;
use crate::rules::pycodestyle::rules::MissingNewlineAtEndOfFile;
use crate::{Edit, Fix};
fn create_diagnostics(
filename: &str,

View File

@@ -1,6 +1,7 @@
//! Insert statements into Python code.
use std::ops::Add;
use ruff_diagnostics::Edit;
use ruff_python_ast::Stmt;
use ruff_python_ast::helpers::is_docstring_stmt;
use ruff_python_codegen::Stylist;
@@ -9,7 +10,6 @@ use ruff_python_trivia::{PythonWhitespace, textwrap::indent};
use ruff_source_file::{LineRanges, UniversalNewlineIterator};
use ruff_text_size::{Ranged, TextSize};
use crate::Edit;
use crate::Locator;
#[derive(Debug, Clone, PartialEq, Eq)]

View File

@@ -8,6 +8,7 @@ use std::error::Error;
use anyhow::Result;
use libcst_native::{ImportAlias, Name as cstName, NameOrAttribute};
use ruff_diagnostics::Edit;
use ruff_python_ast::{self as ast, Expr, ModModule, Stmt};
use ruff_python_codegen::Stylist;
use ruff_python_parser::{Parsed, Tokens};
@@ -17,7 +18,6 @@ use ruff_python_semantic::{
use ruff_python_trivia::textwrap::indent;
use ruff_text_size::{Ranged, TextSize};
use crate::Edit;
use crate::Locator;
use crate::cst::matchers::{match_aliases, match_import_from, match_statement};
use crate::fix;

View File

@@ -14,17 +14,12 @@ pub use rule_selector::RuleSelector;
pub use rule_selector::clap_completion::RuleSelectorParser;
pub use rules::pycodestyle::rules::IOError;
pub use diagnostic::Diagnostic;
pub(crate) use ruff_diagnostics::{Applicability, Edit, Fix};
pub use violation::{AlwaysFixableViolation, FixAvailability, Violation, ViolationMetadata};
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
mod checkers;
pub mod codes;
mod comments;
mod cst;
mod diagnostic;
pub mod directives;
mod doc_lines;
mod docstrings;
@@ -50,7 +45,6 @@ pub mod settings;
pub mod source_kind;
mod text_helpers;
pub mod upstream_categories;
mod violation;
#[cfg(any(test, fuzzing))]
pub mod test;

View File

@@ -9,6 +9,7 @@ use itertools::Itertools;
use ruff_python_parser::semantic_errors::SemanticSyntaxError;
use rustc_hash::FxHashMap;
use ruff_diagnostics::Diagnostic;
use ruff_notebook::Notebook;
use ruff_python_ast::{ModModule, PySourceType, PythonVersion};
use ruff_python_codegen::Stylist;
@@ -17,7 +18,6 @@ use ruff_python_parser::{ParseError, ParseOptions, Parsed, UnsupportedSyntaxErro
use ruff_source_file::SourceFileBuilder;
use ruff_text_size::Ranged;
use crate::Diagnostic;
use crate::checkers::ast::check_ast;
use crate::checkers::filesystem::check_file_path;
use crate::checkers::imports::check_imports;

View File

@@ -6,11 +6,11 @@ use colored::{Color, ColoredString, Colorize, Styles};
use ruff_text_size::{Ranged, TextRange, TextSize};
use similar::{ChangeTag, TextDiff};
use ruff_diagnostics::{Applicability, Fix};
use ruff_source_file::{OneIndexed, SourceFile};
use crate::message::Message;
use crate::text_helpers::ShowNonprinting;
use crate::{Applicability, Fix};
/// Renders a diff that shows the code fixes.
///

View File

@@ -4,11 +4,11 @@ use serde::ser::SerializeSeq;
use serde::{Serialize, Serializer};
use serde_json::{Value, json};
use ruff_diagnostics::Edit;
use ruff_notebook::NotebookIndex;
use ruff_source_file::{LineColumn, OneIndexed, SourceCode};
use ruff_text_size::Ranged;
use crate::Edit;
use crate::message::{Emitter, EmitterContext, Message};
#[derive(Default)]

View File

@@ -16,6 +16,7 @@ pub use json_lines::JsonLinesEmitter;
pub use junit::JunitEmitter;
pub use pylint::PylintEmitter;
pub use rdjson::RdjsonEmitter;
use ruff_diagnostics::{Diagnostic, Fix};
use ruff_notebook::NotebookIndex;
use ruff_python_parser::{ParseError, UnsupportedSyntaxError};
use ruff_source_file::{LineColumn, SourceFile};
@@ -27,7 +28,6 @@ use crate::Locator;
use crate::codes::NoqaCode;
use crate::logging::DisplayParseErrorType;
use crate::registry::Rule;
use crate::{Diagnostic, Fix};
mod azure;
mod diff;
@@ -60,7 +60,6 @@ pub struct Message {
pub fix: Option<Fix>,
pub parent: Option<TextSize>,
pub(crate) noqa_offset: Option<TextSize>,
noqa_code: Option<NoqaCode>,
}
impl Message {
@@ -77,12 +76,12 @@ impl Message {
fix: None,
parent: None,
noqa_offset: None,
noqa_code: None,
}
}
#[expect(clippy::too_many_arguments)]
pub fn diagnostic(
name: &'static str,
body: String,
suggestion: Option<String>,
range: TextRange,
@@ -90,10 +89,9 @@ impl Message {
parent: Option<TextSize>,
file: SourceFile,
noqa_offset: Option<TextSize>,
rule: Rule,
) -> Message {
let mut diagnostic = db::Diagnostic::new(
DiagnosticId::Lint(LintName::of(rule.into())),
DiagnosticId::Lint(LintName::of(name)),
Severity::Error,
body,
);
@@ -109,7 +107,6 @@ impl Message {
fix,
parent,
noqa_offset,
noqa_code: Some(rule.noqa_code()),
}
}
@@ -120,14 +117,15 @@ impl Message {
noqa_offset: Option<TextSize>,
) -> Message {
let Diagnostic {
name,
body,
suggestion,
range,
fix,
parent,
rule,
} = diagnostic;
Self::diagnostic(
name,
body,
suggestion,
range,
@@ -135,7 +133,6 @@ impl Message {
parent,
file,
noqa_offset,
rule,
)
}
@@ -238,7 +235,7 @@ impl Message {
/// Returns the [`NoqaCode`] corresponding to the diagnostic message.
pub fn to_noqa_code(&self) -> Option<NoqaCode> {
self.noqa_code
self.to_rule().map(|rule| rule.noqa_code())
}
/// Returns the URL for the rule documentation, if it exists.
@@ -374,8 +371,7 @@ impl<'a> EmitterContext<'a> {
mod tests {
use rustc_hash::FxHashMap;
use crate::codes::Rule;
use crate::{Edit, Fix};
use ruff_diagnostics::{Edit, Fix};
use ruff_notebook::NotebookIndex;
use ruff_python_parser::{Mode, ParseOptions, parse_unchecked};
use ruff_source_file::{OneIndexed, SourceFileBuilder};
@@ -421,6 +417,7 @@ def fibonacci(n):
let unused_import_start = TextSize::from(7);
let unused_import = Message::diagnostic(
"unused-import",
"`os` imported but unused".to_string(),
Some("Remove unused import: `os`".to_string()),
TextRange::new(unused_import_start, TextSize::from(9)),
@@ -431,11 +428,11 @@ def fibonacci(n):
None,
fib_source.clone(),
Some(unused_import_start),
Rule::UnusedImport,
);
let unused_variable_start = TextSize::from(94);
let unused_variable = Message::diagnostic(
"unused-variable",
"Local variable `x` is assigned to but never used".to_string(),
Some("Remove assignment to unused variable `x`".to_string()),
TextRange::new(unused_variable_start, TextSize::from(95)),
@@ -446,13 +443,13 @@ def fibonacci(n):
None,
fib_source,
Some(unused_variable_start),
Rule::UnusedVariable,
);
let file_2 = r"if a == 1: pass";
let undefined_name_start = TextSize::from(3);
let undefined_name = Message::diagnostic(
"undefined-name",
"Undefined name `a`".to_string(),
None,
TextRange::new(undefined_name_start, TextSize::from(4)),
@@ -460,7 +457,6 @@ def fibonacci(n):
None,
SourceFileBuilder::new("undef.py", file_2).finish(),
Some(undefined_name_start),
Rule::UndefinedName,
);
vec![unused_import, unused_variable, undefined_name]
@@ -483,6 +479,7 @@ def foo():
let unused_import_os_start = TextSize::from(16);
let unused_import_os = Message::diagnostic(
"unused-import",
"`os` imported but unused".to_string(),
Some("Remove unused import: `os`".to_string()),
TextRange::new(unused_import_os_start, TextSize::from(18)),
@@ -493,11 +490,11 @@ def foo():
None,
notebook_source.clone(),
Some(unused_import_os_start),
Rule::UnusedImport,
);
let unused_import_math_start = TextSize::from(35);
let unused_import_math = Message::diagnostic(
"unused-import",
"`math` imported but unused".to_string(),
Some("Remove unused import: `math`".to_string()),
TextRange::new(unused_import_math_start, TextSize::from(39)),
@@ -508,11 +505,11 @@ def foo():
None,
notebook_source.clone(),
Some(unused_import_math_start),
Rule::UnusedImport,
);
let unused_variable_start = TextSize::from(98);
let unused_variable = Message::diagnostic(
"unused-variable",
"Local variable `x` is assigned to but never used".to_string(),
Some("Remove assignment to unused variable `x`".to_string()),
TextRange::new(unused_variable_start, TextSize::from(99)),
@@ -523,7 +520,6 @@ def foo():
None,
notebook_source,
Some(unused_variable_start),
Rule::UnusedVariable,
);
let mut notebook_indexes = FxHashMap::default();

View File

@@ -4,10 +4,10 @@ use serde::ser::SerializeSeq;
use serde::{Serialize, Serializer};
use serde_json::{Value, json};
use ruff_diagnostics::Edit;
use ruff_source_file::SourceCode;
use ruff_text_size::Ranged;
use crate::Edit;
use crate::message::{Emitter, EmitterContext, LineColumn, Message};
#[derive(Default)]

View File

@@ -9,11 +9,11 @@ use anyhow::Result;
use itertools::Itertools;
use log::warn;
use ruff_diagnostics::Edit;
use ruff_python_trivia::{CommentRanges, Cursor, indentation_at_offset};
use ruff_source_file::{LineEnding, LineRanges};
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
use crate::Edit;
use crate::Locator;
use crate::codes::NoqaCode;
use crate::fs::relativize_path;
@@ -1221,6 +1221,7 @@ mod tests {
use insta::assert_debug_snapshot;
use ruff_diagnostics::{Diagnostic, Edit};
use ruff_python_trivia::CommentRanges;
use ruff_source_file::{LineEnding, SourceFileBuilder};
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
@@ -1233,7 +1234,6 @@ mod tests {
use crate::rules::pycodestyle::rules::{AmbiguousVariableName, UselessSemicolon};
use crate::rules::pyflakes::rules::UnusedVariable;
use crate::rules::pyupgrade::rules::PrintfStringFormatting;
use crate::{Diagnostic, Edit};
use crate::{Locator, generate_noqa_edits};
fn assert_lexed_ranges_match_slices(

View File

@@ -3,9 +3,9 @@ use log::warn;
use pyproject_toml::PyProjectToml;
use ruff_text_size::{TextRange, TextSize};
use ruff_diagnostics::Diagnostic;
use ruff_source_file::SourceFile;
use crate::Diagnostic;
use crate::IOError;
use crate::message::Message;
use crate::registry::Rule;

View File

@@ -3,13 +3,13 @@
use anyhow::{Result, anyhow};
use itertools::Itertools;
use ruff_diagnostics::Edit;
use ruff_python_ast as ast;
use ruff_python_codegen::Stylist;
use ruff_python_semantic::{Binding, BindingKind, Scope, ScopeId, SemanticModel};
use ruff_python_stdlib::{builtins::is_python_builtin, keyword::is_keyword};
use ruff_text_size::Ranged;
use crate::Edit;
use crate::checkers::ast::Checker;
pub(crate) struct Renamer;

View File

@@ -1,10 +1,10 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::Expr;
use ruff_python_ast::{self as ast};
use ruff_python_semantic::Modules;
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -86,5 +86,6 @@ pub(crate) fn dag_no_schedule_argument(checker: &Checker, expr: &Expr) {
}
// Produce a diagnostic when the `schedule` keyword argument is not found.
checker.report_diagnostic(AirflowDagNoScheduleArgument, expr.range());
let diagnostic = Diagnostic::new(AirflowDagNoScheduleArgument, expr.range());
checker.report_diagnostic(diagnostic);
}

View File

@@ -1,14 +1,13 @@
use crate::importer::ImportRequest;
use crate::rules::airflow::helpers::{ProviderReplacement, is_guarded_by_try_except};
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::name::QualifiedName;
use ruff_python_ast::{Expr, ExprAttribute};
use ruff_python_semantic::Modules;
use ruff_text_size::Ranged;
use ruff_text_size::TextRange;
use crate::checkers::ast::Checker;
use crate::importer::ImportRequest;
use crate::rules::airflow::helpers::{ProviderReplacement, is_guarded_by_try_except};
use crate::{Edit, Fix, FixAvailability, Violation};
/// ## What it does
/// Checks for uses of Airflow functions and values that have been moved to it providers.
@@ -29,12 +28,12 @@ use crate::{Edit, Fix, FixAvailability, Violation};
/// from airflow.providers.fab.auth_manager.fab_auth_manage import FabAuthManager
/// ```
#[derive(ViolationMetadata)]
pub(crate) struct Airflow3MovedToProvider<'a> {
deprecated: QualifiedName<'a>,
pub(crate) struct Airflow3MovedToProvider {
deprecated: String,
replacement: ProviderReplacement,
}
impl Violation for Airflow3MovedToProvider<'_> {
impl Violation for Airflow3MovedToProvider {
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
#[derive_message_formats]
@@ -1198,42 +1197,34 @@ fn check_names_moved_to_provider(checker: &Checker, expr: &Expr, ranged: TextRan
_ => return,
};
let (module, name) = match &replacement {
ProviderReplacement::AutoImport { module, name, .. } => (module, *name),
let mut diagnostic = Diagnostic::new(
Airflow3MovedToProvider {
deprecated: qualified_name.to_string(),
replacement: replacement.clone(),
},
ranged.range(),
);
let semantic = checker.semantic();
if let Some((module, name)) = match &replacement {
ProviderReplacement::AutoImport { module, name, .. } => Some((module, *name)),
ProviderReplacement::SourceModuleMovedToProvider { module, name, .. } => {
(module, name.as_str())
Some((module, name.as_str()))
}
ProviderReplacement::None => {
checker.report_diagnostic(
Airflow3MovedToProvider {
deprecated: qualified_name,
replacement,
},
ranged,
);
ProviderReplacement::None => None,
} {
if is_guarded_by_try_except(expr, module, name, semantic) {
return;
}
};
if is_guarded_by_try_except(expr, module, name, checker.semantic()) {
return;
}
checker
.report_diagnostic(
Airflow3MovedToProvider {
deprecated: qualified_name,
replacement: replacement.clone(),
},
ranged,
)
.try_set_fix(|| {
diagnostic.try_set_fix(|| {
let (import_edit, binding) = checker.importer().get_or_import_symbol(
&ImportRequest::import_from(module, name),
expr.start(),
checker.semantic(),
)?;
let replacement_edit = Edit::range_replacement(binding, ranged);
let replacement_edit = Edit::range_replacement(binding, ranged.range());
Ok(Fix::safe_edits(import_edit, [replacement_edit]))
});
}
checker.report_diagnostic(diagnostic);
}

View File

@@ -3,7 +3,7 @@ use crate::importer::ImportRequest;
use crate::rules::airflow::helpers::{
Replacement, is_airflow_builtin_or_provider, is_guarded_by_try_except,
};
use crate::{Edit, Fix, FixAvailability, Violation};
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::helpers::map_callable;
use ruff_python_ast::{
@@ -166,13 +166,13 @@ fn check_function_parameters(checker: &Checker, function_def: &StmtFunctionDef)
for param in function_def.parameters.iter_non_variadic_params() {
let param_name = param.name();
if REMOVED_CONTEXT_KEYS.contains(&param_name.as_str()) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
Airflow3Removal {
deprecated: param_name.to_string(),
replacement: Replacement::None,
},
param_name.range(),
);
));
}
}
}
@@ -200,7 +200,7 @@ fn check_call_arguments(checker: &Checker, qualified_name: &QualifiedName, argum
segments => {
if is_airflow_auth_manager(segments) {
if !arguments.is_empty() {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
Airflow3Removal {
deprecated: String::from("appbuilder"),
replacement: Replacement::Message(
@@ -208,7 +208,7 @@ fn check_call_arguments(checker: &Checker, qualified_name: &QualifiedName, argum
),
},
arguments.range(),
);
));
}
} else if is_airflow_task_handler(segments) {
diagnostic_for_argument(checker, arguments, "filename_template", None);
@@ -305,7 +305,7 @@ fn check_class_attribute(checker: &Checker, attribute_expr: &ExprAttribute) {
} else {
None
};
let mut diagnostic = checker.report_diagnostic(
let mut diagnostic = Diagnostic::new(
Airflow3Removal {
deprecated: attr.to_string(),
replacement,
@@ -315,6 +315,7 @@ fn check_class_attribute(checker: &Checker, attribute_expr: &ExprAttribute) {
if let Some(fix) = fix {
diagnostic.set_fix(fix);
}
checker.report_diagnostic(diagnostic);
}
/// Checks whether an Airflow 3.0removed context key is used in a function decorated with `@task`.
@@ -381,13 +382,13 @@ fn check_context_key_usage_in_call(checker: &Checker, call_expr: &ExprCall) {
continue;
};
if value == removed_key {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
Airflow3Removal {
deprecated: removed_key.to_string(),
replacement: Replacement::None,
},
*range,
);
));
}
}
}
@@ -422,13 +423,13 @@ fn check_context_key_usage_in_subscript(checker: &Checker, subscript: &ExprSubsc
}
if REMOVED_CONTEXT_KEYS.contains(&key.to_str()) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
Airflow3Removal {
deprecated: key.to_string(),
replacement: Replacement::None,
},
slice.range(),
);
));
}
}
@@ -536,7 +537,7 @@ fn check_method(checker: &Checker, call_expr: &ExprCall) {
None
};
let mut diagnostic = checker.report_diagnostic(
let mut diagnostic = Diagnostic::new(
Airflow3Removal {
deprecated: attr.to_string(),
replacement,
@@ -546,6 +547,7 @@ fn check_method(checker: &Checker, call_expr: &ExprCall) {
if let Some(fix) = fix {
diagnostic.set_fix(fix);
}
checker.report_diagnostic(diagnostic);
}
/// Check whether a removed Airflow name is used.
@@ -964,36 +966,26 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
_ => return,
};
let (module, name) = match &replacement {
Replacement::AutoImport { module, name } => (module, *name),
Replacement::SourceModuleMoved { module, name } => (module, name.as_str()),
_ => {
checker.report_diagnostic(
Airflow3Removal {
deprecated: qualified_name.to_string(),
replacement: replacement.clone(),
},
range,
);
let mut diagnostic = Diagnostic::new(
Airflow3Removal {
deprecated: qualified_name.to_string(),
replacement: replacement.clone(),
},
range,
);
let semantic = checker.semantic();
if let Some((module, name)) = match &replacement {
Replacement::AutoImport { module, name } => Some((module, *name)),
Replacement::SourceModuleMoved { module, name } => Some((module, name.as_str())),
_ => None,
} {
if is_guarded_by_try_except(expr, module, name, semantic) {
return;
}
};
if is_guarded_by_try_except(expr, module, name, checker.semantic()) {
return;
}
let import_target = name.split('.').next().unwrap_or(name);
let import_target = name.split('.').next().unwrap_or(name);
checker
.report_diagnostic(
Airflow3Removal {
deprecated: qualified_name.to_string(),
replacement: replacement.clone(),
},
range,
)
.try_set_fix(|| {
diagnostic.try_set_fix(|| {
let (import_edit, _) = checker.importer().get_or_import_symbol(
&ImportRequest::import_from(module, import_target),
expr.start(),
@@ -1002,6 +994,9 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
let replacement_edit = Edit::range_replacement(name.to_string(), range);
Ok(Fix::safe_edits(import_edit, [replacement_edit]))
});
}
checker.report_diagnostic(diagnostic);
}
/// Check whether a customized Airflow plugin contains removed extensions.
@@ -1033,7 +1028,7 @@ fn check_airflow_plugin_extension(
)
})
}) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
Airflow3Removal {
deprecated: name.to_string(),
replacement: Replacement::Message(
@@ -1041,7 +1036,7 @@ fn check_airflow_plugin_extension(
),
},
expr.range(),
);
));
}
}
}
@@ -1057,11 +1052,7 @@ fn diagnostic_for_argument(
let Some(keyword) = arguments.find_keyword(deprecated) else {
return;
};
let range = keyword
.arg
.as_ref()
.map_or_else(|| keyword.range(), Ranged::range);
let mut diagnostic = checker.report_diagnostic(
let mut diagnostic = Diagnostic::new(
Airflow3Removal {
deprecated: deprecated.to_string(),
replacement: match replacement {
@@ -1069,15 +1060,20 @@ fn diagnostic_for_argument(
None => Replacement::None,
},
},
range,
keyword
.arg
.as_ref()
.map_or_else(|| keyword.range(), Ranged::range),
);
if let Some(replacement) = replacement {
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
replacement.to_string(),
range,
diagnostic.range,
)));
}
checker.report_diagnostic(diagnostic);
}
/// Check whether the symbol is coming from the `secrets` builtin or provider module which ends

View File

@@ -1,9 +1,8 @@
use crate::importer::ImportRequest;
use crate::rules::airflow::helpers::{ProviderReplacement, is_guarded_by_try_except};
use crate::{Edit, Fix, FixAvailability, Violation};
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::name::QualifiedName;
use ruff_python_ast::{Expr, ExprAttribute};
use ruff_python_semantic::Modules;
use ruff_text_size::Ranged;
@@ -31,12 +30,12 @@ use crate::checkers::ast::Checker;
/// from airflow.providers.standard.operators.python import PythonOperator
/// ```
#[derive(ViolationMetadata)]
pub(crate) struct Airflow3SuggestedToMoveToProvider<'a> {
deprecated: QualifiedName<'a>,
pub(crate) struct Airflow3SuggestedToMoveToProvider {
deprecated: String,
replacement: ProviderReplacement,
}
impl Violation for Airflow3SuggestedToMoveToProvider<'_> {
impl Violation for Airflow3SuggestedToMoveToProvider {
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
#[derive_message_formats]
fn message(&self) -> String {
@@ -282,36 +281,26 @@ fn check_names_moved_to_provider(checker: &Checker, expr: &Expr, ranged: TextRan
_ => return,
};
let (module, name) = match &replacement {
ProviderReplacement::AutoImport { module, name, .. } => (module, *name),
let mut diagnostic = Diagnostic::new(
Airflow3SuggestedToMoveToProvider {
deprecated: qualified_name.to_string(),
replacement: replacement.clone(),
},
ranged.range(),
);
let semantic = checker.semantic();
if let Some((module, name)) = match &replacement {
ProviderReplacement::AutoImport { module, name, .. } => Some((module, *name)),
ProviderReplacement::SourceModuleMovedToProvider { module, name, .. } => {
(module, name.as_str())
Some((module, name.as_str()))
}
ProviderReplacement::None => {
checker.report_diagnostic(
Airflow3SuggestedToMoveToProvider {
deprecated: qualified_name,
replacement: replacement.clone(),
},
ranged.range(),
);
ProviderReplacement::None => None,
} {
if is_guarded_by_try_except(expr, module, name, semantic) {
return;
}
};
if is_guarded_by_try_except(expr, module, name, checker.semantic()) {
return;
}
checker
.report_diagnostic(
Airflow3SuggestedToMoveToProvider {
deprecated: qualified_name,
replacement: replacement.clone(),
},
ranged.range(),
)
.try_set_fix(|| {
diagnostic.try_set_fix(|| {
let (import_edit, binding) = checker.importer().get_or_import_symbol(
&ImportRequest::import_from(module, name),
expr.start(),
@@ -320,4 +309,7 @@ fn check_names_moved_to_provider(checker: &Checker, expr: &Expr, ranged: TextRan
let replacement_edit = Edit::range_replacement(binding, ranged.range());
Ok(Fix::safe_edits(import_edit, [replacement_edit]))
});
}
checker.report_diagnostic(diagnostic);
}

View File

@@ -3,7 +3,7 @@ use crate::importer::ImportRequest;
use crate::rules::airflow::helpers::{
Replacement, is_airflow_builtin_or_provider, is_guarded_by_try_except,
};
use crate::{Edit, Fix, FixAvailability, Violation};
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{Arguments, Expr, ExprAttribute, ExprCall, ExprName, name::QualifiedName};
use ruff_python_semantic::Modules;
@@ -120,11 +120,7 @@ fn diagnostic_for_argument(
let Some(keyword) = arguments.find_keyword(deprecated) else {
return;
};
let range = keyword
.arg
.as_ref()
.map_or_else(|| keyword.range(), Ranged::range);
let mut diagnostic = checker.report_diagnostic(
let mut diagnostic = Diagnostic::new(
Airflow3SuggestedUpdate {
deprecated: deprecated.to_string(),
replacement: match replacement {
@@ -132,15 +128,20 @@ fn diagnostic_for_argument(
None => Replacement::None,
},
},
range,
keyword
.arg
.as_ref()
.map_or_else(|| keyword.range(), Ranged::range),
);
if let Some(replacement) = replacement {
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
replacement.to_string(),
range,
diagnostic.range,
)));
}
checker.report_diagnostic(diagnostic);
}
/// Check whether a removed Airflow argument is passed.
///
@@ -283,34 +284,24 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
_ => return,
};
let (module, name) = match &replacement {
Replacement::AutoImport { module, name } => (module, *name),
Replacement::SourceModuleMoved { module, name } => (module, name.as_str()),
_ => {
checker.report_diagnostic(
Airflow3SuggestedUpdate {
deprecated: qualified_name.to_string(),
replacement: replacement.clone(),
},
range,
);
let mut diagnostic = Diagnostic::new(
Airflow3SuggestedUpdate {
deprecated: qualified_name.to_string(),
replacement: replacement.clone(),
},
range,
);
let semantic = checker.semantic();
if let Some((module, name)) = match &replacement {
Replacement::AutoImport { module, name } => Some((module, *name)),
Replacement::SourceModuleMoved { module, name } => Some((module, name.as_str())),
_ => None,
} {
if is_guarded_by_try_except(expr, module, name, semantic) {
return;
}
};
if is_guarded_by_try_except(expr, module, name, checker.semantic()) {
return;
}
checker
.report_diagnostic(
Airflow3SuggestedUpdate {
deprecated: qualified_name.to_string(),
replacement: replacement.clone(),
},
range,
)
.try_set_fix(|| {
diagnostic.try_set_fix(|| {
let (import_edit, binding) = checker.importer().get_or_import_symbol(
&ImportRequest::import_from(module, name),
expr.start(),
@@ -319,4 +310,7 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
let replacement_edit = Edit::range_replacement(binding, range);
Ok(Fix::safe_edits(import_edit, [replacement_edit]))
});
}
checker.report_diagnostic(diagnostic);
}

View File

@@ -1,4 +1,4 @@
use crate::Violation;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast as ast;
use ruff_python_ast::Expr;
@@ -110,10 +110,11 @@ pub(crate) fn variable_name_task_id(checker: &Checker, targets: &[Expr], value:
return;
}
checker.report_diagnostic(
let diagnostic = Diagnostic::new(
AirflowVariableNameTaskIdMismatch {
task_id: task_id.to_string(),
},
target.range(),
);
checker.report_diagnostic(diagnostic);
}

View File

@@ -1,3 +1,4 @@
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_trivia::CommentRanges;
use ruff_source_file::{LineRanges, UniversalNewlineIterator};
@@ -5,7 +6,6 @@ use ruff_text_size::TextRange;
use crate::Locator;
use crate::settings::LinterSettings;
use crate::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use super::super::detection::comment_contains_code;

View File

@@ -1,3 +1,4 @@
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast as ast;
use ruff_python_ast::helpers::map_callable;
@@ -6,7 +7,6 @@ use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker;
use crate::rules::fastapi::rules::is_fastapi_route;
use crate::{Edit, Fix, FixAvailability, Violation};
use ruff_python_ast::PythonVersion;
/// ## What it does
@@ -237,7 +237,7 @@ fn create_diagnostic(
return seen_default;
};
let mut diagnostic = checker.report_diagnostic(
let mut diagnostic = Diagnostic::new(
FastApiNonAnnotatedDependency {
py_version: checker.target_version(),
},
@@ -308,5 +308,7 @@ fn create_diagnostic(
}
diagnostic.try_set_optional_fix(|| fix);
checker.report_diagnostic(diagnostic);
seen_default
}

View File

@@ -1,3 +1,4 @@
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Fix};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{Decorator, Expr, ExprCall, Keyword, StmtFunctionDef};
use ruff_python_semantic::{Modules, SemanticModel};
@@ -6,7 +7,6 @@ use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::fix::edits::{Parentheses, remove_argument};
use crate::rules::fastapi::rules::is_fastapi_route_decorator;
use crate::{AlwaysFixableViolation, Fix};
/// ## What it does
/// Checks for FastAPI routes that use the optional `response_model` parameter
@@ -85,7 +85,7 @@ pub(crate) fn fastapi_redundant_response_model(checker: &Checker, function_def:
continue;
};
let mut diagnostic =
checker.report_diagnostic(FastApiRedundantResponseModel, response_model_arg.range());
Diagnostic::new(FastApiRedundantResponseModel, response_model_arg.range());
diagnostic.try_set_fix(|| {
remove_argument(
response_model_arg,
@@ -95,6 +95,7 @@ pub(crate) fn fastapi_redundant_response_model(checker: &Checker, function_def:
)
.map(Fix::unsafe_edit)
});
checker.report_diagnostic(diagnostic);
}
}

View File

@@ -2,6 +2,8 @@ use std::iter::Peekable;
use std::ops::Range;
use std::str::CharIndices;
use ruff_diagnostics::Fix;
use ruff_diagnostics::{Diagnostic, FixAvailability, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast as ast;
use ruff_python_ast::{Arguments, Expr, ExprCall, ExprSubscript, Parameter, ParameterWithDefault};
@@ -9,11 +11,9 @@ use ruff_python_semantic::{BindingKind, Modules, ScopeKind, SemanticModel};
use ruff_python_stdlib::identifiers::is_identifier;
use ruff_text_size::{Ranged, TextSize};
use crate::Fix;
use crate::checkers::ast::Checker;
use crate::fix::edits::add_parameter;
use crate::rules::fastapi::rules::is_fastapi_route_decorator;
use crate::{FixAvailability, Violation};
/// ## What it does
/// Identifies FastAPI routes that declare path parameters in the route path
@@ -186,7 +186,7 @@ pub(crate) fn fastapi_unused_path_parameter(
.iter()
.any(|param| param.name() == path_param);
let mut diagnostic = checker.report_diagnostic(
let mut diagnostic = Diagnostic::new(
FastApiUnusedPathParameter {
arg_name: path_param.to_string(),
function_name: function_def.name.to_string(),
@@ -204,6 +204,7 @@ pub(crate) fn fastapi_unused_path_parameter(
checker.locator().contents(),
)));
}
checker.report_diagnostic(diagnostic);
}
}

View File

@@ -1,8 +1,8 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast, CmpOp, Expr};
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
use crate::registry::Rule;
@@ -254,12 +254,12 @@ pub(crate) fn compare(checker: &Checker, left: &Expr, ops: &[CmpOp], comparators
) = (ops, comparators)
{
if *n == 3 && checker.enabled(Rule::SysVersionInfo0Eq3) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
SysVersionInfo0Eq3 {
eq: matches!(*operator, CmpOp::Eq),
},
left.range(),
);
));
}
}
} else if *i == 1 {
@@ -274,7 +274,10 @@ pub(crate) fn compare(checker: &Checker, left: &Expr, ops: &[CmpOp], comparators
) = (ops, comparators)
{
if checker.enabled(Rule::SysVersionInfo1CmpInt) {
checker.report_diagnostic(SysVersionInfo1CmpInt, left.range());
checker.report_diagnostic(Diagnostic::new(
SysVersionInfo1CmpInt,
left.range(),
));
}
}
}
@@ -295,7 +298,10 @@ pub(crate) fn compare(checker: &Checker, left: &Expr, ops: &[CmpOp], comparators
) = (ops, comparators)
{
if checker.enabled(Rule::SysVersionInfoMinorCmpInt) {
checker.report_diagnostic(SysVersionInfoMinorCmpInt, left.range());
checker.report_diagnostic(Diagnostic::new(
SysVersionInfoMinorCmpInt,
left.range(),
));
}
}
}
@@ -311,10 +317,10 @@ pub(crate) fn compare(checker: &Checker, left: &Expr, ops: &[CmpOp], comparators
{
if value.len() == 1 {
if checker.enabled(Rule::SysVersionCmpStr10) {
checker.report_diagnostic(SysVersionCmpStr10, left.range());
checker.report_diagnostic(Diagnostic::new(SysVersionCmpStr10, left.range()));
}
} else if checker.enabled(Rule::SysVersionCmpStr3) {
checker.report_diagnostic(SysVersionCmpStr3, left.range());
checker.report_diagnostic(Diagnostic::new(SysVersionCmpStr3, left.range()));
}
}
}

View File

@@ -1,9 +1,9 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::Expr;
use ruff_python_semantic::Modules;
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -56,6 +56,6 @@ pub(crate) fn name_or_attribute(checker: &Checker, expr: &Expr) {
.resolve_qualified_name(expr)
.is_some_and(|qualified_name| matches!(qualified_name.segments(), ["six", "PY3"]))
{
checker.report_diagnostic(SixPY3, expr.range());
checker.report_diagnostic(Diagnostic::new(SixPY3, expr.range()));
}
}

View File

@@ -1,8 +1,8 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast, Expr};
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
use crate::registry::Rule;
use crate::rules::flake8_2020::helpers::is_sys;
@@ -183,9 +183,9 @@ pub(crate) fn subscript(checker: &Checker, value: &Expr, slice: &Expr) {
}) = upper.as_ref()
{
if *i == 1 && checker.enabled(Rule::SysVersionSlice1) {
checker.report_diagnostic(SysVersionSlice1, value.range());
checker.report_diagnostic(Diagnostic::new(SysVersionSlice1, value.range()));
} else if *i == 3 && checker.enabled(Rule::SysVersionSlice3) {
checker.report_diagnostic(SysVersionSlice3, value.range());
checker.report_diagnostic(Diagnostic::new(SysVersionSlice3, value.range()));
}
}
}
@@ -195,9 +195,9 @@ pub(crate) fn subscript(checker: &Checker, value: &Expr, slice: &Expr) {
..
}) => {
if *i == 2 && checker.enabled(Rule::SysVersion2) {
checker.report_diagnostic(SysVersion2, value.range());
checker.report_diagnostic(Diagnostic::new(SysVersion2, value.range()));
} else if *i == 0 && checker.enabled(Rule::SysVersion0) {
checker.report_diagnostic(SysVersion0, value.range());
checker.report_diagnostic(Diagnostic::new(SysVersion0, value.range()));
}
}

View File

@@ -1,6 +1,7 @@
use itertools::Itertools;
use rustc_hash::FxHashSet;
use ruff_diagnostics::Edit;
use ruff_python_ast::helpers::{
ReturnStatementVisitor, pep_604_union, typing_optional, typing_union,
};
@@ -13,7 +14,6 @@ use ruff_python_semantic::analyze::visibility;
use ruff_python_semantic::{Definition, SemanticModel};
use ruff_text_size::{TextRange, TextSize};
use crate::Edit;
use crate::checkers::ast::Checker;
use ruff_python_ast::PythonVersion;

View File

@@ -1,3 +1,4 @@
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::helpers::ReturnStatementVisitor;
use ruff_python_ast::identifier::Identifier;
@@ -8,11 +9,10 @@ use ruff_python_semantic::analyze::visibility;
use ruff_python_stdlib::typing::simple_magic_return_type;
use ruff_text_size::Ranged;
use crate::checkers::ast::{Checker, DiagnosticGuard};
use crate::checkers::ast::Checker;
use crate::registry::Rule;
use crate::rules::flake8_annotations::helpers::auto_return_type;
use crate::rules::ruff::typing::type_hint_resolves_to_any;
use crate::{Edit, Fix, FixAvailability, Violation};
/// ## What it does
/// Checks that function arguments have type annotations.
@@ -529,11 +529,11 @@ fn is_none_returning(body: &[Stmt]) -> bool {
}
/// ANN401
fn check_dynamically_typed<'a, 'b, F>(
checker: &'a Checker<'b>,
fn check_dynamically_typed<F>(
checker: &Checker,
annotation: &Expr,
func: F,
diagnostics: &mut Vec<DiagnosticGuard<'a, 'b>>,
diagnostics: &mut Vec<Diagnostic>,
) where
F: FnOnce() -> String,
{
@@ -545,14 +545,18 @@ fn check_dynamically_typed<'a, 'b, F>(
checker,
checker.target_version(),
) {
diagnostics
.push(checker.report_diagnostic(AnyType { name: func() }, annotation.range()));
diagnostics.push(Diagnostic::new(
AnyType { name: func() },
annotation.range(),
));
}
}
} else {
if type_hint_resolves_to_any(annotation, checker, checker.target_version()) {
diagnostics
.push(checker.report_diagnostic(AnyType { name: func() }, annotation.range()));
diagnostics.push(Diagnostic::new(
AnyType { name: func() },
annotation.range(),
));
}
}
}
@@ -651,7 +655,7 @@ pub(crate) fn definition(
.is_match(parameter.name()))
{
if checker.enabled(Rule::MissingTypeFunctionArgument) {
diagnostics.push(checker.report_diagnostic(
diagnostics.push(Diagnostic::new(
MissingTypeFunctionArgument {
name: parameter.name().to_string(),
},
@@ -677,7 +681,7 @@ pub(crate) fn definition(
&& checker.settings.dummy_variable_rgx.is_match(&arg.name))
{
if checker.enabled(Rule::MissingTypeArgs) {
diagnostics.push(checker.report_diagnostic(
diagnostics.push(Diagnostic::new(
MissingTypeArgs {
name: arg.name.to_string(),
},
@@ -708,7 +712,7 @@ pub(crate) fn definition(
&& checker.settings.dummy_variable_rgx.is_match(&arg.name))
{
if checker.enabled(Rule::MissingTypeKwargs) {
diagnostics.push(checker.report_diagnostic(
diagnostics.push(Diagnostic::new(
MissingTypeKwargs {
name: arg.name.to_string(),
},
@@ -741,7 +745,7 @@ pub(crate) fn definition(
})
.map(|(return_type, edits)| (checker.generator().expr(&return_type), edits))
};
let mut diagnostic = checker.report_diagnostic(
let mut diagnostic = Diagnostic::new(
MissingReturnTypeClassMethod {
name: name.to_string(),
annotation: return_type.clone().map(|(return_type, ..)| return_type),
@@ -767,7 +771,7 @@ pub(crate) fn definition(
})
.map(|(return_type, edits)| (checker.generator().expr(&return_type), edits))
};
let mut diagnostic = checker.report_diagnostic(
let mut diagnostic = Diagnostic::new(
MissingReturnTypeStaticMethod {
name: name.to_string(),
annotation: return_type.clone().map(|(return_type, ..)| return_type),
@@ -787,7 +791,7 @@ pub(crate) fn definition(
// least one argument is typed.
if checker.enabled(Rule::MissingReturnTypeSpecialMethod) {
if !(checker.settings.flake8_annotations.mypy_init_return && has_any_typed_arg) {
let mut diagnostic = checker.report_diagnostic(
let mut diagnostic = Diagnostic::new(
MissingReturnTypeSpecialMethod {
name: name.to_string(),
annotation: Some("None".to_string()),
@@ -804,7 +808,7 @@ pub(crate) fn definition(
} else if is_method && visibility::is_magic(name) {
if checker.enabled(Rule::MissingReturnTypeSpecialMethod) {
let return_type = simple_magic_return_type(name);
let mut diagnostic = checker.report_diagnostic(
let mut diagnostic = Diagnostic::new(
MissingReturnTypeSpecialMethod {
name: name.to_string(),
annotation: return_type.map(ToString::to_string),
@@ -835,7 +839,7 @@ pub(crate) fn definition(
(checker.generator().expr(&return_type), edits)
})
};
let mut diagnostic = checker.report_diagnostic(
let mut diagnostic = Diagnostic::new(
MissingReturnTypeUndocumentedPublicFunction {
name: name.to_string(),
annotation: return_type
@@ -870,7 +874,7 @@ pub(crate) fn definition(
(checker.generator().expr(&return_type), edits)
})
};
let mut diagnostic = checker.report_diagnostic(
let mut diagnostic = Diagnostic::new(
MissingReturnTypePrivateFunction {
name: name.to_string(),
annotation: return_type
@@ -897,7 +901,7 @@ pub(crate) fn definition(
// If settings say so, don't report any of the
// diagnostics gathered here if there were no type annotations at all.
let diagnostics_enabled = !checker.settings.flake8_annotations.ignore_fully_untyped
if !checker.settings.flake8_annotations.ignore_fully_untyped
|| has_any_typed_arg
|| has_typed_return
|| (is_method
@@ -906,11 +910,10 @@ pub(crate) fn definition(
.posonlyargs
.first()
.or_else(|| parameters.args.first())
.is_some_and(|first_param| first_param.annotation().is_some()));
if !diagnostics_enabled {
.is_some_and(|first_param| first_param.annotation().is_some()))
{
for diagnostic in diagnostics {
diagnostic.defuse();
checker.report_diagnostic(diagnostic);
}
}
}

View File

@@ -1,8 +1,8 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast, Expr, Stmt};
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
use crate::rules::flake8_async::helpers::AsyncModule;
@@ -74,11 +74,11 @@ pub(crate) fn async_busy_wait(checker: &Checker, while_stmt: &ast::StmtWhile) {
qualified_name.segments(),
["trio" | "anyio", "sleep" | "sleep_until"] | ["asyncio", "sleep"]
) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
AsyncBusyWait {
module: AsyncModule::try_from(&qualified_name).unwrap(),
},
while_stmt.range(),
);
));
}
}

View File

@@ -1,9 +1,9 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast as ast;
use ruff_python_semantic::Modules;
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
use crate::rules::flake8_async::helpers::AsyncModule;
use ruff_python_ast::PythonVersion;
@@ -112,5 +112,8 @@ pub(crate) fn async_function_with_timeout(checker: &Checker, function_def: &ast:
return;
}
checker.report_diagnostic(AsyncFunctionWithTimeout { module }, timeout.range());
checker.report_diagnostic(Diagnostic::new(
AsyncFunctionWithTimeout { module },
timeout.range(),
));
}

View File

@@ -1,3 +1,4 @@
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast, Expr, ExprCall, Int, Number};
use ruff_python_semantic::Modules;
@@ -6,7 +7,6 @@ use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::importer::ImportRequest;
use crate::rules::flake8_async::helpers::AsyncModule;
use crate::{AlwaysFixableViolation, Edit, Fix};
/// ## What it does
/// Checks for uses of `trio.sleep(0)` or `anyio.sleep(0)`.
@@ -109,7 +109,7 @@ pub(crate) fn async_zero_sleep(checker: &Checker, call: &ExprCall) {
return;
}
let mut diagnostic = checker.report_diagnostic(AsyncZeroSleep { module }, call.range());
let mut diagnostic = Diagnostic::new(AsyncZeroSleep { module }, call.range());
diagnostic.try_set_fix(|| {
let (import_edit, binding) = checker.importer().get_or_import_symbol(
&ImportRequest::import_from(&module.to_string(), "lowlevel"),
@@ -121,5 +121,6 @@ pub(crate) fn async_zero_sleep(checker: &Checker, call: &ExprCall) {
let arg_edit = Edit::range_replacement("()".to_string(), call.arguments.range());
Ok(Fix::safe_edits(import_edit, [reference_edit, arg_edit]))
});
checker.report_diagnostic(diagnostic);
}
}

View File

@@ -1,10 +1,10 @@
use ruff_python_ast::ExprCall;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::name::QualifiedName;
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -70,7 +70,10 @@ pub(crate) fn blocking_http_call(checker: &Checker, call: &ExprCall) {
.as_ref()
.is_some_and(is_blocking_http_call)
{
checker.report_diagnostic(BlockingHttpCallInAsyncFunction, call.func.range());
checker.report_diagnostic(Diagnostic::new(
BlockingHttpCallInAsyncFunction,
call.func.range(),
));
}
}
}

View File

@@ -1,9 +1,9 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast, Expr};
use ruff_python_semantic::{SemanticModel, analyze};
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -52,7 +52,10 @@ pub(crate) fn blocking_open_call(checker: &Checker, call: &ast::ExprCall) {
if is_open_call(&call.func, checker.semantic())
|| is_open_call_from_pathlib(call.func.as_ref(), checker.semantic())
{
checker.report_diagnostic(BlockingOpenCallInAsyncFunction, call.func.range());
checker.report_diagnostic(Diagnostic::new(
BlockingOpenCallInAsyncFunction,
call.func.range(),
));
}
}

View File

@@ -1,11 +1,12 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast, Expr};
use ruff_python_semantic::SemanticModel;
use ruff_python_semantic::analyze::typing::find_assigned_value;
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
use crate::registry::AsRule;
/// ## What it does
/// Checks that async functions do not create subprocesses with blocking methods.
@@ -124,17 +125,17 @@ pub(crate) fn blocking_process_invocation(checker: &Checker, call: &ast::ExprCal
};
let range = call.func.range();
match qualified_name.segments() {
let diagnostic = match qualified_name.segments() {
["subprocess", "Popen"] | ["os", "popen"] => {
checker.report_diagnostic_if_enabled(CreateSubprocessInAsyncFunction, range)
Diagnostic::new(CreateSubprocessInAsyncFunction, range)
}
["os", "system" | "posix_spawn" | "posix_spawnp"]
| [
"subprocess",
"run" | "call" | "check_call" | "check_output" | "getoutput" | "getstatusoutput",
] => checker.report_diagnostic_if_enabled(RunProcessInAsyncFunction, range),
] => Diagnostic::new(RunProcessInAsyncFunction, range),
["os", "wait" | "wait3" | "wait4" | "waitid" | "waitpid"] => {
checker.report_diagnostic_if_enabled(WaitForProcessInAsyncFunction, range)
Diagnostic::new(WaitForProcessInAsyncFunction, range)
}
[
"os",
@@ -142,13 +143,17 @@ pub(crate) fn blocking_process_invocation(checker: &Checker, call: &ast::ExprCal
| "spawnvpe",
] => {
if is_p_wait(call, checker.semantic()) {
checker.report_diagnostic_if_enabled(RunProcessInAsyncFunction, range)
Diagnostic::new(RunProcessInAsyncFunction, range)
} else {
checker.report_diagnostic_if_enabled(CreateSubprocessInAsyncFunction, range)
Diagnostic::new(CreateSubprocessInAsyncFunction, range)
}
}
_ => return,
};
if checker.enabled(diagnostic.rule()) {
checker.report_diagnostic(diagnostic);
}
}
fn is_p_wait(call: &ast::ExprCall, semantic: &SemanticModel) -> bool {

View File

@@ -1,10 +1,10 @@
use ruff_python_ast::ExprCall;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::name::QualifiedName;
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -51,7 +51,10 @@ pub(crate) fn blocking_sleep(checker: &Checker, call: &ExprCall) {
.as_ref()
.is_some_and(is_blocking_sleep)
{
checker.report_diagnostic(BlockingSleepInAsyncFunction, call.func.range());
checker.report_diagnostic(Diagnostic::new(
BlockingSleepInAsyncFunction,
call.func.range(),
));
}
}
}

View File

@@ -1,9 +1,9 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::helpers::{AwaitVisitor, any_over_body};
use ruff_python_ast::visitor::Visitor;
use ruff_python_ast::{Expr, StmtWith, WithItem};
use crate::Violation;
use crate::checkers::ast::Checker;
use crate::rules::flake8_async::helpers::MethodName;
@@ -100,5 +100,8 @@ pub(crate) fn cancel_scope_no_checkpoint(
return;
}
checker.report_diagnostic(CancelScopeNoCheckpoint { method_name }, with_stmt.range);
checker.report_diagnostic(Diagnostic::new(
CancelScopeNoCheckpoint { method_name },
with_stmt.range,
));
}

View File

@@ -1,3 +1,4 @@
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{Expr, ExprCall, ExprNumberLiteral, Number};
use ruff_python_semantic::Modules;
@@ -6,7 +7,6 @@ use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::importer::ImportRequest;
use crate::rules::flake8_async::helpers::AsyncModule;
use crate::{Edit, Fix, FixAvailability, Violation};
/// ## What it does
/// Checks for uses of `trio.sleep()` or `anyio.sleep()` with a delay greater than 24 hours.
@@ -137,7 +137,7 @@ pub(crate) fn long_sleep_not_forever(checker: &Checker, call: &ExprCall) {
return;
}
let mut diagnostic = checker.report_diagnostic(LongSleepNotForever { module }, call.range());
let mut diagnostic = Diagnostic::new(LongSleepNotForever { module }, call.range());
let replacement_function = "sleep_forever";
diagnostic.try_set_fix(|| {
let (import_edit, binding) = checker.importer().get_or_import_symbol(
@@ -149,4 +149,5 @@ pub(crate) fn long_sleep_not_forever(checker: &Checker, call: &ExprCall) {
let arg_edit = Edit::range_replacement("()".to_string(), call.arguments.range());
Ok(Fix::unsafe_edits(import_edit, [reference_edit, arg_edit]))
});
checker.report_diagnostic(diagnostic);
}

View File

@@ -1,3 +1,4 @@
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{Expr, ExprCall};
use ruff_python_semantic::Modules;
@@ -6,7 +7,6 @@ use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker;
use crate::fix::edits::pad;
use crate::rules::flake8_async::helpers::MethodName;
use crate::{Edit, Fix, FixAvailability, Violation};
/// ## What it does
/// Checks for calls to trio functions that are not immediately awaited.
@@ -80,7 +80,7 @@ pub(crate) fn sync_call(checker: &Checker, call: &ExprCall) {
return;
}
let mut diagnostic = checker.report_diagnostic(TrioSyncCall { method_name }, call.range);
let mut diagnostic = Diagnostic::new(TrioSyncCall { method_name }, call.range);
if checker.semantic().in_async_context() {
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
pad(
@@ -91,4 +91,5 @@ pub(crate) fn sync_call(checker: &Checker, call: &ExprCall) {
call.func.start(),
)));
}
checker.report_diagnostic(diagnostic);
}

View File

@@ -1,11 +1,9 @@
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::Stmt;
use ruff_text_size::Ranged;
use ruff_text_size::{TextLen, TextRange};
use crate::Violation;
use crate::checkers::ast::Checker;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_text_size::Ranged;
/// ## What it does
/// Checks for uses of the `assert` keyword.
@@ -43,6 +41,6 @@ impl Violation for Assert {
}
/// S101
pub(crate) fn assert_used(checker: &Checker, stmt: &Stmt) {
checker.report_diagnostic(Assert, TextRange::at(stmt.start(), "assert".text_len()));
pub(crate) fn assert_used(stmt: &Stmt) -> Diagnostic {
Diagnostic::new(Assert, TextRange::at(stmt.start(), "assert".text_len()))
}

View File

@@ -1,12 +1,12 @@
use anyhow::Result;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::name::QualifiedName;
use ruff_python_ast::{self as ast, Expr, Operator};
use ruff_python_semantic::{Modules, SemanticModel};
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -78,22 +78,22 @@ pub(crate) fn bad_file_permissions(checker: &Checker, call: &ast::ExprCall) {
// The mask is a valid integer value -- check for overly permissive permissions.
Ok(Some(mask)) => {
if (mask & WRITE_WORLD > 0) || (mask & EXECUTE_GROUP > 0) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
BadFilePermissions {
reason: Reason::Permissive(mask),
},
mode_arg.range(),
);
));
}
}
// The mask is an invalid integer value (i.e., it's out of range).
Err(_) => {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
BadFilePermissions {
reason: Reason::Invalid,
},
mode_arg.range(),
);
));
}
}
}

View File

@@ -1,8 +1,8 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast, Expr, ExprAttribute};
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -54,7 +54,7 @@ pub(crate) fn django_extra(checker: &Checker, call: &ast::ExprCall) {
}
if is_call_insecure(call) {
checker.report_diagnostic(DjangoExtra, call.arguments.range());
checker.report_diagnostic(Diagnostic::new(DjangoExtra, call.arguments.range()));
}
}

View File

@@ -1,9 +1,9 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast, Expr};
use ruff_python_semantic::Modules;
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -55,7 +55,7 @@ pub(crate) fn django_raw_sql(checker: &Checker, call: &ast::ExprCall) {
.find_argument_value("sql", 0)
.is_some_and(Expr::is_string_literal_expr)
{
checker.report_diagnostic(DjangoRawSql, call.func.range());
checker.report_diagnostic(Diagnostic::new(DjangoRawSql, call.func.range()));
}
}
}

View File

@@ -1,9 +1,9 @@
use ruff_python_ast::Expr;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -34,6 +34,6 @@ impl Violation for ExecBuiltin {
/// S102
pub(crate) fn exec_used(checker: &Checker, func: &Expr) {
if checker.semantic().match_builtin_expr(func, "exec") {
checker.report_diagnostic(ExecBuiltin, func.range());
checker.report_diagnostic(Diagnostic::new(ExecBuiltin, func.range()));
}
}

View File

@@ -1,10 +1,10 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::helpers::is_const_true;
use ruff_python_ast::{Expr, ExprAttribute, ExprCall};
use ruff_python_semantic::analyze::typing;
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -67,6 +67,6 @@ pub(crate) fn flask_debug_true(checker: &Checker, call: &ExprCall) {
if typing::resolve_assignment(value, checker.semantic())
.is_some_and(|qualified_name| matches!(qualified_name.segments(), ["flask", "Flask"]))
{
checker.report_diagnostic(FlaskDebugTrue, debug_argument.range());
checker.report_diagnostic(Diagnostic::new(FlaskDebugTrue, debug_argument.range()));
}
}

View File

@@ -1,8 +1,8 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast, StringLike};
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -41,7 +41,8 @@ pub(crate) fn hardcoded_bind_all_interfaces(checker: &Checker, string: StringLik
match string {
StringLike::String(ast::ExprStringLiteral { value, .. }) => {
if value == "0.0.0.0" {
checker.report_diagnostic(HardcodedBindAllInterfaces, string.range());
checker
.report_diagnostic(Diagnostic::new(HardcodedBindAllInterfaces, string.range()));
}
}
StringLike::FString(ast::ExprFString { value, .. }) => {
@@ -49,14 +50,19 @@ pub(crate) fn hardcoded_bind_all_interfaces(checker: &Checker, string: StringLik
match part {
ast::FStringPart::Literal(literal) => {
if &**literal == "0.0.0.0" {
checker.report_diagnostic(HardcodedBindAllInterfaces, literal.range());
checker.report_diagnostic(Diagnostic::new(
HardcodedBindAllInterfaces,
literal.range(),
));
}
}
ast::FStringPart::FString(f_string) => {
for literal in f_string.elements.literals() {
if &**literal == "0.0.0.0" {
checker
.report_diagnostic(HardcodedBindAllInterfaces, literal.range());
checker.report_diagnostic(Diagnostic::new(
HardcodedBindAllInterfaces,
literal.range(),
));
}
}
}

View File

@@ -1,9 +1,9 @@
use ruff_python_ast::{Expr, Parameter, Parameters};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
use super::super::helpers::{matches_password_name, string_literal};
@@ -54,20 +54,18 @@ impl Violation for HardcodedPasswordDefault {
}
}
fn check_password_kwarg(checker: &Checker, parameter: &Parameter, default: &Expr) {
if string_literal(default).is_none_or(str::is_empty) {
return;
}
fn check_password_kwarg(parameter: &Parameter, default: &Expr) -> Option<Diagnostic> {
string_literal(default).filter(|string| !string.is_empty())?;
let kwarg_name = &parameter.name;
if !matches_password_name(kwarg_name) {
return;
return None;
}
checker.report_diagnostic(
Some(Diagnostic::new(
HardcodedPasswordDefault {
name: kwarg_name.to_string(),
},
default.range(),
);
))
}
/// S107
@@ -76,6 +74,8 @@ pub(crate) fn hardcoded_password_default(checker: &Checker, parameters: &Paramet
let Some(default) = parameter.default() else {
continue;
};
check_password_kwarg(checker, &parameter.parameter, default);
if let Some(diagnostic) = check_password_kwarg(&parameter.parameter, default) {
checker.report_diagnostic(diagnostic);
}
}
}

View File

@@ -1,9 +1,9 @@
use ruff_python_ast::Keyword;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
use super::super::helpers::{matches_password_name, string_literal};
@@ -62,11 +62,11 @@ pub(crate) fn hardcoded_password_func_arg(checker: &Checker, keywords: &[Keyword
if !matches_password_name(arg) {
continue;
}
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
HardcodedPasswordFuncArg {
name: arg.to_string(),
},
keyword.range(),
);
));
}
}

View File

@@ -1,8 +1,8 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast, Expr};
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
use super::super::helpers::{matches_password_name, string_literal};
@@ -83,12 +83,12 @@ pub(crate) fn compare_to_hardcoded_password_string(
let Some(name) = password_target(left) else {
continue;
};
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
HardcodedPasswordString {
name: name.to_string(),
},
comp.range(),
);
));
}
}
@@ -100,12 +100,12 @@ pub(crate) fn assign_hardcoded_password_string(checker: &Checker, value: &Expr,
{
for target in targets {
if let Some(name) = password_target(target) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
HardcodedPasswordString {
name: name.to_string(),
},
value.range(),
);
));
return;
}
}

View File

@@ -2,13 +2,13 @@ use std::sync::LazyLock;
use regex::Regex;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::str::raw_contents;
use ruff_python_ast::{self as ast, Expr, Operator};
use ruff_text_size::Ranged;
use crate::Locator;
use crate::Violation;
use crate::checkers::ast::Checker;
static SQL_REGEX: LazyLock<Regex> = LazyLock::new(|| {
@@ -113,7 +113,7 @@ pub(crate) fn hardcoded_sql_expression(checker: &Checker, expr: &Expr) {
};
if SQL_REGEX.is_match(&content) {
checker.report_diagnostic(HardcodedSQLExpression, expr.range());
checker.report_diagnostic(Diagnostic::new(HardcodedSQLExpression, expr.range()));
}
}

View File

@@ -1,9 +1,9 @@
use ruff_python_ast::{self as ast, Expr, StringLike};
use ruff_text_size::{Ranged, TextRange};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -102,10 +102,10 @@ fn check(checker: &Checker, value: &str, range: TextRange) {
}
}
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
HardcodedTempFile {
string: value.to_string(),
},
range,
);
));
}

View File

@@ -1,10 +1,10 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::helpers::is_const_false;
use ruff_python_ast::{self as ast, Arguments};
use ruff_python_semantic::Modules;
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
use super::super::helpers::string_literal;
@@ -141,23 +141,23 @@ fn detect_insecure_hashlib_calls(
hash_func_name.to_ascii_lowercase().as_str(),
"md4" | "md5" | "sha" | "sha1"
) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
HashlibInsecureHashFunction {
library: "hashlib".to_string(),
string: hash_func_name.to_string(),
},
name_arg.range(),
);
));
}
}
HashlibCall::WeakHash(func_name) => {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
HashlibInsecureHashFunction {
library: "hashlib".to_string(),
string: (*func_name).to_string(),
},
call.func.range(),
);
));
}
}
}
@@ -186,13 +186,13 @@ fn detect_insecure_crypt_calls(checker: &Checker, call: &ast::ExprCall) {
qualified_name.segments(),
["crypt", "METHOD_CRYPT" | "METHOD_MD5" | "METHOD_BLOWFISH"]
) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
HashlibInsecureHashFunction {
library: "crypt".to_string(),
string: qualified_name.to_string(),
},
method.range(),
);
));
}
}

View File

@@ -1,8 +1,8 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast, Expr};
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -70,20 +70,23 @@ pub(crate) fn jinja2_autoescape_false(checker: &Checker, call: &ast::ExprCall) {
Expr::Call(ast::ExprCall { func, .. }) => {
if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
if id != "select_autoescape" {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
Jinja2AutoescapeFalse { value: true },
keyword.range(),
);
));
}
}
}
_ => {
checker
.report_diagnostic(Jinja2AutoescapeFalse { value: true }, keyword.range());
}
_ => checker.report_diagnostic(Diagnostic::new(
Jinja2AutoescapeFalse { value: true },
keyword.range(),
)),
}
} else {
checker.report_diagnostic(Jinja2AutoescapeFalse { value: false }, call.func.range());
checker.report_diagnostic(Diagnostic::new(
Jinja2AutoescapeFalse { value: false },
call.func.range(),
));
}
}
}

View File

@@ -1,9 +1,9 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast};
use ruff_python_semantic::Modules;
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -51,6 +51,9 @@ pub(crate) fn logging_config_insecure_listen(checker: &Checker, call: &ast::Expr
return;
}
checker.report_diagnostic(LoggingConfigInsecureListen, call.func.range());
checker.report_diagnostic(Diagnostic::new(
LoggingConfigInsecureListen,
call.func.range(),
));
}
}

View File

@@ -1,10 +1,9 @@
use crate::checkers::ast::Checker;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast};
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
/// Checks for uses of the `mako` templates.
///
@@ -51,6 +50,6 @@ pub(crate) fn mako_templates(checker: &Checker, call: &ast::ExprCall) {
matches!(qualified_name.segments(), ["mako", "template", "Template"])
})
{
checker.report_diagnostic(MakoTemplates, call.func.range());
checker.report_diagnostic(Diagnostic::new(MakoTemplates, call.func.range()));
}
}

View File

@@ -1,9 +1,9 @@
use ruff_python_ast::Expr;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -45,6 +45,6 @@ pub(crate) fn paramiko_call(checker: &Checker, func: &Expr) {
matches!(qualified_name.segments(), ["paramiko", "exec_command"])
})
{
checker.report_diagnostic(ParamikoCall, func.range());
checker.report_diagnostic(Diagnostic::new(ParamikoCall, func.range()));
}
}

View File

@@ -1,9 +1,9 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast as ast;
use ruff_python_ast::helpers::is_const_false;
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -65,12 +65,12 @@ pub(crate) fn request_with_no_cert_validation(checker: &Checker, call: &ast::Exp
{
if let Some(keyword) = call.arguments.find_keyword("verify") {
if is_const_false(&keyword.value) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
RequestWithNoCertValidation {
string: target.to_string(),
},
keyword.range(),
);
));
}
}
}

View File

@@ -1,9 +1,9 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast as ast;
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -70,22 +70,22 @@ pub(crate) fn request_without_timeout(checker: &Checker, call: &ast::ExprCall) {
{
if let Some(keyword) = call.arguments.find_keyword("timeout") {
if keyword.value.is_none_literal_expr() {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
RequestWithoutTimeout {
implicit: false,
module: module.to_string(),
},
keyword.range(),
);
));
}
} else if module == "requests" {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
RequestWithoutTimeout {
implicit: true,
module: module.to_string(),
},
call.func.range(),
);
));
}
}
}

View File

@@ -1,13 +1,13 @@
//! Checks relating to shell injection.
use crate::preview::is_shell_injection_only_trusted_input_enabled;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::helpers::Truthiness;
use ruff_python_ast::{self as ast, Arguments, Expr};
use ruff_python_semantic::SemanticModel;
use ruff_text_size::Ranged;
use crate::Violation;
use crate::preview::is_shell_injection_only_trusted_input_enabled;
use crate::{
checkers::ast::Checker, registry::Rule, rules::flake8_bandit::helpers::string_literal,
};
@@ -314,13 +314,13 @@ pub(crate) fn shell_injection(checker: &Checker, call: &ast::ExprCall) {
truthiness: truthiness @ (Truthiness::True | Truthiness::Truthy),
}) => {
if checker.enabled(Rule::SubprocessPopenWithShellEqualsTrue) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
SubprocessPopenWithShellEqualsTrue {
safety: Safety::from(arg),
is_exact: matches!(truthiness, Truthiness::True),
},
call.func.range(),
);
));
}
}
// S603
@@ -329,10 +329,10 @@ pub(crate) fn shell_injection(checker: &Checker, call: &ast::ExprCall) {
|| !is_shell_injection_only_trusted_input_enabled(checker.settings)
{
if checker.enabled(Rule::SubprocessWithoutShellEqualsTrue) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
SubprocessWithoutShellEqualsTrue,
call.func.range(),
);
));
}
}
}
@@ -344,12 +344,12 @@ pub(crate) fn shell_injection(checker: &Checker, call: &ast::ExprCall) {
{
// S604
if checker.enabled(Rule::CallWithShellEqualsTrue) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
CallWithShellEqualsTrue {
is_exact: matches!(truthiness, Truthiness::True),
},
call.func.range(),
);
));
}
}
@@ -357,12 +357,12 @@ pub(crate) fn shell_injection(checker: &Checker, call: &ast::ExprCall) {
if checker.enabled(Rule::StartProcessWithAShell) {
if matches!(call_kind, Some(CallKind::Shell)) {
if let Some(arg) = call.arguments.args.first() {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
StartProcessWithAShell {
safety: Safety::from(arg),
},
call.func.range(),
);
));
}
}
}
@@ -370,7 +370,7 @@ pub(crate) fn shell_injection(checker: &Checker, call: &ast::ExprCall) {
// S606
if checker.enabled(Rule::StartProcessWithNoShell) {
if matches!(call_kind, Some(CallKind::NoShell)) {
checker.report_diagnostic(StartProcessWithNoShell, call.func.range());
checker.report_diagnostic(Diagnostic::new(StartProcessWithNoShell, call.func.range()));
}
}
@@ -379,7 +379,10 @@ pub(crate) fn shell_injection(checker: &Checker, call: &ast::ExprCall) {
if call_kind.is_some() {
if let Some(arg) = call.arguments.args.first() {
if is_partial_path(arg) {
checker.report_diagnostic(StartProcessWithPartialPath, arg.range());
checker.report_diagnostic(Diagnostic::new(
StartProcessWithPartialPath,
arg.range(),
));
}
}
}
@@ -400,7 +403,10 @@ pub(crate) fn shell_injection(checker: &Checker, call: &ast::ExprCall) {
{
if let Some(arg) = call.arguments.args.first() {
if is_wildcard_command(arg) {
checker.report_diagnostic(UnixCommandWildcardInjection, arg.range());
checker.report_diagnostic(Diagnostic::new(
UnixCommandWildcardInjection,
arg.range(),
));
}
}
}

View File

@@ -1,8 +1,8 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast, Expr, Int};
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -60,7 +60,7 @@ pub(crate) fn snmp_insecure_version(checker: &Checker, call: &ast::ExprCall) {
..
})
) {
checker.report_diagnostic(SnmpInsecureVersion, keyword.range());
checker.report_diagnostic(Diagnostic::new(SnmpInsecureVersion, keyword.range()));
}
}
}

View File

@@ -1,8 +1,8 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast};
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -52,7 +52,7 @@ pub(crate) fn snmp_weak_cryptography(checker: &Checker, call: &ast::ExprCall) {
)
})
{
checker.report_diagnostic(SnmpWeakCryptography, call.func.range());
checker.report_diagnostic(Diagnostic::new(SnmpWeakCryptography, call.func.range()));
}
}
}

View File

@@ -1,10 +1,10 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::helpers::map_callable;
use ruff_python_ast::{Expr, ExprAttribute, ExprCall};
use ruff_python_semantic::analyze::typing;
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -78,6 +78,9 @@ pub(crate) fn ssh_no_host_key_verification(checker: &Checker, call: &ExprCall) {
["paramiko", "client", "SSHClient"] | ["paramiko", "SSHClient"]
)
}) {
checker.report_diagnostic(SSHNoHostKeyVerification, policy_argument.range());
checker.report_diagnostic(Diagnostic::new(
SSHNoHostKeyVerification,
policy_argument.range(),
));
}
}

View File

@@ -1,8 +1,8 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast, Expr, ExprCall};
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -68,22 +68,22 @@ pub(crate) fn ssl_insecure_version(checker: &Checker, call: &ExprCall) {
match &keyword.value {
Expr::Name(ast::ExprName { id, .. }) => {
if is_insecure_protocol(id) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
SslInsecureVersion {
protocol: id.to_string(),
},
keyword.range(),
);
));
}
}
Expr::Attribute(ast::ExprAttribute { attr, .. }) => {
if is_insecure_protocol(attr) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
SslInsecureVersion {
protocol: attr.to_string(),
},
keyword.range(),
);
));
}
}
_ => {}

View File

@@ -1,7 +1,7 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast, Expr, StmtFunctionDef};
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -57,22 +57,22 @@ pub(crate) fn ssl_with_bad_defaults(checker: &Checker, function_def: &StmtFuncti
match default {
Expr::Name(ast::ExprName { id, range, .. }) => {
if is_insecure_protocol(id.as_str()) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
SslWithBadDefaults {
protocol: id.to_string(),
},
*range,
);
));
}
}
Expr::Attribute(ast::ExprAttribute { attr, range, .. }) => {
if is_insecure_protocol(attr.as_str()) {
checker.report_diagnostic(
checker.report_diagnostic(Diagnostic::new(
SslWithBadDefaults {
protocol: attr.to_string(),
},
*range,
);
));
}
}
_ => {}

View File

@@ -1,8 +1,8 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::ExprCall;
use ruff_text_size::Ranged;
use crate::Violation;
use crate::checkers::ast::Checker;
/// ## What it does
@@ -43,7 +43,7 @@ pub(crate) fn ssl_with_no_version(checker: &Checker, call: &ExprCall) {
.is_some_and(|qualified_name| matches!(qualified_name.segments(), ["ssl", "wrap_socket"]))
{
if call.arguments.find_keyword("ssl_version").is_none() {
checker.report_diagnostic(SslWithNoVersion, call.range());
checker.report_diagnostic(Diagnostic::new(SslWithNoVersion, call.range()));
}
}
}

View File

@@ -2,13 +2,14 @@
//!
//! See: <https://bandit.readthedocs.io/en/latest/blacklists/blacklist_calls.html>
use itertools::Either;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_python_ast::{self as ast, Arguments, Decorator, Expr, ExprCall, Operator};
use ruff_text_size::{Ranged, TextRange};
use crate::Violation;
use crate::checkers::ast::Checker;
use crate::preview::is_suspicious_function_reference_enabled;
use crate::registry::AsRule;
/// ## What it does
/// Checks for calls to `pickle` functions or modules that wrap them.
@@ -1034,20 +1035,16 @@ fn suspicious_function(
return;
};
match qualified_name.segments() {
let diagnostic = match qualified_name.segments() {
// Pickle
["pickle" | "dill", "load" | "loads" | "Unpickler"]
| ["shelve", "open" | "DbfilenameShelf"]
| ["jsonpickle", "decode"]
| ["jsonpickle", "unpickler", "decode"]
| ["pandas", "read_pickle"] => {
checker.report_diagnostic_if_enabled(SuspiciousPickleUsage, range)
}
| ["pandas", "read_pickle"] => Diagnostic::new(SuspiciousPickleUsage, range),
// Marshal
["marshal", "load" | "loads"] => {
checker.report_diagnostic_if_enabled(SuspiciousMarshalUsage, range)
}
["marshal", "load" | "loads"] => Diagnostic::new(SuspiciousMarshalUsage, range),
// InsecureHash
[
@@ -1062,7 +1059,7 @@ fn suspicious_function(
"primitives",
"hashes",
"SHA1" | "MD5",
] => checker.report_diagnostic_if_enabled(SuspiciousInsecureHashUsage, range),
] => Diagnostic::new(SuspiciousInsecureHashUsage, range),
// InsecureCipher
[
@@ -1078,7 +1075,7 @@ fn suspicious_function(
"ciphers",
"algorithms",
"ARC4" | "Blowfish" | "IDEA",
] => checker.report_diagnostic_if_enabled(SuspiciousInsecureCipherUsage, range),
] => Diagnostic::new(SuspiciousInsecureCipherUsage, range),
// InsecureCipherMode
[
@@ -1088,17 +1085,13 @@ fn suspicious_function(
"ciphers",
"modes",
"ECB",
] => checker.report_diagnostic_if_enabled(SuspiciousInsecureCipherModeUsage, range),
] => Diagnostic::new(SuspiciousInsecureCipherModeUsage, range),
// Mktemp
["tempfile", "mktemp"] => {
checker.report_diagnostic_if_enabled(SuspiciousMktempUsage, range)
}
["tempfile", "mktemp"] => Diagnostic::new(SuspiciousMktempUsage, range),
// Eval
["" | "builtins", "eval"] => {
checker.report_diagnostic_if_enabled(SuspiciousEvalUsage, range)
}
["" | "builtins", "eval"] => Diagnostic::new(SuspiciousEvalUsage, range),
// MarkSafe
["django", "utils", "safestring" | "html", "mark_safe"] => {
@@ -1109,7 +1102,7 @@ fn suspicious_function(
}
}
}
checker.report_diagnostic_if_enabled(SuspiciousMarkSafeUsage, range)
Diagnostic::new(SuspiciousMarkSafeUsage, range)
}
// URLOpen (`Request`)
@@ -1131,7 +1124,7 @@ fn suspicious_function(
}
}
}
checker.report_diagnostic_if_enabled(SuspiciousURLOpenUsage, range)
Diagnostic::new(SuspiciousURLOpenUsage, range)
}
// URLOpen (`urlopen`, `urlretrieve`)
@@ -1183,7 +1176,7 @@ fn suspicious_function(
}
}
}
checker.report_diagnostic_if_enabled(SuspiciousURLOpenUsage, range)
Diagnostic::new(SuspiciousURLOpenUsage, range)
}
// URLOpen (`URLopener`, `FancyURLopener`)
@@ -1194,18 +1187,18 @@ fn suspicious_function(
"urllib",
"request",
"URLopener" | "FancyURLopener",
] => checker.report_diagnostic_if_enabled(SuspiciousURLOpenUsage, range),
] => Diagnostic::new(SuspiciousURLOpenUsage, range),
// NonCryptographicRandom
[
"random",
"Random" | "random" | "randrange" | "randint" | "choice" | "choices" | "uniform"
| "triangular" | "randbytes",
] => checker.report_diagnostic_if_enabled(SuspiciousNonCryptographicRandomUsage, range),
] => Diagnostic::new(SuspiciousNonCryptographicRandomUsage, range),
// UnverifiedContext
["ssl", "_create_unverified_context"] => {
checker.report_diagnostic_if_enabled(SuspiciousUnverifiedContextUsage, range)
Diagnostic::new(SuspiciousUnverifiedContextUsage, range)
}
// XMLCElementTree
@@ -1214,7 +1207,7 @@ fn suspicious_function(
"etree",
"cElementTree",
"parse" | "iterparse" | "fromstring" | "XMLParser",
] => checker.report_diagnostic_if_enabled(SuspiciousXMLCElementTreeUsage, range),
] => Diagnostic::new(SuspiciousXMLCElementTreeUsage, range),
// XMLElementTree
[
@@ -1222,31 +1215,31 @@ fn suspicious_function(
"etree",
"ElementTree",
"parse" | "iterparse" | "fromstring" | "XMLParser",
] => checker.report_diagnostic_if_enabled(SuspiciousXMLElementTreeUsage, range),
] => Diagnostic::new(SuspiciousXMLElementTreeUsage, range),
// XMLExpatReader
["xml", "sax", "expatreader", "create_parser"] => {
checker.report_diagnostic_if_enabled(SuspiciousXMLExpatReaderUsage, range)
Diagnostic::new(SuspiciousXMLExpatReaderUsage, range)
}
// XMLExpatBuilder
["xml", "dom", "expatbuilder", "parse" | "parseString"] => {
checker.report_diagnostic_if_enabled(SuspiciousXMLExpatBuilderUsage, range)
Diagnostic::new(SuspiciousXMLExpatBuilderUsage, range)
}
// XMLSax
["xml", "sax", "parse" | "parseString" | "make_parser"] => {
checker.report_diagnostic_if_enabled(SuspiciousXMLSaxUsage, range)
Diagnostic::new(SuspiciousXMLSaxUsage, range)
}
// XMLMiniDOM
["xml", "dom", "minidom", "parse" | "parseString"] => {
checker.report_diagnostic_if_enabled(SuspiciousXMLMiniDOMUsage, range)
Diagnostic::new(SuspiciousXMLMiniDOMUsage, range)
}
// XMLPullDOM
["xml", "dom", "pulldom", "parse" | "parseString"] => {
checker.report_diagnostic_if_enabled(SuspiciousXMLPullDOMUsage, range)
Diagnostic::new(SuspiciousXMLPullDOMUsage, range)
}
// XMLETree
@@ -1255,16 +1248,20 @@ fn suspicious_function(
"etree",
"parse" | "fromstring" | "RestrictedElement" | "GlobalParserTLS" | "getDefaultParser"
| "check_docinfo",
] => checker.report_diagnostic_if_enabled(SuspiciousXMLETreeUsage, range),
] => Diagnostic::new(SuspiciousXMLETreeUsage, range),
// Telnet
["telnetlib", ..] => checker.report_diagnostic_if_enabled(SuspiciousTelnetUsage, range),
["telnetlib", ..] => Diagnostic::new(SuspiciousTelnetUsage, range),
// FTPLib
["ftplib", ..] => checker.report_diagnostic_if_enabled(SuspiciousFTPLibUsage, range),
["ftplib", ..] => Diagnostic::new(SuspiciousFTPLibUsage, range),
_ => return,
};
if checker.enabled(diagnostic.rule()) {
checker.report_diagnostic(diagnostic);
}
}
/// S308

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