Generate verbatim nodes

This commit is contained in:
Charlie Marsh 2024-01-28 21:10:24 -05:00
parent 7329bf459c
commit 00b3663b2d
14 changed files with 80 additions and 49 deletions

1
Cargo.lock generated
View File

@ -2287,6 +2287,7 @@ dependencies = [
"ruff_python_literal",
"ruff_python_parser",
"ruff_source_file",
"ruff_text_size",
]
[[package]]

View File

@ -180,6 +180,7 @@ impl<'a> Checker<'a> {
self.f_string_quote_style().unwrap_or(self.stylist.quote()),
self.stylist.line_ending(),
)
.with_locator(self.locator)
}
/// Returns the appropriate quoting for f-string by reversing the one used outside of

View File

@ -360,20 +360,6 @@ S608.py:48:12: S608 Possible SQL injection vector through string-based query con
54 | def query38():
|
S608.py:55:12: S608 Possible SQL injection vector through string-based query construction
|
54 | def query38():
55 | return """
| ____________^
56 | | SELECT *
57 | | FROM TABLE
58 | | WHERE var =
59 | | """ + var
| |_____________^ S608
60 |
61 | def query39():
|
S608.py:62:12: S608 Possible SQL injection vector through string-based query construction
|
61 | def query39():

View File

@ -79,14 +79,15 @@ B006_B008.py:77:31: B006 [*] Do not use mutable data structures for argument def
75 75 |
76 76 |
77 |-def multiline_arg_wrong(value={
78 |-
79 |-}):
77 |+def multiline_arg_wrong(value=None):
78 |+ if value is None:
79 |+ value = {}
80 80 | ...
81 81 |
82 82 | def single_line_func_wrong(value = {}): ...
79 |+ value = {
78 80 |
79 |-}):
81 |+ }
80 82 | ...
81 83 |
82 84 | def single_line_func_wrong(value = {}): ...
B006_B008.py:82:36: B006 Do not use mutable data structures for argument defaults
|
@ -486,10 +487,12 @@ B006_B008.py:302:52: B006 [*] Do not use mutable data structures for argument de
302 |+def single_line_func_wrong(value: dict[str, str] = None):
305 303 | """Docstring"""
304 |+ if value is None:
305 |+ value = {}
306 306 |
307 307 |
308 308 | def single_line_func_wrong(value: dict[str, str] = {}) \
305 |+ value = {
306 |+ # This is a comment
307 |+ }
306 308 |
307 309 |
308 310 | def single_line_func_wrong(value: dict[str, str] = {}) \
B006_B008.py:308:52: B006 Do not use mutable data structures for argument defaults
|

View File

@ -195,7 +195,10 @@ pub(crate) fn multiple_starts_ends_with(checker: &mut Checker, expr: &Expr) {
});
let bool_op = node;
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
checker.generator().expr(&bool_op),
checker
.generator()
.with_locator(checker.locator())
.expr(&bool_op),
expr.range(),
)));
checker.diagnostics.push(diagnostic);

View File

@ -382,7 +382,9 @@ PYI016.py:44:30: PYI016 [*] Duplicate union member `typing.Literal[1]`
46 46 | # Shouldn't emit if in new parent type
47 47 | field16: int | dict[int, str] # OK
PYI016.py:57:5: PYI016 Duplicate union member `set[int]`
PYI016.py:57:5: PYI016 Duplicate union member `set[
int # bar
]`
|
55 | int # foo
56 | ],
@ -393,7 +395,9 @@ PYI016.py:57:5: PYI016 Duplicate union member `set[int]`
| |_____^ PYI016
60 | ] # Error, newline and comment will not be emitted in message
|
= help: Remove duplicate union member `set[int]`
= help: Remove duplicate union member `set[
int # bar
]`
PYI016.py:63:28: PYI016 Duplicate union member `int`
|

View File

@ -382,7 +382,9 @@ PYI016.pyi:44:30: PYI016 [*] Duplicate union member `typing.Literal[1]`
46 46 | # Shouldn't emit if in new parent type
47 47 | field16: int | dict[int, str] # OK
PYI016.pyi:57:5: PYI016 Duplicate union member `set[int]`
PYI016.pyi:57:5: PYI016 Duplicate union member `set[
int # bar
]`
|
55 | int # foo
56 | ],
@ -393,7 +395,9 @@ PYI016.pyi:57:5: PYI016 Duplicate union member `set[int]`
| |_____^ PYI016
60 | ] # Error, newline and comment will not be emitted in message
|
= help: Remove duplicate union member `set[int]`
= help: Remove duplicate union member `set[
int # bar
]`
PYI016.pyi:63:28: PYI016 Duplicate union member `int`
|

View File

@ -498,7 +498,7 @@ PT009.py:73:9: PT009 [*] Use a regular `assert` instead of unittest-style `asser
71 71 |
72 72 | def test_assert_regex(self):
73 |- self.assertRegex("abc", r"def") # Error
73 |+ assert re.search("def", "abc") # Error
73 |+ assert re.search(r"def", "abc") # Error
74 74 |
75 75 | def test_assert_not_regex(self):
76 76 | self.assertNotRegex("abc", r"abc") # Error
@ -518,7 +518,7 @@ PT009.py:76:9: PT009 [*] Use a regular `assert` instead of unittest-style `asser
74 74 |
75 75 | def test_assert_not_regex(self):
76 |- self.assertNotRegex("abc", r"abc") # Error
76 |+ assert not re.search("abc", "abc") # Error
76 |+ assert not re.search(r"abc", "abc") # Error
77 77 |
78 78 | def test_assert_regexp_matches(self):
79 79 | self.assertRegexpMatches("abc", r"def") # Error
@ -538,7 +538,7 @@ PT009.py:79:9: PT009 [*] Use a regular `assert` instead of unittest-style `asser
77 77 |
78 78 | def test_assert_regexp_matches(self):
79 |- self.assertRegexpMatches("abc", r"def") # Error
79 |+ assert re.search("def", "abc") # Error
79 |+ assert re.search(r"def", "abc") # Error
80 80 |
81 81 | def test_assert_not_regexp_matches(self):
82 82 | self.assertNotRegex("abc", r"abc") # Error
@ -558,7 +558,7 @@ PT009.py:82:9: PT009 [*] Use a regular `assert` instead of unittest-style `asser
80 80 |
81 81 | def test_assert_not_regexp_matches(self):
82 |- self.assertNotRegex("abc", r"abc") # Error
82 |+ assert not re.search("abc", "abc") # Error
82 |+ assert not re.search(r"abc", "abc") # Error
83 83 |
84 84 | def test_fail_if(self):
85 85 | self.failIf("abc") # Error
@ -658,6 +658,7 @@ PT009.py:98:2: PT009 [*] Use a regular `assert` instead of unittest-style `asser
98 |-(self.assertTrue(
99 |- "piAx_piAy_beta[r][x][y] = {17}".format(
100 |- self.model.piAx_piAy_beta[r][x][y])))
98 |+assert "piAx_piAy_beta[r][x][y] = {17}".format(self.model.piAx_piAy_beta[r][x][y])
98 |+assert "piAx_piAy_beta[r][x][y] = {17}".format(
99 |+ self.model.piAx_piAy_beta[r][x][y])

View File

@ -534,7 +534,7 @@ SIM222.py:85:6: SIM222 [*] Use `True` instead of `... or True`
87 87 | a or (1,) or True or (2,) # SIM222
88 88 |
SIM222.py:87:6: SIM222 [*] Use `(1,)` instead of `(1,) or ...`
SIM222.py:87:6: SIM222 [*] Use `((1,))` instead of `((1,)) or ...`
|
85 | a or tuple(()) or True # SIM222
86 |
@ -543,14 +543,14 @@ SIM222.py:87:6: SIM222 [*] Use `(1,)` instead of `(1,) or ...`
88 |
89 | a or tuple((1,)) or True or tuple((2,)) # SIM222
|
= help: Replace with `(1,)`
= help: Replace with `((1,))`
Unsafe fix
84 84 |
85 85 | a or tuple(()) or True # SIM222
86 86 |
87 |-a or (1,) or True or (2,) # SIM222
87 |+a or (1,) # SIM222
87 |+a or ((1,)) # SIM222
88 88 |
89 89 | a or tuple((1,)) or True or tuple((2,)) # SIM222
90 90 |
@ -1006,39 +1006,39 @@ SIM222.py:153:11: SIM222 [*] Use `[1]` instead of `[1] or ...`
155 155 |
156 156 | # Regression test for: https://github.com/astral-sh/ruff/issues/7099
SIM222.py:157:30: SIM222 [*] Use `(int, int, int)` instead of `(int, int, int) or ...`
SIM222.py:157:30: SIM222 [*] Use `((int, int, int))` instead of `((int, int, int)) or ...`
|
156 | # Regression test for: https://github.com/astral-sh/ruff/issues/7099
157 | def secondToTime(s0: int) -> (int, int, int) or str:
| ^^^^^^^^^^^^^^^^^^^^^^ SIM222
158 | m, s = divmod(s0, 60)
|
= help: Replace with `(int, int, int)`
= help: Replace with `((int, int, int))`
Unsafe fix
154 154 | pass
155 155 |
156 156 | # Regression test for: https://github.com/astral-sh/ruff/issues/7099
157 |-def secondToTime(s0: int) -> (int, int, int) or str:
157 |+def secondToTime(s0: int) -> (int, int, int):
157 |+def secondToTime(s0: int) -> ((int, int, int)):
158 158 | m, s = divmod(s0, 60)
159 159 |
160 160 |
SIM222.py:161:31: SIM222 [*] Use `(int, int, int)` instead of `(int, int, int) or ...`
SIM222.py:161:31: SIM222 [*] Use `((int, int, int))` instead of `((int, int, int)) or ...`
|
161 | def secondToTime(s0: int) -> ((int, int, int) or str):
| ^^^^^^^^^^^^^^^^^^^^^^ SIM222
162 | m, s = divmod(s0, 60)
|
= help: Replace with `(int, int, int)`
= help: Replace with `((int, int, int))`
Unsafe fix
158 158 | m, s = divmod(s0, 60)
159 159 |
160 160 |
161 |-def secondToTime(s0: int) -> ((int, int, int) or str):
161 |+def secondToTime(s0: int) -> ((int, int, int)):
161 |+def secondToTime(s0: int) -> (((int, int, int))):
162 162 | m, s = divmod(s0, 60)
163 163 |
164 164 |

View File

@ -10,7 +10,7 @@ magic_value_comparison.py:59:22: PLR2004 Magic value used in comparison, conside
60 | pass
|
magic_value_comparison.py:65:21: PLR2004 Magic value used in comparison, consider replacing 3.141592653589793 with a constant variable
magic_value_comparison.py:65:21: PLR2004 Magic value used in comparison, consider replacing 3.141592653589793238 with a constant variable
|
63 | pi_estimation = 3.14
64 |

View File

@ -17,6 +17,7 @@ ruff_python_ast = { path = "../ruff_python_ast" }
ruff_python_literal = { path = "../ruff_python_literal" }
ruff_python_parser = { path = "../ruff_python_parser" }
ruff_source_file = { path = "../ruff_source_file" }
ruff_text_size = { path = "../ruff_text_size" }
once_cell = { workspace = true }

View File

@ -10,7 +10,8 @@ use ruff_python_ast::{
};
use ruff_python_ast::{ParameterWithDefault, TypeParams};
use ruff_python_literal::escape::{AsciiEscape, Escape, UnicodeEscape};
use ruff_source_file::LineEnding;
use ruff_source_file::{LineEnding, Locator};
use ruff_text_size::{Ranged, TextRange};
use super::stylist::{Indentation, Quote, Stylist};
@ -61,6 +62,11 @@ mod precedence {
pub(crate) const MAX: u8 = 63;
}
pub struct Verbatim<'a> {
locator: &'a Locator<'a>,
nodes: Vec<TextRange>,
}
pub struct Generator<'a> {
/// The indentation style to use.
indent: &'a Indentation,
@ -72,6 +78,7 @@ pub struct Generator<'a> {
indent_depth: usize,
num_newlines: usize,
initial: bool,
locator: Option<&'a Locator<'a>>,
}
impl<'a> From<&'a Stylist<'a>> for Generator<'a> {
@ -84,6 +91,7 @@ impl<'a> From<&'a Stylist<'a>> for Generator<'a> {
indent_depth: 0,
num_newlines: 0,
initial: true,
locator: None,
}
}
}
@ -100,6 +108,15 @@ impl<'a> Generator<'a> {
indent_depth: 0,
num_newlines: 0,
initial: true,
locator: None,
}
}
#[must_use]
pub fn with_locator(self, locator: &'a Locator<'a>) -> Self {
Self {
locator: Some(locator),
..self
}
}
@ -796,6 +813,14 @@ impl<'a> Generator<'a> {
ret
}};
}
if let Some(locator) = &self.locator {
if !ast.range().is_empty() {
self.p(locator.slice(ast));
return;
}
}
match ast {
Expr::BoolOp(ast::ExprBoolOp {
op,

2
foo.py Normal file
View File

@ -0,0 +1,2 @@
if x.startswith(("a", "b")) or re.match(r"a\.b", x):
pass