Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 0 additions & 51 deletions crates/ruff/tests/cli/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1075,57 +1075,6 @@ include = ["*.ipy"]
Ok(())
}

#[test]
fn warn_invalid_noqa_with_no_diagnostics() {
assert_cmd_snapshot!(
Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args(["--isolated"])
.arg("--select")
.arg("F401")
.arg("-")
.pass_stdin(
r#"
# ruff: noqa: AAA101
print("Hello world!")
"#
)
);
}

#[test]
fn file_noqa_external() -> Result<()> {
let fixture = CliTest::with_file(
"ruff.toml",
r#"
[lint]
external = ["AAA"]
"#,
)?;

assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("ruff.toml")
.arg("-")
.pass_stdin(r#"
# flake8: noqa: AAA101, BBB102
import os
"#), @"
success: false
exit_code: 1
----- stdout -----
-:3:8: F401 [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.

----- stderr -----
warning: Invalid rule code provided to `# ruff: noqa` at -:2: BBB102
");

Ok(())
}

#[test]
fn required_version_fails_to_parse() -> Result<()> {
let fixture = CliTest::with_file(
Expand Down

This file was deleted.

8 changes: 8 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/ruff/RUF102_1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Invalid file-level code
# ruff: noqa: INVALID123

# External file-level code
# ruff: noqa: V123

# Valid file-level code
# ruff: noqa: E402
8 changes: 7 additions & 1 deletion crates/ruff_linter/src/checkers/noqa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,13 @@ pub(crate) fn check_noqa(
if context.is_rule_enabled(Rule::InvalidRuleCode)
&& !exemption.enumerates(Rule::InvalidRuleCode)
{
ruff::rules::invalid_noqa_code(context, &noqa_directives, locator, &settings.external);
ruff::rules::invalid_noqa_code(
context,
&file_noqa_directives,
&noqa_directives,
locator,
&settings.external,
);
}

ignored_diagnostics.sort_unstable();
Expand Down
30 changes: 12 additions & 18 deletions crates/ruff_linter/src/noqa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,24 +277,18 @@ impl<'a> FileNoqaDirectives<'a> {
vec![]
}
Directive::Codes(codes) => {
codes.iter().filter_map(|code| {
let code = code.as_str();
// Ignore externally-defined rules.
if external.iter().any(|external| code.starts_with(external)) {
return None;
}

if let Ok(rule) = Rule::from_code(get_redirect_target(code).unwrap_or(code))
{
Some(rule)
} else {
#[expect(deprecated)]
let line = locator.compute_line_index(range.start());
let path_display = relativize_path(path);
warn!("Invalid rule code provided to `# ruff: noqa` at {path_display}:{line}: {code}");
None
}
}).collect()
codes
.iter()
.filter_map(|code| {
let code = code.as_str();
// Ignore externally-defined rules.
if external.iter().any(|external| code.starts_with(external)) {
return None;
}

Rule::from_code(get_redirect_target(code).unwrap_or(code)).ok()
})
.collect()
}
};

Expand Down
5 changes: 5 additions & 0 deletions crates/ruff_linter/src/preview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,8 @@ pub(crate) const fn is_baseloader_safe_in_yaml_load_enabled(settings: &LinterSet
pub(crate) const fn is_expanded_import_conventions_enabled(preview: PreviewMode) -> bool {
preview.is_enabled()
}

// https://github.com/astral-sh/ruff/pull/23535
pub(crate) const fn is_file_level_invalid_rule_code_enabled(settings: &LinterSettings) -> bool {
settings.preview.is_enabled()
}
15 changes: 11 additions & 4 deletions crates/ruff_linter/src/rules/ruff/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ mod tests {
#[test_case(Rule::RedirectedNOQA, Path::new("RUF101_0.py"))]
#[test_case(Rule::RedirectedNOQA, Path::new("RUF101_1.py"))]
#[test_case(Rule::InvalidRuleCode, Path::new("RUF102.py"))]
#[test_case(Rule::InvalidRuleCode, Path::new("RUF102_1.py"))]
#[test_case(Rule::NonEmptyInitModule, Path::new("RUF067/modules/__init__.py"))]
#[test_case(Rule::NonEmptyInitModule, Path::new("RUF067/modules/okay.py"))]
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
Expand Down Expand Up @@ -497,16 +498,22 @@ mod tests {
Ok(())
}

#[test]
fn invalid_rule_code_external_rules() -> Result<()> {
#[test_case(Path::new("ruff/RUF102.py"))]
#[test_case(Path::new("ruff/RUF102_1.py"))]
fn invalid_rule_code_external_rules(path: &Path) -> Result<()> {
let snapshot = format!(
"invalid_rule_code_external_rules_{}",
path.to_string_lossy(),
);
let diagnostics = test_path(
Path::new("ruff/RUF102.py"),
path,
&settings::LinterSettings {
external: vec!["V".to_string()],
preview: PreviewMode::Enabled,
..settings::LinterSettings::for_rule(Rule::InvalidRuleCode)
},
)?;
assert_diagnostics!(diagnostics);
assert_diagnostics!(snapshot, diagnostics);
Ok(())
}

Expand Down
33 changes: 22 additions & 11 deletions crates/ruff_linter/src/rules/ruff/rules/invalid_rule_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
use crate::Locator;
use crate::checkers::ast::LintContext;
use crate::fix::edits::delete_comment;
use crate::noqa::{Code, Directive};
use crate::noqa::{Code, Directive, FileNoqaDirectives};
use crate::noqa::{Codes, NoqaDirectives};
use crate::preview::is_file_level_invalid_rule_code_enabled;
use crate::registry::Rule;
use crate::rule_redirects::get_redirect_target;
use crate::{AlwaysFixableViolation, Edit, Fix};
Expand Down Expand Up @@ -83,35 +84,45 @@ impl AlwaysFixableViolation for InvalidRuleCode {
/// RUF102 for invalid noqa codes
pub(crate) fn invalid_noqa_code(
context: &LintContext,
file_noqa_directives: &FileNoqaDirectives,
noqa_directives: &NoqaDirectives,
locator: &Locator,
external: &[String],
) {
for line in noqa_directives.lines() {
let Directive::Codes(directive) = &line.directive else {
continue;
};

let all_valid = directive
let check_codes = |codes: &Codes<'_>| {
let all_valid = codes
.iter()
.all(|code| code_is_valid(code.as_str(), external));

if all_valid {
continue;
return;
}

let (valid_codes, invalid_codes): (Vec<_>, Vec<_>) = directive
let (valid_codes, invalid_codes): (Vec<_>, Vec<_>) = codes
.iter()
.partition(|&code| code_is_valid(code.as_str(), external));

if valid_codes.is_empty() {
all_codes_invalid_diagnostic(directive, invalid_codes, locator, context);
all_codes_invalid_diagnostic(codes, invalid_codes, locator, context);
} else {
for invalid_code in invalid_codes {
some_codes_are_invalid_diagnostic(directive, invalid_code, locator, context);
some_codes_are_invalid_diagnostic(codes, invalid_code, locator, context);
}
}
};

if is_file_level_invalid_rule_code_enabled(context.settings()) {
for line in file_noqa_directives.lines() {
if let Directive::Codes(codes) = &line.parsed_file_exemption {
check_codes(codes);
}
}
}
for line in noqa_directives.lines() {
if let Directive::Codes(codes) = &line.directive {
check_codes(codes);
}
}
}

pub(crate) fn code_is_valid(code: &str, external: &[String]) -> bool {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
source: crates/ruff_linter/src/rules/ruff/mod.rs
---

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
source: crates/ruff_linter/src/rules/ruff/mod.rs
---
RUF102 [*] Invalid rule code in `# noqa`: INVALID123
--> RUF102_1.py:2:1
|
1 | # Invalid file-level code
2 | # ruff: noqa: INVALID123
| ^^^^^^^^^^^^^^^^^^^^^^^^
3 |
4 | # External file-level code
|
help: Add non-Ruff rule codes to the `lint.external` configuration option
help: Remove the `# noqa` comment
1 | # Invalid file-level code
- # ruff: noqa: INVALID123
2 |
3 | # External file-level code
4 | # ruff: noqa: V123