Implement autofix for E731 (#814)

This commit is contained in:
Harutaka Kawamura 2022-11-20 09:51:41 +09:00 committed by GitHub
parent 4161d4ae32
commit 13e8ed0a0a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 216 additions and 129 deletions

View file

@ -380,7 +380,7 @@ For more, see [pycodestyle](https://pypi.org/project/pycodestyle/2.9.1/) on PyPI
| E714 | NotIsTest | Test for object identity should be `is not` | 🛠 | | E714 | NotIsTest | Test for object identity should be `is not` | 🛠 |
| E721 | TypeComparison | Do not compare types, use `isinstance()` | | | E721 | TypeComparison | Do not compare types, use `isinstance()` | |
| E722 | DoNotUseBareExcept | Do not use bare `except` | | | E722 | DoNotUseBareExcept | Do not use bare `except` | |
| E731 | DoNotAssignLambda | Do not assign a lambda expression, use a def | | | E731 | DoNotAssignLambda | Do not assign a lambda expression, use a def | 🛠 |
| E741 | AmbiguousVariableName | Ambiguous variable name: `...` | | | E741 | AmbiguousVariableName | Ambiguous variable name: `...` | |
| E742 | AmbiguousClassName | Ambiguous class name: `...` | | | E742 | AmbiguousClassName | Ambiguous class name: `...` | |
| E743 | AmbiguousFunctionName | Ambiguous function name: `...` | | | E743 | AmbiguousFunctionName | Ambiguous function name: `...` | |

View file

@ -1,7 +1,10 @@
use fnv::{FnvHashMap, FnvHashSet}; use fnv::{FnvHashMap, FnvHashSet};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprKind, Location, StmtKind}; use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind};
use crate::ast::types::Range;
use crate::SourceCodeLocator;
#[inline(always)] #[inline(always)]
fn collect_call_path_inner<'a>(expr: &'a Expr, parts: &mut Vec<&'a str>) { fn collect_call_path_inner<'a>(expr: &'a Expr, parts: &mut Vec<&'a str>) {
@ -261,6 +264,34 @@ pub fn to_absolute(relative: &Location, base: &Location) -> Location {
} }
} }
/// Return `true` if a `Stmt` has leading content.
pub fn match_leading_content(stmt: &Stmt, locator: &SourceCodeLocator) -> bool {
let range = Range {
location: Location::new(stmt.location.row(), 0),
end_location: stmt.location,
};
let prefix = locator.slice_source_code_range(&range);
prefix.chars().any(|char| !char.is_whitespace())
}
/// Return `true` if a `Stmt` has trailing content.
pub fn match_trailing_content(stmt: &Stmt, locator: &SourceCodeLocator) -> bool {
let range = Range {
location: stmt.end_location.unwrap(),
end_location: Location::new(stmt.end_location.unwrap().row() + 1, 0),
};
let suffix = locator.slice_source_code_range(&range);
for char in suffix.chars() {
if char == '#' {
return false;
}
if !char.is_whitespace() {
return true;
}
}
false
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use anyhow::Result; use anyhow::Result;

View file

@ -3,3 +3,4 @@ pub mod operations;
pub mod relocate; pub mod relocate;
pub mod types; pub mod types;
pub mod visitor; pub mod visitor;
pub mod whitespace;

View file

@ -3,12 +3,6 @@ use rustpython_ast::{Located, Location};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::check_ast::Checker; use crate::check_ast::Checker;
pub const TRIPLE_QUOTE_PREFIXES: &[&str] = &[
"ur\"\"\"", "ur'''", "u\"\"\"", "u'''", "r\"\"\"", "r'''", "\"\"\"", "'''",
];
pub const SINGLE_QUOTE_PREFIXES: &[&str] = &["ur\"", "ur'", "u\"", "u'", "r\"", "r'", "\"", "'"];
/// Extract the leading words from a line of text. /// Extract the leading words from a line of text.
pub fn leading_words(line: &str) -> String { pub fn leading_words(line: &str) -> String {
line.trim() line.trim()

View file

@ -981,11 +981,7 @@ where
StmtKind::Assign { targets, value, .. } => { StmtKind::Assign { targets, value, .. } => {
if self.settings.enabled.contains(&CheckCode::E731) { if self.settings.enabled.contains(&CheckCode::E731) {
if let [target] = &targets[..] { if let [target] = &targets[..] {
if let Some(check) = pycodestyle::plugins::do_not_assign_lambda(self, target, value, stmt)
pycodestyle::checks::do_not_assign_lambda(target, value, stmt)
{
self.add_check(check);
}
} }
} }
if self.settings.enabled.contains(&CheckCode::U001) { if self.settings.enabled.contains(&CheckCode::U001) {
@ -1010,11 +1006,7 @@ where
StmtKind::AnnAssign { target, value, .. } => { StmtKind::AnnAssign { target, value, .. } => {
if self.settings.enabled.contains(&CheckCode::E731) { if self.settings.enabled.contains(&CheckCode::E731) {
if let Some(value) = value { if let Some(value) = value {
if let Some(check) = pycodestyle::plugins::do_not_assign_lambda(self, target, value, stmt);
pycodestyle::checks::do_not_assign_lambda(target, value, stmt)
{
self.add_check(check);
}
} }
} }
} }

View file

@ -2038,46 +2038,51 @@ impl CheckKind {
pub fn fixable(&self) -> bool { pub fn fixable(&self) -> bool {
matches!( matches!(
self, self,
CheckKind::AmbiguousUnicodeCharacterString(_, _) CheckKind::AmbiguousUnicodeCharacterString(..)
| CheckKind::AmbiguousUnicodeCharacterDocstring(_, _) | CheckKind::AmbiguousUnicodeCharacterDocstring(..)
| CheckKind::BlankLineAfterLastSection(_) | CheckKind::BlankLineAfterLastSection(..)
| CheckKind::BlankLineAfterSection(_) | CheckKind::BlankLineAfterSection(..)
| CheckKind::BlankLineAfterSummary | CheckKind::BlankLineAfterSummary
| CheckKind::BlankLineBeforeSection(_) | CheckKind::BlankLineBeforeSection(..)
| CheckKind::CapitalizeSectionName(_) | CheckKind::CapitalizeSectionName(..)
| CheckKind::DashedUnderlineAfterSection(_) | CheckKind::ConvertTypedDictFunctionalToClass
| CheckKind::DeprecatedUnittestAlias(_, _) | CheckKind::DashedUnderlineAfterSection(..)
| CheckKind::DeprecatedUnittestAlias(..)
| CheckKind::DoNotAssertFalse | CheckKind::DoNotAssertFalse
| CheckKind::DuplicateHandlerException(_) | CheckKind::DoNotAssignLambda
| CheckKind::DuplicateHandlerException(..)
| CheckKind::GetAttrWithConstant | CheckKind::GetAttrWithConstant
| CheckKind::IsLiteral | CheckKind::IsLiteral
| CheckKind::NewLineAfterLastParagraph | CheckKind::NewLineAfterLastParagraph
| CheckKind::NewLineAfterSectionName(_) | CheckKind::NewLineAfterSectionName(..)
| CheckKind::NoBlankLineAfterFunction(_) | CheckKind::NoBlankLineAfterFunction(..)
| CheckKind::NoBlankLineBeforeClass(_) | CheckKind::NoBlankLineBeforeClass(..)
| CheckKind::NoBlankLineBeforeFunction(_) | CheckKind::NoBlankLineBeforeFunction(..)
| CheckKind::NoBlankLinesBetweenHeaderAndContent(_) | CheckKind::NoBlankLinesBetweenHeaderAndContent(..)
| CheckKind::NoOverIndentation | CheckKind::NoOverIndentation
| CheckKind::NoSurroundingWhitespace | CheckKind::NoSurroundingWhitespace
| CheckKind::NoUnderIndentation | CheckKind::NoUnderIndentation
| CheckKind::OneBlankLineAfterClass(_) | CheckKind::NoneComparison(..)
| CheckKind::OneBlankLineBeforeClass(_) | CheckKind::NotInTest
| CheckKind::NotIsTest
| CheckKind::OneBlankLineAfterClass(..)
| CheckKind::OneBlankLineBeforeClass(..)
| CheckKind::PEP3120UnnecessaryCodingComment | CheckKind::PEP3120UnnecessaryCodingComment
| CheckKind::PPrintFound | CheckKind::PPrintFound
| CheckKind::PrintFound | CheckKind::PrintFound
| CheckKind::RaiseNotImplemented | CheckKind::RaiseNotImplemented
| CheckKind::SectionNameEndsInColon(_) | CheckKind::SectionNameEndsInColon(..)
| CheckKind::SectionNotOverIndented(_) | CheckKind::SectionNotOverIndented(..)
| CheckKind::SectionUnderlineAfterName(_) | CheckKind::SectionUnderlineAfterName(..)
| CheckKind::SectionUnderlineMatchesSectionLength(_) | CheckKind::SectionUnderlineMatchesSectionLength(..)
| CheckKind::SectionUnderlineNotOverIndented(_) | CheckKind::SectionUnderlineNotOverIndented(..)
| CheckKind::SuperCallWithParameters | CheckKind::SuperCallWithParameters
| CheckKind::TypeOfPrimitive(_) | CheckKind::TrueFalseComparison(..)
| CheckKind::UnnecessaryCollectionCall(_) | CheckKind::TypeOfPrimitive(..)
| CheckKind::UnnecessaryComprehension(_) | CheckKind::UnnecessaryCollectionCall(..)
| CheckKind::UnnecessaryComprehension(..)
| CheckKind::UnnecessaryEncodeUTF8 | CheckKind::UnnecessaryEncodeUTF8
| CheckKind::ConvertTypedDictFunctionalToClass | CheckKind::UnnecessaryFutureImport(..)
| CheckKind::UnnecessaryFutureImport(_)
| CheckKind::UnnecessaryGeneratorDict | CheckKind::UnnecessaryGeneratorDict
| CheckKind::UnnecessaryGeneratorList | CheckKind::UnnecessaryGeneratorList
| CheckKind::UnnecessaryGeneratorSet | CheckKind::UnnecessaryGeneratorSet
@ -2085,18 +2090,18 @@ impl CheckKind {
| CheckKind::UnnecessaryListCall | CheckKind::UnnecessaryListCall
| CheckKind::UnnecessaryListComprehensionDict | CheckKind::UnnecessaryListComprehensionDict
| CheckKind::UnnecessaryListComprehensionSet | CheckKind::UnnecessaryListComprehensionSet
| CheckKind::UnnecessaryLiteralDict(_) | CheckKind::UnnecessaryLiteralDict(..)
| CheckKind::UnnecessaryLiteralSet(_) | CheckKind::UnnecessaryLiteralSet(..)
| CheckKind::UnnecessaryLiteralWithinListCall(_) | CheckKind::UnnecessaryLiteralWithinListCall(..)
| CheckKind::UnnecessaryLiteralWithinTupleCall(_) | CheckKind::UnnecessaryLiteralWithinTupleCall(..)
| CheckKind::UnsortedImports | CheckKind::UnsortedImports
| CheckKind::UnusedImport(_, false) | CheckKind::UnusedImport(_, false)
| CheckKind::UnusedLoopControlVariable(_) | CheckKind::UnusedLoopControlVariable(..)
| CheckKind::UnusedNOQA(_) | CheckKind::UnusedNOQA(..)
| CheckKind::UsePEP585Annotation(_) | CheckKind::UsePEP585Annotation(..)
| CheckKind::UsePEP604Annotation | CheckKind::UsePEP604Annotation
| CheckKind::UselessMetaclassType | CheckKind::UselessMetaclassType
| CheckKind::UselessObjectInheritance(_) | CheckKind::UselessObjectInheritance(..)
) )
} }
} }

View file

@ -0,0 +1,5 @@
pub const TRIPLE_QUOTE_PREFIXES: &[&str] = &[
"ur\"\"\"", "ur'''", "u\"\"\"", "u'''", "r\"\"\"", "r'''", "\"\"\"", "'''",
];
pub const SINGLE_QUOTE_PREFIXES: &[&str] = &["ur\"", "ur'", "u\"", "u'", "r\"", "r'", "\"", "'"];

View file

@ -1,7 +1,7 @@
pub mod constants;
pub mod definition; pub mod definition;
pub mod extraction; pub mod extraction;
pub mod google; pub mod google;
pub mod helpers;
pub mod numpy; pub mod numpy;
pub mod sections; pub mod sections;
pub mod styles; pub mod styles;

View file

@ -1,4 +1,4 @@
use crate::docstrings::helpers; use crate::ast::whitespace;
use crate::docstrings::styles::SectionStyle; use crate::docstrings::styles::SectionStyle;
#[derive(Debug)] #[derive(Debug)]
@ -14,7 +14,7 @@ pub(crate) struct SectionContext<'a> {
fn suspected_as_section(line: &str, style: &SectionStyle) -> bool { fn suspected_as_section(line: &str, style: &SectionStyle) -> bool {
style style
.lowercase_section_names() .lowercase_section_names()
.contains(&helpers::leading_words(line).to_lowercase().as_str()) .contains(&whitespace::leading_words(line).to_lowercase().as_str())
} }
/// Check if the suspected context is really a section header. /// Check if the suspected context is really a section header.
@ -64,7 +64,7 @@ pub(crate) fn section_contexts<'a>(
let mut contexts = vec![]; let mut contexts = vec![];
for lineno in suspected_section_indices { for lineno in suspected_section_indices {
let context = SectionContext { let context = SectionContext {
section_name: helpers::leading_words(lines[lineno]), section_name: whitespace::leading_words(lines[lineno]),
previous_line: lines[lineno - 1], previous_line: lines[lineno - 1],
line: lines[lineno], line: lines[lineno],
following_lines: &lines[lineno + 1..], following_lines: &lines[lineno + 1..],

View file

@ -1,10 +1,11 @@
use rustpython_ast::{Location, Stmt}; use rustpython_ast::{Location, Stmt};
use textwrap::{dedent, indent}; use textwrap::{dedent, indent};
use crate::ast::helpers::{match_leading_content, match_trailing_content};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::ast::whitespace::leading_space;
use crate::autofix::{fixer, Fix}; use crate::autofix::{fixer, Fix};
use crate::checks::CheckKind; use crate::checks::CheckKind;
use crate::docstrings::helpers::leading_space;
use crate::isort::{comments, format_imports}; use crate::isort::{comments, format_imports};
use crate::{Check, Settings, SourceCodeLocator}; use crate::{Check, Settings, SourceCodeLocator};
@ -27,34 +28,6 @@ fn extract_indentation(body: &[&Stmt], locator: &SourceCodeLocator) -> String {
leading_space(&existing) leading_space(&existing)
} }
fn match_leading_content(body: &[&Stmt], locator: &SourceCodeLocator) -> bool {
let location = body.first().unwrap().location;
let range = Range {
location: Location::new(location.row(), 0),
end_location: location,
};
let prefix = locator.slice_source_code_range(&range);
prefix.chars().any(|char| !char.is_whitespace())
}
fn match_trailing_content(body: &[&Stmt], locator: &SourceCodeLocator) -> bool {
let end_location = body.last().unwrap().end_location.unwrap();
let range = Range {
location: end_location,
end_location: Location::new(end_location.row() + 1, 0),
};
let suffix = locator.slice_source_code_range(&range);
for char in suffix.chars() {
if char == '#' {
return false;
}
if !char.is_whitespace() {
return true;
}
}
false
}
/// I001 /// I001
pub fn check_imports( pub fn check_imports(
body: Vec<&Stmt>, body: Vec<&Stmt>,
@ -75,8 +48,8 @@ pub fn check_imports(
); );
// Special-cases: there's leading or trailing content in the import block. // Special-cases: there's leading or trailing content in the import block.
let has_leading_content = match_leading_content(&body, locator); let has_leading_content = match_leading_content(body.first().unwrap(), locator);
let has_trailing_content = match_trailing_content(&body, locator); let has_trailing_content = match_trailing_content(body.last().unwrap(), locator);
// Generate the sorted import block. // Generate the sorted import block.
let expected = format_imports( let expected = format_imports(

View file

@ -1,6 +1,6 @@
use itertools::izip; use itertools::izip;
use rustpython_ast::Location; use rustpython_ast::Location;
use rustpython_parser::ast::{Cmpop, Expr, ExprKind, Stmt}; use rustpython_parser::ast::{Cmpop, Expr, ExprKind};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::checks::{Check, CheckKind}; use crate::checks::{Check, CheckKind};
@ -46,19 +46,6 @@ pub fn ambiguous_function_name(name: &str, location: Range) -> Option<Check> {
} }
} }
/// E731
pub fn do_not_assign_lambda(target: &Expr, value: &Expr, stmt: &Stmt) -> Option<Check> {
if let ExprKind::Name { .. } = &target.node {
if let ExprKind::Lambda { .. } = &value.node {
return Some(Check::new(
CheckKind::DoNotAssignLambda,
Range::from_located(stmt),
));
}
}
None
}
/// E721 /// E721
pub fn type_comparison(ops: &[Cmpop], comparators: &[Expr], location: Range) -> Vec<Check> { pub fn type_comparison(ops: &[Cmpop], comparators: &[Expr], location: Range) -> Vec<Check> {
let mut checks: Vec<Check> = vec![]; let mut checks: Vec<Check> = vec![];

View file

@ -1,8 +1,13 @@
use anyhow::Result;
use fnv::FnvHashMap; use fnv::FnvHashMap;
use itertools::izip; use itertools::izip;
use rustpython_parser::ast::{Cmpop, Constant, Expr, ExprKind, Unaryop}; use log::error;
use rustpython_ast::{Arguments, Location, StmtKind};
use rustpython_parser::ast::{Cmpop, Constant, Expr, ExprKind, Stmt, Unaryop};
use crate::ast::helpers::{match_leading_content, match_trailing_content};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::ast::whitespace::leading_space;
use crate::autofix::Fix; use crate::autofix::Fix;
use crate::check_ast::Checker; use crate::check_ast::Checker;
use crate::checks::{Check, CheckKind, RejectedCmpop}; use crate::checks::{Check, CheckKind, RejectedCmpop};
@ -260,3 +265,69 @@ pub fn not_tests(
} }
} }
} }
fn function(name: &str, args: &Arguments, body: &Expr) -> Result<String> {
let body = Stmt::new(
Default::default(),
Default::default(),
StmtKind::Return {
value: Some(Box::new(body.clone())),
},
);
let func = Stmt::new(
Default::default(),
Default::default(),
StmtKind::FunctionDef {
name: name.to_string(),
args: Box::new(args.clone()),
body: vec![body],
decorator_list: vec![],
returns: None,
type_comment: None,
},
);
let mut generator = SourceGenerator::new();
generator.unparse_stmt(&func)?;
generator.generate().map_err(|e| e.into())
}
/// E731
pub fn do_not_assign_lambda(checker: &mut Checker, target: &Expr, value: &Expr, stmt: &Stmt) {
if let ExprKind::Name { id, .. } = &target.node {
if let ExprKind::Lambda { args, body } = &value.node {
let mut check = Check::new(CheckKind::DoNotAssignLambda, Range::from_located(stmt));
if checker.patch(check.kind.code()) {
if !match_leading_content(stmt, checker.locator)
&& !match_trailing_content(stmt, checker.locator)
{
match function(id, args, body) {
Ok(content) => {
let indentation =
&leading_space(&checker.locator.slice_source_code_range(&Range {
location: Location::new(stmt.location.row(), 0),
end_location: Location::new(stmt.location.row() + 1, 0),
}));
let mut indented = String::new();
for (idx, line) in content.lines().enumerate() {
if idx == 0 {
indented.push_str(line);
} else {
indented.push('\n');
indented.push_str(indentation);
indented.push_str(line);
}
}
check.amend(Fix::replacement(
indented,
stmt.location,
stmt.end_location.unwrap(),
));
}
Err(e) => error!("Failed to generate fix: {}", e),
}
}
}
checker.add_check(check);
}
}
}

View file

@ -7,11 +7,12 @@ use regex::Regex;
use rustpython_ast::{Arg, Constant, ExprKind, Location, StmtKind}; use rustpython_ast::{Arg, Constant, ExprKind, Location, StmtKind};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::ast::whitespace;
use crate::autofix::Fix; use crate::autofix::Fix;
use crate::check_ast::Checker; use crate::check_ast::Checker;
use crate::checks::{Check, CheckCode, CheckKind}; use crate::checks::{Check, CheckCode, CheckKind};
use crate::docstrings::constants;
use crate::docstrings::definition::{Definition, DefinitionKind}; use crate::docstrings::definition::{Definition, DefinitionKind};
use crate::docstrings::helpers;
use crate::docstrings::sections::{section_contexts, SectionContext}; use crate::docstrings::sections::{section_contexts, SectionContext};
use crate::docstrings::styles::SectionStyle; use crate::docstrings::styles::SectionStyle;
use crate::visibility::{is_init, is_magic, is_overload, is_staticmethod, Visibility}; use crate::visibility::{is_init, is_magic, is_overload, is_staticmethod, Visibility};
@ -391,7 +392,7 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
return; return;
} }
let docstring_indent = helpers::indentation(checker, docstring); let docstring_indent = whitespace::indentation(checker, docstring);
let mut has_seen_tab = docstring_indent.contains('\t'); let mut has_seen_tab = docstring_indent.contains('\t');
let mut is_over_indented = true; let mut is_over_indented = true;
let mut over_indented_lines = vec![]; let mut over_indented_lines = vec![];
@ -408,7 +409,7 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
continue; continue;
} }
let line_indent = helpers::leading_space(lines[i]); let line_indent = whitespace::leading_space(lines[i]);
// We only report tab indentation once, so only check if we haven't seen a tab // We only report tab indentation once, so only check if we haven't seen a tab
// yet. // yet.
@ -427,7 +428,7 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
); );
if checker.patch(check.kind.code()) { if checker.patch(check.kind.code()) {
check.amend(Fix::replacement( check.amend(Fix::replacement(
helpers::clean(&docstring_indent), whitespace::clean(&docstring_indent),
Location::new(docstring.location.row() + i, 0), Location::new(docstring.location.row() + i, 0),
Location::new(docstring.location.row() + i, line_indent.len()), Location::new(docstring.location.row() + i, line_indent.len()),
)); ));
@ -464,7 +465,7 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
// If every line (except the last) is over-indented... // If every line (except the last) is over-indented...
if is_over_indented { if is_over_indented {
for i in over_indented_lines { for i in over_indented_lines {
let line_indent = helpers::leading_space(lines[i]); let line_indent = whitespace::leading_space(lines[i]);
if line_indent.len() > docstring_indent.len() { if line_indent.len() > docstring_indent.len() {
// We report over-indentation on every line. This isn't great, but // We report over-indentation on every line. This isn't great, but
// enables autofix. // enables autofix.
@ -477,7 +478,7 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
); );
if checker.patch(check.kind.code()) { if checker.patch(check.kind.code()) {
check.amend(Fix::replacement( check.amend(Fix::replacement(
helpers::clean(&docstring_indent), whitespace::clean(&docstring_indent),
Location::new(docstring.location.row() + i, 0), Location::new(docstring.location.row() + i, 0),
Location::new(docstring.location.row() + i, line_indent.len()), Location::new(docstring.location.row() + i, line_indent.len()),
)); ));
@ -490,7 +491,7 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
// If the last line is over-indented... // If the last line is over-indented...
if !lines.is_empty() { if !lines.is_empty() {
let i = lines.len() - 1; let i = lines.len() - 1;
let line_indent = helpers::leading_space(lines[i]); let line_indent = whitespace::leading_space(lines[i]);
if line_indent.len() > docstring_indent.len() { if line_indent.len() > docstring_indent.len() {
let mut check = Check::new( let mut check = Check::new(
CheckKind::NoOverIndentation, CheckKind::NoOverIndentation,
@ -501,7 +502,7 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
); );
if checker.patch(check.kind.code()) { if checker.patch(check.kind.code()) {
check.amend(Fix::replacement( check.amend(Fix::replacement(
helpers::clean(&docstring_indent), whitespace::clean(&docstring_indent),
Location::new(docstring.location.row() + i, 0), Location::new(docstring.location.row() + i, 0),
Location::new(docstring.location.row() + i, line_indent.len()), Location::new(docstring.location.row() + i, line_indent.len()),
)); ));
@ -541,7 +542,7 @@ pub fn newline_after_last_paragraph(checker: &mut Checker, definition: &Definiti
// Insert a newline just before the end-quote(s). // Insert a newline just before the end-quote(s).
let content = format!( let content = format!(
"\n{}", "\n{}",
helpers::clean(&helpers::indentation(checker, docstring)) whitespace::clean(&whitespace::indentation(checker, docstring))
); );
check.amend(Fix::insertion( check.amend(Fix::insertion(
content, content,
@ -588,9 +589,9 @@ pub fn no_surrounding_whitespace(checker: &mut Checker, definition: &Definition)
.next() .next()
.map(|line| line.to_lowercase()) .map(|line| line.to_lowercase())
{ {
for pattern in helpers::TRIPLE_QUOTE_PREFIXES for pattern in constants::TRIPLE_QUOTE_PREFIXES
.iter() .iter()
.chain(helpers::SINGLE_QUOTE_PREFIXES) .chain(constants::SINGLE_QUOTE_PREFIXES)
{ {
if first_line.starts_with(pattern) { if first_line.starts_with(pattern) {
check.amend(Fix::replacement( check.amend(Fix::replacement(
@ -634,7 +635,7 @@ pub fn multi_line_summary_start(checker: &mut Checker, definition: &Definition)
.next() .next()
.map(|line| line.to_lowercase()) .map(|line| line.to_lowercase())
{ {
if helpers::TRIPLE_QUOTE_PREFIXES.contains(&first_line.as_str()) { if constants::TRIPLE_QUOTE_PREFIXES.contains(&first_line.as_str()) {
if checker.settings.enabled.contains(&CheckCode::D212) { if checker.settings.enabled.contains(&CheckCode::D212) {
checker.add_check(Check::new( checker.add_check(Check::new(
CheckKind::MultiLineSummaryFirstLine, CheckKind::MultiLineSummaryFirstLine,
@ -920,7 +921,7 @@ fn blanks_and_section_underline(
// Add a dashed line (of the appropriate length) under the section header. // Add a dashed line (of the appropriate length) under the section header.
let content = format!( let content = format!(
"{}{}\n", "{}{}\n",
helpers::clean(&helpers::indentation(checker, docstring)), whitespace::clean(&whitespace::indentation(checker, docstring)),
"-".repeat(context.section_name.len()) "-".repeat(context.section_name.len())
); );
check.amend(Fix::insertion( check.amend(Fix::insertion(
@ -954,7 +955,7 @@ fn blanks_and_section_underline(
// Add a dashed line (of the appropriate length) under the section header. // Add a dashed line (of the appropriate length) under the section header.
let content = format!( let content = format!(
"{}{}\n", "{}{}\n",
helpers::clean(&helpers::indentation(checker, docstring)), whitespace::clean(&whitespace::indentation(checker, docstring)),
"-".repeat(context.section_name.len()) "-".repeat(context.section_name.len())
); );
check.amend(Fix::insertion( check.amend(Fix::insertion(
@ -1030,7 +1031,7 @@ fn blanks_and_section_underline(
// Replace the existing underline with a line of the appropriate length. // Replace the existing underline with a line of the appropriate length.
let content = format!( let content = format!(
"{}{}\n", "{}{}\n",
helpers::clean(&helpers::indentation(checker, docstring)), whitespace::clean(&whitespace::indentation(checker, docstring)),
"-".repeat(context.section_name.len()) "-".repeat(context.section_name.len())
); );
check.amend(Fix::replacement( check.amend(Fix::replacement(
@ -1057,8 +1058,8 @@ fn blanks_and_section_underline(
} }
if checker.settings.enabled.contains(&CheckCode::D215) { if checker.settings.enabled.contains(&CheckCode::D215) {
let leading_space = helpers::leading_space(non_empty_line); let leading_space = whitespace::leading_space(non_empty_line);
let indentation = helpers::indentation(checker, docstring); let indentation = whitespace::indentation(checker, docstring);
if leading_space.len() > indentation.len() { if leading_space.len() > indentation.len() {
let mut check = Check::new( let mut check = Check::new(
CheckKind::SectionUnderlineNotOverIndented(context.section_name.to_string()), CheckKind::SectionUnderlineNotOverIndented(context.section_name.to_string()),
@ -1067,7 +1068,7 @@ fn blanks_and_section_underline(
if checker.patch(check.kind.code()) { if checker.patch(check.kind.code()) {
// Replace the existing indentation with whitespace of the appropriate length. // Replace the existing indentation with whitespace of the appropriate length.
check.amend(Fix::replacement( check.amend(Fix::replacement(
helpers::clean(&indentation), whitespace::clean(&indentation),
Location::new( Location::new(
docstring.location.row() docstring.location.row()
+ context.original_index + context.original_index
@ -1198,8 +1199,8 @@ fn common_section(
} }
if checker.settings.enabled.contains(&CheckCode::D214) { if checker.settings.enabled.contains(&CheckCode::D214) {
let leading_space = helpers::leading_space(context.line); let leading_space = whitespace::leading_space(context.line);
let indentation = helpers::indentation(checker, docstring); let indentation = whitespace::indentation(checker, docstring);
if leading_space.len() > indentation.len() { if leading_space.len() > indentation.len() {
let mut check = Check::new( let mut check = Check::new(
CheckKind::SectionNotOverIndented(context.section_name.to_string()), CheckKind::SectionNotOverIndented(context.section_name.to_string()),
@ -1208,7 +1209,7 @@ fn common_section(
if checker.patch(check.kind.code()) { if checker.patch(check.kind.code()) {
// Replace the existing indentation with whitespace of the appropriate length. // Replace the existing indentation with whitespace of the appropriate length.
check.amend(Fix::replacement( check.amend(Fix::replacement(
helpers::clean(&indentation), whitespace::clean(&indentation),
Location::new(docstring.location.row() + context.original_index, 0), Location::new(docstring.location.row() + context.original_index, 0),
Location::new( Location::new(
docstring.location.row() + context.original_index, docstring.location.row() + context.original_index,
@ -1400,13 +1401,13 @@ fn args_section(checker: &mut Checker, definition: &Definition, context: &Sectio
fn parameters_section(checker: &mut Checker, definition: &Definition, context: &SectionContext) { fn parameters_section(checker: &mut Checker, definition: &Definition, context: &SectionContext) {
// Collect the list of arguments documented in the docstring. // Collect the list of arguments documented in the docstring.
let mut docstring_args: FnvHashSet<&str> = FnvHashSet::default(); let mut docstring_args: FnvHashSet<&str> = FnvHashSet::default();
let section_level_indent = helpers::leading_space(context.line); let section_level_indent = whitespace::leading_space(context.line);
for i in 1..context.following_lines.len() { for i in 1..context.following_lines.len() {
let current_line = context.following_lines[i - 1]; let current_line = context.following_lines[i - 1];
let current_leading_space = helpers::leading_space(current_line); let current_leading_space = whitespace::leading_space(current_line);
let next_line = context.following_lines[i]; let next_line = context.following_lines[i];
if current_leading_space == section_level_indent if current_leading_space == section_level_indent
&& (helpers::leading_space(next_line).len() > current_leading_space.len()) && (whitespace::leading_space(next_line).len() > current_leading_space.len())
&& !next_line.trim().is_empty() && !next_line.trim().is_empty()
{ {
let parameters = if let Some(semi_index) = current_line.find(':') { let parameters = if let Some(semi_index) = current_line.find(':') {

View file

@ -9,7 +9,16 @@ expression: checks
end_location: end_location:
row: 2 row: 2
column: 19 column: 19
fix: ~ fix:
patch:
content: "def f(x):\n return (2 * x)"
location:
row: 2
column: 0
end_location:
row: 2
column: 19
applied: false
- kind: DoNotAssignLambda - kind: DoNotAssignLambda
location: location:
row: 4 row: 4
@ -17,7 +26,16 @@ expression: checks
end_location: end_location:
row: 4 row: 4
column: 19 column: 19
fix: ~ fix:
patch:
content: "def f(x):\n return (2 * x)"
location:
row: 4
column: 0
end_location:
row: 4
column: 19
applied: false
- kind: DoNotAssignLambda - kind: DoNotAssignLambda
location: location:
row: 7 row: 7
@ -25,5 +43,14 @@ expression: checks
end_location: end_location:
row: 7 row: 7
column: 29 column: 29
fix: ~ fix:
patch:
content: "def this(y, z):\n return (2 * x)"
location:
row: 7
column: 4
end_location:
row: 7
column: 29
applied: false