Skip to content

Commit

Permalink
Respect Q00* ignores in flake8-quotes rules (#10728)
Browse files Browse the repository at this point in the history
## Summary

We lost the per-rule ignores when these were migrated to the AST, so if
_any_ `Q` rule is enabled, they're now all enabled.

Closes #10724.

## Test Plan

Ran:

```shell
ruff check . --isolated --select Q --ignore Q000
ruff check . --isolated --select Q --ignore Q001
ruff check . --isolated --select Q --ignore Q002
ruff check . --isolated --select Q --ignore Q000,Q001
ruff check . --isolated --select Q --ignore Q000,Q002
ruff check . --isolated --select Q --ignore Q001,Q002
```

...against:

```python
'''
bad docsting
'''
a = 'single'
b = '''
bad multi line
'''
```
  • Loading branch information
charliermarsh committed Apr 2, 2024
1 parent d36f609 commit 7b48443
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 3 deletions.
@@ -0,0 +1,7 @@
"""This is a docstring."""

this_is_an_inline_string = "double quote string"

this_is_a_multiline_string = """
double quote string
"""
57 changes: 57 additions & 0 deletions crates/ruff_linter/src/rules/flake8_quotes/mod.rs
Expand Up @@ -195,4 +195,61 @@ mod tests {
assert_messages!(snapshot, diagnostics);
Ok(())
}

#[test_case(Path::new("doubles_all.py"))]
fn only_inline(path: &Path) -> Result<()> {
let snapshot = format!("only_inline_{}", path.to_string_lossy());
let diagnostics = test_path(
Path::new("flake8_quotes").join(path).as_path(),
&LinterSettings {
flake8_quotes: super::settings::Settings {
inline_quotes: Quote::Single,
multiline_quotes: Quote::Single,
docstring_quotes: Quote::Single,
avoid_escape: true,
},
..LinterSettings::for_rules(vec![Rule::BadQuotesInlineString])
},
)?;
assert_messages!(snapshot, diagnostics);
Ok(())
}

#[test_case(Path::new("doubles_all.py"))]
fn only_multiline(path: &Path) -> Result<()> {
let snapshot = format!("only_multiline_{}", path.to_string_lossy());
let diagnostics = test_path(
Path::new("flake8_quotes").join(path).as_path(),
&LinterSettings {
flake8_quotes: super::settings::Settings {
inline_quotes: Quote::Single,
multiline_quotes: Quote::Single,
docstring_quotes: Quote::Single,
avoid_escape: true,
},
..LinterSettings::for_rules(vec![Rule::BadQuotesMultilineString])
},
)?;
assert_messages!(snapshot, diagnostics);
Ok(())
}

#[test_case(Path::new("doubles_all.py"))]
fn only_docstring(path: &Path) -> Result<()> {
let snapshot = format!("only_docstring_{}", path.to_string_lossy());
let diagnostics = test_path(
Path::new("flake8_quotes").join(path).as_path(),
&LinterSettings {
flake8_quotes: super::settings::Settings {
inline_quotes: Quote::Single,
multiline_quotes: Quote::Single,
docstring_quotes: Quote::Single,
avoid_escape: true,
},
..LinterSettings::for_rules(vec![Rule::BadQuotesDocstring])
},
)?;
assert_messages!(snapshot, diagnostics);
Ok(())
}
}
Expand Up @@ -5,6 +5,7 @@ use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextRange};

use crate::checkers::ast::Checker;
use crate::registry::Rule;

use super::super::settings::Quote;

Expand Down Expand Up @@ -334,6 +335,11 @@ fn strings(checker: &mut Checker, sequence: &[TextRange]) {

for (range, trivia) in sequence.iter().zip(trivia) {
if trivia.is_multiline {
// If multiline strings aren't enforced, ignore it.
if !checker.enabled(Rule::BadQuotesMultilineString) {
continue;
}

// If our string is or contains a known good string, ignore it.
if trivia
.raw_text
Expand Down Expand Up @@ -375,6 +381,11 @@ fn strings(checker: &mut Checker, sequence: &[TextRange]) {
// If we're not using the preferred type, only allow use to avoid escapes.
&& !relax_quote
{
// If inline strings aren't enforced, ignore it.
if !checker.enabled(Rule::BadQuotesInlineString) {
continue;
}

if trivia.has_empty_text()
&& text_ends_at_quote(locator, *range, quotes_settings.inline_quotes)
{
Expand Down Expand Up @@ -455,10 +466,14 @@ pub(crate) fn check_string_quotes(checker: &mut Checker, string_like: StringLike
};

if checker.semantic().in_docstring() {
for range in ranges {
docstring(checker, range);
if checker.enabled(Rule::BadQuotesDocstring) {
for range in ranges {
docstring(checker, range);
}
}
} else {
strings(checker, &ranges);
if checker.any_enabled(&[Rule::BadQuotesInlineString, Rule::BadQuotesMultilineString]) {
strings(checker, &ranges);
}
}
}
@@ -0,0 +1,18 @@
---
source: crates/ruff_linter/src/rules/flake8_quotes/mod.rs
---
doubles_all.py:1:1: Q002 [*] Double quote docstring found but single quotes preferred
|
1 | """This is a docstring."""
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ Q002
2 |
3 | this_is_an_inline_string = "double quote string"
|
= help: Replace double quotes docstring with single quotes

Safe fix
1 |-"""This is a docstring."""
1 |+'''This is a docstring.'''
2 2 |
3 3 | this_is_an_inline_string = "double quote string"
4 4 |
@@ -0,0 +1,22 @@
---
source: crates/ruff_linter/src/rules/flake8_quotes/mod.rs
---
doubles_all.py:3:28: Q000 [*] Double quotes found but single quotes preferred
|
1 | """This is a docstring."""
2 |
3 | this_is_an_inline_string = "double quote string"
| ^^^^^^^^^^^^^^^^^^^^^ Q000
4 |
5 | this_is_a_multiline_string = """
|
= help: Replace double quotes with single quotes

Safe fix
1 1 | """This is a docstring."""
2 2 |
3 |-this_is_an_inline_string = "double quote string"
3 |+this_is_an_inline_string = 'double quote string'
4 4 |
5 5 | this_is_a_multiline_string = """
6 6 | double quote string
@@ -0,0 +1,24 @@
---
source: crates/ruff_linter/src/rules/flake8_quotes/mod.rs
---
doubles_all.py:5:30: Q001 [*] Double quote multiline found but single quotes preferred
|
3 | this_is_an_inline_string = "double quote string"
4 |
5 | this_is_a_multiline_string = """
| ______________________________^
6 | | double quote string
7 | | """
| |___^ Q001
|
= help: Replace double multiline quotes with single quotes

Safe fix
2 2 |
3 3 | this_is_an_inline_string = "double quote string"
4 4 |
5 |-this_is_a_multiline_string = """
5 |+this_is_a_multiline_string = '''
6 6 | double quote string
7 |-"""
7 |+'''

0 comments on commit 7b48443

Please sign in to comment.