diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index ebec5f4acc..04fb7eee11 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -1063,6 +1063,8 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Ruff, "100") => rules::ruff::rules::UnusedNOQA, (Ruff, "101") => rules::ruff::rules::RedirectedNOQA, (Ruff, "102") => rules::ruff::rules::InvalidRuleCode, + (Ruff, "103") => rules::ruff::rules::InvalidSuppressionComment, + (Ruff, "104") => rules::ruff::rules::UnmatchedSuppressionComment, (Ruff, "200") => rules::ruff::rules::InvalidPyprojectToml, #[cfg(any(feature = "test-rules", test))] diff --git a/crates/ruff_linter/src/rules/ruff/rules/invalid_suppression_comment.rs b/crates/ruff_linter/src/rules/ruff/rules/invalid_suppression_comment.rs new file mode 100644 index 0000000000..8e6ac88dd3 --- /dev/null +++ b/crates/ruff_linter/src/rules/ruff/rules/invalid_suppression_comment.rs @@ -0,0 +1,39 @@ +use ruff_macros::{ViolationMetadata, derive_message_formats}; + +use crate::AlwaysFixableViolation; + +/// ## What it does +/// Checks for invalid suppression comments +/// +/// ## Why is this bad? +/// Invalid suppression comments are ignored by Ruff, and should either +/// be fixed or removed to avoid confusion. +/// +/// ## Example +/// ```python +/// ruff: disable # missing codes +/// ``` +/// +/// Use instead: +/// ```python +/// # ruff: disable[E501] +/// ``` +/// +/// Or delete the invalid suppression comment. +/// +/// ## References +/// - [Ruff error suppression](https://docs.astral.sh/ruff/linter/#error-suppression) +#[derive(ViolationMetadata)] +#[violation_metadata(preview_since = "0.14.9")] +pub(crate) struct InvalidSuppressionComment; + +impl AlwaysFixableViolation for InvalidSuppressionComment { + #[derive_message_formats] + fn message(&self) -> String { + "Invalid suppression comment".to_string() + } + + fn fix_title(&self) -> String { + "Remove invalid suppression comment".to_string() + } +} diff --git a/crates/ruff_linter/src/rules/ruff/rules/mod.rs b/crates/ruff_linter/src/rules/ruff/rules/mod.rs index 206a504e74..70363d3f8b 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/mod.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/mod.rs @@ -22,6 +22,7 @@ pub(crate) use invalid_formatter_suppression_comment::*; pub(crate) use invalid_index_type::*; pub(crate) use invalid_pyproject_toml::*; pub(crate) use invalid_rule_code::*; +pub(crate) use invalid_suppression_comment::*; pub(crate) use legacy_form_pytest_raises::*; pub(crate) use logging_eager_conversion::*; pub(crate) use map_int_version_parsing::*; @@ -46,6 +47,7 @@ pub(crate) use starmap_zip::*; pub(crate) use static_key_dict_comprehension::*; #[cfg(any(feature = "test-rules", test))] pub(crate) use test_rules::*; +pub(crate) use unmatched_suppression_comment::*; pub(crate) use unnecessary_cast_to_int::*; pub(crate) use unnecessary_iterable_allocation_for_first_element::*; pub(crate) use unnecessary_key_check::*; @@ -87,6 +89,7 @@ mod invalid_formatter_suppression_comment; mod invalid_index_type; mod invalid_pyproject_toml; mod invalid_rule_code; +mod invalid_suppression_comment; mod legacy_form_pytest_raises; mod logging_eager_conversion; mod map_int_version_parsing; @@ -113,6 +116,7 @@ mod static_key_dict_comprehension; mod suppression_comment_visitor; #[cfg(any(feature = "test-rules", test))] pub(crate) mod test_rules; +mod unmatched_suppression_comment; mod unnecessary_cast_to_int; mod unnecessary_iterable_allocation_for_first_element; mod unnecessary_key_check; diff --git a/crates/ruff_linter/src/rules/ruff/rules/unmatched_suppression_comment.rs b/crates/ruff_linter/src/rules/ruff/rules/unmatched_suppression_comment.rs new file mode 100644 index 0000000000..2dae90a8c1 --- /dev/null +++ b/crates/ruff_linter/src/rules/ruff/rules/unmatched_suppression_comment.rs @@ -0,0 +1,38 @@ +use ruff_macros::{ViolationMetadata, derive_message_formats}; + +use crate::Violation; + +/// ## What it does +/// Checks for unmatched range suppression comments +/// +/// ## Why is this bad? +/// Unmatched range suppression comments can inadvertently suppress violations +/// over larger sections of code than intended, particularly at module scope. +/// +/// ## Example +/// ```python +/// def foo(): +/// ruff: disable[E501] # unmatched +/// ... +/// ``` +/// +/// Use instead: +/// ```python +/// def foo(): +/// # ruff: disable[E501] +/// ... +/// # ruff: enable[E501] +/// ``` +/// +/// ## References +/// - [Ruff error suppression](https://docs.astral.sh/ruff/linter/#error-suppression) +#[derive(ViolationMetadata)] +#[violation_metadata(preview_since = "0.14.9")] +pub(crate) struct UnmatchedSuppressionComment; + +impl Violation for UnmatchedSuppressionComment { + #[derive_message_formats] + fn message(&self) -> String { + "Unmatched suppression comment".to_string() + } +} diff --git a/ruff.schema.json b/ruff.schema.json index 1c8a092042..7ff15dbe14 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -4050,6 +4050,8 @@ "RUF100", "RUF101", "RUF102", + "RUF103", + "RUF104", "RUF2", "RUF20", "RUF200",