Implement B021 (#719)

This commit is contained in:
Harutaka Kawamura 2022-11-14 01:40:24 +09:00 committed by GitHub
parent 439642addf
commit e4d168bb4f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 216 additions and 10 deletions

View file

@ -526,6 +526,7 @@ For more, see [flake8-bugbear](https://pypi.org/project/flake8-bugbear/22.10.27/
| B017 | NoAssertRaisesException | `assertRaises(Exception)` should be considered evil | |
| B018 | UselessExpression | Found useless expression. Either assign it to a variable or remove it. | |
| B019 | CachedInstanceMethod | Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks | |
| B021 | FStringDocstring | f-string used as docstring. This will be interpreted by python as a joined string rather than a docstring. | |
| B025 | DuplicateTryBlockException | try-except block with duplicate exception `Exception` | |
| B026 | StarArgUnpackingAfterKeywordArg | Star-arg unpacking after a keyword argument is strongly discouraged | |

76
resources/test/fixtures/B021.py vendored Normal file
View file

@ -0,0 +1,76 @@
f"""
Should emit:
B021 - on lines 14, 22, 30, 38, 46, 54, 62, 70, 73
"""
VARIABLE = "world"
def foo1():
"""hello world!"""
def foo2():
f"""hello {VARIABLE}!"""
class bar1:
"""hello world!"""
class bar2:
f"""hello {VARIABLE}!"""
def foo1():
"""hello world!"""
def foo2():
f"""hello {VARIABLE}!"""
class bar1:
"""hello world!"""
class bar2:
f"""hello {VARIABLE}!"""
def foo1():
"hello world!"
def foo2():
f"hello {VARIABLE}!"
class bar1:
"hello world!"
class bar2:
f"hello {VARIABLE}!"
def foo1():
"hello world!"
def foo2():
f"hello {VARIABLE}!"
class bar1:
"hello world!"
class bar2:
f"hello {VARIABLE}!"
def baz():
f"""I'm probably a docstring: {VARIABLE}!"""
print(f"""I'm a normal string""")
f"""Don't detect me!"""

View file

@ -875,6 +875,9 @@ where
let prev_visible_scope = self.visible_scope.clone();
match &stmt.node {
StmtKind::FunctionDef { body, .. } | StmtKind::AsyncFunctionDef { body, .. } => {
if self.settings.enabled.contains(&CheckCode::B021) {
flake8_bugbear::plugins::f_string_docstring(self, body);
}
let definition = docstrings::extraction::extract(
&self.visible_scope,
stmt,
@ -894,6 +897,9 @@ where
));
}
StmtKind::ClassDef { body, .. } => {
if self.settings.enabled.contains(&CheckCode::B021) {
flake8_bugbear::plugins::f_string_docstring(self, body);
}
let definition = docstrings::extraction::extract(
&self.visible_scope,
stmt,
@ -2245,6 +2251,9 @@ impl<'a> Checker<'a> {
where
'b: 'a,
{
if self.settings.enabled.contains(&CheckCode::B021) {
flake8_bugbear::plugins::f_string_docstring(self, python_ast);
}
let docstring = docstrings::extraction::docstring_from(python_ast);
self.definitions.push((
Definition {

View file

@ -94,6 +94,7 @@ pub enum CheckCode {
B017,
B018,
B019,
B021,
B025,
B026,
// flake8-comprehensions
@ -393,6 +394,7 @@ pub enum CheckKind {
NoAssertRaisesException,
UselessExpression,
CachedInstanceMethod,
FStringDocstring,
DuplicateTryBlockException(String),
StarArgUnpackingAfterKeywordArg,
// flake8-comprehensions
@ -632,6 +634,7 @@ impl CheckCode {
CheckCode::B017 => CheckKind::NoAssertRaisesException,
CheckCode::B018 => CheckKind::UselessExpression,
CheckCode::B019 => CheckKind::CachedInstanceMethod,
CheckCode::B021 => CheckKind::FStringDocstring,
CheckCode::B025 => CheckKind::DuplicateTryBlockException("Exception".to_string()),
CheckCode::B026 => CheckKind::StarArgUnpackingAfterKeywordArg,
// flake8-comprehensions
@ -872,6 +875,7 @@ impl CheckCode {
CheckCode::B017 => CheckCategory::Flake8Bugbear,
CheckCode::B018 => CheckCategory::Flake8Bugbear,
CheckCode::B019 => CheckCategory::Flake8Bugbear,
CheckCode::B021 => CheckCategory::Flake8Bugbear,
CheckCode::B025 => CheckCategory::Flake8Bugbear,
CheckCode::B026 => CheckCategory::Flake8Bugbear,
CheckCode::C400 => CheckCategory::Flake8Comprehensions,
@ -1075,6 +1079,7 @@ impl CheckKind {
CheckKind::NoAssertRaisesException => &CheckCode::B017,
CheckKind::UselessExpression => &CheckCode::B018,
CheckKind::CachedInstanceMethod => &CheckCode::B019,
CheckKind::FStringDocstring => &CheckCode::B021,
CheckKind::DuplicateTryBlockException(_) => &CheckCode::B025,
CheckKind::StarArgUnpackingAfterKeywordArg => &CheckCode::B026,
// flake8-comprehensions
@ -1442,6 +1447,9 @@ impl CheckKind {
CheckKind::CachedInstanceMethod => "Use of `functools.lru_cache` or `functools.cache` \
on methods can lead to memory leaks"
.to_string(),
CheckKind::FStringDocstring => "f-string used as docstring. This will be interpreted \
by python as a joined string rather than a docstring."
.to_string(),
CheckKind::DuplicateTryBlockException(name) => {
format!("try-except block with duplicate exception `{name}`")
}

View file

@ -55,6 +55,7 @@ pub enum CheckCodePrefix {
B018,
B019,
B02,
B021,
B025,
B026,
C,
@ -380,6 +381,7 @@ impl CheckCodePrefix {
CheckCode::B017,
CheckCode::B018,
CheckCode::B019,
CheckCode::B021,
CheckCode::B025,
CheckCode::B026,
],
@ -401,6 +403,7 @@ impl CheckCodePrefix {
CheckCode::B017,
CheckCode::B018,
CheckCode::B019,
CheckCode::B021,
CheckCode::B025,
CheckCode::B026,
],
@ -442,7 +445,8 @@ impl CheckCodePrefix {
CheckCodePrefix::B017 => vec![CheckCode::B017],
CheckCodePrefix::B018 => vec![CheckCode::B018],
CheckCodePrefix::B019 => vec![CheckCode::B019],
CheckCodePrefix::B02 => vec![CheckCode::B025, CheckCode::B026],
CheckCodePrefix::B02 => vec![CheckCode::B021, CheckCode::B025, CheckCode::B026],
CheckCodePrefix::B021 => vec![CheckCode::B021],
CheckCodePrefix::B025 => vec![CheckCode::B025],
CheckCodePrefix::B026 => vec![CheckCode::B026],
CheckCodePrefix::C => vec![
@ -1184,6 +1188,7 @@ impl CheckCodePrefix {
CheckCodePrefix::B018 => PrefixSpecificity::Explicit,
CheckCodePrefix::B019 => PrefixSpecificity::Explicit,
CheckCodePrefix::B02 => PrefixSpecificity::Tens,
CheckCodePrefix::B021 => PrefixSpecificity::Explicit,
CheckCodePrefix::B025 => PrefixSpecificity::Explicit,
CheckCodePrefix::B026 => PrefixSpecificity::Explicit,
CheckCodePrefix::C => PrefixSpecificity::Category,
@ -1338,15 +1343,6 @@ impl CheckCodePrefix {
CheckCodePrefix::I0 => PrefixSpecificity::Hundreds,
CheckCodePrefix::I00 => PrefixSpecificity::Tens,
CheckCodePrefix::I001 => PrefixSpecificity::Explicit,
CheckCodePrefix::S => PrefixSpecificity::Category,
CheckCodePrefix::S1 => PrefixSpecificity::Hundreds,
CheckCodePrefix::S10 => PrefixSpecificity::Tens,
CheckCodePrefix::S101 => PrefixSpecificity::Explicit,
CheckCodePrefix::S102 => PrefixSpecificity::Explicit,
CheckCodePrefix::S104 => PrefixSpecificity::Explicit,
CheckCodePrefix::S105 => PrefixSpecificity::Explicit,
CheckCodePrefix::S106 => PrefixSpecificity::Explicit,
CheckCodePrefix::S107 => PrefixSpecificity::Explicit,
CheckCodePrefix::M => PrefixSpecificity::Category,
CheckCodePrefix::M0 => PrefixSpecificity::Hundreds,
CheckCodePrefix::M00 => PrefixSpecificity::Tens,
@ -1383,6 +1379,15 @@ impl CheckCodePrefix {
CheckCodePrefix::RUF001 => PrefixSpecificity::Explicit,
CheckCodePrefix::RUF002 => PrefixSpecificity::Explicit,
CheckCodePrefix::RUF003 => PrefixSpecificity::Explicit,
CheckCodePrefix::S => PrefixSpecificity::Category,
CheckCodePrefix::S1 => PrefixSpecificity::Hundreds,
CheckCodePrefix::S10 => PrefixSpecificity::Tens,
CheckCodePrefix::S101 => PrefixSpecificity::Explicit,
CheckCodePrefix::S102 => PrefixSpecificity::Explicit,
CheckCodePrefix::S104 => PrefixSpecificity::Explicit,
CheckCodePrefix::S105 => PrefixSpecificity::Explicit,
CheckCodePrefix::S106 => PrefixSpecificity::Explicit,
CheckCodePrefix::S107 => PrefixSpecificity::Explicit,
CheckCodePrefix::T => PrefixSpecificity::Category,
CheckCodePrefix::T2 => PrefixSpecificity::Hundreds,
CheckCodePrefix::T20 => PrefixSpecificity::Tens,

View file

@ -0,0 +1,19 @@
use rustpython_ast::{ExprKind, Stmt, StmtKind};
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checks::{Check, CheckKind};
/// B021
pub fn f_string_docstring(checker: &mut Checker, body: &[Stmt]) {
if let Some(stmt) = body.first() {
if let StmtKind::Expr { value } = &stmt.node {
if let ExprKind::JoinedStr { .. } = value.node {
checker.add_check(Check::new(
CheckKind::FStringDocstring,
Range::from_located(stmt),
));
}
}
}
}

View file

@ -4,6 +4,7 @@ pub use assignment_to_os_environ::assignment_to_os_environ;
pub use cached_instance_method::cached_instance_method;
pub use cannot_raise_literal::cannot_raise_literal;
pub use duplicate_exceptions::{duplicate_exceptions, duplicate_handler_exceptions};
pub use f_string_docstring::f_string_docstring;
pub use function_call_argument_default::function_call_argument_default;
pub use getattr_with_constant::getattr_with_constant;
pub use mutable_argument_default::mutable_argument_default;
@ -23,6 +24,7 @@ mod assignment_to_os_environ;
mod cached_instance_method;
mod cannot_raise_literal;
mod duplicate_exceptions;
mod f_string_docstring;
mod function_call_argument_default;
mod getattr_with_constant;
mod mutable_argument_default;

View file

@ -343,6 +343,7 @@ mod tests {
#[test_case(CheckCode::B017, Path::new("B017.py"); "B017")]
#[test_case(CheckCode::B018, Path::new("B018.py"); "B018")]
#[test_case(CheckCode::B019, Path::new("B019.py"); "B019")]
#[test_case(CheckCode::B021, Path::new("B021.py"); "B021")]
#[test_case(CheckCode::B025, Path::new("B025.py"); "B025")]
#[test_case(CheckCode::B026, Path::new("B026.py"); "B026")]
#[test_case(CheckCode::C400, Path::new("C400.py"); "C400")]

View file

@ -0,0 +1,85 @@
---
source: src/linter.rs
expression: checks
---
- kind: FStringDocstring
location:
row: 1
column: 0
end_location:
row: 4
column: 3
fix: ~
- kind: FStringDocstring
location:
row: 14
column: 4
end_location:
row: 14
column: 28
fix: ~
- kind: FStringDocstring
location:
row: 22
column: 4
end_location:
row: 22
column: 28
fix: ~
- kind: FStringDocstring
location:
row: 30
column: 4
end_location:
row: 30
column: 28
fix: ~
- kind: FStringDocstring
location:
row: 38
column: 4
end_location:
row: 38
column: 28
fix: ~
- kind: FStringDocstring
location:
row: 46
column: 4
end_location:
row: 46
column: 24
fix: ~
- kind: FStringDocstring
location:
row: 54
column: 4
end_location:
row: 54
column: 24
fix: ~
- kind: FStringDocstring
location:
row: 62
column: 4
end_location:
row: 62
column: 24
fix: ~
- kind: FStringDocstring
location:
row: 70
column: 4
end_location:
row: 70
column: 24
fix: ~
- kind: FStringDocstring
location:
row: 74
column: 4
end_location:
row: 74
column: 48
fix: ~