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

[ruff] Add assert-with-print-message rule (#11974) #11981

Merged
merged 5 commits into from
Jun 23, 2024
Merged
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
108 changes: 108 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/ruff/RUF030.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
U00A0 = "\u00a0"

# Standard Case
# Expects:
# - single StringLiteral
assert True, print("This print is not intentional.")

# Concatenated string literals
# Expects:
# - single StringLiteral
assert True, print("This print" " is not intentional.")

# Positional arguments, string literals
# Expects:
# - single StringLiteral concatenated with " "
assert True, print("This print", "is not intentional")

# Concatenated string literals combined with Positional arguments
# Expects:
# - single stringliteral concatenated with " " only between `print` and `is`
assert True, print("This " "print", "is not intentional.")

# Positional arguments, string literals with a variable
# Expects:
# - single FString concatenated with " "
assert True, print("This", print.__name__, "is not intentional.")

# Mixed brackets string literals
# Expects:
# - single StringLiteral concatenated with " "
assert True, print("This print", 'is not intentional', """and should be removed""")

# Mixed brackets with other brackets inside
# Expects:
# - single StringLiteral concatenated with " " and escaped brackets
assert True, print("This print", 'is not "intentional"', """and "should" be 'removed'""")

# Positional arguments, string literals with a separator
# Expects:
# - single StringLiteral concatenated with "|"
assert True, print("This print", "is not intentional", sep="|")

# Positional arguments, string literals with None as separator
# Expects:
# - single StringLiteral concatenated with " "
assert True, print("This print", "is not intentional", sep=None)

# Positional arguments, string literals with variable as separator, needs f-string
# Expects:
# - single FString concatenated with "{U00A0}"
assert True, print("This print", "is not intentional", sep=U00A0)

# Unnecessary f-string
# Expects:
# - single StringLiteral
assert True, print(f"This f-string is just a literal.")

# Positional arguments, string literals and f-strings
# Expects:
# - single FString concatenated with " "
assert True, print("This print", f"is not {'intentional':s}")

# Positional arguments, string literals and f-strings with a separator
# Expects:
# - single FString concatenated with "|"
assert True, print("This print", f"is not {'intentional':s}", sep="|")

# A single f-string
# Expects:
# - single FString
assert True, print(f"This print is not {'intentional':s}")

# A single f-string with a redundant separator
# Expects:
# - single FString
assert True, print(f"This print is not {'intentional':s}", sep="|")

# Complex f-string with variable as separator
# Expects:
# - single FString concatenated with "{U00A0}", all placeholders preserved
condition = "True is True"
maintainer = "John Doe"
assert True, print("Unreachable due to", condition, f", ask {maintainer} for advice", sep=U00A0)

# Empty print
# Expects:
# - `msg` entirely removed from assertion
assert True, print()

# Empty print with separator
# Expects:
# - `msg` entirely removed from assertion
assert True, print(sep=" ")

# Custom print function that actually returns a string
# Expects:
# - no violation as the function is not a built-in print
def print(s: str):
return "This is my assertion error message: " + s

assert True, print("this print shall not be removed.")

import builtins

# Use of `builtins.print`
# Expects:
# - single StringLiteral
assert True, builtins.print("This print should be removed.")
15 changes: 10 additions & 5 deletions crates/ruff_linter/src/checkers/ast/analyze/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1232,11 +1232,13 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
}
}
}
Stmt::Assert(ast::StmtAssert {
test,
msg,
range: _,
}) => {
Stmt::Assert(
assert_stmt @ ast::StmtAssert {
test,
msg,
range: _,
},
) => {
if !checker.semantic.in_type_checking_block() {
if checker.enabled(Rule::Assert) {
checker
Expand Down Expand Up @@ -1267,6 +1269,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
if checker.enabled(Rule::InvalidMockAccess) {
pygrep_hooks::rules::non_existent_mock_method(checker, test);
}
if checker.enabled(Rule::AssertWithPrintMessage) {
ruff::rules::assert_with_print_message(checker, assert_stmt);
}
}
Stmt::With(with_stmt @ ast::StmtWith { items, body, .. }) => {
if checker.enabled(Rule::TooManyNestedBlocks) {
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 @@ -977,6 +977,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Ruff, "027") => (RuleGroup::Preview, rules::ruff::rules::MissingFStringSyntax),
(Ruff, "028") => (RuleGroup::Preview, rules::ruff::rules::InvalidFormatterSuppressionComment),
(Ruff, "029") => (RuleGroup::Preview, rules::ruff::rules::UnusedAsync),
(Ruff, "030") => (RuleGroup::Preview, rules::ruff::rules::AssertWithPrintMessage),
(Ruff, "100") => (RuleGroup::Stable, rules::ruff::rules::UnusedNOQA),
(Ruff, "101") => (RuleGroup::Preview, rules::ruff::rules::RedirectedNOQA),
(Ruff, "200") => (RuleGroup::Stable, rules::ruff::rules::InvalidPyprojectToml),
Expand Down
1 change: 1 addition & 0 deletions crates/ruff_linter/src/rules/ruff/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ mod tests {
#[test_case(Rule::MissingFStringSyntax, Path::new("RUF027_2.py"))]
#[test_case(Rule::InvalidFormatterSuppressionComment, Path::new("RUF028.py"))]
#[test_case(Rule::UnusedAsync, Path::new("RUF029.py"))]
#[test_case(Rule::AssertWithPrintMessage, Path::new("RUF030.py"))]
#[test_case(Rule::RedirectedNOQA, Path::new("RUF101.py"))]
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
Expand Down
Loading
Loading