-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
## Summary add [`FURB167`/`use-long-regex-flag`](https://github.com/dosisod/refurb/blob/master/refurb/checks/regex/use_long_flag.py) with autofix See: #1348 ## Test Plan `cargo test` --------- Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
- Loading branch information
1 parent
0c0d3db
commit 6183b8e
Showing
8 changed files
with
211 additions
and
0 deletions.
There are no files selected for viewing
22 changes: 22 additions & 0 deletions
22
crates/ruff_linter/resources/test/fixtures/refurb/FURB167.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
def func(): | ||
import re | ||
|
||
# OK | ||
if re.match("^hello", "hello world", re.IGNORECASE): | ||
pass | ||
|
||
|
||
def func(): | ||
import re | ||
|
||
# FURB167 | ||
if re.match("^hello", "hello world", re.I): | ||
pass | ||
|
||
|
||
def func(): | ||
from re import match, I | ||
|
||
# FURB167 | ||
if match("^hello", "hello world", I): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
133 changes: 133 additions & 0 deletions
133
crates/ruff_linter/src/rules/refurb/rules/regex_flag_alias.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; | ||
use ruff_macros::{derive_message_formats, violation}; | ||
use ruff_python_ast::Expr; | ||
use ruff_text_size::Ranged; | ||
|
||
use crate::checkers::ast::Checker; | ||
use crate::importer::ImportRequest; | ||
|
||
/// ## What it does | ||
/// Checks for the use of shorthand aliases for regular expression flags | ||
/// (e.g., `re.I` instead of `re.IGNORECASE`). | ||
/// | ||
/// ## Why is this bad? | ||
/// The regular expression module provides descriptive names for each flag, | ||
/// along with single-letter aliases. Prefer the descriptive names, as they | ||
/// are more readable and self-documenting. | ||
/// | ||
/// ## Example | ||
/// ```python | ||
/// import re | ||
/// | ||
/// if re.match("^hello", "hello world", re.I): | ||
/// ... | ||
/// ``` | ||
/// | ||
/// Use instead: | ||
/// ```python | ||
/// import re | ||
/// | ||
/// if re.match("^hello", "hello world", re.IGNORECASE): | ||
/// ... | ||
/// ``` | ||
/// | ||
#[violation] | ||
pub struct RegexFlagAlias { | ||
alias: &'static str, | ||
full_name: &'static str, | ||
} | ||
|
||
impl AlwaysFixableViolation for RegexFlagAlias { | ||
#[derive_message_formats] | ||
fn message(&self) -> String { | ||
let RegexFlagAlias { alias, .. } = self; | ||
format!("Use of regular expression alias `re.{alias}`") | ||
} | ||
|
||
fn fix_title(&self) -> String { | ||
let RegexFlagAlias { full_name, .. } = self; | ||
format!("Replace with `re.{full_name}`") | ||
} | ||
} | ||
|
||
/// FURB167 | ||
pub(crate) fn regex_flag_alias(checker: &mut Checker, expr: &Expr) { | ||
let Some(flag) = | ||
checker | ||
.semantic() | ||
.resolve_call_path(expr) | ||
.and_then(|call_path| match call_path.as_slice() { | ||
["re", "A"] => Some(RegexFlag::Ascii), | ||
["re", "I"] => Some(RegexFlag::IgnoreCase), | ||
["re", "L"] => Some(RegexFlag::Locale), | ||
["re", "M"] => Some(RegexFlag::Multiline), | ||
["re", "S"] => Some(RegexFlag::DotAll), | ||
["re", "T"] => Some(RegexFlag::Template), | ||
["re", "U"] => Some(RegexFlag::Unicode), | ||
["re", "X"] => Some(RegexFlag::Verbose), | ||
_ => None, | ||
}) | ||
else { | ||
return; | ||
}; | ||
|
||
let mut diagnostic = Diagnostic::new( | ||
RegexFlagAlias { | ||
alias: flag.alias(), | ||
full_name: flag.full_name(), | ||
}, | ||
expr.range(), | ||
); | ||
diagnostic.try_set_fix(|| { | ||
let (edit, binding) = checker.importer().get_or_import_symbol( | ||
&ImportRequest::import("re", flag.full_name()), | ||
expr.start(), | ||
checker.semantic(), | ||
)?; | ||
Ok(Fix::safe_edits( | ||
Edit::range_replacement(binding, expr.range()), | ||
[edit], | ||
)) | ||
}); | ||
checker.diagnostics.push(diagnostic); | ||
} | ||
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
enum RegexFlag { | ||
Ascii, | ||
IgnoreCase, | ||
Locale, | ||
Multiline, | ||
DotAll, | ||
Template, | ||
Unicode, | ||
Verbose, | ||
} | ||
|
||
impl RegexFlag { | ||
fn alias(self) -> &'static str { | ||
match self { | ||
Self::Ascii => "A", | ||
Self::IgnoreCase => "I", | ||
Self::Locale => "L", | ||
Self::Multiline => "M", | ||
Self::DotAll => "S", | ||
Self::Template => "T", | ||
Self::Unicode => "U", | ||
Self::Verbose => "X", | ||
} | ||
} | ||
|
||
fn full_name(self) -> &'static str { | ||
match self { | ||
Self::Ascii => "ASCII", | ||
Self::IgnoreCase => "IGNORECASE", | ||
Self::Locale => "LOCALE", | ||
Self::Multiline => "MULTILINE", | ||
Self::DotAll => "DOTALL", | ||
Self::Template => "TEMPLATE", | ||
Self::Unicode => "UNICODE", | ||
Self::Verbose => "VERBOSE", | ||
} | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
...ter/src/rules/refurb/snapshots/ruff_linter__rules__refurb__tests__FURB167_FURB167.py.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
--- | ||
source: crates/ruff_linter/src/rules/refurb/mod.rs | ||
--- | ||
FURB167.py:13:42: FURB167 [*] Use of regular expression alias `re.I` | ||
| | ||
12 | # FURB167 | ||
13 | if re.match("^hello", "hello world", re.I): | ||
| ^^^^ FURB167 | ||
14 | pass | ||
| | ||
= help: Replace with `re.IGNORECASE` | ||
|
||
ℹ Safe fix | ||
10 10 | import re | ||
11 11 | | ||
12 12 | # FURB167 | ||
13 |- if re.match("^hello", "hello world", re.I): | ||
13 |+ if re.match("^hello", "hello world", re.IGNORECASE): | ||
14 14 | pass | ||
15 15 | | ||
16 16 | | ||
|
||
FURB167.py:21:39: FURB167 [*] Use of regular expression alias `re.I` | ||
| | ||
20 | # FURB167 | ||
21 | if match("^hello", "hello world", I): | ||
| ^ FURB167 | ||
22 | pass | ||
| | ||
= help: Replace with `re.IGNORECASE` | ||
|
||
ℹ Safe fix | ||
1 |+import re | ||
1 2 | def func(): | ||
2 3 | import re | ||
3 4 | | ||
-------------------------------------------------------------------------------- | ||
18 19 | from re import match, I | ||
19 20 | | ||
20 21 | # FURB167 | ||
21 |- if match("^hello", "hello world", I): | ||
22 |+ if match("^hello", "hello world", re.IGNORECASE): | ||
22 23 | pass | ||
|
||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.