fix-21062

This commit is contained in:
Dan 2025-10-25 21:27:37 -04:00
parent 64ab79e572
commit eae8f6f5e7
12 changed files with 220 additions and 6 deletions

View File

@ -1039,13 +1039,13 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
&checker.settings().flake8_gettext.functions_names, &checker.settings().flake8_gettext.functions_names,
) { ) {
if checker.is_rule_enabled(Rule::FStringInGetTextFuncCall) { if checker.is_rule_enabled(Rule::FStringInGetTextFuncCall) {
flake8_gettext::rules::f_string_in_gettext_func_call(checker, args); flake8_gettext::rules::f_string_in_gettext_func_call(checker, func, args);
} }
if checker.is_rule_enabled(Rule::FormatInGetTextFuncCall) { if checker.is_rule_enabled(Rule::FormatInGetTextFuncCall) {
flake8_gettext::rules::format_in_gettext_func_call(checker, args); flake8_gettext::rules::format_in_gettext_func_call(checker, func, args);
} }
if checker.is_rule_enabled(Rule::PrintfInGetTextFuncCall) { if checker.is_rule_enabled(Rule::PrintfInGetTextFuncCall) {
flake8_gettext::rules::printf_in_gettext_func_call(checker, args); flake8_gettext::rules::printf_in_gettext_func_call(checker, func, args);
} }
} }
if checker.is_rule_enabled(Rule::UncapitalizedEnvironmentVariables) { if checker.is_rule_enabled(Rule::UncapitalizedEnvironmentVariables) {

View File

@ -0,0 +1,21 @@
use crate::checkers::ast::Checker;
use ruff_python_ast::Expr;
/// Returns true if the function call is ngettext
pub(crate) fn is_ngettext_call(checker: &Checker, func: &Expr) -> bool {
let semantic = checker.semantic();
// Check if it's a direct name reference to ngettext
if let Some(name) = func.as_name_expr() {
if name.id == "ngettext" {
return true;
}
}
// Check if it's a qualified name ending with ngettext
if let Some(qualified_name) = semantic.resolve_qualified_name(func) {
return matches!(qualified_name.segments(), [.., "ngettext"]);
}
false
}

View File

@ -5,6 +5,7 @@ use ruff_python_ast::name::Name;
use ruff_python_ast::{self as ast, Expr}; use ruff_python_ast::{self as ast, Expr};
use ruff_python_semantic::Modules; use ruff_python_semantic::Modules;
pub(crate) mod helpers;
pub(crate) mod rules; pub(crate) mod rules;
pub mod settings; pub mod settings;

View File

@ -5,6 +5,7 @@ use ruff_text_size::Ranged;
use crate::Violation; use crate::Violation;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::rules::flake8_gettext::helpers;
/// ## What it does /// ## What it does
/// Checks for f-strings in `gettext` function calls. /// Checks for f-strings in `gettext` function calls.
@ -52,10 +53,20 @@ impl Violation for FStringInGetTextFuncCall {
} }
/// INT001 /// INT001
pub(crate) fn f_string_in_gettext_func_call(checker: &Checker, args: &[Expr]) { pub(crate) fn f_string_in_gettext_func_call(checker: &Checker, func: &Expr, args: &[Expr]) {
// Check first argument (singular)
if let Some(first) = args.first() { if let Some(first) = args.first() {
if first.is_f_string_expr() { if first.is_f_string_expr() {
checker.report_diagnostic(FStringInGetTextFuncCall {}, first.range()); checker.report_diagnostic(FStringInGetTextFuncCall {}, first.range());
} }
} }
// Check second argument (plural) for ngettext calls
if helpers::is_ngettext_call(checker, func) {
if let Some(second) = args.get(1) {
if second.is_f_string_expr() {
checker.report_diagnostic(FStringInGetTextFuncCall {}, second.range());
}
}
}
} }

View File

@ -5,6 +5,7 @@ use ruff_text_size::Ranged;
use crate::Violation; use crate::Violation;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::rules::flake8_gettext::helpers;
/// ## What it does /// ## What it does
/// Checks for `str.format` calls in `gettext` function calls. /// Checks for `str.format` calls in `gettext` function calls.
@ -52,7 +53,8 @@ impl Violation for FormatInGetTextFuncCall {
} }
/// INT002 /// INT002
pub(crate) fn format_in_gettext_func_call(checker: &Checker, args: &[Expr]) { pub(crate) fn format_in_gettext_func_call(checker: &Checker, func: &Expr, args: &[Expr]) {
// Check first argument (singular)
if let Some(first) = args.first() { if let Some(first) = args.first() {
if let Expr::Call(ast::ExprCall { func, .. }) = &first { if let Expr::Call(ast::ExprCall { func, .. }) = &first {
if let Expr::Attribute(ast::ExprAttribute { attr, .. }) = func.as_ref() { if let Expr::Attribute(ast::ExprAttribute { attr, .. }) = func.as_ref() {
@ -62,4 +64,17 @@ pub(crate) fn format_in_gettext_func_call(checker: &Checker, args: &[Expr]) {
} }
} }
} }
// Check second argument (plural) for ngettext calls
if helpers::is_ngettext_call(checker, func) {
if let Some(second) = args.get(1) {
if let Expr::Call(ast::ExprCall { func, .. }) = &second {
if let Expr::Attribute(ast::ExprAttribute { attr, .. }) = func.as_ref() {
if attr == "format" {
checker.report_diagnostic(FormatInGetTextFuncCall {}, second.range());
}
}
}
}
}
} }

View File

@ -4,6 +4,7 @@ use ruff_text_size::Ranged;
use crate::Violation; use crate::Violation;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::rules::flake8_gettext::helpers;
/// ## What it does /// ## What it does
/// Checks for printf-style formatted strings in `gettext` function calls. /// Checks for printf-style formatted strings in `gettext` function calls.
@ -52,7 +53,8 @@ impl Violation for PrintfInGetTextFuncCall {
} }
/// INT003 /// INT003
pub(crate) fn printf_in_gettext_func_call(checker: &Checker, args: &[Expr]) { pub(crate) fn printf_in_gettext_func_call(checker: &Checker, func: &Expr, args: &[Expr]) {
// Check first argument (singular)
if let Some(first) = args.first() { if let Some(first) = args.first() {
if let Expr::BinOp(ast::ExprBinOp { if let Expr::BinOp(ast::ExprBinOp {
op: Operator::Mod, op: Operator::Mod,
@ -65,4 +67,20 @@ pub(crate) fn printf_in_gettext_func_call(checker: &Checker, args: &[Expr]) {
} }
} }
} }
// Check second argument (plural) for ngettext calls
if helpers::is_ngettext_call(checker, func) {
if let Some(second) = args.get(1) {
if let Expr::BinOp(ast::ExprBinOp {
op: Operator::Mod,
left,
..
}) = &second
{
if left.is_string_literal_expr() {
checker.report_diagnostic(PrintfInGetTextFuncCall {}, second.range());
}
}
}
}
} }

View File

@ -30,6 +30,16 @@ INT001 f-string is resolved before function call; consider `_("string %s") % arg
9 | _gettext(f"{'value'}") # no lint 9 | _gettext(f"{'value'}") # no lint
| |
INT001 f-string is resolved before function call; consider `_("string %s") % arg`
--> INT001.py:7:24
|
6 | gettext(f"{'value'}")
7 | ngettext(f"{'value'}", f"{'values'}", 2)
| ^^^^^^^^^^^^^
8 |
9 | _gettext(f"{'value'}") # no lint
|
INT001 f-string is resolved before function call; consider `_("string %s") % arg` INT001 f-string is resolved before function call; consider `_("string %s") % arg`
--> INT001.py:31:14 --> INT001.py:31:14
| |

View File

@ -30,6 +30,17 @@ INT002 `format` method argument is resolved before function call; consider `_("s
5 | _gettext("{}".format("line")) # no lint 5 | _gettext("{}".format("line")) # no lint
| |
INT002 `format` method argument is resolved before function call; consider `_("string %s") % arg`
--> INT002.py:3:31
|
1 | _("{}".format("line"))
2 | gettext("{}".format("line"))
3 | ngettext("{}".format("line"), "{}".format("lines"), 2)
| ^^^^^^^^^^^^^^^^^^^^
4 |
5 | _gettext("{}".format("line")) # no lint
|
INT002 `format` method argument is resolved before function call; consider `_("string %s") % arg` INT002 `format` method argument is resolved before function call; consider `_("string %s") % arg`
--> INT002.py:27:14 --> INT002.py:27:14
| |

View File

@ -30,6 +30,16 @@ INT001 f-string is resolved before function call; consider `_("string %s") % arg
9 | _gettext(f"{'value'}") # no lint 9 | _gettext(f"{'value'}") # no lint
| |
INT001 f-string is resolved before function call; consider `_("string %s") % arg`
--> INT001.py:7:24
|
6 | gettext(f"{'value'}")
7 | ngettext(f"{'value'}", f"{'values'}", 2)
| ^^^^^^^^^^^^^
8 |
9 | _gettext(f"{'value'}") # no lint
|
INT001 f-string is resolved before function call; consider `_("string %s") % arg` INT001 f-string is resolved before function call; consider `_("string %s") % arg`
--> INT001.py:22:21 --> INT001.py:22:21
| |
@ -51,6 +61,16 @@ INT001 f-string is resolved before function call; consider `_("string %s") % arg
25 | ngettext_fn(f"Hello, {name}!", f"Hello, {name}s!", 2) 25 | ngettext_fn(f"Hello, {name}!", f"Hello, {name}s!", 2)
| |
INT001 f-string is resolved before function call; consider `_("string %s") % arg`
--> INT001.py:23:41
|
22 | gettext_mod.gettext(f"Hello, {name}!")
23 | gettext_mod.ngettext(f"Hello, {name}!", f"Hello, {name}s!", 2)
| ^^^^^^^^^^^^^^^^^^
24 | gettext_fn(f"Hello, {name}!")
25 | ngettext_fn(f"Hello, {name}!", f"Hello, {name}s!", 2)
|
INT001 f-string is resolved before function call; consider `_("string %s") % arg` INT001 f-string is resolved before function call; consider `_("string %s") % arg`
--> INT001.py:24:12 --> INT001.py:24:12
| |
@ -70,6 +90,15 @@ INT001 f-string is resolved before function call; consider `_("string %s") % arg
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
| |
INT001 f-string is resolved before function call; consider `_("string %s") % arg`
--> INT001.py:25:32
|
23 | gettext_mod.ngettext(f"Hello, {name}!", f"Hello, {name}s!", 2)
24 | gettext_fn(f"Hello, {name}!")
25 | ngettext_fn(f"Hello, {name}!", f"Hello, {name}s!", 2)
| ^^^^^^^^^^^^^^^^^^
|
INT001 f-string is resolved before function call; consider `_("string %s") % arg` INT001 f-string is resolved before function call; consider `_("string %s") % arg`
--> INT001.py:31:14 --> INT001.py:31:14
| |
@ -160,3 +189,12 @@ INT001 f-string is resolved before function call; consider `_("string %s") % arg
53 | builtins.ngettext(f"{'value'}", f"{'values'}", 2) 53 | builtins.ngettext(f"{'value'}", f"{'values'}", 2)
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
| |
INT001 f-string is resolved before function call; consider `_("string %s") % arg`
--> INT001.py:53:33
|
51 | builtins._(f"{'value'}")
52 | builtins.gettext(f"{'value'}")
53 | builtins.ngettext(f"{'value'}", f"{'values'}", 2)
| ^^^^^^^^^^^^^
|

View File

@ -30,6 +30,17 @@ INT002 `format` method argument is resolved before function call; consider `_("s
5 | _gettext("{}".format("line")) # no lint 5 | _gettext("{}".format("line")) # no lint
| |
INT002 `format` method argument is resolved before function call; consider `_("string %s") % arg`
--> INT002.py:3:31
|
1 | _("{}".format("line"))
2 | gettext("{}".format("line"))
3 | ngettext("{}".format("line"), "{}".format("lines"), 2)
| ^^^^^^^^^^^^^^^^^^^^
4 |
5 | _gettext("{}".format("line")) # no lint
|
INT002 `format` method argument is resolved before function call; consider `_("string %s") % arg` INT002 `format` method argument is resolved before function call; consider `_("string %s") % arg`
--> INT002.py:18:21 --> INT002.py:18:21
| |
@ -51,6 +62,16 @@ INT002 `format` method argument is resolved before function call; consider `_("s
21 | ngettext_fn("Hello, {}!".format(name), "Hello, {}s!".format(name), 2) 21 | ngettext_fn("Hello, {}!".format(name), "Hello, {}s!".format(name), 2)
| |
INT002 `format` method argument is resolved before function call; consider `_("string %s") % arg`
--> INT002.py:19:49
|
18 | gettext_mod.gettext("Hello, {}!".format(name))
19 | gettext_mod.ngettext("Hello, {}!".format(name), "Hello, {}s!".format(name), 2)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
20 | gettext_fn("Hello, {}!".format(name))
21 | ngettext_fn("Hello, {}!".format(name), "Hello, {}s!".format(name), 2)
|
INT002 `format` method argument is resolved before function call; consider `_("string %s") % arg` INT002 `format` method argument is resolved before function call; consider `_("string %s") % arg`
--> INT002.py:20:12 --> INT002.py:20:12
| |
@ -70,6 +91,15 @@ INT002 `format` method argument is resolved before function call; consider `_("s
| ^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^
| |
INT002 `format` method argument is resolved before function call; consider `_("string %s") % arg`
--> INT002.py:21:40
|
19 | gettext_mod.ngettext("Hello, {}!".format(name), "Hello, {}s!".format(name), 2)
20 | gettext_fn("Hello, {}!".format(name))
21 | ngettext_fn("Hello, {}!".format(name), "Hello, {}s!".format(name), 2)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
INT002 `format` method argument is resolved before function call; consider `_("string %s") % arg` INT002 `format` method argument is resolved before function call; consider `_("string %s") % arg`
--> INT002.py:27:14 --> INT002.py:27:14
| |
@ -160,3 +190,12 @@ INT002 `format` method argument is resolved before function call; consider `_("s
49 | builtins.ngettext("{}".format("line"), "{}".format("lines"), 2) 49 | builtins.ngettext("{}".format("line"), "{}".format("lines"), 2)
| ^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^
| |
INT002 `format` method argument is resolved before function call; consider `_("string %s") % arg`
--> INT002.py:49:40
|
47 | builtins._("{}".format("line"))
48 | builtins.gettext("{}".format("line"))
49 | builtins.ngettext("{}".format("line"), "{}".format("lines"), 2)
| ^^^^^^^^^^^^^^^^^^^^
|

View File

@ -30,6 +30,17 @@ INT003 printf-style format is resolved before function call; consider `_("string
5 | _gettext("%s" % "line") # no lint 5 | _gettext("%s" % "line") # no lint
| |
INT003 printf-style format is resolved before function call; consider `_("string %s") % arg`
--> INT003.py:3:25
|
1 | _("%s" % "line")
2 | gettext("%s" % "line")
3 | ngettext("%s" % "line", "%s" % "lines", 2)
| ^^^^^^^^^^^^^^
4 |
5 | _gettext("%s" % "line") # no lint
|
INT003 printf-style format is resolved before function call; consider `_("string %s") % arg` INT003 printf-style format is resolved before function call; consider `_("string %s") % arg`
--> INT003.py:18:21 --> INT003.py:18:21
| |
@ -51,6 +62,16 @@ INT003 printf-style format is resolved before function call; consider `_("string
21 | ngettext_fn("Hello, %s!" % name, "Hello, %ss!" % name, 2) 21 | ngettext_fn("Hello, %s!" % name, "Hello, %ss!" % name, 2)
| |
INT003 printf-style format is resolved before function call; consider `_("string %s") % arg`
--> INT003.py:19:43
|
18 | gettext_mod.gettext("Hello, %s!" % name)
19 | gettext_mod.ngettext("Hello, %s!" % name, "Hello, %ss!" % name, 2)
| ^^^^^^^^^^^^^^^^^^^^
20 | gettext_fn("Hello, %s!" % name)
21 | ngettext_fn("Hello, %s!" % name, "Hello, %ss!" % name, 2)
|
INT003 printf-style format is resolved before function call; consider `_("string %s") % arg` INT003 printf-style format is resolved before function call; consider `_("string %s") % arg`
--> INT003.py:20:12 --> INT003.py:20:12
| |
@ -70,6 +91,15 @@ INT003 printf-style format is resolved before function call; consider `_("string
| ^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^
| |
INT003 printf-style format is resolved before function call; consider `_("string %s") % arg`
--> INT003.py:21:34
|
19 | gettext_mod.ngettext("Hello, %s!" % name, "Hello, %ss!" % name, 2)
20 | gettext_fn("Hello, %s!" % name)
21 | ngettext_fn("Hello, %s!" % name, "Hello, %ss!" % name, 2)
| ^^^^^^^^^^^^^^^^^^^^
|
INT003 printf-style format is resolved before function call; consider `_("string %s") % arg` INT003 printf-style format is resolved before function call; consider `_("string %s") % arg`
--> INT003.py:27:14 --> INT003.py:27:14
| |
@ -160,3 +190,12 @@ INT003 printf-style format is resolved before function call; consider `_("string
49 | builtins.ngettext("%s" % "line", "%s" % "lines", 2) 49 | builtins.ngettext("%s" % "line", "%s" % "lines", 2)
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
| |
INT003 printf-style format is resolved before function call; consider `_("string %s") % arg`
--> INT003.py:49:34
|
47 | builtins._("%s" % "line")
48 | builtins.gettext("%s" % "line")
49 | builtins.ngettext("%s" % "line", "%s" % "lines", 2)
| ^^^^^^^^^^^^^^
|

View File

@ -30,6 +30,17 @@ INT003 printf-style format is resolved before function call; consider `_("string
5 | _gettext("%s" % "line") # no lint 5 | _gettext("%s" % "line") # no lint
| |
INT003 printf-style format is resolved before function call; consider `_("string %s") % arg`
--> INT003.py:3:25
|
1 | _("%s" % "line")
2 | gettext("%s" % "line")
3 | ngettext("%s" % "line", "%s" % "lines", 2)
| ^^^^^^^^^^^^^^
4 |
5 | _gettext("%s" % "line") # no lint
|
INT003 printf-style format is resolved before function call; consider `_("string %s") % arg` INT003 printf-style format is resolved before function call; consider `_("string %s") % arg`
--> INT003.py:27:14 --> INT003.py:27:14
| |