mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 20:10:09 +00:00
Add check for W292 (#339)
This commit is contained in:
parent
95dfc61315
commit
473675fffb
9 changed files with 130 additions and 9 deletions
|
@ -246,6 +246,7 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com
|
||||||
| E743 | AmbiguousFunctionName | Ambiguous function name: `...` | ✅ | |
|
| E743 | AmbiguousFunctionName | Ambiguous function name: `...` | ✅ | |
|
||||||
| E902 | IOError | IOError: `...` | ✅ | |
|
| E902 | IOError | IOError: `...` | ✅ | |
|
||||||
| E999 | SyntaxError | SyntaxError: `...` | ✅ | |
|
| E999 | SyntaxError | SyntaxError: `...` | ✅ | |
|
||||||
|
| W292 | NoNewLineAtEndOfFile | No newline at end of file | ✅ | |
|
||||||
| F401 | UnusedImport | `...` imported but unused | ✅ | 🛠 |
|
| F401 | UnusedImport | `...` imported but unused | ✅ | 🛠 |
|
||||||
| F402 | ImportShadowedByLoopVar | Import `...` from line 1 shadowed by loop variable | ✅ | |
|
| F402 | ImportShadowedByLoopVar | Import `...` from line 1 shadowed by loop variable | ✅ | |
|
||||||
| F403 | ImportStarUsed | `from ... import *` used; unable to detect undefined names | ✅ | |
|
| F403 | ImportStarUsed | `from ... import *` used; unable to detect undefined names | ✅ | |
|
||||||
|
@ -291,6 +292,7 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com
|
||||||
| U005 | NoAssertEquals | `assertEquals` is deprecated, use `assertEqual` instead | | 🛠 |
|
| U005 | NoAssertEquals | `assertEquals` is deprecated, use `assertEqual` instead | | 🛠 |
|
||||||
| M001 | UnusedNOQA | Unused `noqa` directive | | 🛠 |
|
| M001 | UnusedNOQA | Unused `noqa` directive | | 🛠 |
|
||||||
|
|
||||||
|
|
||||||
## Integrations
|
## Integrations
|
||||||
|
|
||||||
### PyCharm
|
### PyCharm
|
||||||
|
|
2
resources/test/fixtures/W292_0.py
vendored
Normal file
2
resources/test/fixtures/W292_0.py
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
def fn() -> None:
|
||||||
|
pass
|
2
resources/test/fixtures/W292_1.py
vendored
Normal file
2
resources/test/fixtures/W292_1.py
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
def fn() -> None:
|
||||||
|
pass # noqa: W292
|
|
@ -113,6 +113,46 @@ pub fn check_lines(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enforce newlines at end of files.
|
||||||
|
if settings.enabled.contains(&CheckCode::W292) {
|
||||||
|
// If the file terminates with a newline, the last line should be an empty string slice.
|
||||||
|
if let Some(line) = lines.last() {
|
||||||
|
if !line.is_empty() {
|
||||||
|
let lineno = lines.len() - 1;
|
||||||
|
let noqa_lineno = noqa_line_for
|
||||||
|
.get(lineno)
|
||||||
|
.map(|lineno| lineno - 1)
|
||||||
|
.unwrap_or(lineno);
|
||||||
|
|
||||||
|
let noqa = noqa_directives
|
||||||
|
.entry(noqa_lineno)
|
||||||
|
.or_insert_with(|| (noqa::extract_noqa_directive(lines[noqa_lineno]), vec![]));
|
||||||
|
|
||||||
|
let check = Check::new(
|
||||||
|
CheckKind::NoNewLineAtEndOfFile,
|
||||||
|
Range {
|
||||||
|
location: Location::new(lines.len(), line.len() + 1),
|
||||||
|
end_location: Location::new(lines.len(), line.len() + 1),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
match noqa {
|
||||||
|
(Directive::All(_, _), matches) => {
|
||||||
|
matches.push(check.kind.code().as_str());
|
||||||
|
}
|
||||||
|
(Directive::Codes(_, _, codes), matches) => {
|
||||||
|
if codes.contains(&check.kind.code().as_str()) {
|
||||||
|
matches.push(check.kind.code().as_str());
|
||||||
|
} else {
|
||||||
|
line_checks.push(check);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Directive::None, _) => line_checks.push(check),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Enforce that the noqa directive was actually used.
|
// Enforce that the noqa directive was actually used.
|
||||||
if enforce_noqa {
|
if enforce_noqa {
|
||||||
for (row, (directive, matches)) in noqa_directives {
|
for (row, (directive, matches)) in noqa_directives {
|
||||||
|
|
|
@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::ast::types::Range;
|
use crate::ast::types::Range;
|
||||||
|
|
||||||
pub const DEFAULT_CHECK_CODES: [CheckCode; 42] = [
|
pub const DEFAULT_CHECK_CODES: [CheckCode; 43] = [
|
||||||
// pycodestyle
|
// pycodestyle errors
|
||||||
CheckCode::E402,
|
CheckCode::E402,
|
||||||
CheckCode::E501,
|
CheckCode::E501,
|
||||||
CheckCode::E711,
|
CheckCode::E711,
|
||||||
|
@ -24,6 +24,8 @@ pub const DEFAULT_CHECK_CODES: [CheckCode; 42] = [
|
||||||
CheckCode::E743,
|
CheckCode::E743,
|
||||||
CheckCode::E902,
|
CheckCode::E902,
|
||||||
CheckCode::E999,
|
CheckCode::E999,
|
||||||
|
// pycodestyle warnings
|
||||||
|
CheckCode::W292,
|
||||||
// pyflakes
|
// pyflakes
|
||||||
CheckCode::F401,
|
CheckCode::F401,
|
||||||
CheckCode::F402,
|
CheckCode::F402,
|
||||||
|
@ -55,8 +57,8 @@ pub const DEFAULT_CHECK_CODES: [CheckCode; 42] = [
|
||||||
CheckCode::F901,
|
CheckCode::F901,
|
||||||
];
|
];
|
||||||
|
|
||||||
pub const ALL_CHECK_CODES: [CheckCode; 58] = [
|
pub const ALL_CHECK_CODES: [CheckCode; 59] = [
|
||||||
// pycodestyle
|
// pycodestyle errors
|
||||||
CheckCode::E402,
|
CheckCode::E402,
|
||||||
CheckCode::E501,
|
CheckCode::E501,
|
||||||
CheckCode::E711,
|
CheckCode::E711,
|
||||||
|
@ -71,6 +73,8 @@ pub const ALL_CHECK_CODES: [CheckCode; 58] = [
|
||||||
CheckCode::E743,
|
CheckCode::E743,
|
||||||
CheckCode::E902,
|
CheckCode::E902,
|
||||||
CheckCode::E999,
|
CheckCode::E999,
|
||||||
|
// pycodestyle warnings
|
||||||
|
CheckCode::W292,
|
||||||
// pyflakes
|
// pyflakes
|
||||||
CheckCode::F401,
|
CheckCode::F401,
|
||||||
CheckCode::F402,
|
CheckCode::F402,
|
||||||
|
@ -126,7 +130,7 @@ pub const ALL_CHECK_CODES: [CheckCode; 58] = [
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Hash, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Hash, PartialOrd, Ord)]
|
||||||
pub enum CheckCode {
|
pub enum CheckCode {
|
||||||
// pycodestyle
|
// pycodestyle errors
|
||||||
E402,
|
E402,
|
||||||
E501,
|
E501,
|
||||||
E711,
|
E711,
|
||||||
|
@ -141,6 +145,8 @@ pub enum CheckCode {
|
||||||
E743,
|
E743,
|
||||||
E902,
|
E902,
|
||||||
E999,
|
E999,
|
||||||
|
// pycodestyle warnings
|
||||||
|
W292,
|
||||||
// pyflakes
|
// pyflakes
|
||||||
F401,
|
F401,
|
||||||
F402,
|
F402,
|
||||||
|
@ -199,7 +205,7 @@ impl FromStr for CheckCode {
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self> {
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
match s {
|
match s {
|
||||||
// pycodestyle
|
// pycodestyle errors
|
||||||
"E402" => Ok(CheckCode::E402),
|
"E402" => Ok(CheckCode::E402),
|
||||||
"E501" => Ok(CheckCode::E501),
|
"E501" => Ok(CheckCode::E501),
|
||||||
"E711" => Ok(CheckCode::E711),
|
"E711" => Ok(CheckCode::E711),
|
||||||
|
@ -214,6 +220,8 @@ impl FromStr for CheckCode {
|
||||||
"E743" => Ok(CheckCode::E743),
|
"E743" => Ok(CheckCode::E743),
|
||||||
"E902" => Ok(CheckCode::E902),
|
"E902" => Ok(CheckCode::E902),
|
||||||
"E999" => Ok(CheckCode::E999),
|
"E999" => Ok(CheckCode::E999),
|
||||||
|
// pycodestyle warnings
|
||||||
|
"W292" => Ok(CheckCode::W292),
|
||||||
// pyflakes
|
// pyflakes
|
||||||
"F401" => Ok(CheckCode::F401),
|
"F401" => Ok(CheckCode::F401),
|
||||||
"F402" => Ok(CheckCode::F402),
|
"F402" => Ok(CheckCode::F402),
|
||||||
|
@ -271,7 +279,7 @@ impl FromStr for CheckCode {
|
||||||
impl CheckCode {
|
impl CheckCode {
|
||||||
pub fn as_str(&self) -> &str {
|
pub fn as_str(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
// pycodestyle
|
// pycodestyle errors
|
||||||
CheckCode::E402 => "E402",
|
CheckCode::E402 => "E402",
|
||||||
CheckCode::E501 => "E501",
|
CheckCode::E501 => "E501",
|
||||||
CheckCode::E711 => "E711",
|
CheckCode::E711 => "E711",
|
||||||
|
@ -286,6 +294,8 @@ impl CheckCode {
|
||||||
CheckCode::E743 => "E743",
|
CheckCode::E743 => "E743",
|
||||||
CheckCode::E902 => "E902",
|
CheckCode::E902 => "E902",
|
||||||
CheckCode::E999 => "E999",
|
CheckCode::E999 => "E999",
|
||||||
|
// pycodestyle warnings
|
||||||
|
CheckCode::W292 => "W292",
|
||||||
// pyflakes
|
// pyflakes
|
||||||
CheckCode::F401 => "F401",
|
CheckCode::F401 => "F401",
|
||||||
CheckCode::F402 => "F402",
|
CheckCode::F402 => "F402",
|
||||||
|
@ -352,7 +362,7 @@ impl CheckCode {
|
||||||
/// A placeholder representation of the CheckKind for the check.
|
/// A placeholder representation of the CheckKind for the check.
|
||||||
pub fn kind(&self) -> CheckKind {
|
pub fn kind(&self) -> CheckKind {
|
||||||
match self {
|
match self {
|
||||||
// pycodestyle
|
// pycodestyle errors
|
||||||
CheckCode::E402 => CheckKind::ModuleImportNotAtTopOfFile,
|
CheckCode::E402 => CheckKind::ModuleImportNotAtTopOfFile,
|
||||||
CheckCode::E501 => CheckKind::LineTooLong(89, 88),
|
CheckCode::E501 => CheckKind::LineTooLong(89, 88),
|
||||||
CheckCode::E711 => CheckKind::NoneComparison(RejectedCmpop::Eq),
|
CheckCode::E711 => CheckKind::NoneComparison(RejectedCmpop::Eq),
|
||||||
|
@ -367,6 +377,8 @@ impl CheckCode {
|
||||||
CheckCode::E743 => CheckKind::AmbiguousFunctionName("...".to_string()),
|
CheckCode::E743 => CheckKind::AmbiguousFunctionName("...".to_string()),
|
||||||
CheckCode::E902 => CheckKind::IOError("IOError: `...`".to_string()),
|
CheckCode::E902 => CheckKind::IOError("IOError: `...`".to_string()),
|
||||||
CheckCode::E999 => CheckKind::SyntaxError("`...`".to_string()),
|
CheckCode::E999 => CheckKind::SyntaxError("`...`".to_string()),
|
||||||
|
// pycodestyle warnings
|
||||||
|
CheckCode::W292 => CheckKind::NoNewLineAtEndOfFile,
|
||||||
// pyflakes
|
// pyflakes
|
||||||
CheckCode::F401 => CheckKind::UnusedImport(vec!["...".to_string()]),
|
CheckCode::F401 => CheckKind::UnusedImport(vec!["...".to_string()]),
|
||||||
CheckCode::F402 => CheckKind::ImportShadowedByLoopVar("...".to_string(), 1),
|
CheckCode::F402 => CheckKind::ImportShadowedByLoopVar("...".to_string(), 1),
|
||||||
|
@ -481,6 +493,8 @@ pub enum CheckKind {
|
||||||
UnusedImport(Vec<String>),
|
UnusedImport(Vec<String>),
|
||||||
UnusedVariable(String),
|
UnusedVariable(String),
|
||||||
YieldOutsideFunction,
|
YieldOutsideFunction,
|
||||||
|
// More style
|
||||||
|
NoNewLineAtEndOfFile,
|
||||||
// flake8-builtin
|
// flake8-builtin
|
||||||
BuiltinVariableShadowing(String),
|
BuiltinVariableShadowing(String),
|
||||||
BuiltinArgumentShadowing(String),
|
BuiltinArgumentShadowing(String),
|
||||||
|
@ -551,6 +565,8 @@ impl CheckKind {
|
||||||
CheckKind::UnusedImport(_) => "UnusedImport",
|
CheckKind::UnusedImport(_) => "UnusedImport",
|
||||||
CheckKind::UnusedVariable(_) => "UnusedVariable",
|
CheckKind::UnusedVariable(_) => "UnusedVariable",
|
||||||
CheckKind::YieldOutsideFunction => "YieldOutsideFunction",
|
CheckKind::YieldOutsideFunction => "YieldOutsideFunction",
|
||||||
|
// More style
|
||||||
|
CheckKind::NoNewLineAtEndOfFile => "NoNewLineAtEndOfFile",
|
||||||
// flake8-builtins
|
// flake8-builtins
|
||||||
CheckKind::BuiltinVariableShadowing(_) => "BuiltinVariableShadowing",
|
CheckKind::BuiltinVariableShadowing(_) => "BuiltinVariableShadowing",
|
||||||
CheckKind::BuiltinArgumentShadowing(_) => "BuiltinArgumentShadowing",
|
CheckKind::BuiltinArgumentShadowing(_) => "BuiltinArgumentShadowing",
|
||||||
|
@ -621,6 +637,8 @@ impl CheckKind {
|
||||||
CheckKind::UnusedImport(_) => &CheckCode::F401,
|
CheckKind::UnusedImport(_) => &CheckCode::F401,
|
||||||
CheckKind::UnusedVariable(_) => &CheckCode::F841,
|
CheckKind::UnusedVariable(_) => &CheckCode::F841,
|
||||||
CheckKind::YieldOutsideFunction => &CheckCode::F704,
|
CheckKind::YieldOutsideFunction => &CheckCode::F704,
|
||||||
|
// More style
|
||||||
|
CheckKind::NoNewLineAtEndOfFile => &CheckCode::W292,
|
||||||
// flake8-builtins
|
// flake8-builtins
|
||||||
CheckKind::BuiltinVariableShadowing(_) => &CheckCode::A001,
|
CheckKind::BuiltinVariableShadowing(_) => &CheckCode::A001,
|
||||||
CheckKind::BuiltinArgumentShadowing(_) => &CheckCode::A002,
|
CheckKind::BuiltinArgumentShadowing(_) => &CheckCode::A002,
|
||||||
|
@ -776,7 +794,8 @@ impl CheckKind {
|
||||||
CheckKind::YieldOutsideFunction => {
|
CheckKind::YieldOutsideFunction => {
|
||||||
"`yield` or `yield from` statement outside of a function/method".to_string()
|
"`yield` or `yield from` statement outside of a function/method".to_string()
|
||||||
}
|
}
|
||||||
|
// More style
|
||||||
|
CheckKind::NoNewLineAtEndOfFile => "No newline at end of file".to_string(),
|
||||||
// flake8-builtins
|
// flake8-builtins
|
||||||
CheckKind::BuiltinVariableShadowing(name) => {
|
CheckKind::BuiltinVariableShadowing(name) => {
|
||||||
format!("Variable `{name}` is shadowing a python builtin")
|
format!("Variable `{name}` is shadowing a python builtin")
|
||||||
|
|
|
@ -339,6 +339,30 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn w292_0() -> Result<()> {
|
||||||
|
let mut checks = check_path(
|
||||||
|
Path::new("./resources/test/fixtures/W292_0.py"),
|
||||||
|
&settings::Settings::for_rule(CheckCode::W292),
|
||||||
|
&fixer::Mode::Generate,
|
||||||
|
)?;
|
||||||
|
checks.sort_by_key(|check| check.location);
|
||||||
|
insta::assert_yaml_snapshot!(checks);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn w292_1() -> Result<()> {
|
||||||
|
let mut checks = check_path(
|
||||||
|
Path::new("./resources/test/fixtures/W292_1.py"),
|
||||||
|
&settings::Settings::for_rule(CheckCode::W292),
|
||||||
|
&fixer::Mode::Generate,
|
||||||
|
)?;
|
||||||
|
checks.sort_by_key(|check| check.location);
|
||||||
|
insta::assert_yaml_snapshot!(checks);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn f401() -> Result<()> {
|
fn f401() -> Result<()> {
|
||||||
let mut checks = check_path(
|
let mut checks = check_path(
|
||||||
|
|
13
src/snapshots/ruff__linter__tests__w292.snap
Normal file
13
src/snapshots/ruff__linter__tests__w292.snap
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
source: src/linter.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind: NoNewLineAtEndOfFile
|
||||||
|
location:
|
||||||
|
row: 2
|
||||||
|
column: 9
|
||||||
|
end_location:
|
||||||
|
row: 2
|
||||||
|
column: 9
|
||||||
|
fix: ~
|
||||||
|
|
13
src/snapshots/ruff__linter__tests__w292_0.snap
Normal file
13
src/snapshots/ruff__linter__tests__w292_0.snap
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
source: src/linter.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
- kind: NoNewLineAtEndOfFile
|
||||||
|
location:
|
||||||
|
row: 2
|
||||||
|
column: 9
|
||||||
|
end_location:
|
||||||
|
row: 2
|
||||||
|
column: 9
|
||||||
|
fix: ~
|
||||||
|
|
6
src/snapshots/ruff__linter__tests__w292_1.snap
Normal file
6
src/snapshots/ruff__linter__tests__w292_1.snap
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
source: src/linter.rs
|
||||||
|
expression: checks
|
||||||
|
---
|
||||||
|
[]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue