Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[flake8-bugbear] Implement quoted-fstring-value (B907) #11977

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 200 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B907.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
def foo():
return "hello"


var = var2 = "hello"

# warnings
f"begin '{var}' end"
f"'{var}' end"
f"begin '{var}'"

f'begin "{var}" end'
f'"{var}" end'
f'begin "{var}"'

f'a "{"hello"}" b'
f'a "{foo()}" b'

# fmt: off
k = (f'"'
f'{var}'
f'"'
f'"')

k = (f'"'
f'{var}'
'"'
f'"')

k = ('"'
f'{var}'
'"')

k = ('a'
f'{var}' # no warning
'"'
f'{var2}' # warning
'"')

k = ('\''
f'{var}'
"'")

k = ('"'
f'{var}'
"\"")

k = (r'\"'
f'{var}'
r'"')
# fmt: on

f'{"hello"}"{var}"' # warn for var and not hello
f'"{var}"{"hello"}' # warn for var and not hello
f'"{var}" and {"hello"} and "{var2}"' # warn for var and var2
f'"{var}" and "{var2}"' # warn for both
f'"{var}""{var2}"' # warn for both

# check that pre-quote & variable is reset if no post-quote is found
f'"{var}abc "{var2}"' # warn on var2

# check for escaped quotes
f'\'{var}\''
f'\"{var}\"'
f"\'{var}\'"
f"\"{var}\""

# check formatting on different contained types
f'"{var}"'
f'"{var.__str__}"'
f'"{var.__str__.__repr__}"'
f'"{3+5}"'
f'"{foo()}"'
f'"{None}"'
f'"{...}"' # although f'"{...!r}"' == 'Ellipsis'
f'"{True}"'

# various format strings
# with "add type hint" suggestion
f'"{var:}"'
f'"{var:<}"'
f'"{var:>}"'
f'"{var:^}"'
f'"{var:h<}"'
f'"{var:h>}"'
f'"{var:h^}"'
f'"{var:s<}"'
f'"{var:s>}"'
f'"{var:s^}"'
f'"{var:9}"'
f'"{var:<9}"'
f'"{var:>9}"'
f'"{var:^9}"'
f'"{var:h<9}"'
f'"{var:19}"'
f'"{var:<19}"'
f'"{var:>19}"'
f'"{var:^19}"'
f'"{var:h<19}"'
f'"{var:.}"'
f'"{var:<.}"'
f'"{var:>.}"'
f'"{var:^.}"'
f'"{var:h<.}"'
f'"{var:9.}"'
f'"{var:19.}"'
f'"{var:.9}"'
f'"{var:.19}"'
# with "add !s" suggestion
f'"{var:s}"'
f'"{var:<s}"'
f'"{var:>s}"'
f'"{var:^s}"'
f'"{var:h<s}"'
f'"{var:9s}"'
f'"{var:19s}"'
f'"{var:.19s}"'

# These all currently give warnings, but could be considered false alarms
# multiple quote marks
f'"""{var}"""'
# two variables fighting over the same quote mark
f'"{var}"{var2}"' # currently gives warning on the first one


# ***no warnings*** #

# str conversion specified
f'"{var!s}"'

# quote mark not immediately adjacent
f'" {var} "'
f'"{var} "'
f'" {var}"'

# mixed quote marks
f"'{var}\""

# repr specifier already given
f'"{var!r}"'

# two variables in a row with no quote mark inbetween
f'"{var}{var}"'

# don't crash on non-string constants
f'5{var}"'
f"\"{var}'"

# sign option (only valid for number types)
f'"{var:+}"'

# integer presentation type specified
f'"{var:b}"'
f'"{var:c}"'
f'"{var:d}"'
f'"{var:o}"'
f'"{var:x}"'
f'"{var:X}"'
f'"{var:n}"'

# float presentation type
f'"{var:e}"'
f'"{var:E}"'
f'"{var:f}"'
f'"{var:F}"'
f'"{var:g}"'
f'"{var:G}"'
f'"{var:n}"'
f'"{var:%}"'

# datetime presentation type
f'"{var:%B %d, %Y}"'

# alignment specifier invalid for strings
f'"{var:=}"'

# don't attempt to parse complex format specs
f'"{var:{var}}"'
f'"{var:5{var}}"'

# even if explicit string type (not implemented)
f'"{var:{var}s}"'

# check for escaped non-quotes
f'"{var}\\'
# and be careful with raw strings and escaped characters
rf'\"{var}\"'
# fmt: off
k = ('"'
f'{var}'
'a')

k = (r'\''
f'{var}'
r'\'')
# fmt: on

# check for debug formatting
f'"{var=}"'

3 changes: 3 additions & 0 deletions crates/ruff_linter/src/checkers/ast/analyze/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1055,6 +1055,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
if checker.enabled(Rule::HardcodedSQLExpression) {
flake8_bandit::rules::hardcoded_sql_expression(checker, expr);
}
if checker.enabled(Rule::QuotedFStringValue) {
flake8_bugbear::rules::quoted_fstring_value(checker, f_string_expr);
}
if checker.enabled(Rule::UnicodeKindPrefix) {
for string_literal in value.literals() {
pyupgrade::rules::unicode_kind_prefix(checker, string_literal);
Expand Down
1 change: 1 addition & 0 deletions crates/ruff_linter/src/codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Flake8Bugbear, "901") => (RuleGroup::Preview, rules::flake8_bugbear::rules::ReturnInGenerator),
(Flake8Bugbear, "904") => (RuleGroup::Stable, rules::flake8_bugbear::rules::RaiseWithoutFromInsideExcept),
(Flake8Bugbear, "905") => (RuleGroup::Stable, rules::flake8_bugbear::rules::ZipWithoutExplicitStrict),
(Flake8Bugbear, "907") => (RuleGroup::Preview, rules::flake8_bugbear::rules::QuotedFStringValue),
(Flake8Bugbear, "909") => (RuleGroup::Preview, rules::flake8_bugbear::rules::LoopIteratorMutation),

// flake8-blind-except
Expand Down
1 change: 1 addition & 0 deletions crates/ruff_linter/src/rules/flake8_bugbear/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ mod tests {
#[test_case(Rule::UselessExpression, Path::new("B018.ipynb"))]
#[test_case(Rule::UselessExpression, Path::new("B018.py"))]
#[test_case(Rule::ReturnInGenerator, Path::new("B901.py"))]
#[test_case(Rule::QuotedFStringValue, Path::new("B907.py"))]
#[test_case(Rule::LoopIteratorMutation, Path::new("B909.py"))]
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
Expand Down
2 changes: 2 additions & 0 deletions crates/ruff_linter/src/rules/flake8_bugbear/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub(crate) use loop_iterator_mutation::*;
pub(crate) use loop_variable_overrides_iterator::*;
pub(crate) use mutable_argument_default::*;
pub(crate) use no_explicit_stacklevel::*;
pub(crate) use quoted_fstring_value::*;
pub(crate) use raise_literal::*;
pub(crate) use raise_without_from_inside_except::*;
pub(crate) use re_sub_positional_args::*;
Expand Down Expand Up @@ -53,6 +54,7 @@ mod loop_iterator_mutation;
mod loop_variable_overrides_iterator;
mod mutable_argument_default;
mod no_explicit_stacklevel;
mod quoted_fstring_value;
mod raise_literal;
mod raise_without_from_inside_except;
mod re_sub_positional_args;
Expand Down
Loading
Loading