Implement flake8-errmsg (#1258)

This commit is contained in:
Edgar R. M
2022-12-15 22:10:59 -06:00
committed by GitHub
parent 7e45a9f2e2
commit 8281d414ca
18 changed files with 366 additions and 9 deletions

View File

@@ -37,9 +37,9 @@ use crate::visibility::{module_visibility, transition_scope, Modifier, Visibilit
use crate::{
docstrings, flake8_2020, flake8_annotations, flake8_bandit, flake8_blind_except,
flake8_boolean_trap, flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_debugger,
flake8_import_conventions, flake8_print, flake8_return, flake8_simplify, flake8_tidy_imports,
flake8_unused_arguments, mccabe, pandas_vet, pep8_naming, pycodestyle, pydocstyle, pyflakes,
pygrep_hooks, pylint, pyupgrade, visibility,
flake8_errmsg, flake8_import_conventions, flake8_print, flake8_return, flake8_simplify,
flake8_tidy_imports, flake8_unused_arguments, mccabe, pandas_vet, pep8_naming, pycodestyle,
pydocstyle, pyflakes, pygrep_hooks, pylint, pyupgrade, visibility,
};
const GLOBAL_SCOPE_INDEX: usize = 0;
@@ -1020,6 +1020,20 @@ where
flake8_bugbear::plugins::cannot_raise_literal(self, exc);
}
}
if self.settings.enabled.contains(&CheckCode::EM101)
| self.settings.enabled.contains(&CheckCode::EM102)
| self.settings.enabled.contains(&CheckCode::EM103)
{
if let Some(exc) = exc {
self.add_checks(
flake8_errmsg::checks::check_string_in_exception(
exc,
self.settings.flake8_errmsg.max_string_length,
)
.into_iter(),
);
}
}
}
StmtKind::AugAssign { target, .. } => {
self.handle_node_load(target);

View File

@@ -329,6 +329,10 @@ pub enum CheckCode {
PDV013,
PDV015,
PDV901,
// flake8-errmsg
EM101,
EM102,
EM103,
}
#[derive(EnumIter, Debug, PartialEq, Eq)]
@@ -349,6 +353,7 @@ pub enum CheckCategory {
Flake8Builtins,
Flake8Comprehensions,
Flake8Debugger,
Flake8ErrMsg,
Flake8ImportConventions,
Flake8Print,
Flake8Quotes,
@@ -390,6 +395,7 @@ impl CheckCategory {
CheckCategory::Flake8Builtins => "flake8-builtins",
CheckCategory::Flake8Comprehensions => "flake8-comprehensions",
CheckCategory::Flake8Debugger => "flake8-debugger",
CheckCategory::Flake8ErrMsg => "flake8-errmsg",
CheckCategory::Flake8ImportConventions => "flake8-import-conventions",
CheckCategory::Flake8Print => "flake8-print",
CheckCategory::Flake8Quotes => "flake8-quotes",
@@ -423,6 +429,7 @@ impl CheckCategory {
CheckCategory::Flake8Builtins => vec![CheckCodePrefix::A],
CheckCategory::Flake8Comprehensions => vec![CheckCodePrefix::C4],
CheckCategory::Flake8Debugger => vec![CheckCodePrefix::T10],
CheckCategory::Flake8ErrMsg => vec![CheckCodePrefix::EM],
CheckCategory::Flake8Print => vec![CheckCodePrefix::T20],
CheckCategory::Flake8Quotes => vec![CheckCodePrefix::Q],
CheckCategory::Flake8Return => vec![CheckCodePrefix::RET],
@@ -490,6 +497,10 @@ impl CheckCategory {
"https://pypi.org/project/flake8-debugger/4.1.2/",
&Platform::PyPI,
)),
CheckCategory::Flake8ErrMsg => Some((
"https://pypi.org/project/flake8-errmsg/0.4.0/",
&Platform::PyPI,
)),
CheckCategory::Flake8ImportConventions => None,
CheckCategory::Flake8Print => Some((
"https://pypi.org/project/flake8-print/5.0.0/",
@@ -896,6 +907,10 @@ pub enum CheckKind {
UseOfDotStack,
UseOfPdMerge,
DfIsABadVariableName,
// flake8-errmsg
RawStringInException,
FStringInException,
DotFormatInException,
// Ruff
AmbiguousUnicodeCharacterString(char, char),
AmbiguousUnicodeCharacterDocstring(char, char),
@@ -1268,6 +1283,10 @@ impl CheckCode {
CheckCode::PDV013 => CheckKind::UseOfDotStack,
CheckCode::PDV015 => CheckKind::UseOfPdMerge,
CheckCode::PDV901 => CheckKind::DfIsABadVariableName,
// flake8-errmsg
CheckCode::EM101 => CheckKind::RawStringInException,
CheckCode::EM102 => CheckKind::FStringInException,
CheckCode::EM103 => CheckKind::DotFormatInException,
// Ruff
CheckCode::RUF001 => CheckKind::AmbiguousUnicodeCharacterString('𝐁', 'B'),
CheckCode::RUF002 => CheckKind::AmbiguousUnicodeCharacterDocstring('𝐁', 'B'),
@@ -1403,6 +1422,9 @@ impl CheckCode {
CheckCode::E743 => CheckCategory::Pycodestyle,
CheckCode::E902 => CheckCategory::Pycodestyle,
CheckCode::E999 => CheckCategory::Pycodestyle,
CheckCode::EM101 => CheckCategory::Flake8ErrMsg,
CheckCode::EM102 => CheckCategory::Flake8ErrMsg,
CheckCode::EM103 => CheckCategory::Flake8ErrMsg,
CheckCode::ERA001 => CheckCategory::Eradicate,
CheckCode::F401 => CheckCategory::Pyflakes,
CheckCode::F402 => CheckCategory::Pyflakes,
@@ -1846,6 +1868,10 @@ impl CheckKind {
CheckKind::UseOfDotStack => &CheckCode::PDV013,
CheckKind::UseOfPdMerge => &CheckCode::PDV015,
CheckKind::DfIsABadVariableName => &CheckCode::PDV901,
// flake8-errmsg
CheckKind::RawStringInException => &CheckCode::EM101,
CheckKind::FStringInException => &CheckCode::EM102,
CheckKind::DotFormatInException => &CheckCode::EM103,
// Ruff
CheckKind::AmbiguousUnicodeCharacterString(..) => &CheckCode::RUF001,
CheckKind::AmbiguousUnicodeCharacterDocstring(..) => &CheckCode::RUF002,
@@ -2709,6 +2735,16 @@ impl CheckKind {
CheckKind::UseOfPdMerge => "Use `.merge` method instead of `pd.merge` function. They \
have equivalent functionality."
.to_string(),
// flake8-errmsg
CheckKind::RawStringInException => {
"Exception must not use a string literal, assign to variable first".to_string()
}
CheckKind::FStringInException => {
"Exception must not use an f-string literal, assign to variable first".to_string()
}
CheckKind::DotFormatInException => "Exception must not use a `.format()` string \
directly, assign to variable first"
.to_string(),
// Ruff
CheckKind::AmbiguousUnicodeCharacterString(confusable, representant) => {
format!(

View File

@@ -189,6 +189,12 @@ pub enum CheckCodePrefix {
E902,
E99,
E999,
EM,
EM1,
EM10,
EM101,
EM102,
EM103,
ERA,
ERA0,
ERA00,
@@ -1023,6 +1029,12 @@ impl CheckCodePrefix {
CheckCodePrefix::E902 => vec![CheckCode::E902],
CheckCodePrefix::E99 => vec![CheckCode::E999],
CheckCodePrefix::E999 => vec![CheckCode::E999],
CheckCodePrefix::EM => vec![CheckCode::EM101, CheckCode::EM102, CheckCode::EM103],
CheckCodePrefix::EM1 => vec![CheckCode::EM101, CheckCode::EM102, CheckCode::EM103],
CheckCodePrefix::EM10 => vec![CheckCode::EM101, CheckCode::EM102, CheckCode::EM103],
CheckCodePrefix::EM101 => vec![CheckCode::EM101],
CheckCodePrefix::EM102 => vec![CheckCode::EM102],
CheckCodePrefix::EM103 => vec![CheckCode::EM103],
CheckCodePrefix::ERA => vec![CheckCode::ERA001],
CheckCodePrefix::ERA0 => vec![CheckCode::ERA001],
CheckCodePrefix::ERA00 => vec![CheckCode::ERA001],
@@ -2110,6 +2122,12 @@ impl CheckCodePrefix {
CheckCodePrefix::E902 => SuffixLength::Three,
CheckCodePrefix::E99 => SuffixLength::Two,
CheckCodePrefix::E999 => SuffixLength::Three,
CheckCodePrefix::EM => SuffixLength::Zero,
CheckCodePrefix::EM1 => SuffixLength::One,
CheckCodePrefix::EM10 => SuffixLength::Two,
CheckCodePrefix::EM101 => SuffixLength::Three,
CheckCodePrefix::EM102 => SuffixLength::Three,
CheckCodePrefix::EM103 => SuffixLength::Three,
CheckCodePrefix::ERA => SuffixLength::Zero,
CheckCodePrefix::ERA0 => SuffixLength::One,
CheckCodePrefix::ERA00 => SuffixLength::Two,
@@ -2412,6 +2430,7 @@ pub const CATEGORIES: &[CheckCodePrefix] = &[
CheckCodePrefix::C,
CheckCodePrefix::D,
CheckCodePrefix::E,
CheckCodePrefix::EM,
CheckCodePrefix::ERA,
CheckCodePrefix::F,
CheckCodePrefix::FBT,

View File

@@ -0,0 +1,46 @@
use rustpython_ast::{Constant, Expr, ExprKind};
use crate::ast::types::Range;
use crate::checks::{Check, CheckKind};
pub fn check_string_in_exception(exc: &Expr, max_string_length: usize) -> Vec<Check> {
let mut checks = vec![];
if let ExprKind::Call { args, .. } = &exc.node {
if let Some(first) = args.first() {
match &first.node {
// Check for string literals
ExprKind::Constant {
value: Constant::Str(string),
..
} => {
if string.len() > max_string_length {
checks.push(Check::new(
CheckKind::RawStringInException,
Range::from_located(first),
));
}
}
// Check for f-strings
ExprKind::JoinedStr { .. } => checks.push(Check::new(
CheckKind::FStringInException,
Range::from_located(first),
)),
// Check for .format() calls
ExprKind::Call { func, .. } => {
if let ExprKind::Attribute { value, attr, .. } = &func.node {
if attr == "format" && matches!(value.node, ExprKind::Constant { .. }) {
checks.push(Check::new(
CheckKind::DotFormatInException,
Range::from_located(first),
));
}
}
}
_ => {}
}
}
}
checks
}

50
src/flake8_errmsg/mod.rs Normal file
View File

@@ -0,0 +1,50 @@
pub mod checks;
pub mod settings;
#[cfg(test)]
mod tests {
use std::path::Path;
use anyhow::Result;
use crate::checks::CheckCode;
use crate::linter::test_path;
use crate::{flake8_errmsg, settings};
#[test]
fn defaults() -> Result<()> {
let mut checks = test_path(
Path::new("./resources/test/fixtures/flake8_errmsg/EM.py"),
&settings::Settings::for_rules(vec![
CheckCode::EM101,
CheckCode::EM102,
CheckCode::EM103,
]),
false,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!("defaults", checks);
Ok(())
}
#[test]
fn custom() -> Result<()> {
let mut checks = test_path(
Path::new("./resources/test/fixtures/flake8_errmsg/EM.py"),
&settings::Settings {
flake8_errmsg: flake8_errmsg::settings::Settings {
max_string_length: 20,
},
..settings::Settings::for_rules(vec![
CheckCode::EM101,
CheckCode::EM102,
CheckCode::EM103,
])
},
false,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!("custom", checks);
Ok(())
}
}

View File

@@ -0,0 +1,32 @@
//! Settings for the `flake8-errmsg` plugin.
use ruff_macros::ConfigurationOptions;
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
pub struct Options {
#[option(
doc = r#"
Maximum string length for string literals in exception messages.
"#,
default = "0",
value_type = "usize",
example = "max-string-length = 20"
)]
pub max_string_length: Option<usize>,
}
#[derive(Debug, Default, Hash)]
pub struct Settings {
pub max_string_length: usize,
}
impl Settings {
#[allow(clippy::needless_pass_by_value)]
pub fn from_options(options: Options) -> Self {
Self {
max_string_length: options.max_string_length.unwrap_or_default(),
}
}
}

View File

@@ -0,0 +1,29 @@
---
source: src/flake8_errmsg/mod.rs
expression: checks
---
- kind: RawStringInException
location:
row: 5
column: 23
end_location:
row: 5
column: 53
fix: ~
- kind: FStringInException
location:
row: 10
column: 23
end_location:
row: 10
column: 56
fix: ~
- kind: DotFormatInException
location:
row: 14
column: 23
end_location:
row: 14
column: 81
fix: ~

View File

@@ -0,0 +1,29 @@
---
source: src/flake8_errmsg/mod.rs
expression: checks
---
- kind: RawStringInException
location:
row: 5
column: 23
end_location:
row: 5
column: 53
fix: ~
- kind: FStringInException
location:
row: 10
column: 23
end_location:
row: 10
column: 56
fix: ~
- kind: DotFormatInException
location:
row: 14
column: 23
end_location:
row: 14
column: 81
fix: ~

View File

@@ -50,6 +50,7 @@ pub mod flake8_bugbear;
mod flake8_builtins;
mod flake8_comprehensions;
mod flake8_debugger;
pub mod flake8_errmsg;
mod flake8_import_conventions;
mod flake8_print;
pub mod flake8_quotes;

View File

@@ -14,7 +14,7 @@ use crate::settings::options::Options;
use crate::settings::pyproject::load_options;
use crate::settings::types::{FilePattern, PerFileIgnore, PythonVersion, SerializationFormat};
use crate::{
flake8_annotations, flake8_bugbear, flake8_import_conventions, flake8_quotes,
flake8_annotations, flake8_bugbear, flake8_errmsg, flake8_import_conventions, flake8_quotes,
flake8_tidy_imports, fs, isort, mccabe, pep8_naming, pyupgrade,
};
@@ -44,6 +44,7 @@ pub struct Configuration {
// Plugins
pub flake8_annotations: Option<flake8_annotations::settings::Options>,
pub flake8_bugbear: Option<flake8_bugbear::settings::Options>,
pub flake8_errmsg: Option<flake8_errmsg::settings::Options>,
pub flake8_import_conventions: Option<flake8_import_conventions::settings::Options>,
pub flake8_quotes: Option<flake8_quotes::settings::Options>,
pub flake8_tidy_imports: Option<flake8_tidy_imports::settings::Options>,
@@ -118,6 +119,7 @@ impl Configuration {
// Plugins
flake8_annotations: options.flake8_annotations,
flake8_bugbear: options.flake8_bugbear,
flake8_errmsg: options.flake8_errmsg,
flake8_import_conventions: options.flake8_import_conventions,
flake8_quotes: options.flake8_quotes,
flake8_tidy_imports: options.flake8_tidy_imports,
@@ -169,6 +171,7 @@ impl Configuration {
// Plugins
flake8_annotations: self.flake8_annotations.or(config.flake8_annotations),
flake8_bugbear: self.flake8_bugbear.or(config.flake8_bugbear),
flake8_errmsg: self.flake8_errmsg.or(config.flake8_errmsg),
flake8_import_conventions: self
.flake8_import_conventions
.or(config.flake8_import_conventions),

View File

@@ -18,7 +18,7 @@ use crate::checks_gen::{CheckCodePrefix, SuffixLength, CATEGORIES};
use crate::settings::configuration::Configuration;
use crate::settings::types::{FilePattern, PerFileIgnore, PythonVersion, SerializationFormat};
use crate::{
flake8_annotations, flake8_bugbear, flake8_import_conventions, flake8_quotes,
flake8_annotations, flake8_bugbear, flake8_errmsg, flake8_import_conventions, flake8_quotes,
flake8_tidy_imports, isort, mccabe, pep8_naming, pyupgrade,
};
@@ -50,6 +50,7 @@ pub struct Settings {
// Plugins
pub flake8_annotations: flake8_annotations::settings::Settings,
pub flake8_bugbear: flake8_bugbear::settings::Settings,
pub flake8_errmsg: flake8_errmsg::settings::Settings,
pub flake8_import_conventions: flake8_import_conventions::settings::Settings,
pub flake8_quotes: flake8_quotes::settings::Settings,
pub flake8_tidy_imports: flake8_tidy_imports::settings::Settings,
@@ -144,6 +145,10 @@ impl Settings {
.flake8_bugbear
.map(flake8_bugbear::settings::Settings::from_options)
.unwrap_or_default(),
flake8_errmsg: config
.flake8_errmsg
.map(flake8_errmsg::settings::Settings::from_options)
.unwrap_or_default(),
flake8_import_conventions: config
.flake8_import_conventions
.map(flake8_import_conventions::settings::Settings::from_options)
@@ -197,6 +202,7 @@ impl Settings {
target_version: PythonVersion::Py310,
flake8_annotations: flake8_annotations::settings::Settings::default(),
flake8_bugbear: flake8_bugbear::settings::Settings::default(),
flake8_errmsg: flake8_errmsg::settings::Settings::default(),
flake8_import_conventions: flake8_import_conventions::settings::Settings::default(),
flake8_quotes: flake8_quotes::settings::Settings::default(),
flake8_tidy_imports: flake8_tidy_imports::settings::Settings::default(),
@@ -227,6 +233,7 @@ impl Settings {
target_version: PythonVersion::Py310,
flake8_annotations: flake8_annotations::settings::Settings::default(),
flake8_bugbear: flake8_bugbear::settings::Settings::default(),
flake8_errmsg: flake8_errmsg::settings::Settings::default(),
flake8_import_conventions: flake8_import_conventions::settings::Settings::default(),
flake8_quotes: flake8_quotes::settings::Settings::default(),
flake8_tidy_imports: flake8_tidy_imports::settings::Settings::default(),

View File

@@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
use crate::checks_gen::CheckCodePrefix;
use crate::settings::types::{PythonVersion, SerializationFormat};
use crate::{
flake8_annotations, flake8_bugbear, flake8_import_conventions, flake8_quotes,
flake8_annotations, flake8_bugbear, flake8_errmsg, flake8_import_conventions, flake8_quotes,
flake8_tidy_imports, isort, mccabe, pep8_naming, pyupgrade,
};
@@ -307,6 +307,8 @@ pub struct Options {
#[option_group]
pub flake8_bugbear: Option<flake8_bugbear::settings::Options>,
#[option_group]
pub flake8_errmsg: Option<flake8_errmsg::settings::Options>,
#[option_group]
pub flake8_quotes: Option<flake8_quotes::settings::Options>,
#[option_group]
pub flake8_tidy_imports: Option<flake8_tidy_imports::settings::Options>,

View File

@@ -87,8 +87,8 @@ mod tests {
};
use crate::settings::types::PatternPrefixPair;
use crate::{
flake8_bugbear, flake8_import_conventions, flake8_quotes, flake8_tidy_imports, mccabe,
pep8_naming,
flake8_bugbear, flake8_errmsg, flake8_import_conventions, flake8_quotes,
flake8_tidy_imports, mccabe, pep8_naming,
};
#[test]
@@ -136,6 +136,7 @@ mod tests {
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
@@ -181,6 +182,7 @@ line-length = 79
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
@@ -225,6 +227,7 @@ exclude = ["foo.py"]
target_version: None,
show_source: None,
flake8_annotations: None,
flake8_errmsg: None,
flake8_bugbear: None,
flake8_quotes: None,
flake8_tidy_imports: None,
@@ -271,6 +274,7 @@ select = ["E501"]
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
@@ -317,6 +321,7 @@ ignore = ["E501"]
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_errmsg: None,
flake8_quotes: None,
flake8_tidy_imports: None,
flake8_import_conventions: None,
@@ -409,6 +414,9 @@ other-attribute = 1
"fastapi.Query".to_string(),
]),
}),
flake8_errmsg: Some(flake8_errmsg::settings::Options {
max_string_length: Some(20),
}),
flake8_quotes: Some(flake8_quotes::settings::Options {
inline_quotes: Some(Quote::Single),
multiline_quotes: Some(Quote::Double),