Implement F541 (#12)

This commit is contained in:
Charlie Marsh 2022-08-16 10:47:13 -04:00 committed by GitHub
parent 3b1b53dacf
commit ddd554f9de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 69 additions and 7 deletions

View file

@ -0,0 +1,9 @@
a = "abc"
b = f"ghi{'jkl'}"
c = f"def"
d = f"def" + "ghi"
e = (
f"def" +
"ghi"
)

View file

@ -1,9 +1,10 @@
use std::collections::HashSet;
use rustpython_parser::ast::{Arg, Arguments, ExprKind, Stmt, StmtKind, Suite};
use rustpython_parser::ast::{Arg, Arguments, Expr, ExprKind, Stmt, StmtKind, Suite};
use crate::checks::{Check, CheckKind};
use crate::visitor::{walk_arguments, walk_stmt, Visitor};
use crate::visitor;
use crate::visitor::Visitor;
#[derive(Default)]
struct Checker {
@ -34,7 +35,23 @@ impl Visitor for Checker {
_ => {}
}
walk_stmt(self, stmt);
visitor::walk_stmt(self, stmt);
}
fn visit_expr(&mut self, expr: &Expr) {
if let ExprKind::JoinedStr { values } = &expr.node {
if !values
.iter()
.any(|value| matches!(value.node, ExprKind::FormattedValue { .. }))
{
self.checks.push(Check {
kind: CheckKind::FStringMissingPlaceholders,
location: expr.location,
});
}
}
visitor::walk_expr(self, expr);
}
fn visit_arguments(&mut self, arguments: &Arguments) {
@ -66,7 +83,7 @@ impl Visitor for Checker {
idents.insert(ident.clone());
}
walk_arguments(self, arguments);
visitor::walk_arguments(self, arguments);
}
}

View file

@ -4,8 +4,9 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum CheckKind {
DuplicateArgumentName,
ImportStarUsage,
FStringMissingPlaceholders,
IfTuple,
ImportStarUsage,
LineTooLong,
}
@ -14,6 +15,7 @@ impl CheckKind {
pub fn code(&self) -> &'static str {
match self {
CheckKind::DuplicateArgumentName => "F831",
CheckKind::FStringMissingPlaceholders => "F541",
CheckKind::IfTuple => "F634",
CheckKind::ImportStarUsage => "F403",
CheckKind::LineTooLong => "E501",
@ -24,6 +26,7 @@ impl CheckKind {
pub fn body(&self) -> &'static str {
match self {
CheckKind::DuplicateArgumentName => "Duplicate argument name in function definition",
CheckKind::FStringMissingPlaceholders => "f-string without any placeholders",
CheckKind::IfTuple => "If test is a tuple, which is always `True`",
CheckKind::ImportStarUsage => "Unable to detect undefined names",
CheckKind::LineTooLong => "Line too long",

View file

@ -44,7 +44,9 @@ mod tests {
use rustpython_parser::ast::Location;
use crate::cache;
use crate::checks::CheckKind::{DuplicateArgumentName, IfTuple, ImportStarUsage, LineTooLong};
use crate::checks::CheckKind::{
DuplicateArgumentName, FStringMissingPlaceholders, IfTuple, ImportStarUsage, LineTooLong,
};
use crate::linter::check_path;
use crate::message::Message;
@ -79,6 +81,37 @@ mod tests {
Ok(())
}
#[test]
fn f_string_missing_placeholders() -> Result<()> {
let actual = check_path(
&Path::new("./resources/test/src/f_string_missing_placeholders.py"),
&cache::Mode::None,
)?;
let expected = vec![
Message {
kind: FStringMissingPlaceholders,
location: Location::new(4, 7),
filename: "./resources/test/src/f_string_missing_placeholders.py".to_string(),
},
Message {
kind: FStringMissingPlaceholders,
location: Location::new(5, 7),
filename: "./resources/test/src/f_string_missing_placeholders.py".to_string(),
},
Message {
kind: FStringMissingPlaceholders,
location: Location::new(7, 7),
filename: "./resources/test/src/f_string_missing_placeholders.py".to_string(),
},
];
assert_eq!(actual.len(), expected.len());
for i in 1..actual.len() {
assert_eq!(actual[i], expected[i]);
}
Ok(())
}
#[test]
fn if_tuple() -> Result<()> {
let actual = check_path(

View file

@ -295,7 +295,7 @@ pub fn walk_stmt<V: Visitor + ?Sized>(visitor: &mut V, stmt: &Stmt) {
}
#[allow(unused_variables)]
fn walk_expr<V: Visitor + ?Sized>(visitor: &mut V, expr: &Expr) {
pub fn walk_expr<V: Visitor + ?Sized>(visitor: &mut V, expr: &Expr) {
match &expr.node {
ExprKind::BoolOp { op, values } => {
visitor.visit_boolop(op);