diff --git a/crates/ruff_linter/src/rules/ruff/mod.rs b/crates/ruff_linter/src/rules/ruff/mod.rs index 9bc6363731..5732e88a8b 100644 --- a/crates/ruff_linter/src/rules/ruff/mod.rs +++ b/crates/ruff_linter/src/rules/ruff/mod.rs @@ -332,6 +332,25 @@ mod tests { Ok(()) } + #[test] + fn range_suppressions_full() -> Result<()> { + let diagnostics = test_path( + Path::new("ruff/suppressions.py"), + &settings::LinterSettings::for_rules(vec![ + Rule::UnusedVariable, + Rule::AmbiguousVariableName, + Rule::UnusedNOQA, + Rule::InvalidRuleCode, + Rule::InvalidSuppressionComment, + Rule::UnmatchedSuppressionComment, + ]) + .with_external_rules(&["TK421"]) + .with_preview_mode(), + )?; + assert_diagnostics!(diagnostics); + Ok(()) + } + #[test] fn ruf100_0() -> Result<()> { let diagnostics = test_path( diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__range_suppressions.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__range_suppressions.snap index ddac917621..264df075d2 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__range_suppressions.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__range_suppressions.snap @@ -7,7 +7,7 @@ source: crates/ruff_linter/src/rules/ruff/mod.rs --- Summary --- Removed: 15 -Added: 23 +Added: 20 --- Removed --- E741 Ambiguous variable name: `I` @@ -301,6 +301,7 @@ RUF100 [*] Unused suppression (non-enabled: `E501`) | ^^^^^^^^^^^^^^^^^^^^^ 47 | I = 1 48 | # ruff: enable[E501] + | -------------------- | help: Remove unused suppression 43 | def f(): @@ -308,26 +309,10 @@ help: Remove unused suppression 45 | # logged to user - # ruff: disable[E501] 46 | I = 1 -47 | # ruff: enable[E501] -48 | - - -RUF100 [*] Unused suppression (non-enabled: `E501`) - --> suppressions.py:48:5 - | -46 | # ruff: disable[E501] -47 | I = 1 -48 | # ruff: enable[E501] - | ^^^^^^^^^^^^^^^^^^^^ - | -help: Remove unused suppression -45 | # logged to user -46 | # ruff: disable[E501] -47 | I = 1 - # ruff: enable[E501] +47 | 48 | -49 | -50 | def f(): +49 | def f(): RUF100 [*] Unused `noqa` directive (unused: `E741`, `F841`) @@ -563,6 +548,9 @@ RUF102 [*] Invalid rule code in suppression: YF829 | ^^^^^ 94 | # ruff: disable[F841, RQW320] 95 | value = 0 +96 | # ruff: enable[F841, RQW320] +97 | # ruff: enable[YF829] + | ----- | help: Remove the rule code 90 | @@ -572,6 +560,10 @@ help: Remove the rule code 93 | # ruff: disable[F841, RQW320] 94 | value = 0 95 | # ruff: enable[F841, RQW320] + - # ruff: enable[YF829] +96 | +97 | +98 | def f(): RUF102 [*] Invalid rule code in suppression: RQW320 @@ -583,6 +575,8 @@ RUF102 [*] Invalid rule code in suppression: RQW320 | ^^^^^^ 95 | value = 0 96 | # ruff: enable[F841, RQW320] + | ------ +97 | # ruff: enable[YF829] | help: Remove the rule code 91 | def f(): @@ -590,23 +584,6 @@ help: Remove the rule code 93 | # ruff: disable[YF829] - # ruff: disable[F841, RQW320] 94 + # ruff: disable[F841] -95 | value = 0 -96 | # ruff: enable[F841, RQW320] -97 | # ruff: enable[YF829] - - -RUF102 [*] Invalid rule code in suppression: RQW320 - --> suppressions.py:96:26 - | -94 | # ruff: disable[F841, RQW320] -95 | value = 0 -96 | # ruff: enable[F841, RQW320] - | ^^^^^^ -97 | # ruff: enable[YF829] - | -help: Remove the rule code -93 | # ruff: disable[YF829] -94 | # ruff: disable[F841, RQW320] 95 | value = 0 - # ruff: enable[F841, RQW320] 96 + # ruff: enable[F841] @@ -615,24 +592,6 @@ help: Remove the rule code 99 | -RUF102 [*] Invalid rule code in suppression: YF829 - --> suppressions.py:97:20 - | -95 | value = 0 -96 | # ruff: enable[F841, RQW320] -97 | # ruff: enable[YF829] - | ^^^^^ - | -help: Remove the rule code -94 | # ruff: disable[F841, RQW320] -95 | value = 0 -96 | # ruff: enable[F841, RQW320] - - # ruff: enable[YF829] -97 | -98 | -99 | def f(): - - RUF103 [*] Invalid suppression comment: missing suppression codes like `[E501, ...]` --> suppressions.py:109:5 | diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__range_suppressions_full.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__range_suppressions_full.snap new file mode 100644 index 0000000000..98bee33a70 --- /dev/null +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__range_suppressions_full.snap @@ -0,0 +1,460 @@ +--- +source: crates/ruff_linter/src/rules/ruff/mod.rs +--- +RUF104 Suppression comment without matching `#ruff:enable` comment + --> suppressions.py:11:5 + | + 9 | # These should both be ignored by the implicit range suppression. +10 | # Should also generate an "unmatched suppression" warning. +11 | # ruff:disable[E741,F841] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +12 | I = 1 + | + +E741 Ambiguous variable name: `I` + --> suppressions.py:18:5 + | +16 | # Neither warning is ignored, and an "unmatched suppression" +17 | # should be generated. +18 | I = 1 + | ^ +19 | # ruff: enable[E741, F841] + | + +F841 [*] Local variable `I` is assigned to but never used + --> suppressions.py:18:5 + | +16 | # Neither warning is ignored, and an "unmatched suppression" +17 | # should be generated. +18 | I = 1 + | ^ +19 | # ruff: enable[E741, F841] + | +help: Remove assignment to unused variable `I` +15 | def f(): +16 | # Neither warning is ignored, and an "unmatched suppression" +17 | # should be generated. + - I = 1 +18 + pass +19 | # ruff: enable[E741, F841] +20 | +21 | +note: This is an unsafe fix and may change runtime behavior + +RUF103 [*] Invalid suppression comment: no matching 'disable' comment + --> suppressions.py:19:5 + | +17 | # should be generated. +18 | I = 1 +19 | # ruff: enable[E741, F841] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: Remove suppression comment +16 | # Neither warning is ignored, and an "unmatched suppression" +17 | # should be generated. +18 | I = 1 + - # ruff: enable[E741, F841] +19 | +20 | +21 | def f(): +note: This is an unsafe fix and may change runtime behavior + +F841 [*] Local variable `I` is assigned to but never used + --> suppressions.py:26:5 + | +24 | # the other logged to the user. +25 | # ruff: disable[E741] +26 | I = 1 + | ^ +27 | # ruff: enable[E741] + | +help: Remove assignment to unused variable `I` +23 | # One should be ignored by the range suppression, and +24 | # the other logged to the user. +25 | # ruff: disable[E741] + - I = 1 +26 + pass +27 | # ruff: enable[E741] +28 | +29 | +note: This is an unsafe fix and may change runtime behavior + +F841 [*] Local variable `l` is assigned to but never used + --> suppressions.py:35:5 + | +33 | # middle line should be completely silenced. +34 | # ruff: disable[E741] +35 | l = 0 + | ^ +36 | # ruff: disable[F841] +37 | O = 1 + | +help: Remove assignment to unused variable `l` +32 | # lines should each log a different warning, while the +33 | # middle line should be completely silenced. +34 | # ruff: disable[E741] + - l = 0 +35 | # ruff: disable[F841] +36 | O = 1 +37 | # ruff: enable[E741] +note: This is an unsafe fix and may change runtime behavior + +E741 Ambiguous variable name: `I` + --> suppressions.py:39:5 + | +37 | O = 1 +38 | # ruff: enable[E741] +39 | I = 2 + | ^ +40 | # ruff: enable[F841] + | + +RUF100 [*] Unused suppression (non-enabled: `E501`) + --> suppressions.py:46:5 + | +44 | # Neither of these are ignored and warnings are +45 | # logged to user +46 | # ruff: disable[E501] + | ^^^^^^^^^^^^^^^^^^^^^ +47 | I = 1 +48 | # ruff: enable[E501] + | -------------------- + | +help: Remove unused suppression +43 | def f(): +44 | # Neither of these are ignored and warnings are +45 | # logged to user + - # ruff: disable[E501] +46 | I = 1 + - # ruff: enable[E501] +47 | +48 | +49 | def f(): + +E741 Ambiguous variable name: `I` + --> suppressions.py:47:5 + | +45 | # logged to user +46 | # ruff: disable[E501] +47 | I = 1 + | ^ +48 | # ruff: enable[E501] + | + +F841 [*] Local variable `I` is assigned to but never used + --> suppressions.py:47:5 + | +45 | # logged to user +46 | # ruff: disable[E501] +47 | I = 1 + | ^ +48 | # ruff: enable[E501] + | +help: Remove assignment to unused variable `I` +44 | # Neither of these are ignored and warnings are +45 | # logged to user +46 | # ruff: disable[E501] + - I = 1 +47 + pass +48 | # ruff: enable[E501] +49 | +50 | +note: This is an unsafe fix and may change runtime behavior + +RUF100 [*] Unused `noqa` directive (unused: `E741`, `F841`) + --> suppressions.py:55:12 + | +53 | # and an unusued noqa diagnostic should be logged. +54 | # ruff:disable[E741,F841] +55 | I = 1 # noqa: E741,F841 + | ^^^^^^^^^^^^^^^^^ +56 | # ruff:enable[E741,F841] + | +help: Remove unused `noqa` directive +52 | # These should both be ignored by the range suppression, +53 | # and an unusued noqa diagnostic should be logged. +54 | # ruff:disable[E741,F841] + - I = 1 # noqa: E741,F841 +55 + I = 1 +56 | # ruff:enable[E741,F841] +57 | +58 | + +RUF104 Suppression comment without matching `#ruff:enable` comment + --> suppressions.py:61:5 + | +59 | def f(): +60 | # TODO: Duplicate codes should be counted as duplicate, not unused +61 | # ruff: disable[F841, F841] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +62 | foo = 0 + | + +RUF100 [*] Unused suppression (unused: `F841`) + --> suppressions.py:61:21 + | +59 | def f(): +60 | # TODO: Duplicate codes should be counted as duplicate, not unused +61 | # ruff: disable[F841, F841] + | ^^^^ +62 | foo = 0 + | +help: Remove unused suppression +58 | +59 | def f(): +60 | # TODO: Duplicate codes should be counted as duplicate, not unused + - # ruff: disable[F841, F841] +61 + # ruff: disable[F841] +62 | foo = 0 +63 | +64 | + +RUF104 Suppression comment without matching `#ruff:enable` comment + --> suppressions.py:68:5 + | +66 | # Overlapping range suppressions, one should be marked as used, +67 | # and the other should trigger an unused suppression diagnostic +68 | # ruff: disable[F841] + | ^^^^^^^^^^^^^^^^^^^^^ +69 | # ruff: disable[F841] +70 | foo = 0 + | + +RUF100 [*] Unused suppression (unused: `F841`) + --> suppressions.py:69:5 + | +67 | # and the other should trigger an unused suppression diagnostic +68 | # ruff: disable[F841] +69 | # ruff: disable[F841] + | ^^^^^^^^^^^^^^^^^^^^^ +70 | foo = 0 + | +help: Remove unused suppression +66 | # Overlapping range suppressions, one should be marked as used, +67 | # and the other should trigger an unused suppression diagnostic +68 | # ruff: disable[F841] + - # ruff: disable[F841] +69 | foo = 0 +70 | +71 | + +RUF104 Suppression comment without matching `#ruff:enable` comment + --> suppressions.py:75:5 + | +73 | def f(): +74 | # Multiple codes but only one is used +75 | # ruff: disable[E741, F401, F841] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +76 | foo = 0 + | + +RUF100 [*] Unused suppression (unused: `E741`) + --> suppressions.py:75:21 + | +73 | def f(): +74 | # Multiple codes but only one is used +75 | # ruff: disable[E741, F401, F841] + | ^^^^ +76 | foo = 0 + | +help: Remove unused suppression +72 | +73 | def f(): +74 | # Multiple codes but only one is used + - # ruff: disable[E741, F401, F841] +75 + # ruff: disable[F401, F841] +76 | foo = 0 +77 | +78 | + +RUF100 [*] Unused suppression (non-enabled: `F401`) + --> suppressions.py:75:27 + | +73 | def f(): +74 | # Multiple codes but only one is used +75 | # ruff: disable[E741, F401, F841] + | ^^^^ +76 | foo = 0 + | +help: Remove unused suppression +72 | +73 | def f(): +74 | # Multiple codes but only one is used + - # ruff: disable[E741, F401, F841] +75 + # ruff: disable[E741, F841] +76 | foo = 0 +77 | +78 | + +RUF104 Suppression comment without matching `#ruff:enable` comment + --> suppressions.py:81:5 + | +79 | def f(): +80 | # Multiple codes but only two are used +81 | # ruff: disable[E741, F401, F841] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +82 | I = 0 + | + +RUF100 [*] Unused suppression (non-enabled: `F401`) + --> suppressions.py:81:27 + | +79 | def f(): +80 | # Multiple codes but only two are used +81 | # ruff: disable[E741, F401, F841] + | ^^^^ +82 | I = 0 + | +help: Remove unused suppression +78 | +79 | def f(): +80 | # Multiple codes but only two are used + - # ruff: disable[E741, F401, F841] +81 + # ruff: disable[E741, F841] +82 | I = 0 +83 | +84 | + +RUF100 [*] Unused suppression (unused: `E741`) + --> suppressions.py:87:21 + | +85 | def f(): +86 | # Multiple codes but none are used +87 | # ruff: disable[E741, F401, F841] + | ^^^^ +88 | print("hello") + | +help: Remove unused suppression +84 | +85 | def f(): +86 | # Multiple codes but none are used + - # ruff: disable[E741, F401, F841] +87 + # ruff: disable[F401, F841] +88 | print("hello") +89 | +90 | + +RUF100 [*] Unused suppression (non-enabled: `F401`) + --> suppressions.py:87:27 + | +85 | def f(): +86 | # Multiple codes but none are used +87 | # ruff: disable[E741, F401, F841] + | ^^^^ +88 | print("hello") + | +help: Remove unused suppression +84 | +85 | def f(): +86 | # Multiple codes but none are used + - # ruff: disable[E741, F401, F841] +87 + # ruff: disable[E741, F841] +88 | print("hello") +89 | +90 | + +RUF100 [*] Unused suppression (unused: `F841`) + --> suppressions.py:87:33 + | +85 | def f(): +86 | # Multiple codes but none are used +87 | # ruff: disable[E741, F401, F841] + | ^^^^ +88 | print("hello") + | +help: Remove unused suppression +84 | +85 | def f(): +86 | # Multiple codes but none are used + - # ruff: disable[E741, F401, F841] +87 + # ruff: disable[E741, F401] +88 | print("hello") +89 | +90 | + +RUF102 [*] Invalid rule code in suppression: YF829 + --> suppressions.py:93:21 + | +91 | def f(): +92 | # Unknown rule codes +93 | # ruff: disable[YF829] + | ^^^^^ +94 | # ruff: disable[F841, RQW320] +95 | value = 0 +96 | # ruff: enable[F841, RQW320] +97 | # ruff: enable[YF829] + | ----- + | +help: Remove the rule code +90 | +91 | def f(): +92 | # Unknown rule codes + - # ruff: disable[YF829] +93 | # ruff: disable[F841, RQW320] +94 | value = 0 +95 | # ruff: enable[F841, RQW320] + - # ruff: enable[YF829] +96 | +97 | +98 | def f(): + +RUF102 [*] Invalid rule code in suppression: RQW320 + --> suppressions.py:94:27 + | +92 | # Unknown rule codes +93 | # ruff: disable[YF829] +94 | # ruff: disable[F841, RQW320] + | ^^^^^^ +95 | value = 0 +96 | # ruff: enable[F841, RQW320] + | ------ +97 | # ruff: enable[YF829] + | +help: Remove the rule code +91 | def f(): +92 | # Unknown rule codes +93 | # ruff: disable[YF829] + - # ruff: disable[F841, RQW320] +94 + # ruff: disable[F841] +95 | value = 0 + - # ruff: enable[F841, RQW320] +96 + # ruff: enable[F841] +97 | # ruff: enable[YF829] +98 | +99 | + +RUF103 [*] Invalid suppression comment: missing suppression codes like `[E501, ...]` + --> suppressions.py:109:5 + | +107 | def f(): +108 | # Empty or missing rule codes +109 | # ruff: disable + | ^^^^^^^^^^^^^^^ +110 | # ruff: disable[] +111 | print("hello") + | +help: Remove suppression comment +106 | +107 | def f(): +108 | # Empty or missing rule codes + - # ruff: disable +109 | # ruff: disable[] +110 | print("hello") +note: This is an unsafe fix and may change runtime behavior + +RUF103 [*] Invalid suppression comment: missing suppression codes like `[E501, ...]` + --> suppressions.py:110:5 + | +108 | # Empty or missing rule codes +109 | # ruff: disable +110 | # ruff: disable[] + | ^^^^^^^^^^^^^^^^^ +111 | print("hello") + | +help: Remove suppression comment +107 | def f(): +108 | # Empty or missing rule codes +109 | # ruff: disable + - # ruff: disable[] +110 | print("hello") +note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/suppression.rs b/crates/ruff_linter/src/suppression.rs index 4cb5deca69..cbb191a83a 100644 --- a/crates/ruff_linter/src/suppression.rs +++ b/crates/ruff_linter/src/suppression.rs @@ -1,7 +1,7 @@ use compact_str::CompactString; use core::fmt; use ruff_db::diagnostic::Diagnostic; -use ruff_diagnostics::{Edit, Fix}; +use ruff_diagnostics::{Applicability, Edit, Fix}; use ruff_python_ast::token::{TokenKind, Tokens}; use ruff_python_ast::whitespace::indentation; use rustc_hash::FxHashSet; @@ -13,7 +13,6 @@ use ruff_python_trivia::Cursor; use ruff_text_size::{Ranged, TextLen, TextRange, TextSize, TextSlice}; use smallvec::{SmallVec, smallvec}; -use crate::Locator; use crate::checkers::ast::LintContext; use crate::codes::Rule; use crate::fix::edits::delete_comment; @@ -24,6 +23,7 @@ use crate::rules::ruff::rules::{ UnmatchedSuppressionComment, UnusedCodes, UnusedNOQA, UnusedNOQAKind, code_is_valid, }; use crate::settings::LinterSettings; +use crate::{Locator, Violation}; #[derive(Clone, Debug, Eq, PartialEq)] enum SuppressionAction { @@ -85,11 +85,11 @@ pub(crate) struct Suppression { /// Range for which the suppression applies range: TextRange, - /// Any comments associated with the suppression - comments: SmallVec<[SuppressionComment; 2]>, - /// Whether this suppression actually suppressed a diagnostic used: Cell, + + disable_comment: Option, + enable_comment: Option, } #[derive(Copy, Clone, Debug)] @@ -171,23 +171,17 @@ impl Suppressions { if !code_is_valid(&suppression.code, &context.settings().external) { // InvalidRuleCode if context.is_rule_enabled(Rule::InvalidRuleCode) { - for comment in &suppression.comments { - let (range, edit) = Suppressions::delete_code_or_comment( - locator, - suppression, - comment, - true, - ); - context - .report_diagnostic( - InvalidRuleCode { - rule_code: suppression.code.to_string(), - kind: InvalidRuleCodeKind::Suppression, - }, - range, - ) - .set_fix(Fix::safe_edit(edit)); - } + Suppressions::report_suppression( + context, + locator, + suppression, + true, + Applicability::Safe, + InvalidRuleCode { + rule_code: suppression.code.to_string(), + kind: InvalidRuleCodeKind::Suppression, + }, + ); } } else if !suppression.used.get() { // UnusedNOQA @@ -197,40 +191,35 @@ impl Suppressions { ) else { continue; // "external" lint code, don't treat it as unused }; - for comment in &suppression.comments { - let (range, edit) = Suppressions::delete_code_or_comment( - locator, - suppression, - comment, - false, - ); - let codes = if context.is_rule_enabled(rule) { - UnusedCodes { - unmatched: vec![suppression.code.to_string()], - ..Default::default() - } - } else { - UnusedCodes { - disabled: vec![suppression.code.to_string()], - ..Default::default() - } - }; + let codes = if context.is_rule_enabled(rule) { + UnusedCodes { + unmatched: vec![suppression.code.to_string()], + ..Default::default() + } + } else { + UnusedCodes { + disabled: vec![suppression.code.to_string()], + ..Default::default() + } + }; - context - .report_diagnostic( - UnusedNOQA { - codes: Some(codes), - kind: UnusedNOQAKind::Suppression, - }, - range, - ) - .set_fix(Fix::safe_edit(edit)); - } + Suppressions::report_suppression( + context, + locator, + suppression, + false, + Applicability::Safe, + UnusedNOQA { + codes: Some(codes), + kind: UnusedNOQAKind::Suppression, + }, + ); } - } else if suppression.comments.len() == 1 { + } else if suppression.disable_comment.is_some() && suppression.enable_comment.is_none() + { // UnmatchedSuppressionComment - let range = suppression.comments[0].range; + let range = suppression.disable_comment.as_ref().unwrap().range; if unmatched_ranges.insert(range) { context.report_diagnostic_if_enabled(UnmatchedSuppressionComment {}, range); } @@ -267,6 +256,37 @@ impl Suppressions { } } + fn report_suppression( + context: &LintContext, + locator: &Locator, + suppression: &Suppression, + highlight_only_code: bool, + applicability: Applicability, + kind: T, + ) { + if suppression.disable_comment.is_some() { + let (range, edit) = Suppressions::delete_code_or_comment( + locator, + suppression, + suppression.disable_comment.as_ref().unwrap(), + highlight_only_code, + ); + let mut diagnostic = context.report_diagnostic(kind, range); + if suppression.enable_comment.is_some() { + let (range2, edit2) = Suppressions::delete_code_or_comment( + locator, + suppression, + suppression.enable_comment.as_ref().unwrap(), + highlight_only_code, + ); + diagnostic.secondary_annotation("", range2); + diagnostic.set_fix(Fix::applicable_edits(edit, vec![edit2], applicability)); + } else { + diagnostic.set_fix(Fix::applicable_edit(edit, applicability)); + } + } + } + fn delete_code_or_comment( locator: &Locator<'_>, suppression: &Suppression, @@ -424,7 +444,8 @@ impl<'a> SuppressionsBuilder<'a> { self.valid.push(Suppression { code: code.into(), range: combined_range, - comments: smallvec![comment.comment.clone(), other.comment.clone()], + disable_comment: Some(comment.comment.clone()), + enable_comment: Some(other.comment.clone()), used: false.into(), }); } @@ -441,7 +462,8 @@ impl<'a> SuppressionsBuilder<'a> { self.valid.push(Suppression { code: code.into(), range: implicit_range, - comments: smallvec![comment.comment.clone()], + disable_comment: Some(comment.comment.clone()), + enable_comment: None, used: false.into(), }); } @@ -705,24 +727,22 @@ print('hello') Suppression { covered_source: "# ruff: disable[foo]\nprint('hello')\n# ruff: enable[foo]", code: "foo", - comments: [ - SuppressionComment { - text: "# ruff: disable[foo]", - action: Disable, - codes: [ - "foo", - ], - reason: "", - }, - SuppressionComment { - text: "# ruff: enable[foo]", - action: Enable, - codes: [ - "foo", - ], - reason: "", - }, - ], + disable_comment: SuppressionComment { + text: "# ruff: disable[foo]", + action: Disable, + codes: [ + "foo", + ], + reason: "", + }, + enable_comment: SuppressionComment { + text: "# ruff: enable[foo]", + action: Enable, + codes: [ + "foo", + ], + reason: "", + }, }, ], invalid: [], @@ -751,30 +771,28 @@ def foo(): Suppression { covered_source: "# ruff: disable[bar]\n print('hello')\n\n", code: "bar", - comments: [ - SuppressionComment { - text: "# ruff: disable[bar]", - action: Disable, - codes: [ - "bar", - ], - reason: "", - }, - ], + disable_comment: SuppressionComment { + text: "# ruff: disable[bar]", + action: Disable, + codes: [ + "bar", + ], + reason: "", + }, + enable_comment: None, }, Suppression { covered_source: "# ruff: disable[foo]\nprint('hello')\n\ndef foo():\n # ruff: disable[bar]\n print('hello')\n\n", code: "foo", - comments: [ - SuppressionComment { - text: "# ruff: disable[foo]", - action: Disable, - codes: [ - "foo", - ], - reason: "", - }, - ], + disable_comment: SuppressionComment { + text: "# ruff: disable[foo]", + action: Disable, + codes: [ + "foo", + ], + reason: "", + }, + enable_comment: None, }, ], invalid: [], @@ -803,46 +821,42 @@ class Foo: Suppression { covered_source: "# ruff: disable[bar]\n print('hello')\n # ruff: enable[bar]", code: "bar", - comments: [ - SuppressionComment { - text: "# ruff: disable[bar]", - action: Disable, - codes: [ - "bar", - ], - reason: "", - }, - SuppressionComment { - text: "# ruff: enable[bar]", - action: Enable, - codes: [ - "bar", - ], - reason: "", - }, - ], + disable_comment: SuppressionComment { + text: "# ruff: disable[bar]", + action: Disable, + codes: [ + "bar", + ], + reason: "", + }, + enable_comment: SuppressionComment { + text: "# ruff: enable[bar]", + action: Enable, + codes: [ + "bar", + ], + reason: "", + }, }, Suppression { covered_source: "# ruff: disable[foo]\n def bar(self):\n # ruff: disable[bar]\n print('hello')\n # ruff: enable[bar]\n # ruff: enable[foo]", code: "foo", - comments: [ - SuppressionComment { - text: "# ruff: disable[foo]", - action: Disable, - codes: [ - "foo", - ], - reason: "", - }, - SuppressionComment { - text: "# ruff: enable[foo]", - action: Enable, - codes: [ - "foo", - ], - reason: "", - }, - ], + disable_comment: SuppressionComment { + text: "# ruff: disable[foo]", + action: Disable, + codes: [ + "foo", + ], + reason: "", + }, + enable_comment: SuppressionComment { + text: "# ruff: enable[foo]", + action: Enable, + codes: [ + "foo", + ], + reason: "", + }, }, ], invalid: [], @@ -872,46 +886,42 @@ def foo(): Suppression { covered_source: "# ruff: disable[foo]\n print('hello')\n # ruff: disable[bar]\n print('hello')\n # ruff: enable[foo]", code: "foo", - comments: [ - SuppressionComment { - text: "# ruff: disable[foo]", - action: Disable, - codes: [ - "foo", - ], - reason: "", - }, - SuppressionComment { - text: "# ruff: enable[foo]", - action: Enable, - codes: [ - "foo", - ], - reason: "", - }, - ], + disable_comment: SuppressionComment { + text: "# ruff: disable[foo]", + action: Disable, + codes: [ + "foo", + ], + reason: "", + }, + enable_comment: SuppressionComment { + text: "# ruff: enable[foo]", + action: Enable, + codes: [ + "foo", + ], + reason: "", + }, }, Suppression { covered_source: "# ruff: disable[bar]\n print('hello')\n # ruff: enable[foo]\n print('hello')\n # ruff: enable[bar]", code: "bar", - comments: [ - SuppressionComment { - text: "# ruff: disable[bar]", - action: Disable, - codes: [ - "bar", - ], - reason: "", - }, - SuppressionComment { - text: "# ruff: enable[bar]", - action: Enable, - codes: [ - "bar", - ], - reason: "", - }, - ], + disable_comment: SuppressionComment { + text: "# ruff: disable[bar]", + action: Disable, + codes: [ + "bar", + ], + reason: "", + }, + enable_comment: SuppressionComment { + text: "# ruff: enable[bar]", + action: Enable, + codes: [ + "bar", + ], + reason: "", + }, }, ], invalid: [], @@ -936,50 +946,46 @@ print('hello') Suppression { covered_source: "# ruff: disable[foo, bar]\nprint('hello')\n# ruff: enable[foo, bar]", code: "foo", - comments: [ - SuppressionComment { - text: "# ruff: disable[foo, bar]", - action: Disable, - codes: [ - "foo", - "bar", - ], - reason: "", - }, - SuppressionComment { - text: "# ruff: enable[foo, bar]", - action: Enable, - codes: [ - "foo", - "bar", - ], - reason: "", - }, - ], + disable_comment: SuppressionComment { + text: "# ruff: disable[foo, bar]", + action: Disable, + codes: [ + "foo", + "bar", + ], + reason: "", + }, + enable_comment: SuppressionComment { + text: "# ruff: enable[foo, bar]", + action: Enable, + codes: [ + "foo", + "bar", + ], + reason: "", + }, }, Suppression { covered_source: "# ruff: disable[foo, bar]\nprint('hello')\n# ruff: enable[foo, bar]", code: "bar", - comments: [ - SuppressionComment { - text: "# ruff: disable[foo, bar]", - action: Disable, - codes: [ - "foo", - "bar", - ], - reason: "", - }, - SuppressionComment { - text: "# ruff: enable[foo, bar]", - action: Enable, - codes: [ - "foo", - "bar", - ], - reason: "", - }, - ], + disable_comment: SuppressionComment { + text: "# ruff: disable[foo, bar]", + action: Disable, + codes: [ + "foo", + "bar", + ], + reason: "", + }, + enable_comment: SuppressionComment { + text: "# ruff: enable[foo, bar]", + action: Enable, + codes: [ + "foo", + "bar", + ], + reason: "", + }, }, ], invalid: [], @@ -1005,16 +1011,15 @@ print('world') Suppression { covered_source: "# ruff: disable[foo]\nprint('hello')\n# ruff: enable[bar]\nprint('world')\n", code: "foo", - comments: [ - SuppressionComment { - text: "# ruff: disable[foo]", - action: Disable, - codes: [ - "foo", - ], - reason: "", - }, - ], + disable_comment: SuppressionComment { + text: "# ruff: disable[foo]", + action: Disable, + codes: [ + "foo", + ], + reason: "", + }, + enable_comment: None, }, ], invalid: [ @@ -1051,32 +1056,30 @@ print('hello') Suppression { covered_source: "# ruff: disable[foo, bar]\nprint('hello')\n# ruff: enable[bar, foo]\n", code: "foo", - comments: [ - SuppressionComment { - text: "# ruff: disable[foo, bar]", - action: Disable, - codes: [ - "foo", - "bar", - ], - reason: "", - }, - ], + disable_comment: SuppressionComment { + text: "# ruff: disable[foo, bar]", + action: Disable, + codes: [ + "foo", + "bar", + ], + reason: "", + }, + enable_comment: None, }, Suppression { covered_source: "# ruff: disable[foo, bar]\nprint('hello')\n# ruff: enable[bar, foo]\n", code: "bar", - comments: [ - SuppressionComment { - text: "# ruff: disable[foo, bar]", - action: Disable, - codes: [ - "foo", - "bar", - ], - reason: "", - }, - ], + disable_comment: SuppressionComment { + text: "# ruff: disable[foo, bar]", + action: Disable, + codes: [ + "foo", + "bar", + ], + reason: "", + }, + enable_comment: None, }, ], invalid: [ @@ -1116,38 +1119,35 @@ print('hello') Suppression { covered_source: "# ruff: disable[foo] first\nprint('hello')\n# ruff: disable[foo] second\nprint('hello')\n# ruff: enable[foo]", code: "foo", - comments: [ - SuppressionComment { - text: "# ruff: disable[foo] first", - action: Disable, - codes: [ - "foo", - ], - reason: "first", - }, - SuppressionComment { - text: "# ruff: enable[foo]", - action: Enable, - codes: [ - "foo", - ], - reason: "", - }, - ], + disable_comment: SuppressionComment { + text: "# ruff: disable[foo] first", + action: Disable, + codes: [ + "foo", + ], + reason: "first", + }, + enable_comment: SuppressionComment { + text: "# ruff: enable[foo]", + action: Enable, + codes: [ + "foo", + ], + reason: "", + }, }, Suppression { covered_source: "# ruff: disable[foo] second\nprint('hello')\n# ruff: enable[foo]\n", code: "foo", - comments: [ - SuppressionComment { - text: "# ruff: disable[foo] second", - action: Disable, - codes: [ - "foo", - ], - reason: "second", - }, - ], + disable_comment: SuppressionComment { + text: "# ruff: disable[foo] second", + action: Disable, + codes: [ + "foo", + ], + reason: "second", + }, + enable_comment: None, }, ], invalid: [], @@ -1189,100 +1189,92 @@ def bar(): Suppression { covered_source: "# ruff: disable[delta] unmatched\n pass\n # ruff: enable[beta,gamma]\n# ruff: enable[alpha]\n\n# ruff: disable # parse error!\n", code: "delta", - comments: [ - SuppressionComment { - text: "# ruff: disable[delta] unmatched", - action: Disable, - codes: [ - "delta", - ], - reason: "unmatched", - }, - ], + disable_comment: SuppressionComment { + text: "# ruff: disable[delta] unmatched", + action: Disable, + codes: [ + "delta", + ], + reason: "unmatched", + }, + enable_comment: None, }, Suppression { covered_source: "# ruff: disable[beta,gamma]\n if True:\n # ruff: disable[delta] unmatched\n pass\n # ruff: enable[beta,gamma]", code: "beta", - comments: [ - SuppressionComment { - text: "# ruff: disable[beta,gamma]", - action: Disable, - codes: [ - "beta", - "gamma", - ], - reason: "", - }, - SuppressionComment { - text: "# ruff: enable[beta,gamma]", - action: Enable, - codes: [ - "beta", - "gamma", - ], - reason: "", - }, - ], + disable_comment: SuppressionComment { + text: "# ruff: disable[beta,gamma]", + action: Disable, + codes: [ + "beta", + "gamma", + ], + reason: "", + }, + enable_comment: SuppressionComment { + text: "# ruff: enable[beta,gamma]", + action: Enable, + codes: [ + "beta", + "gamma", + ], + reason: "", + }, }, Suppression { covered_source: "# ruff: disable[beta,gamma]\n if True:\n # ruff: disable[delta] unmatched\n pass\n # ruff: enable[beta,gamma]", code: "gamma", - comments: [ - SuppressionComment { - text: "# ruff: disable[beta,gamma]", - action: Disable, - codes: [ - "beta", - "gamma", - ], - reason: "", - }, - SuppressionComment { - text: "# ruff: enable[beta,gamma]", - action: Enable, - codes: [ - "beta", - "gamma", - ], - reason: "", - }, - ], + disable_comment: SuppressionComment { + text: "# ruff: disable[beta,gamma]", + action: Disable, + codes: [ + "beta", + "gamma", + ], + reason: "", + }, + enable_comment: SuppressionComment { + text: "# ruff: enable[beta,gamma]", + action: Enable, + codes: [ + "beta", + "gamma", + ], + reason: "", + }, }, Suppression { covered_source: "# ruff: disable[zeta] unmatched\n pass\n# ruff: enable[zeta] underindented\n pass\n", code: "zeta", - comments: [ - SuppressionComment { - text: "# ruff: disable[zeta] unmatched", - action: Disable, - codes: [ - "zeta", - ], - reason: "unmatched", - }, - ], + disable_comment: SuppressionComment { + text: "# ruff: disable[zeta] unmatched", + action: Disable, + codes: [ + "zeta", + ], + reason: "unmatched", + }, + enable_comment: None, }, Suppression { covered_source: "# ruff: disable[alpha]\ndef foo():\n # ruff: disable[beta,gamma]\n if True:\n # ruff: disable[delta] unmatched\n pass\n # ruff: enable[beta,gamma]\n# ruff: enable[alpha]", code: "alpha", - comments: [ - SuppressionComment { - text: "# ruff: disable[alpha]", - action: Disable, - codes: [ - "alpha", - ], - reason: "", - }, - SuppressionComment { - text: "# ruff: enable[alpha]", - action: Enable, - codes: [ - "alpha", - ], - reason: "", - }, - ], + disable_comment: SuppressionComment { + text: "# ruff: disable[alpha]", + action: Disable, + codes: [ + "alpha", + ], + reason: "", + }, + enable_comment: SuppressionComment { + text: "# ruff: enable[alpha]", + action: Enable, + codes: [ + "alpha", + ], + reason: "", + }, }, ], invalid: [ @@ -1559,7 +1551,10 @@ def bar(): TextRange::new(offset, TextSize::try_from(source.len()).unwrap()), ); match parser.parse_comment() { - Ok(comment) => Ok(DebugSuppressionComment { source, comment }), + Ok(comment) => Ok(DebugSuppressionComment { + source, + comment: Some(comment), + }), Err(error) => Err(error), } } @@ -1639,16 +1634,18 @@ def bar(): .field("covered_source", &&self.source[self.suppression.range]) .field("code", &self.suppression.code) .field( - "comments", - &self - .suppression - .comments - .iter() - .map(|comment| DebugSuppressionComment { - source: self.source, - comment: comment.clone(), - }) - .collect_vec(), + "disable_comment", + &DebugSuppressionComment { + source: self.source, + comment: self.suppression.disable_comment.clone(), + }, + ) + .field( + "enable_comment", + &DebugSuppressionComment { + source: self.source, + comment: self.suppression.enable_comment.clone(), + }, ) .finish() } @@ -1667,7 +1664,7 @@ def bar(): "comment", &DebugSuppressionComment { source: self.source, - comment: self.invalid.comment.clone(), + comment: Some(self.invalid.comment.clone()), }, ) .finish() @@ -1690,23 +1687,27 @@ def bar(): struct DebugSuppressionComment<'a> { source: &'a str, - comment: SuppressionComment, + comment: Option, } impl fmt::Debug for DebugSuppressionComment<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("SuppressionComment") - .field("text", &&self.source[self.comment.range]) - .field("action", &self.comment.action) - .field( - "codes", - &DebugCodes { - source: self.source, - codes: &self.comment.codes, - }, - ) - .field("reason", &&self.source[self.comment.reason]) - .finish() + match &self.comment { + Some(comment) => f + .debug_struct("SuppressionComment") + .field("text", &&self.source[comment.range]) + .field("action", &comment.action) + .field( + "codes", + &DebugCodes { + source: self.source, + codes: &comment.codes, + }, + ) + .field("reason", &&self.source[comment.reason]) + .finish(), + None => f.debug_tuple("None").finish(), + } } }