Compare commits
1 Commits
0.11.12
...
david/hasa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae6dd04910 |
28
CHANGELOG.md
28
CHANGELOG.md
@@ -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
9
Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.11.12"
|
||||
version = "0.11.11"
|
||||
publish = true
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = [] }
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_linter"
|
||||
version = "0.11.12"
|
||||
version = "0.11.11"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
///
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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(¶m_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.0–removed 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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 = ¶meter.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, ¶meter.parameter, default);
|
||||
if let Some(diagnostic) = check_password_kwarg(¶meter.parameter, default) {
|
||||
checker.report_diagnostic(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user