Modify visibility and shuffle around some modules (#1807)

This commit is contained in:
Charlie Marsh 2023-01-11 23:57:05 -05:00 committed by GitHub
parent d8162ce79d
commit b36d4a15b0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
144 changed files with 734 additions and 798 deletions

View file

@ -5,9 +5,7 @@ use std::path::PathBuf;
use anyhow::Result; use anyhow::Result;
use clap::Args; use clap::Args;
use ruff::source_code_generator::SourceCodeGenerator; use ruff::source_code::{Generator, Locator, Stylist};
use ruff::source_code_locator::SourceCodeLocator;
use ruff::source_code_style::SourceCodeStyleDetector;
use rustpython_parser::parser; use rustpython_parser::parser;
#[derive(Args)] #[derive(Args)]
@ -20,9 +18,9 @@ pub struct Cli {
pub fn main(cli: &Cli) -> Result<()> { pub fn main(cli: &Cli) -> Result<()> {
let contents = fs::read_to_string(&cli.file)?; let contents = fs::read_to_string(&cli.file)?;
let python_ast = parser::parse_program(&contents, &cli.file.to_string_lossy())?; let python_ast = parser::parse_program(&contents, &cli.file.to_string_lossy())?;
let locator = SourceCodeLocator::new(&contents); let locator = Locator::new(&contents);
let stylist = SourceCodeStyleDetector::from_contents(&contents, &locator); let stylist = Stylist::from_contents(&contents, &locator);
let mut generator: SourceCodeGenerator = (&stylist).into(); let mut generator: Generator = (&stylist).into();
generator.unparse_suite(&python_ast); generator.unparse_suite(&python_ast);
println!("{}", generator.generate()); println!("{}", generator.generate());
Ok(()) Ok(())

View file

@ -34,7 +34,7 @@ def main(*, plugin: str, url: str) -> None:
with open(os.path.join(ROOT_DIR, f"src/{dir_name(plugin)}/rules.rs"), "w+") as fp: with open(os.path.join(ROOT_DIR, f"src/{dir_name(plugin)}/rules.rs"), "w+") as fp:
fp.write("use crate::checkers::ast::Checker;\n") fp.write("use crate::checkers::ast::Checker;\n")
with open(os.path.join(ROOT_DIR, f"src/{dir_name(plugin)}/mod.rs"), "w+") as fp: with open(os.path.join(ROOT_DIR, f"src/{dir_name(plugin)}/mod.rs"), "w+") as fp:
fp.write("pub mod rules;\n") fp.write("pub(crate) mod rules;\n")
fp.write("\n") fp.write("\n")
fp.write( fp.write(
"""#[cfg(test)] """#[cfg(test)]

View file

@ -12,9 +12,7 @@ use rustpython_parser::lexer::Tok;
use rustpython_parser::token::StringKind; use rustpython_parser::token::StringKind;
use crate::ast::types::{Binding, BindingKind, Range}; use crate::ast::types::{Binding, BindingKind, Range};
use crate::source_code_generator::SourceCodeGenerator; use crate::source_code::{Generator, Locator, Stylist};
use crate::source_code_style::SourceCodeStyleDetector;
use crate::SourceCodeLocator;
/// Create an `Expr` with default location from an `ExprKind`. /// Create an `Expr` with default location from an `ExprKind`.
pub fn create_expr(node: ExprKind) -> Expr { pub fn create_expr(node: ExprKind) -> Expr {
@ -27,15 +25,15 @@ pub fn create_stmt(node: StmtKind) -> Stmt {
} }
/// Generate source code from an `Expr`. /// Generate source code from an `Expr`.
pub fn unparse_expr(expr: &Expr, stylist: &SourceCodeStyleDetector) -> String { pub fn unparse_expr(expr: &Expr, stylist: &Stylist) -> String {
let mut generator: SourceCodeGenerator = stylist.into(); let mut generator: Generator = stylist.into();
generator.unparse_expr(expr, 0); generator.unparse_expr(expr, 0);
generator.generate() generator.generate()
} }
/// Generate source code from an `Stmt`. /// Generate source code from an `Stmt`.
pub fn unparse_stmt(stmt: &Stmt, stylist: &SourceCodeStyleDetector) -> String { pub fn unparse_stmt(stmt: &Stmt, stylist: &Stylist) -> String {
let mut generator: SourceCodeGenerator = stylist.into(); let mut generator: Generator = stylist.into();
generator.unparse_stmt(stmt); generator.unparse_stmt(stmt);
generator.generate() generator.generate()
} }
@ -431,7 +429,7 @@ pub fn collect_arg_names<'a>(arguments: &'a Arguments) -> FxHashSet<&'a str> {
} }
/// Returns `true` if a statement or expression includes at least one comment. /// Returns `true` if a statement or expression includes at least one comment.
pub fn has_comments<T>(located: &Located<T>, locator: &SourceCodeLocator) -> bool { pub fn has_comments<T>(located: &Located<T>, locator: &Locator) -> bool {
lexer::make_tokenizer(&locator.slice_source_code_range(&Range::from_located(located))) lexer::make_tokenizer(&locator.slice_source_code_range(&Range::from_located(located)))
.flatten() .flatten()
.any(|(_, tok, _)| matches!(tok, Tok::Comment(..))) .any(|(_, tok, _)| matches!(tok, Tok::Comment(..)))
@ -483,14 +481,14 @@ pub fn to_absolute(relative: Location, base: Location) -> Location {
} }
/// Return `true` if a `Stmt` has leading content. /// Return `true` if a `Stmt` has leading content.
pub fn match_leading_content(stmt: &Stmt, locator: &SourceCodeLocator) -> bool { pub fn match_leading_content(stmt: &Stmt, locator: &Locator) -> bool {
let range = Range::new(Location::new(stmt.location.row(), 0), stmt.location); let range = Range::new(Location::new(stmt.location.row(), 0), stmt.location);
let prefix = locator.slice_source_code_range(&range); let prefix = locator.slice_source_code_range(&range);
prefix.chars().any(|char| !char.is_whitespace()) prefix.chars().any(|char| !char.is_whitespace())
} }
/// Return `true` if a `Stmt` has trailing content. /// Return `true` if a `Stmt` has trailing content.
pub fn match_trailing_content(stmt: &Stmt, locator: &SourceCodeLocator) -> bool { pub fn match_trailing_content(stmt: &Stmt, locator: &Locator) -> bool {
let range = Range::new( let range = Range::new(
stmt.end_location.unwrap(), stmt.end_location.unwrap(),
Location::new(stmt.end_location.unwrap().row() + 1, 0), Location::new(stmt.end_location.unwrap().row() + 1, 0),
@ -508,7 +506,7 @@ pub fn match_trailing_content(stmt: &Stmt, locator: &SourceCodeLocator) -> bool
} }
/// Return the number of trailing empty lines following a statement. /// Return the number of trailing empty lines following a statement.
pub fn count_trailing_lines(stmt: &Stmt, locator: &SourceCodeLocator) -> usize { pub fn count_trailing_lines(stmt: &Stmt, locator: &Locator) -> usize {
let suffix = let suffix =
locator.slice_source_code_at(&Location::new(stmt.end_location.unwrap().row() + 1, 0)); locator.slice_source_code_at(&Location::new(stmt.end_location.unwrap().row() + 1, 0));
suffix suffix
@ -520,7 +518,7 @@ pub fn count_trailing_lines(stmt: &Stmt, locator: &SourceCodeLocator) -> usize {
/// Return the appropriate visual `Range` for any message that spans a `Stmt`. /// Return the appropriate visual `Range` for any message that spans a `Stmt`.
/// Specifically, this method returns the range of a function or class name, /// Specifically, this method returns the range of a function or class name,
/// rather than that of the entire function or class body. /// rather than that of the entire function or class body.
pub fn identifier_range(stmt: &Stmt, locator: &SourceCodeLocator) -> Range { pub fn identifier_range(stmt: &Stmt, locator: &Locator) -> Range {
if matches!( if matches!(
stmt.node, stmt.node,
StmtKind::ClassDef { .. } StmtKind::ClassDef { .. }
@ -539,7 +537,7 @@ pub fn identifier_range(stmt: &Stmt, locator: &SourceCodeLocator) -> Range {
} }
/// Like `identifier_range`, but accepts a `Binding`. /// Like `identifier_range`, but accepts a `Binding`.
pub fn binding_range(binding: &Binding, locator: &SourceCodeLocator) -> Range { pub fn binding_range(binding: &Binding, locator: &Locator) -> Range {
if matches!( if matches!(
binding.kind, binding.kind,
BindingKind::ClassDefinition | BindingKind::FunctionDefinition BindingKind::ClassDefinition | BindingKind::FunctionDefinition
@ -555,7 +553,7 @@ pub fn binding_range(binding: &Binding, locator: &SourceCodeLocator) -> Range {
} }
// Return the ranges of `Name` tokens within a specified node. // Return the ranges of `Name` tokens within a specified node.
pub fn find_names<T>(located: &Located<T>, locator: &SourceCodeLocator) -> Vec<Range> { pub fn find_names<T>(located: &Located<T>, locator: &Locator) -> Vec<Range> {
let contents = locator.slice_source_code_range(&Range::from_located(located)); let contents = locator.slice_source_code_range(&Range::from_located(located));
lexer::make_tokenizer_located(&contents, located.location) lexer::make_tokenizer_located(&contents, located.location)
.flatten() .flatten()
@ -568,10 +566,7 @@ pub fn find_names<T>(located: &Located<T>, locator: &SourceCodeLocator) -> Vec<R
} }
/// Return the `Range` of `name` in `Excepthandler`. /// Return the `Range` of `name` in `Excepthandler`.
pub fn excepthandler_name_range( pub fn excepthandler_name_range(handler: &Excepthandler, locator: &Locator) -> Option<Range> {
handler: &Excepthandler,
locator: &SourceCodeLocator,
) -> Option<Range> {
let ExcepthandlerKind::ExceptHandler { let ExcepthandlerKind::ExceptHandler {
name, type_, body, .. name, type_, body, ..
} = &handler.node; } = &handler.node;
@ -594,7 +589,7 @@ pub fn excepthandler_name_range(
} }
/// Return the `Range` of `except` in `Excepthandler`. /// Return the `Range` of `except` in `Excepthandler`.
pub fn except_range(handler: &Excepthandler, locator: &SourceCodeLocator) -> Range { pub fn except_range(handler: &Excepthandler, locator: &Locator) -> Range {
let ExcepthandlerKind::ExceptHandler { body, type_, .. } = &handler.node; let ExcepthandlerKind::ExceptHandler { body, type_, .. } = &handler.node;
let end = if let Some(type_) = type_ { let end = if let Some(type_) = type_ {
type_.location type_.location
@ -619,7 +614,7 @@ pub fn except_range(handler: &Excepthandler, locator: &SourceCodeLocator) -> Ran
} }
/// Find f-strings that don't contain any formatted values in a `JoinedStr`. /// Find f-strings that don't contain any formatted values in a `JoinedStr`.
pub fn find_useless_f_strings(expr: &Expr, locator: &SourceCodeLocator) -> Vec<(Range, Range)> { pub fn find_useless_f_strings(expr: &Expr, locator: &Locator) -> Vec<(Range, Range)> {
let contents = locator.slice_source_code_range(&Range::from_located(expr)); let contents = locator.slice_source_code_range(&Range::from_located(expr));
lexer::make_tokenizer_located(&contents, expr.location) lexer::make_tokenizer_located(&contents, expr.location)
.flatten() .flatten()
@ -656,7 +651,7 @@ pub fn find_useless_f_strings(expr: &Expr, locator: &SourceCodeLocator) -> Vec<(
} }
/// Return the `Range` of `else` in `For`, `AsyncFor`, and `While` statements. /// Return the `Range` of `else` in `For`, `AsyncFor`, and `While` statements.
pub fn else_range(stmt: &Stmt, locator: &SourceCodeLocator) -> Option<Range> { pub fn else_range(stmt: &Stmt, locator: &Locator) -> Option<Range> {
match &stmt.node { match &stmt.node {
StmtKind::For { body, orelse, .. } StmtKind::For { body, orelse, .. }
| StmtKind::AsyncFor { body, orelse, .. } | StmtKind::AsyncFor { body, orelse, .. }
@ -690,7 +685,7 @@ pub fn else_range(stmt: &Stmt, locator: &SourceCodeLocator) -> Option<Range> {
/// Return `true` if a `Stmt` appears to be part of a multi-statement line, with /// Return `true` if a `Stmt` appears to be part of a multi-statement line, with
/// other statements preceding it. /// other statements preceding it.
pub fn preceded_by_continuation(stmt: &Stmt, locator: &SourceCodeLocator) -> bool { pub fn preceded_by_continuation(stmt: &Stmt, locator: &Locator) -> bool {
// Does the previous line end in a continuation? This will have a specific // Does the previous line end in a continuation? This will have a specific
// false-positive, which is that if the previous line ends in a comment, it // false-positive, which is that if the previous line ends in a comment, it
// will be treated as a continuation. So we should only use this information to // will be treated as a continuation. So we should only use this information to
@ -711,13 +706,13 @@ pub fn preceded_by_continuation(stmt: &Stmt, locator: &SourceCodeLocator) -> boo
/// Return `true` if a `Stmt` appears to be part of a multi-statement line, with /// Return `true` if a `Stmt` appears to be part of a multi-statement line, with
/// other statements preceding it. /// other statements preceding it.
pub fn preceded_by_multi_statement_line(stmt: &Stmt, locator: &SourceCodeLocator) -> bool { pub fn preceded_by_multi_statement_line(stmt: &Stmt, locator: &Locator) -> bool {
match_leading_content(stmt, locator) || preceded_by_continuation(stmt, locator) match_leading_content(stmt, locator) || preceded_by_continuation(stmt, locator)
} }
/// Return `true` if a `Stmt` appears to be part of a multi-statement line, with /// Return `true` if a `Stmt` appears to be part of a multi-statement line, with
/// other statements following it. /// other statements following it.
pub fn followed_by_multi_statement_line(stmt: &Stmt, locator: &SourceCodeLocator) -> bool { pub fn followed_by_multi_statement_line(stmt: &Stmt, locator: &Locator) -> bool {
match_trailing_content(stmt, locator) match_trailing_content(stmt, locator)
} }
@ -799,7 +794,7 @@ mod tests {
else_range, identifier_range, match_module_member, match_trailing_content, else_range, identifier_range, match_module_member, match_trailing_content,
}; };
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::source_code_locator::SourceCodeLocator; use crate::source_code::Locator;
#[test] #[test]
fn builtin() -> Result<()> { fn builtin() -> Result<()> {
@ -949,25 +944,25 @@ mod tests {
let contents = "x = 1"; let contents = "x = 1";
let program = parser::parse_program(contents, "<filename>")?; let program = parser::parse_program(contents, "<filename>")?;
let stmt = program.first().unwrap(); let stmt = program.first().unwrap();
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
assert!(!match_trailing_content(stmt, &locator)); assert!(!match_trailing_content(stmt, &locator));
let contents = "x = 1; y = 2"; let contents = "x = 1; y = 2";
let program = parser::parse_program(contents, "<filename>")?; let program = parser::parse_program(contents, "<filename>")?;
let stmt = program.first().unwrap(); let stmt = program.first().unwrap();
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
assert!(match_trailing_content(stmt, &locator)); assert!(match_trailing_content(stmt, &locator));
let contents = "x = 1 "; let contents = "x = 1 ";
let program = parser::parse_program(contents, "<filename>")?; let program = parser::parse_program(contents, "<filename>")?;
let stmt = program.first().unwrap(); let stmt = program.first().unwrap();
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
assert!(!match_trailing_content(stmt, &locator)); assert!(!match_trailing_content(stmt, &locator));
let contents = "x = 1 # Comment"; let contents = "x = 1 # Comment";
let program = parser::parse_program(contents, "<filename>")?; let program = parser::parse_program(contents, "<filename>")?;
let stmt = program.first().unwrap(); let stmt = program.first().unwrap();
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
assert!(!match_trailing_content(stmt, &locator)); assert!(!match_trailing_content(stmt, &locator));
let contents = r#" let contents = r#"
@ -977,7 +972,7 @@ y = 2
.trim(); .trim();
let program = parser::parse_program(contents, "<filename>")?; let program = parser::parse_program(contents, "<filename>")?;
let stmt = program.first().unwrap(); let stmt = program.first().unwrap();
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
assert!(!match_trailing_content(stmt, &locator)); assert!(!match_trailing_content(stmt, &locator));
Ok(()) Ok(())
@ -988,7 +983,7 @@ y = 2
let contents = "def f(): pass".trim(); let contents = "def f(): pass".trim();
let program = parser::parse_program(contents, "<filename>")?; let program = parser::parse_program(contents, "<filename>")?;
let stmt = program.first().unwrap(); let stmt = program.first().unwrap();
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
assert_eq!( assert_eq!(
identifier_range(stmt, &locator), identifier_range(stmt, &locator),
Range::new(Location::new(1, 4), Location::new(1, 5),) Range::new(Location::new(1, 4), Location::new(1, 5),)
@ -1002,7 +997,7 @@ def \
.trim(); .trim();
let program = parser::parse_program(contents, "<filename>")?; let program = parser::parse_program(contents, "<filename>")?;
let stmt = program.first().unwrap(); let stmt = program.first().unwrap();
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
assert_eq!( assert_eq!(
identifier_range(stmt, &locator), identifier_range(stmt, &locator),
Range::new(Location::new(2, 2), Location::new(2, 3),) Range::new(Location::new(2, 2), Location::new(2, 3),)
@ -1011,7 +1006,7 @@ def \
let contents = "class Class(): pass".trim(); let contents = "class Class(): pass".trim();
let program = parser::parse_program(contents, "<filename>")?; let program = parser::parse_program(contents, "<filename>")?;
let stmt = program.first().unwrap(); let stmt = program.first().unwrap();
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
assert_eq!( assert_eq!(
identifier_range(stmt, &locator), identifier_range(stmt, &locator),
Range::new(Location::new(1, 6), Location::new(1, 11),) Range::new(Location::new(1, 6), Location::new(1, 11),)
@ -1020,7 +1015,7 @@ def \
let contents = "class Class: pass".trim(); let contents = "class Class: pass".trim();
let program = parser::parse_program(contents, "<filename>")?; let program = parser::parse_program(contents, "<filename>")?;
let stmt = program.first().unwrap(); let stmt = program.first().unwrap();
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
assert_eq!( assert_eq!(
identifier_range(stmt, &locator), identifier_range(stmt, &locator),
Range::new(Location::new(1, 6), Location::new(1, 11),) Range::new(Location::new(1, 6), Location::new(1, 11),)
@ -1034,7 +1029,7 @@ class Class():
.trim(); .trim();
let program = parser::parse_program(contents, "<filename>")?; let program = parser::parse_program(contents, "<filename>")?;
let stmt = program.first().unwrap(); let stmt = program.first().unwrap();
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
assert_eq!( assert_eq!(
identifier_range(stmt, &locator), identifier_range(stmt, &locator),
Range::new(Location::new(2, 6), Location::new(2, 11),) Range::new(Location::new(2, 6), Location::new(2, 11),)
@ -1043,7 +1038,7 @@ class Class():
let contents = r#"x = y + 1"#.trim(); let contents = r#"x = y + 1"#.trim();
let program = parser::parse_program(contents, "<filename>")?; let program = parser::parse_program(contents, "<filename>")?;
let stmt = program.first().unwrap(); let stmt = program.first().unwrap();
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
assert_eq!( assert_eq!(
identifier_range(stmt, &locator), identifier_range(stmt, &locator),
Range::new(Location::new(1, 0), Location::new(1, 9),) Range::new(Location::new(1, 0), Location::new(1, 9),)
@ -1063,7 +1058,7 @@ else:
.trim(); .trim();
let program = parser::parse_program(contents, "<filename>")?; let program = parser::parse_program(contents, "<filename>")?;
let stmt = program.first().unwrap(); let stmt = program.first().unwrap();
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
let range = else_range(stmt, &locator).unwrap(); let range = else_range(stmt, &locator).unwrap();
assert_eq!(range.location.row(), 3); assert_eq!(range.location.row(), 3);
assert_eq!(range.location.column(), 0); assert_eq!(range.location.column(), 0);

View file

@ -74,7 +74,6 @@ pub enum ScopeKind<'a> {
Function(FunctionDef<'a>), Function(FunctionDef<'a>),
Generator, Generator,
Module, Module,
Arg,
Lambda(Lambda<'a>), Lambda(Lambda<'a>),
} }

View file

@ -1,225 +1 @@
use std::borrow::Cow;
use std::collections::BTreeSet;
use itertools::Itertools;
use ropey::RopeBuilder;
use rustpython_parser::ast::Location;
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::registry::Diagnostic;
use crate::source_code_locator::SourceCodeLocator;
#[derive(Debug, Copy, Clone, Hash)]
pub enum Mode {
Generate,
Apply,
Diff,
None,
}
impl From<bool> for Mode {
fn from(value: bool) -> Self {
if value {
Mode::Apply
} else {
Mode::None
}
}
}
/// Auto-fix errors in a file, and write the fixed source code to disk.
pub fn fix_file<'a>(
diagnostics: &'a [Diagnostic],
locator: &'a SourceCodeLocator<'a>,
) -> Option<(Cow<'a, str>, usize)> {
if diagnostics.iter().all(|check| check.fix.is_none()) {
return None;
}
Some(apply_fixes(
diagnostics.iter().filter_map(|check| check.fix.as_ref()),
locator,
))
}
/// Apply a series of fixes.
fn apply_fixes<'a>(
fixes: impl Iterator<Item = &'a Fix>,
locator: &'a SourceCodeLocator<'a>,
) -> (Cow<'a, str>, usize) {
let mut output = RopeBuilder::new();
let mut last_pos: Location = Location::new(1, 0);
let mut applied: BTreeSet<&Fix> = BTreeSet::default();
let mut num_fixed: usize = 0;
for fix in fixes.sorted_by_key(|fix| fix.location) {
// If we already applied an identical fix as part of another correction, skip
// any re-application.
if applied.contains(&fix) {
num_fixed += 1;
continue;
}
// Best-effort approach: if this fix overlaps with a fix we've already applied,
// skip it.
if last_pos > fix.location {
continue;
}
// Add all contents from `last_pos` to `fix.location`.
let slice = locator.slice_source_code_range(&Range::new(last_pos, fix.location));
output.append(&slice);
// Add the patch itself.
output.append(&fix.content);
// Track that the fix was applied.
last_pos = fix.end_location;
applied.insert(fix);
num_fixed += 1;
}
// Add the remaining content.
let slice = locator.slice_source_code_at(&last_pos);
output.append(&slice);
(Cow::from(output.finish()), num_fixed)
}
#[cfg(test)]
mod tests {
use rustpython_parser::ast::Location;
use crate::autofix::fixer::apply_fixes;
use crate::autofix::Fix;
use crate::SourceCodeLocator;
#[test]
fn empty_file() {
let fixes = vec![];
let locator = SourceCodeLocator::new(r#""#);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(contents, "");
assert_eq!(fixed, 0);
}
#[test]
fn apply_single_replacement() {
let fixes = vec![Fix {
content: "Bar".to_string(),
location: Location::new(1, 8),
end_location: Location::new(1, 14),
}];
let locator = SourceCodeLocator::new(
r#"
class A(object):
...
"#
.trim(),
);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(
contents,
r#"
class A(Bar):
...
"#
.trim(),
);
assert_eq!(fixed, 1);
}
#[test]
fn apply_single_removal() {
let fixes = vec![Fix {
content: String::new(),
location: Location::new(1, 7),
end_location: Location::new(1, 15),
}];
let locator = SourceCodeLocator::new(
r#"
class A(object):
...
"#
.trim(),
);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(
contents,
r#"
class A:
...
"#
.trim()
);
assert_eq!(fixed, 1);
}
#[test]
fn apply_double_removal() {
let fixes = vec![
Fix {
content: String::new(),
location: Location::new(1, 7),
end_location: Location::new(1, 16),
},
Fix {
content: String::new(),
location: Location::new(1, 16),
end_location: Location::new(1, 23),
},
];
let locator = SourceCodeLocator::new(
r#"
class A(object, object):
...
"#
.trim(),
);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(
contents,
r#"
class A:
...
"#
.trim()
);
assert_eq!(fixed, 2);
}
#[test]
fn ignore_overlapping_fixes() {
let fixes = vec![
Fix {
content: String::new(),
location: Location::new(1, 7),
end_location: Location::new(1, 15),
},
Fix {
content: "ignored".to_string(),
location: Location::new(1, 9),
end_location: Location::new(1, 11),
},
];
let locator = SourceCodeLocator::new(
r#"
class A(object):
...
"#
.trim(),
);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(
contents,
r#"
class A:
...
"#
.trim(),
);
assert_eq!(fixed, 1);
}
}

View file

@ -9,10 +9,10 @@ use crate::ast::helpers;
use crate::ast::helpers::to_absolute; use crate::ast::helpers::to_absolute;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::ast::whitespace::LinesWithTrailingNewline; use crate::ast::whitespace::LinesWithTrailingNewline;
use crate::autofix::Fix;
use crate::cst::helpers::compose_module_path; use crate::cst::helpers::compose_module_path;
use crate::cst::matchers::match_module; use crate::cst::matchers::match_module;
use crate::source_code_locator::SourceCodeLocator; use crate::fix::Fix;
use crate::source_code::Locator;
/// Determine if a body contains only a single statement, taking into account /// Determine if a body contains only a single statement, taking into account
/// deleted. /// deleted.
@ -78,7 +78,7 @@ fn is_lone_child(child: &Stmt, parent: &Stmt, deleted: &[&Stmt]) -> Result<bool>
/// Return the location of a trailing semicolon following a `Stmt`, if it's part /// Return the location of a trailing semicolon following a `Stmt`, if it's part
/// of a multi-statement line. /// of a multi-statement line.
fn trailing_semicolon(stmt: &Stmt, locator: &SourceCodeLocator) -> Option<Location> { fn trailing_semicolon(stmt: &Stmt, locator: &Locator) -> Option<Location> {
let contents = locator.slice_source_code_at(&stmt.end_location.unwrap()); let contents = locator.slice_source_code_at(&stmt.end_location.unwrap());
for (row, line) in LinesWithTrailingNewline::from(&contents).enumerate() { for (row, line) in LinesWithTrailingNewline::from(&contents).enumerate() {
let trimmed = line.trim(); let trimmed = line.trim();
@ -100,7 +100,7 @@ fn trailing_semicolon(stmt: &Stmt, locator: &SourceCodeLocator) -> Option<Locati
} }
/// Find the next valid break for a `Stmt` after a semicolon. /// Find the next valid break for a `Stmt` after a semicolon.
fn next_stmt_break(semicolon: Location, locator: &SourceCodeLocator) -> Location { fn next_stmt_break(semicolon: Location, locator: &Locator) -> Location {
let start_location = Location::new(semicolon.row(), semicolon.column() + 1); let start_location = Location::new(semicolon.row(), semicolon.column() + 1);
let contents = locator.slice_source_code_at(&start_location); let contents = locator.slice_source_code_at(&start_location);
for (row, line) in LinesWithTrailingNewline::from(&contents).enumerate() { for (row, line) in LinesWithTrailingNewline::from(&contents).enumerate() {
@ -133,7 +133,7 @@ fn next_stmt_break(semicolon: Location, locator: &SourceCodeLocator) -> Location
} }
/// Return `true` if a `Stmt` occurs at the end of a file. /// Return `true` if a `Stmt` occurs at the end of a file.
fn is_end_of_file(stmt: &Stmt, locator: &SourceCodeLocator) -> bool { fn is_end_of_file(stmt: &Stmt, locator: &Locator) -> bool {
let contents = locator.slice_source_code_at(&stmt.end_location.unwrap()); let contents = locator.slice_source_code_at(&stmt.end_location.unwrap());
contents.is_empty() contents.is_empty()
} }
@ -155,7 +155,7 @@ pub fn delete_stmt(
stmt: &Stmt, stmt: &Stmt,
parent: Option<&Stmt>, parent: Option<&Stmt>,
deleted: &[&Stmt], deleted: &[&Stmt],
locator: &SourceCodeLocator, locator: &Locator,
) -> Result<Fix> { ) -> Result<Fix> {
if parent if parent
.map(|parent| is_lone_child(stmt, parent, deleted)) .map(|parent| is_lone_child(stmt, parent, deleted))
@ -197,7 +197,7 @@ pub fn remove_unused_imports<'a>(
stmt: &Stmt, stmt: &Stmt,
parent: Option<&Stmt>, parent: Option<&Stmt>,
deleted: &[&Stmt], deleted: &[&Stmt],
locator: &SourceCodeLocator, locator: &Locator,
) -> Result<Fix> { ) -> Result<Fix> {
let module_text = locator.slice_source_code_range(&Range::from_located(stmt)); let module_text = locator.slice_source_code_range(&Range::from_located(stmt));
let mut tree = match_module(&module_text)?; let mut tree = match_module(&module_text)?;
@ -299,20 +299,20 @@ mod tests {
use rustpython_parser::parser; use rustpython_parser::parser;
use crate::autofix::helpers::{next_stmt_break, trailing_semicolon}; use crate::autofix::helpers::{next_stmt_break, trailing_semicolon};
use crate::source_code_locator::SourceCodeLocator; use crate::source_code::Locator;
#[test] #[test]
fn find_semicolon() -> Result<()> { fn find_semicolon() -> Result<()> {
let contents = "x = 1"; let contents = "x = 1";
let program = parser::parse_program(contents, "<filename>")?; let program = parser::parse_program(contents, "<filename>")?;
let stmt = program.first().unwrap(); let stmt = program.first().unwrap();
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
assert_eq!(trailing_semicolon(stmt, &locator), None); assert_eq!(trailing_semicolon(stmt, &locator), None);
let contents = "x = 1; y = 1"; let contents = "x = 1; y = 1";
let program = parser::parse_program(contents, "<filename>")?; let program = parser::parse_program(contents, "<filename>")?;
let stmt = program.first().unwrap(); let stmt = program.first().unwrap();
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
assert_eq!( assert_eq!(
trailing_semicolon(stmt, &locator), trailing_semicolon(stmt, &locator),
Some(Location::new(1, 5)) Some(Location::new(1, 5))
@ -321,7 +321,7 @@ mod tests {
let contents = "x = 1 ; y = 1"; let contents = "x = 1 ; y = 1";
let program = parser::parse_program(contents, "<filename>")?; let program = parser::parse_program(contents, "<filename>")?;
let stmt = program.first().unwrap(); let stmt = program.first().unwrap();
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
assert_eq!( assert_eq!(
trailing_semicolon(stmt, &locator), trailing_semicolon(stmt, &locator),
Some(Location::new(1, 6)) Some(Location::new(1, 6))
@ -334,7 +334,7 @@ x = 1 \
.trim(); .trim();
let program = parser::parse_program(contents, "<filename>")?; let program = parser::parse_program(contents, "<filename>")?;
let stmt = program.first().unwrap(); let stmt = program.first().unwrap();
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
assert_eq!( assert_eq!(
trailing_semicolon(stmt, &locator), trailing_semicolon(stmt, &locator),
Some(Location::new(2, 2)) Some(Location::new(2, 2))
@ -346,14 +346,14 @@ x = 1 \
#[test] #[test]
fn find_next_stmt_break() { fn find_next_stmt_break() {
let contents = "x = 1; y = 1"; let contents = "x = 1; y = 1";
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
assert_eq!( assert_eq!(
next_stmt_break(Location::new(1, 4), &locator), next_stmt_break(Location::new(1, 4), &locator),
Location::new(1, 5) Location::new(1, 5)
); );
let contents = "x = 1 ; y = 1"; let contents = "x = 1 ; y = 1";
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
assert_eq!( assert_eq!(
next_stmt_break(Location::new(1, 5), &locator), next_stmt_break(Location::new(1, 5), &locator),
Location::new(1, 6) Location::new(1, 6)
@ -364,7 +364,7 @@ x = 1 \
; y = 1 ; y = 1
"# "#
.trim(); .trim();
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
assert_eq!( assert_eq!(
next_stmt_break(Location::new(2, 2), &locator), next_stmt_break(Location::new(2, 2), &locator),
Location::new(2, 4) Location::new(2, 4)

View file

@ -1,38 +1,210 @@
use std::borrow::Cow;
use std::collections::BTreeSet;
use itertools::Itertools;
use ropey::RopeBuilder;
use rustpython_ast::Location; use rustpython_ast::Location;
use serde::{Deserialize, Serialize};
use crate::ast::types::Range;
use crate::fix::Fix;
use crate::registry::Diagnostic;
use crate::source_code::Locator;
pub mod fixer; pub mod fixer;
pub mod helpers; pub mod helpers;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] /// Auto-fix errors in a file, and write the fixed source code to disk.
pub struct Fix { pub fn fix_file<'a>(
pub content: String, diagnostics: &'a [Diagnostic],
pub location: Location, locator: &'a Locator<'a>,
pub end_location: Location, ) -> Option<(Cow<'a, str>, usize)> {
if diagnostics.iter().all(|check| check.fix.is_none()) {
return None;
}
Some(apply_fixes(
diagnostics.iter().filter_map(|check| check.fix.as_ref()),
locator,
))
} }
impl Fix { /// Apply a series of fixes.
pub fn deletion(start: Location, end: Location) -> Self { fn apply_fixes<'a>(
Self { fixes: impl Iterator<Item = &'a Fix>,
locator: &'a Locator<'a>,
) -> (Cow<'a, str>, usize) {
let mut output = RopeBuilder::new();
let mut last_pos: Location = Location::new(1, 0);
let mut applied: BTreeSet<&Fix> = BTreeSet::default();
let mut num_fixed: usize = 0;
for fix in fixes.sorted_by_key(|fix| fix.location) {
// If we already applied an identical fix as part of another correction, skip
// any re-application.
if applied.contains(&fix) {
num_fixed += 1;
continue;
}
// Best-effort approach: if this fix overlaps with a fix we've already applied,
// skip it.
if last_pos > fix.location {
continue;
}
// Add all contents from `last_pos` to `fix.location`.
let slice = locator.slice_source_code_range(&Range::new(last_pos, fix.location));
output.append(&slice);
// Add the patch itself.
output.append(&fix.content);
// Track that the fix was applied.
last_pos = fix.end_location;
applied.insert(fix);
num_fixed += 1;
}
// Add the remaining content.
let slice = locator.slice_source_code_at(&last_pos);
output.append(&slice);
(Cow::from(output.finish()), num_fixed)
}
#[cfg(test)]
mod tests {
use rustpython_parser::ast::Location;
use crate::autofix::apply_fixes;
use crate::fix::Fix;
use crate::source_code::Locator;
#[test]
fn empty_file() {
let fixes = vec![];
let locator = Locator::new(r#""#);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(contents, "");
assert_eq!(fixed, 0);
}
#[test]
fn apply_single_replacement() {
let fixes = vec![Fix {
content: "Bar".to_string(),
location: Location::new(1, 8),
end_location: Location::new(1, 14),
}];
let locator = Locator::new(
r#"
class A(object):
...
"#
.trim(),
);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(
contents,
r#"
class A(Bar):
...
"#
.trim(),
);
assert_eq!(fixed, 1);
}
#[test]
fn apply_single_removal() {
let fixes = vec![Fix {
content: String::new(), content: String::new(),
location: start, location: Location::new(1, 7),
end_location: end, end_location: Location::new(1, 15),
} }];
let locator = Locator::new(
r#"
class A(object):
...
"#
.trim(),
);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(
contents,
r#"
class A:
...
"#
.trim()
);
assert_eq!(fixed, 1);
} }
pub fn replacement(content: String, start: Location, end: Location) -> Self { #[test]
Self { fn apply_double_removal() {
content, let fixes = vec![
location: start, Fix {
end_location: end, content: String::new(),
} location: Location::new(1, 7),
end_location: Location::new(1, 16),
},
Fix {
content: String::new(),
location: Location::new(1, 16),
end_location: Location::new(1, 23),
},
];
let locator = Locator::new(
r#"
class A(object, object):
...
"#
.trim(),
);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(
contents,
r#"
class A:
...
"#
.trim()
);
assert_eq!(fixed, 2);
} }
pub fn insertion(content: String, at: Location) -> Self { #[test]
Self { fn ignore_overlapping_fixes() {
content, let fixes = vec![
location: at, Fix {
end_location: at, content: String::new(),
} location: Location::new(1, 7),
end_location: Location::new(1, 15),
},
Fix {
content: "ignored".to_string(),
location: Location::new(1, 9),
end_location: Location::new(1, 11),
},
];
let locator = Locator::new(
r#"
class A(object):
...
"#
.trim(),
);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(
contents,
r#"
class A:
...
"#
.trim(),
);
assert_eq!(fixed, 1);
} }
} }

View file

@ -53,6 +53,7 @@ fn cache_key<P: AsRef<Path>>(path: P, settings: &Settings, autofix: flags::Autof
hasher.finish() hasher.finish()
} }
#[allow(dead_code)]
/// Initialize the cache at the specified `Path`. /// Initialize the cache at the specified `Path`.
pub fn init(path: &Path) -> Result<()> { pub fn init(path: &Path) -> Result<()> {
// Create the cache directories. // Create the cache directories.

View file

@ -33,8 +33,7 @@ use crate::python::typing::SubscriptKind;
use crate::registry::{Diagnostic, RuleCode}; use crate::registry::{Diagnostic, RuleCode};
use crate::settings::types::PythonVersion; use crate::settings::types::PythonVersion;
use crate::settings::{flags, Settings}; use crate::settings::{flags, Settings};
use crate::source_code_locator::SourceCodeLocator; use crate::source_code::{Locator, Stylist};
use crate::source_code_style::SourceCodeStyleDetector;
use crate::violations::DeferralKeyword; use crate::violations::DeferralKeyword;
use crate::visibility::{module_visibility, transition_scope, Modifier, Visibility, VisibleScope}; use crate::visibility::{module_visibility, transition_scope, Modifier, Visibility, VisibleScope};
use crate::{ use crate::{
@ -59,8 +58,8 @@ pub struct Checker<'a> {
noqa: flags::Noqa, noqa: flags::Noqa,
pub(crate) settings: &'a Settings, pub(crate) settings: &'a Settings,
pub(crate) noqa_line_for: &'a IntMap<usize, usize>, pub(crate) noqa_line_for: &'a IntMap<usize, usize>,
pub(crate) locator: &'a SourceCodeLocator<'a>, pub(crate) locator: &'a Locator<'a>,
pub(crate) style: &'a SourceCodeStyleDetector<'a>, pub(crate) style: &'a Stylist<'a>,
// Computed diagnostics. // Computed diagnostics.
pub(crate) diagnostics: Vec<Diagnostic>, pub(crate) diagnostics: Vec<Diagnostic>,
// Function and class definition tracking (e.g., for docstring enforcement). // Function and class definition tracking (e.g., for docstring enforcement).
@ -110,8 +109,8 @@ impl<'a> Checker<'a> {
autofix: flags::Autofix, autofix: flags::Autofix,
noqa: flags::Noqa, noqa: flags::Noqa,
path: &'a Path, path: &'a Path,
locator: &'a SourceCodeLocator, locator: &'a Locator,
style: &'a SourceCodeStyleDetector, style: &'a Stylist,
) -> Checker<'a> { ) -> Checker<'a> {
Checker { Checker {
settings, settings,
@ -3238,16 +3237,6 @@ impl<'a> Checker<'a> {
self.parents.iter().rev().nth(1) self.parents.iter().rev().nth(1)
} }
/// Return the grandparent `Stmt` of the current `Stmt`, if any.
pub fn current_stmt_grandparent(&self) -> Option<&RefEquality<'a, Stmt>> {
self.parents.iter().rev().nth(2)
}
/// Return the current `Expr`.
pub fn current_expr(&self) -> Option<&RefEquality<'a, Expr>> {
self.exprs.iter().rev().next()
}
/// Return the parent `Expr` of the current `Expr`. /// Return the parent `Expr` of the current `Expr`.
pub fn current_expr_parent(&self) -> Option<&RefEquality<'a, Expr>> { pub fn current_expr_parent(&self) -> Option<&RefEquality<'a, Expr>> {
self.exprs.iter().rev().nth(1) self.exprs.iter().rev().nth(1)
@ -4308,8 +4297,8 @@ impl<'a> Checker<'a> {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn check_ast( pub fn check_ast(
python_ast: &Suite, python_ast: &Suite,
locator: &SourceCodeLocator, locator: &Locator,
stylist: &SourceCodeStyleDetector, stylist: &Stylist,
noqa_line_for: &IntMap<usize, usize>, noqa_line_for: &IntMap<usize, usize>,
settings: &Settings, settings: &Settings,
autofix: flags::Autofix, autofix: flags::Autofix,

View file

@ -10,16 +10,15 @@ use crate::isort;
use crate::isort::track::{Block, ImportTracker}; use crate::isort::track::{Block, ImportTracker};
use crate::registry::{Diagnostic, RuleCode}; use crate::registry::{Diagnostic, RuleCode};
use crate::settings::{flags, Settings}; use crate::settings::{flags, Settings};
use crate::source_code_locator::SourceCodeLocator; use crate::source_code::{Locator, Stylist};
use crate::source_code_style::SourceCodeStyleDetector;
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn check_imports( pub fn check_imports(
python_ast: &Suite, python_ast: &Suite,
locator: &SourceCodeLocator, locator: &Locator,
directives: &IsortDirectives, directives: &IsortDirectives,
settings: &Settings, settings: &Settings,
stylist: &SourceCodeStyleDetector, stylist: &Stylist,
autofix: flags::Autofix, autofix: flags::Autofix,
path: &Path, path: &Path,
package: Option<&Path>, package: Option<&Path>,

View file

@ -6,7 +6,7 @@ use nohash_hasher::IntMap;
use rustpython_parser::ast::Location; use rustpython_parser::ast::Location;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix; use crate::fix::Fix;
use crate::noqa::{is_file_exempt, Directive}; use crate::noqa::{is_file_exempt, Directive};
use crate::registry::{Diagnostic, DiagnosticKind, RuleCode, CODE_REDIRECTS}; use crate::registry::{Diagnostic, DiagnosticKind, RuleCode, CODE_REDIRECTS};
use crate::settings::{flags, Settings}; use crate::settings::{flags, Settings};

View file

@ -5,12 +5,12 @@ use rustpython_parser::lexer::{LexResult, Tok};
use crate::lex::docstring_detection::StateMachine; use crate::lex::docstring_detection::StateMachine;
use crate::registry::{Diagnostic, RuleCode}; use crate::registry::{Diagnostic, RuleCode};
use crate::ruff::rules::Context; use crate::ruff::rules::Context;
use crate::settings::flags; use crate::settings::{flags, Settings};
use crate::source_code_locator::SourceCodeLocator; use crate::source_code::Locator;
use crate::{eradicate, flake8_implicit_str_concat, flake8_quotes, pycodestyle, ruff, Settings}; use crate::{eradicate, flake8_implicit_str_concat, flake8_quotes, pycodestyle, ruff};
pub fn check_tokens( pub fn check_tokens(
locator: &SourceCodeLocator, locator: &Locator,
tokens: &[LexResult], tokens: &[LexResult],
settings: &Settings, settings: &Settings,
autofix: flags::Autofix, autofix: flags::Autofix,

View file

@ -15,7 +15,6 @@ use rustpython_ast::Location;
use serde::Serialize; use serde::Serialize;
use walkdir::WalkDir; use walkdir::WalkDir;
use crate::autofix::fixer;
use crate::cache::CACHE_DIR_NAME; use crate::cache::CACHE_DIR_NAME;
use crate::cli::Overrides; use crate::cli::Overrides;
use crate::iterators::par_iter; use crate::iterators::par_iter;
@ -26,7 +25,7 @@ use crate::registry::RuleCode;
use crate::resolver::{FileDiscovery, PyprojectDiscovery}; use crate::resolver::{FileDiscovery, PyprojectDiscovery};
use crate::settings::flags; use crate::settings::flags;
use crate::settings::types::SerializationFormat; use crate::settings::types::SerializationFormat;
use crate::{cache, fs, packages, resolver, violations, warn_user_once}; use crate::{cache, fix, fs, packaging, resolver, violations, warn_user_once};
/// Run the linter over a collection of files. /// Run the linter over a collection of files.
pub fn run( pub fn run(
@ -35,7 +34,7 @@ pub fn run(
file_strategy: &FileDiscovery, file_strategy: &FileDiscovery,
overrides: &Overrides, overrides: &Overrides,
cache: flags::Cache, cache: flags::Cache,
autofix: fixer::Mode, autofix: fix::FixMode,
) -> Result<Diagnostics> { ) -> Result<Diagnostics> {
// Collect all the Python files to check. // Collect all the Python files to check.
let start = Instant::now(); let start = Instant::now();
@ -77,7 +76,7 @@ pub fn run(
}; };
// Discover the package root for each Python file. // Discover the package root for each Python file.
let package_roots = packages::detect_package_roots( let package_roots = packaging::detect_package_roots(
&paths &paths
.iter() .iter()
.flatten() .flatten()
@ -156,7 +155,7 @@ pub fn run_stdin(
pyproject_strategy: &PyprojectDiscovery, pyproject_strategy: &PyprojectDiscovery,
file_strategy: &FileDiscovery, file_strategy: &FileDiscovery,
overrides: &Overrides, overrides: &Overrides,
autofix: fixer::Mode, autofix: fix::FixMode,
) -> Result<Diagnostics> { ) -> Result<Diagnostics> {
if let Some(filename) = filename { if let Some(filename) = filename {
if !resolver::python_file_at_path(filename, pyproject_strategy, file_strategy, overrides)? { if !resolver::python_file_at_path(filename, pyproject_strategy, file_strategy, overrides)? {
@ -169,7 +168,7 @@ pub fn run_stdin(
}; };
let package_root = filename let package_root = filename
.and_then(Path::parent) .and_then(Path::parent)
.and_then(packages::detect_package_root); .and_then(packaging::detect_package_root);
let stdin = read_from_stdin()?; let stdin = read_from_stdin()?;
let mut diagnostics = lint_stdin(filename, package_root, &stdin, settings, autofix)?; let mut diagnostics = lint_stdin(filename, package_root, &stdin, settings, autofix)?;
diagnostics.messages.sort_unstable(); diagnostics.messages.sort_unstable();

View file

@ -6,7 +6,7 @@ use rustpython_ast::Location;
use rustpython_parser::lexer::{LexResult, Tok}; use rustpython_parser::lexer::{LexResult, Tok};
use crate::registry::LintSource; use crate::registry::LintSource;
use crate::Settings; use crate::settings::Settings;
bitflags! { bitflags! {
pub struct Flags: u32 { pub struct Flags: u32 {

View file

@ -1,5 +1,5 @@
pub mod detection; pub(crate) mod detection;
pub mod rules; pub(crate) mod rules;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -1,11 +1,12 @@
use rustpython_ast::Location; use rustpython_ast::Location;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::eradicate::detection::comment_contains_code; use crate::eradicate::detection::comment_contains_code;
use crate::registry::RuleCode; use crate::fix::Fix;
use crate::settings::flags; use crate::registry::{Diagnostic, RuleCode};
use crate::{violations, Diagnostic, Settings, SourceCodeLocator}; use crate::settings::{flags, Settings};
use crate::source_code::Locator;
use crate::violations;
fn is_standalone_comment(line: &str) -> bool { fn is_standalone_comment(line: &str) -> bool {
for char in line.chars() { for char in line.chars() {
@ -20,7 +21,7 @@ fn is_standalone_comment(line: &str) -> bool {
/// ERA001 /// ERA001
pub fn commented_out_code( pub fn commented_out_code(
locator: &SourceCodeLocator, locator: &Locator,
start: Location, start: Location,
end: Location, end: Location,
settings: &Settings, settings: &Settings,

53
src/fix.rs Normal file
View file

@ -0,0 +1,53 @@
use rustpython_ast::Location;
use serde::{Deserialize, Serialize};
#[derive(Debug, Copy, Clone, Hash)]
pub enum FixMode {
Generate,
Apply,
Diff,
None,
}
impl From<bool> for FixMode {
fn from(value: bool) -> Self {
if value {
FixMode::Apply
} else {
FixMode::None
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Fix {
pub content: String,
pub location: Location,
pub end_location: Location,
}
impl Fix {
pub fn deletion(start: Location, end: Location) -> Self {
Self {
content: String::new(),
location: start,
end_location: end,
}
}
pub fn replacement(content: String, start: Location, end: Location) -> Self {
Self {
content,
location: start,
end_location: end,
}
}
pub fn insertion(content: String, at: Location) -> Self {
Self {
content,
location: at,
end_location: at,
}
}
}

View file

@ -1,4 +1,4 @@
pub mod rules; pub(crate) mod rules;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -4,11 +4,11 @@ use rustpython_parser::lexer;
use rustpython_parser::lexer::Tok; use rustpython_parser::lexer::Tok;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix; use crate::fix::Fix;
use crate::source_code_locator::SourceCodeLocator; use crate::source_code::Locator;
/// ANN204 /// ANN204
pub fn add_return_none_annotation(locator: &SourceCodeLocator, stmt: &Stmt) -> Result<Fix> { pub fn add_return_none_annotation(locator: &Locator, stmt: &Stmt) -> Result<Fix> {
let range = Range::from_located(stmt); let range = Range::from_located(stmt);
let contents = locator.slice_source_code_range(&range); let contents = locator.slice_source_code_range(&range);

View file

@ -1,6 +1,6 @@
mod fixes; mod fixes;
pub mod helpers; pub(crate) mod helpers;
pub mod rules; pub(crate) mod rules;
pub mod settings; pub mod settings;
#[cfg(test)] #[cfg(test)]
@ -9,9 +9,10 @@ mod tests {
use anyhow::Result; use anyhow::Result;
use crate::flake8_annotations;
use crate::linter::test_path; use crate::linter::test_path;
use crate::registry::RuleCode; use crate::registry::RuleCode;
use crate::{flake8_annotations, Settings}; use crate::settings::Settings;
#[test] #[test]
fn defaults() -> Result<()> { fn defaults() -> Result<()> {

View file

@ -8,9 +8,9 @@ use crate::checkers::ast::Checker;
use crate::docstrings::definition::{Definition, DefinitionKind}; use crate::docstrings::definition::{Definition, DefinitionKind};
use crate::flake8_annotations::fixes; use crate::flake8_annotations::fixes;
use crate::flake8_annotations::helpers::match_function_def; use crate::flake8_annotations::helpers::match_function_def;
use crate::registry::RuleCode; use crate::registry::{Diagnostic, RuleCode};
use crate::visibility::Visibility; use crate::visibility::Visibility;
use crate::{violations, visibility, Diagnostic}; use crate::{violations, visibility};
#[derive(Default)] #[derive(Default)]
struct ReturnStatementVisitor<'a> { struct ReturnStatementVisitor<'a> {

View file

@ -1,5 +1,5 @@
mod helpers; mod helpers;
pub mod rules; pub(crate) mod rules;
pub mod settings; pub mod settings;
#[cfg(test)] #[cfg(test)]
@ -9,9 +9,10 @@ mod tests {
use anyhow::Result; use anyhow::Result;
use test_case::test_case; use test_case::test_case;
use crate::flake8_bandit;
use crate::linter::test_path; use crate::linter::test_path;
use crate::registry::RuleCode; use crate::registry::RuleCode;
use crate::{flake8_bandit, Settings}; use crate::settings::Settings;
#[test_case(RuleCode::S101, Path::new("S101.py"); "S101")] #[test_case(RuleCode::S101, Path::new("S101.py"); "S101")]
#[test_case(RuleCode::S102, Path::new("S102.py"); "S102")] #[test_case(RuleCode::S102, Path::new("S102.py"); "S102")]

View file

@ -1,4 +1,4 @@
pub mod rules; pub(crate) mod rules;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -1,4 +1,4 @@
pub mod rules; pub(crate) mod rules;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -1,4 +1,4 @@
pub mod rules; pub(crate) mod rules;
pub mod settings; pub mod settings;
#[cfg(test)] #[cfg(test)]
@ -8,9 +8,10 @@ mod tests {
use anyhow::Result; use anyhow::Result;
use test_case::test_case; use test_case::test_case;
use crate::flake8_bugbear;
use crate::linter::test_path; use crate::linter::test_path;
use crate::registry::RuleCode; use crate::registry::RuleCode;
use crate::{flake8_bugbear, Settings}; use crate::settings::Settings;
#[test_case(RuleCode::B002, Path::new("B002.py"); "B002")] #[test_case(RuleCode::B002, Path::new("B002.py"); "B002")]
#[test_case(RuleCode::B003, Path::new("B003.py"); "B003")] #[test_case(RuleCode::B003, Path::new("B003.py"); "B003")]

View file

@ -1,10 +1,10 @@
use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Location, Stmt, StmtKind}; use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Location, Stmt, StmtKind};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::source_code_generator::SourceCodeGenerator; use crate::source_code::Generator;
use crate::violations; use crate::violations;
fn assertion_error(msg: Option<&Expr>) -> Stmt { fn assertion_error(msg: Option<&Expr>) -> Stmt {
@ -48,7 +48,7 @@ pub fn assert_false(checker: &mut Checker, stmt: &Stmt, test: &Expr, msg: Option
let mut diagnostic = Diagnostic::new(violations::DoNotAssertFalse, Range::from_located(test)); let mut diagnostic = Diagnostic::new(violations::DoNotAssertFalse, Range::from_located(test));
if checker.patch(diagnostic.kind.code()) { if checker.patch(diagnostic.kind.code()) {
let mut generator: SourceCodeGenerator = checker.style.into(); let mut generator: Generator = checker.style.into();
generator.unparse_stmt(&assertion_error(msg)); generator.unparse_stmt(&assertion_error(msg));
diagnostic.amend(Fix::replacement( diagnostic.amend(Fix::replacement(
generator.generate(), generator.generate(),

View file

@ -4,10 +4,10 @@ use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKi
use crate::ast::helpers; use crate::ast::helpers;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::{Diagnostic, RuleCode}; use crate::registry::{Diagnostic, RuleCode};
use crate::source_code_generator::SourceCodeGenerator; use crate::source_code::Generator;
use crate::violations; use crate::violations;
fn type_pattern(elts: Vec<&Expr>) -> Expr { fn type_pattern(elts: Vec<&Expr>) -> Expr {
@ -55,7 +55,7 @@ fn duplicate_handler_exceptions<'a>(
Range::from_located(expr), Range::from_located(expr),
); );
if checker.patch(diagnostic.kind.code()) { if checker.patch(diagnostic.kind.code()) {
let mut generator: SourceCodeGenerator = checker.style.into(); let mut generator: Generator = checker.style.into();
if unique_elts.len() == 1 { if unique_elts.len() == 1 {
generator.unparse_expr(unique_elts[0], 0); generator.unparse_expr(unique_elts[0], 0);
} else { } else {

View file

@ -1,12 +1,12 @@
use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Location}; use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Location};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::python::identifiers::IDENTIFIER_REGEX; use crate::python::identifiers::IDENTIFIER_REGEX;
use crate::python::keyword::KWLIST; use crate::python::keyword::KWLIST;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::source_code_generator::SourceCodeGenerator; use crate::source_code::Generator;
use crate::violations; use crate::violations;
fn attribute(value: &Expr, attr: &str) -> Expr { fn attribute(value: &Expr, attr: &str) -> Expr {
@ -48,7 +48,7 @@ pub fn getattr_with_constant(checker: &mut Checker, expr: &Expr, func: &Expr, ar
let mut diagnostic = let mut diagnostic =
Diagnostic::new(violations::GetAttrWithConstant, Range::from_located(expr)); Diagnostic::new(violations::GetAttrWithConstant, Range::from_located(expr));
if checker.patch(diagnostic.kind.code()) { if checker.patch(diagnostic.kind.code()) {
let mut generator: SourceCodeGenerator = checker.style.into(); let mut generator: Generator = checker.style.into();
generator.unparse_expr(&attribute(obj, value), 0); generator.unparse_expr(&attribute(obj, value), 0);
diagnostic.amend(Fix::replacement( diagnostic.amend(Fix::replacement(
generator.generate(), generator.generate(),

View file

@ -1,10 +1,10 @@
use rustpython_ast::{Excepthandler, ExcepthandlerKind, ExprKind}; use rustpython_ast::{Excepthandler, ExcepthandlerKind, ExprKind};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::source_code_generator::SourceCodeGenerator; use crate::source_code::Generator;
use crate::violations; use crate::violations;
/// B013 /// B013
@ -24,7 +24,7 @@ pub fn redundant_tuple_in_exception_handler(checker: &mut Checker, handlers: &[E
Range::from_located(type_), Range::from_located(type_),
); );
if checker.patch(diagnostic.kind.code()) { if checker.patch(diagnostic.kind.code()) {
let mut generator: SourceCodeGenerator = checker.style.into(); let mut generator: Generator = checker.style.into();
generator.unparse_expr(elt, 0); generator.unparse_expr(elt, 0);
diagnostic.amend(Fix::replacement( diagnostic.amend(Fix::replacement(
generator.generate(), generator.generate(),

View file

@ -1,16 +1,15 @@
use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Location, Stmt, StmtKind}; use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Location, Stmt, StmtKind};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::python::identifiers::IDENTIFIER_REGEX; use crate::python::identifiers::IDENTIFIER_REGEX;
use crate::python::keyword::KWLIST; use crate::python::keyword::KWLIST;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::source_code_generator::SourceCodeGenerator; use crate::source_code::{Generator, Stylist};
use crate::source_code_style::SourceCodeStyleDetector;
use crate::violations; use crate::violations;
fn assignment(obj: &Expr, name: &str, value: &Expr, stylist: &SourceCodeStyleDetector) -> String { fn assignment(obj: &Expr, name: &str, value: &Expr, stylist: &Stylist) -> String {
let stmt = Stmt::new( let stmt = Stmt::new(
Location::default(), Location::default(),
Location::default(), Location::default(),
@ -28,7 +27,7 @@ fn assignment(obj: &Expr, name: &str, value: &Expr, stylist: &SourceCodeStyleDet
type_comment: None, type_comment: None,
}, },
); );
let mut generator: SourceCodeGenerator = stylist.into(); let mut generator: Generator = stylist.into();
generator.unparse_stmt(&stmt); generator.unparse_stmt(&stmt);
generator.generate() generator.generate()
} }

View file

@ -4,8 +4,8 @@ use rustpython_ast::{Expr, ExprKind, Stmt};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::ast::visitor; use crate::ast::visitor;
use crate::ast::visitor::Visitor; use crate::ast::visitor::Visitor;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::violations; use crate::violations;

View file

@ -1,5 +1,5 @@
pub mod rules; pub(crate) mod rules;
pub mod types; pub(crate) mod types;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -7,9 +7,9 @@ use libcst_native::{
}; };
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::cst::matchers::{match_expr, match_module}; use crate::cst::matchers::{match_expr, match_module};
use crate::source_code_locator::SourceCodeLocator; use crate::fix::Fix;
use crate::source_code::Locator;
fn match_call<'a, 'b>(expr: &'a mut Expr<'b>) -> Result<&'a mut Call<'b>> { fn match_call<'a, 'b>(expr: &'a mut Expr<'b>) -> Result<&'a mut Call<'b>> {
if let Expression::Call(call) = &mut expr.value { if let Expression::Call(call) = &mut expr.value {
@ -29,7 +29,7 @@ fn match_arg<'a, 'b>(call: &'a Call<'b>) -> Result<&'a Arg<'b>> {
/// (C400) Convert `list(x for x in y)` to `[x for x in y]`. /// (C400) Convert `list(x for x in y)` to `[x for x in y]`.
pub fn fix_unnecessary_generator_list( pub fn fix_unnecessary_generator_list(
locator: &SourceCodeLocator, locator: &Locator,
expr: &rustpython_ast::Expr, expr: &rustpython_ast::Expr,
) -> Result<Fix> { ) -> Result<Fix> {
// Expr(Call(GeneratorExp)))) -> Expr(ListComp))) // Expr(Call(GeneratorExp)))) -> Expr(ListComp)))
@ -70,7 +70,7 @@ pub fn fix_unnecessary_generator_list(
/// (C401) Convert `set(x for x in y)` to `{x for x in y}`. /// (C401) Convert `set(x for x in y)` to `{x for x in y}`.
pub fn fix_unnecessary_generator_set( pub fn fix_unnecessary_generator_set(
locator: &SourceCodeLocator, locator: &Locator,
expr: &rustpython_ast::Expr, expr: &rustpython_ast::Expr,
) -> Result<Fix> { ) -> Result<Fix> {
// Expr(Call(GeneratorExp)))) -> Expr(SetComp))) // Expr(Call(GeneratorExp)))) -> Expr(SetComp)))
@ -112,7 +112,7 @@ pub fn fix_unnecessary_generator_set(
/// (C402) Convert `dict((x, x) for x in range(3))` to `{x: x for x in /// (C402) Convert `dict((x, x) for x in range(3))` to `{x: x for x in
/// range(3)}`. /// range(3)}`.
pub fn fix_unnecessary_generator_dict( pub fn fix_unnecessary_generator_dict(
locator: &SourceCodeLocator, locator: &Locator,
expr: &rustpython_ast::Expr, expr: &rustpython_ast::Expr,
) -> Result<Fix> { ) -> Result<Fix> {
let module_text = locator.slice_source_code_range(&Range::from_located(expr)); let module_text = locator.slice_source_code_range(&Range::from_located(expr));
@ -169,7 +169,7 @@ pub fn fix_unnecessary_generator_dict(
/// (C403) Convert `set([x for x in y])` to `{x for x in y}`. /// (C403) Convert `set([x for x in y])` to `{x for x in y}`.
pub fn fix_unnecessary_list_comprehension_set( pub fn fix_unnecessary_list_comprehension_set(
locator: &SourceCodeLocator, locator: &Locator,
expr: &rustpython_ast::Expr, expr: &rustpython_ast::Expr,
) -> Result<Fix> { ) -> Result<Fix> {
// Expr(Call(ListComp)))) -> // Expr(Call(ListComp)))) ->
@ -210,7 +210,7 @@ pub fn fix_unnecessary_list_comprehension_set(
/// (C404) Convert `dict([(i, i) for i in range(3)])` to `{i: i for i in /// (C404) Convert `dict([(i, i) for i in range(3)])` to `{i: i for i in
/// range(3)}`. /// range(3)}`.
pub fn fix_unnecessary_list_comprehension_dict( pub fn fix_unnecessary_list_comprehension_dict(
locator: &SourceCodeLocator, locator: &Locator,
expr: &rustpython_ast::Expr, expr: &rustpython_ast::Expr,
) -> Result<Fix> { ) -> Result<Fix> {
let module_text = locator.slice_source_code_range(&Range::from_located(expr)); let module_text = locator.slice_source_code_range(&Range::from_located(expr));
@ -259,10 +259,7 @@ pub fn fix_unnecessary_list_comprehension_dict(
} }
/// (C405) Convert `set((1, 2))` to `{1, 2}`. /// (C405) Convert `set((1, 2))` to `{1, 2}`.
pub fn fix_unnecessary_literal_set( pub fn fix_unnecessary_literal_set(locator: &Locator, expr: &rustpython_ast::Expr) -> Result<Fix> {
locator: &SourceCodeLocator,
expr: &rustpython_ast::Expr,
) -> Result<Fix> {
// Expr(Call(List|Tuple)))) -> Expr(Set))) // Expr(Call(List|Tuple)))) -> Expr(Set)))
let module_text = locator.slice_source_code_range(&Range::from_located(expr)); let module_text = locator.slice_source_code_range(&Range::from_located(expr));
let mut tree = match_module(&module_text)?; let mut tree = match_module(&module_text)?;
@ -305,10 +302,7 @@ pub fn fix_unnecessary_literal_set(
} }
/// (C406) Convert `dict([(1, 2)])` to `{1: 2}`. /// (C406) Convert `dict([(1, 2)])` to `{1: 2}`.
pub fn fix_unnecessary_literal_dict( pub fn fix_unnecessary_literal_dict(locator: &Locator, expr: &rustpython_ast::Expr) -> Result<Fix> {
locator: &SourceCodeLocator,
expr: &rustpython_ast::Expr,
) -> Result<Fix> {
// Expr(Call(List|Tuple)))) -> Expr(Dict))) // Expr(Call(List|Tuple)))) -> Expr(Dict)))
let module_text = locator.slice_source_code_range(&Range::from_located(expr)); let module_text = locator.slice_source_code_range(&Range::from_located(expr));
let mut tree = match_module(&module_text)?; let mut tree = match_module(&module_text)?;
@ -374,7 +368,7 @@ pub fn fix_unnecessary_literal_dict(
/// (C408) /// (C408)
pub fn fix_unnecessary_collection_call( pub fn fix_unnecessary_collection_call(
locator: &SourceCodeLocator, locator: &Locator,
expr: &rustpython_ast::Expr, expr: &rustpython_ast::Expr,
) -> Result<Fix> { ) -> Result<Fix> {
// Expr(Call("list" | "tuple" | "dict")))) -> Expr(List|Tuple|Dict) // Expr(Call("list" | "tuple" | "dict")))) -> Expr(List|Tuple|Dict)
@ -483,7 +477,7 @@ pub fn fix_unnecessary_collection_call(
/// (C409) Convert `tuple([1, 2])` to `tuple(1, 2)` /// (C409) Convert `tuple([1, 2])` to `tuple(1, 2)`
pub fn fix_unnecessary_literal_within_tuple_call( pub fn fix_unnecessary_literal_within_tuple_call(
locator: &SourceCodeLocator, locator: &Locator,
expr: &rustpython_ast::Expr, expr: &rustpython_ast::Expr,
) -> Result<Fix> { ) -> Result<Fix> {
let module_text = locator.slice_source_code_range(&Range::from_located(expr)); let module_text = locator.slice_source_code_range(&Range::from_located(expr));
@ -537,7 +531,7 @@ pub fn fix_unnecessary_literal_within_tuple_call(
/// (C410) Convert `list([1, 2])` to `[1, 2]` /// (C410) Convert `list([1, 2])` to `[1, 2]`
pub fn fix_unnecessary_literal_within_list_call( pub fn fix_unnecessary_literal_within_list_call(
locator: &SourceCodeLocator, locator: &Locator,
expr: &rustpython_ast::Expr, expr: &rustpython_ast::Expr,
) -> Result<Fix> { ) -> Result<Fix> {
let module_text = locator.slice_source_code_range(&Range::from_located(expr)); let module_text = locator.slice_source_code_range(&Range::from_located(expr));
@ -592,10 +586,7 @@ pub fn fix_unnecessary_literal_within_list_call(
} }
/// (C411) Convert `list([i * i for i in x])` to `[i * i for i in x]`. /// (C411) Convert `list([i * i for i in x])` to `[i * i for i in x]`.
pub fn fix_unnecessary_list_call( pub fn fix_unnecessary_list_call(locator: &Locator, expr: &rustpython_ast::Expr) -> Result<Fix> {
locator: &SourceCodeLocator,
expr: &rustpython_ast::Expr,
) -> Result<Fix> {
// Expr(Call(List|Tuple)))) -> Expr(List|Tuple))) // Expr(Call(List|Tuple)))) -> Expr(List|Tuple)))
let module_text = locator.slice_source_code_range(&Range::from_located(expr)); let module_text = locator.slice_source_code_range(&Range::from_located(expr));
let mut tree = match_module(&module_text)?; let mut tree = match_module(&module_text)?;
@ -619,7 +610,7 @@ pub fn fix_unnecessary_list_call(
/// (C413) Convert `reversed(sorted([2, 3, 1]))` to `sorted([2, 3, 1], /// (C413) Convert `reversed(sorted([2, 3, 1]))` to `sorted([2, 3, 1],
/// reverse=True)`. /// reverse=True)`.
pub fn fix_unnecessary_call_around_sorted( pub fn fix_unnecessary_call_around_sorted(
locator: &SourceCodeLocator, locator: &Locator,
expr: &rustpython_ast::Expr, expr: &rustpython_ast::Expr,
) -> Result<Fix> { ) -> Result<Fix> {
let module_text = locator.slice_source_code_range(&Range::from_located(expr)); let module_text = locator.slice_source_code_range(&Range::from_located(expr));
@ -701,7 +692,7 @@ pub fn fix_unnecessary_call_around_sorted(
/// (C416) Convert `[i for i in x]` to `list(x)`. /// (C416) Convert `[i for i in x]` to `list(x)`.
pub fn fix_unnecessary_comprehension( pub fn fix_unnecessary_comprehension(
locator: &SourceCodeLocator, locator: &Locator,
expr: &rustpython_ast::Expr, expr: &rustpython_ast::Expr,
) -> Result<Fix> { ) -> Result<Fix> {
let module_text = locator.slice_source_code_range(&Range::from_located(expr)); let module_text = locator.slice_source_code_range(&Range::from_located(expr));

View file

@ -1,5 +1,5 @@
mod fixes; mod fixes;
pub mod rules; pub(crate) mod rules;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -1,4 +1,4 @@
pub mod rules; pub(crate) mod rules;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -1,5 +1,5 @@
pub mod rules; pub(crate) mod rules;
pub mod types; pub(crate) mod types;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -1,4 +1,4 @@
pub mod rules; pub(crate) mod rules;
pub mod settings; pub mod settings;
#[cfg(test)] #[cfg(test)]

View file

@ -1,4 +1,4 @@
pub mod rules; pub(crate) mod rules;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -4,11 +4,11 @@ use rustpython_parser::lexer::{LexResult, Tok};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::source_code_locator::SourceCodeLocator; use crate::source_code::Locator;
use crate::violations; use crate::violations;
/// ISC001, ISC002 /// ISC001, ISC002
pub fn implicit(tokens: &[LexResult], locator: &SourceCodeLocator) -> Vec<Diagnostic> { pub fn implicit(tokens: &[LexResult], locator: &Locator) -> Vec<Diagnostic> {
let mut diagnostics = vec![]; let mut diagnostics = vec![];
for ((a_start, a_tok, a_end), (b_start, b_tok, b_end)) in for ((a_start, a_tok, a_end), (b_start, b_tok, b_end)) in
tokens.iter().flatten().tuple_windows() tokens.iter().flatten().tuple_windows()

View file

@ -1,17 +1,17 @@
pub mod rules; pub(crate) mod rules;
pub mod settings; pub mod settings;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::path::Path; use std::path::Path;
use anyhow::Result; use anyhow::Result;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use crate::flake8_import_conventions;
use crate::linter::test_path; use crate::linter::test_path;
use crate::registry::RuleCode; use crate::registry::RuleCode;
use crate::{flake8_import_conventions, Settings}; use crate::settings::Settings;
#[test] #[test]
fn defaults() -> Result<()> { fn defaults() -> Result<()> {

View file

@ -1,4 +1,4 @@
pub mod rules; pub(crate) mod rules;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -4,8 +4,8 @@ use rustpython_ast::{Constant, Expr, ExprKind, Stmt, StmtKind};
use crate::ast::types::{Range, RefEquality}; use crate::ast::types::{Range, RefEquality};
use crate::autofix::helpers::delete_stmt; use crate::autofix::helpers::delete_stmt;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::{Diagnostic, RuleCode}; use crate::registry::{Diagnostic, RuleCode};
use crate::violations; use crate::violations;

View file

@ -1,4 +1,4 @@
pub mod rules; pub(crate) mod rules;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -1,4 +1,4 @@
pub mod rules; pub(crate) mod rules;
pub mod settings; pub mod settings;
pub mod types; pub mod types;

View file

@ -8,8 +8,8 @@ use crate::ast::helpers::unparse_stmt;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::ast::visitor; use crate::ast::visitor;
use crate::ast::visitor::Visitor; use crate::ast::visitor::Visitor;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::violations; use crate::violations;

View file

@ -8,8 +8,8 @@ use crate::ast::helpers::{collect_arg_names, collect_call_paths};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::ast::visitor; use crate::ast::visitor;
use crate::ast::visitor::Visitor; use crate::ast::visitor::Visitor;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::{Diagnostic, RuleCode}; use crate::registry::{Diagnostic, RuleCode};
use crate::violations; use crate::violations;

View file

@ -2,8 +2,8 @@ use rustpython_ast::{Expr, ExprKind, Location};
use super::helpers::{get_mark_decorators, get_mark_name}; use super::helpers::{get_mark_decorators, get_mark_name};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::{Diagnostic, RuleCode}; use crate::registry::{Diagnostic, RuleCode};
use crate::violations; use crate::violations;

View file

@ -3,11 +3,11 @@ use rustpython_ast::{Constant, Expr, ExprContext, ExprKind};
use super::helpers::is_pytest_parametrize; use super::helpers::is_pytest_parametrize;
use crate::ast::helpers::create_expr; use crate::ast::helpers::create_expr;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::flake8_pytest_style::types; use crate::flake8_pytest_style::types;
use crate::registry::{Diagnostic, RuleCode}; use crate::registry::{Diagnostic, RuleCode};
use crate::source_code_generator::SourceCodeGenerator; use crate::source_code::Generator;
use crate::violations; use crate::violations;
fn get_parametrize_decorator<'a>(checker: &Checker, decorators: &'a [Expr]) -> Option<&'a Expr> { fn get_parametrize_decorator<'a>(checker: &Checker, decorators: &'a [Expr]) -> Option<&'a Expr> {
@ -31,7 +31,7 @@ fn elts_to_csv(elts: &[Expr], checker: &Checker) -> Option<String> {
return None; return None;
} }
let mut generator: SourceCodeGenerator = checker.style.into(); let mut generator: Generator = checker.style.into();
generator.unparse_expr( generator.unparse_expr(
&create_expr(ExprKind::Constant { &create_expr(ExprKind::Constant {
value: Constant::Str(elts.iter().fold(String::new(), |mut acc, elt| { value: Constant::Str(elts.iter().fold(String::new(), |mut acc, elt| {
@ -85,7 +85,7 @@ fn check_names(checker: &mut Checker, expr: &Expr) {
Range::from_located(expr), Range::from_located(expr),
); );
if checker.patch(diagnostic.kind.code()) { if checker.patch(diagnostic.kind.code()) {
let mut generator: SourceCodeGenerator = checker.style.into(); let mut generator: Generator = checker.style.into();
generator.unparse_expr( generator.unparse_expr(
&create_expr(ExprKind::Tuple { &create_expr(ExprKind::Tuple {
elts: names elts: names
@ -115,7 +115,7 @@ fn check_names(checker: &mut Checker, expr: &Expr) {
Range::from_located(expr), Range::from_located(expr),
); );
if checker.patch(diagnostic.kind.code()) { if checker.patch(diagnostic.kind.code()) {
let mut generator: SourceCodeGenerator = checker.style.into(); let mut generator: Generator = checker.style.into();
generator.unparse_expr( generator.unparse_expr(
&create_expr(ExprKind::List { &create_expr(ExprKind::List {
elts: names elts: names
@ -157,7 +157,7 @@ fn check_names(checker: &mut Checker, expr: &Expr) {
Range::from_located(expr), Range::from_located(expr),
); );
if checker.patch(diagnostic.kind.code()) { if checker.patch(diagnostic.kind.code()) {
let mut generator: SourceCodeGenerator = checker.style.into(); let mut generator: Generator = checker.style.into();
generator.unparse_expr( generator.unparse_expr(
&create_expr(ExprKind::List { &create_expr(ExprKind::List {
elts: elts.clone(), elts: elts.clone(),
@ -206,7 +206,7 @@ fn check_names(checker: &mut Checker, expr: &Expr) {
Range::from_located(expr), Range::from_located(expr),
); );
if checker.patch(diagnostic.kind.code()) { if checker.patch(diagnostic.kind.code()) {
let mut generator: SourceCodeGenerator = checker.style.into(); let mut generator: Generator = checker.style.into();
generator.unparse_expr( generator.unparse_expr(
&create_expr(ExprKind::Tuple { &create_expr(ExprKind::Tuple {
elts: elts.clone(), elts: elts.clone(),
@ -284,7 +284,7 @@ fn handle_single_name(checker: &mut Checker, expr: &Expr, value: &Expr) {
); );
if checker.patch(diagnostic.kind.code()) { if checker.patch(diagnostic.kind.code()) {
let mut generator: SourceCodeGenerator = checker.style.into(); let mut generator: Generator = checker.style.into();
generator.unparse_expr(&create_expr(value.node.clone()), 0); generator.unparse_expr(&create_expr(value.node.clone()), 0);
diagnostic.amend(Fix::replacement( diagnostic.amend(Fix::replacement(
generator.generate(), generator.generate(),

View file

@ -1,4 +1,4 @@
pub mod rules; pub(crate) mod rules;
pub mod settings; pub mod settings;
#[cfg(test)] #[cfg(test)]
@ -8,10 +8,11 @@ mod tests {
use anyhow::Result; use anyhow::Result;
use test_case::test_case; use test_case::test_case;
use crate::flake8_quotes;
use crate::flake8_quotes::settings::Quote; use crate::flake8_quotes::settings::Quote;
use crate::linter::test_path; use crate::linter::test_path;
use crate::registry::RuleCode; use crate::registry::RuleCode;
use crate::{flake8_quotes, Settings}; use crate::settings::Settings;
#[test_case(Path::new("doubles.py"))] #[test_case(Path::new("doubles.py"))]
#[test_case(Path::new("doubles_escaped.py"))] #[test_case(Path::new("doubles_escaped.py"))]

View file

@ -3,7 +3,7 @@ use rustpython_ast::Location;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::flake8_quotes::settings::{Quote, Settings}; use crate::flake8_quotes::settings::{Quote, Settings};
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::source_code_locator::SourceCodeLocator; use crate::source_code::Locator;
use crate::violations; use crate::violations;
fn good_single(quote: &Quote) -> char { fn good_single(quote: &Quote) -> char {
@ -42,7 +42,7 @@ fn good_docstring(quote: &Quote) -> &str {
} }
pub fn quotes( pub fn quotes(
locator: &SourceCodeLocator, locator: &Locator,
start: Location, start: Location,
end: Location, end: Location,
is_docstring: bool, is_docstring: bool,

View file

@ -1,5 +1,5 @@
mod helpers; mod helpers;
pub mod rules; pub(crate) mod rules;
mod visitor; mod visitor;
#[cfg(test)] #[cfg(test)]
@ -11,7 +11,7 @@ mod tests {
use crate::linter::test_path; use crate::linter::test_path;
use crate::registry::RuleCode; use crate::registry::RuleCode;
use crate::Settings; use crate::settings::Settings;
#[test_case(RuleCode::RET501, Path::new("RET501.py"); "RET501")] #[test_case(RuleCode::RET501, Path::new("RET501.py"); "RET501")]
#[test_case(RuleCode::RET502, Path::new("RET502.py"); "RET502")] #[test_case(RuleCode::RET502, Path::new("RET502.py"); "RET502")]

View file

@ -4,13 +4,13 @@ use rustpython_ast::{Constant, Expr, ExprKind, Location, Stmt, StmtKind};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::ast::visitor::Visitor; use crate::ast::visitor::Visitor;
use crate::ast::whitespace::indentation; use crate::ast::whitespace::indentation;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::flake8_return::helpers::result_exists; use crate::flake8_return::helpers::result_exists;
use crate::flake8_return::visitor::{ReturnVisitor, Stack}; use crate::flake8_return::visitor::{ReturnVisitor, Stack};
use crate::registry::RuleCode; use crate::registry::{Diagnostic, RuleCode};
use crate::violations;
use crate::violations::Branch; use crate::violations::Branch;
use crate::{violations, Diagnostic};
/// RET501 /// RET501
fn unnecessary_return_none(checker: &mut Checker, stack: &Stack) { fn unnecessary_return_none(checker: &mut Checker, stack: &Stack) {

View file

@ -1,4 +1,4 @@
pub mod rules; pub(crate) mod rules;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -7,8 +7,8 @@ use rustpython_ast::{Boolop, Cmpop, Constant, Expr, ExprContext, ExprKind, Unary
use crate::ast::helpers::{create_expr, unparse_expr}; use crate::ast::helpers::{create_expr, unparse_expr};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::{Diagnostic, RuleCode}; use crate::registry::{Diagnostic, RuleCode};
use crate::violations; use crate::violations;

View file

@ -2,8 +2,8 @@ use rustpython_ast::{Constant, Expr, ExprKind};
use crate::ast::helpers::{create_expr, match_module_member, unparse_expr}; use crate::ast::helpers::{create_expr, match_module_member, unparse_expr};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::{Diagnostic, RuleCode}; use crate::registry::{Diagnostic, RuleCode};
use crate::violations; use crate::violations;

View file

@ -4,11 +4,10 @@ use rustpython_ast::{
use crate::ast::helpers::{create_expr, create_stmt}; use crate::ast::helpers::{create_expr, create_stmt};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::{Diagnostic, RuleCode}; use crate::registry::{Diagnostic, RuleCode};
use crate::source_code_generator::SourceCodeGenerator; use crate::source_code::{Generator, Stylist};
use crate::source_code_style::SourceCodeStyleDetector;
use crate::violations; use crate::violations;
struct Loop<'a> { struct Loop<'a> {
@ -77,14 +76,8 @@ fn return_values<'a>(stmt: &'a Stmt, sibling: &'a Stmt) -> Option<Loop<'a>> {
} }
/// Generate a return statement for an `any` or `all` builtin comprehension. /// Generate a return statement for an `any` or `all` builtin comprehension.
fn return_stmt( fn return_stmt(id: &str, test: &Expr, target: &Expr, iter: &Expr, stylist: &Stylist) -> String {
id: &str, let mut generator: Generator = stylist.into();
test: &Expr,
target: &Expr,
iter: &Expr,
stylist: &SourceCodeStyleDetector,
) -> String {
let mut generator: SourceCodeGenerator = stylist.into();
generator.unparse_stmt(&create_stmt(StmtKind::Return { generator.unparse_stmt(&create_stmt(StmtKind::Return {
value: Some(Box::new(create_expr(ExprKind::Call { value: Some(Box::new(create_expr(ExprKind::Call {
func: Box::new(create_expr(ExprKind::Name { func: Box::new(create_expr(ExprKind::Name {

View file

@ -5,8 +5,8 @@ use crate::ast::helpers::{
contains_call_path, create_expr, create_stmt, has_comments, unparse_expr, unparse_stmt, contains_call_path, create_expr, create_stmt, has_comments, unparse_expr, unparse_stmt,
}; };
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::{Diagnostic, RuleCode}; use crate::registry::{Diagnostic, RuleCode};
use crate::violations; use crate::violations;

View file

@ -2,8 +2,8 @@ use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Unaryop};
use crate::ast::helpers::{create_expr, unparse_expr}; use crate::ast::helpers::{create_expr, unparse_expr};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::violations; use crate::violations;

View file

@ -2,8 +2,8 @@ use rustpython_ast::{Cmpop, Expr, ExprKind, Stmt, StmtKind, Unaryop};
use crate::ast::helpers::{create_expr, unparse_expr}; use crate::ast::helpers::{create_expr, unparse_expr};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::violations; use crate::violations;

View file

@ -1,8 +1,8 @@
use rustpython_ast::{Cmpop, Expr, ExprKind}; use rustpython_ast::{Cmpop, Expr, ExprKind};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::violations; use crate::violations;

View file

@ -1,8 +1,8 @@
use rustpython_ast::{Cmpop, Expr, ExprKind}; use rustpython_ast::{Cmpop, Expr, ExprKind};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::violations; use crate::violations;

View file

@ -1,4 +1,4 @@
pub mod rules; pub(crate) mod rules;
pub mod settings; pub mod settings;
#[cfg(test)] #[cfg(test)]
@ -8,10 +8,11 @@ mod tests {
use anyhow::Result; use anyhow::Result;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use crate::flake8_tidy_imports;
use crate::flake8_tidy_imports::settings::{BannedApi, Strictness}; use crate::flake8_tidy_imports::settings::{BannedApi, Strictness};
use crate::linter::test_path; use crate::linter::test_path;
use crate::registry::RuleCode; use crate::registry::RuleCode;
use crate::{flake8_tidy_imports, Settings}; use crate::settings::Settings;
#[test] #[test]
fn ban_parent_imports() -> Result<()> { fn ban_parent_imports() -> Result<()> {

View file

@ -1,5 +1,5 @@
mod helpers; mod helpers;
pub mod rules; pub(crate) mod rules;
pub mod settings; pub mod settings;
mod types; mod types;

View file

@ -10,7 +10,8 @@ use crate::ast::types::{Binding, BindingKind, FunctionDef, Lambda, Scope, ScopeK
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::flake8_unused_arguments::helpers; use crate::flake8_unused_arguments::helpers;
use crate::flake8_unused_arguments::types::Argumentable; use crate::flake8_unused_arguments::types::Argumentable;
use crate::{visibility, Diagnostic}; use crate::registry::Diagnostic;
use crate::visibility;
/// Check a plain function for unused arguments. /// Check a plain function for unused arguments.
fn function( fn function(

View file

@ -5,7 +5,7 @@ use rustpython_parser::lexer;
use rustpython_parser::lexer::Tok; use rustpython_parser::lexer::Tok;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::SourceCodeLocator; use crate::source_code::Locator;
#[derive(Debug)] #[derive(Debug)]
pub struct Comment<'a> { pub struct Comment<'a> {
@ -15,7 +15,7 @@ pub struct Comment<'a> {
} }
/// Collect all comments in an import block. /// Collect all comments in an import block.
pub fn collect_comments<'a>(range: &Range, locator: &'a SourceCodeLocator) -> Vec<Comment<'a>> { pub fn collect_comments<'a>(range: &Range, locator: &'a Locator) -> Vec<Comment<'a>> {
let contents = locator.slice_source_code_range(range); let contents = locator.slice_source_code_range(range);
lexer::make_tokenizer_located(&contents, range.location) lexer::make_tokenizer_located(&contents, range.location)
.flatten() .flatten()

View file

@ -1,5 +1,5 @@
use crate::isort::types::{AliasData, CommentSet, ImportFromData, Importable}; use crate::isort::types::{AliasData, CommentSet, ImportFromData, Importable};
use crate::source_code_style::SourceCodeStyleDetector; use crate::source_code::Stylist;
// Hard-code four-space indentation for the imports themselves, to match Black. // Hard-code four-space indentation for the imports themselves, to match Black.
const INDENT: &str = " "; const INDENT: &str = " ";
@ -12,7 +12,7 @@ pub fn format_import(
alias: &AliasData, alias: &AliasData,
comments: &CommentSet, comments: &CommentSet,
is_first: bool, is_first: bool,
stylist: &SourceCodeStyleDetector, stylist: &Stylist,
) -> String { ) -> String {
let mut output = String::with_capacity(CAPACITY); let mut output = String::with_capacity(CAPACITY);
if !is_first && !comments.atop.is_empty() { if !is_first && !comments.atop.is_empty() {
@ -46,7 +46,7 @@ pub fn format_import_from(
comments: &CommentSet, comments: &CommentSet,
aliases: &[(AliasData, CommentSet)], aliases: &[(AliasData, CommentSet)],
line_length: usize, line_length: usize,
stylist: &SourceCodeStyleDetector, stylist: &Stylist,
force_wrap_aliases: bool, force_wrap_aliases: bool,
is_first: bool, is_first: bool,
trailing_comma: bool, trailing_comma: bool,
@ -89,7 +89,7 @@ fn format_single_line(
comments: &CommentSet, comments: &CommentSet,
aliases: &[(AliasData, CommentSet)], aliases: &[(AliasData, CommentSet)],
is_first: bool, is_first: bool,
stylist: &SourceCodeStyleDetector, stylist: &Stylist,
) -> (String, usize) { ) -> (String, usize) {
let mut output = String::with_capacity(CAPACITY); let mut output = String::with_capacity(CAPACITY);
let mut line_length = 0; let mut line_length = 0;
@ -149,7 +149,7 @@ fn format_multi_line(
comments: &CommentSet, comments: &CommentSet,
aliases: &[(AliasData, CommentSet)], aliases: &[(AliasData, CommentSet)],
is_first: bool, is_first: bool,
stylist: &SourceCodeStyleDetector, stylist: &Stylist,
) -> String { ) -> String {
let mut output = String::with_capacity(CAPACITY); let mut output = String::with_capacity(CAPACITY);

View file

@ -5,11 +5,11 @@ use rustpython_parser::lexer::Tok;
use crate::ast::helpers::is_docstring_stmt; use crate::ast::helpers::is_docstring_stmt;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::isort::types::TrailingComma; use crate::isort::types::TrailingComma;
use crate::source_code_locator::SourceCodeLocator; use crate::source_code::Locator;
/// Return `true` if a `StmtKind::ImportFrom` statement ends with a magic /// Return `true` if a `StmtKind::ImportFrom` statement ends with a magic
/// trailing comma. /// trailing comma.
pub fn trailing_comma(stmt: &Stmt, locator: &SourceCodeLocator) -> TrailingComma { pub fn trailing_comma(stmt: &Stmt, locator: &Locator) -> TrailingComma {
let contents = locator.slice_source_code_range(&Range::from_located(stmt)); let contents = locator.slice_source_code_range(&Range::from_located(stmt));
let mut count: usize = 0; let mut count: usize = 0;
let mut trailing_comma = TrailingComma::Absent; let mut trailing_comma = TrailingComma::Absent;
@ -37,7 +37,7 @@ pub fn trailing_comma(stmt: &Stmt, locator: &SourceCodeLocator) -> TrailingComma
} }
/// Return `true` if a `Stmt` is preceded by a "comment break" /// Return `true` if a `Stmt` is preceded by a "comment break"
pub fn has_comment_break(stmt: &Stmt, locator: &SourceCodeLocator) -> bool { pub fn has_comment_break(stmt: &Stmt, locator: &Locator) -> bool {
// Starting from the `Stmt` (`def f(): pass`), we want to detect patterns like // Starting from the `Stmt` (`def f(): pass`), we want to detect patterns like
// this: // this:
// //
@ -108,7 +108,7 @@ fn match_docstring_end(body: &[Stmt]) -> Option<Location> {
/// Find the end of the first token that isn't a docstring, comment, or /// Find the end of the first token that isn't a docstring, comment, or
/// whitespace. /// whitespace.
pub fn find_splice_location(body: &[Stmt], locator: &SourceCodeLocator) -> Location { pub fn find_splice_location(body: &[Stmt], locator: &Locator) -> Location {
// Find the first AST node that isn't a docstring. // Find the first AST node that isn't a docstring.
let mut splice = match_docstring_end(body).unwrap_or_default(); let mut splice = match_docstring_end(body).unwrap_or_default();
@ -132,11 +132,11 @@ mod tests {
use rustpython_parser::parser; use rustpython_parser::parser;
use crate::isort::helpers::find_splice_location; use crate::isort::helpers::find_splice_location;
use crate::source_code_locator::SourceCodeLocator; use crate::source_code::Locator;
fn splice_contents(contents: &str) -> Result<Location> { fn splice_contents(contents: &str) -> Result<Location> {
let program = parser::parse_program(contents, "<filename>")?; let program = parser::parse_program(contents, "<filename>")?;
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
Ok(find_splice_location(&program, &locator)) Ok(find_splice_location(&program, &locator))
} }

View file

@ -18,18 +18,17 @@ use crate::isort::types::{
AliasData, CommentSet, EitherImport, ImportBlock, ImportFromData, Importable, AliasData, CommentSet, EitherImport, ImportBlock, ImportFromData, Importable,
OrderedImportBlock, TrailingComma, OrderedImportBlock, TrailingComma,
}; };
use crate::source_code_style::SourceCodeStyleDetector; use crate::source_code::{Locator, Stylist};
use crate::SourceCodeLocator;
mod categorize; mod categorize;
mod comments; mod comments;
pub mod format; mod format;
pub mod helpers; mod helpers;
pub mod rules; pub(crate) mod rules;
pub mod settings; pub mod settings;
mod sorting; mod sorting;
pub mod track; pub(crate) mod track;
pub mod types; mod types;
#[derive(Debug)] #[derive(Debug)]
pub struct AnnotatedAliasData<'a> { pub struct AnnotatedAliasData<'a> {
@ -59,7 +58,7 @@ pub enum AnnotatedImport<'a> {
fn annotate_imports<'a>( fn annotate_imports<'a>(
imports: &'a [&'a Stmt], imports: &'a [&'a Stmt],
comments: Vec<Comment<'a>>, comments: Vec<Comment<'a>>,
locator: &SourceCodeLocator, locator: &Locator,
split_on_trailing_comma: bool, split_on_trailing_comma: bool,
) -> Vec<AnnotatedImport<'a>> { ) -> Vec<AnnotatedImport<'a>> {
let mut annotated = vec![]; let mut annotated = vec![];
@ -536,9 +535,9 @@ fn force_single_line_imports<'a>(
pub fn format_imports( pub fn format_imports(
block: &Block, block: &Block,
comments: Vec<Comment>, comments: Vec<Comment>,
locator: &SourceCodeLocator, locator: &Locator,
line_length: usize, line_length: usize,
stylist: &SourceCodeStyleDetector, stylist: &Stylist,
src: &[PathBuf], src: &[PathBuf],
package: Option<&Path>, package: Option<&Path>,
known_first_party: &BTreeSet<String>, known_first_party: &BTreeSet<String>,
@ -647,9 +646,10 @@ mod tests {
use anyhow::Result; use anyhow::Result;
use test_case::test_case; use test_case::test_case;
use crate::isort;
use crate::linter::test_path; use crate::linter::test_path;
use crate::registry::RuleCode; use crate::registry::RuleCode;
use crate::{isort, Settings}; use crate::settings::Settings;
#[test_case(Path::new("add_newline_before_comments.py"))] #[test_case(Path::new("add_newline_before_comments.py"))]
#[test_case(Path::new("combine_as_imports.py"))] #[test_case(Path::new("combine_as_imports.py"))]

View file

@ -5,12 +5,12 @@ use rustpython_ast::{Location, StmtKind, Suite};
use crate::ast::helpers::is_docstring_stmt; use crate::ast::helpers::is_docstring_stmt;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix; use crate::fix::Fix;
use crate::isort::helpers; use crate::isort::helpers;
use crate::isort::track::Block; use crate::isort::track::Block;
use crate::registry::{Diagnostic, RuleCode}; use crate::registry::{Diagnostic, RuleCode};
use crate::settings::{flags, Settings}; use crate::settings::{flags, Settings};
use crate::source_code_locator::SourceCodeLocator; use crate::source_code::Locator;
use crate::violations; use crate::violations;
struct Alias<'a> { struct Alias<'a> {
@ -101,7 +101,7 @@ fn add_required_import(
required_import: &AnyImport, required_import: &AnyImport,
blocks: &[&Block], blocks: &[&Block],
python_ast: &Suite, python_ast: &Suite,
locator: &SourceCodeLocator, locator: &Locator,
settings: &Settings, settings: &Settings,
autofix: flags::Autofix, autofix: flags::Autofix,
) -> Option<Diagnostic> { ) -> Option<Diagnostic> {
@ -157,7 +157,7 @@ fn add_required_import(
pub fn add_required_imports( pub fn add_required_imports(
blocks: &[&Block], blocks: &[&Block],
python_ast: &Suite, python_ast: &Suite,
locator: &SourceCodeLocator, locator: &Locator,
settings: &Settings, settings: &Settings,
autofix: flags::Autofix, autofix: flags::Autofix,
) -> Vec<Diagnostic> { ) -> Vec<Diagnostic> {

View file

@ -8,12 +8,13 @@ use crate::ast::helpers::{
}; };
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::ast::whitespace::leading_space; use crate::ast::whitespace::leading_space;
use crate::autofix::Fix; use crate::fix::Fix;
use crate::isort::track::Block; use crate::isort::track::Block;
use crate::isort::{comments, format_imports}; use crate::isort::{comments, format_imports};
use crate::settings::flags; use crate::registry::Diagnostic;
use crate::source_code_style::SourceCodeStyleDetector; use crate::settings::{flags, Settings};
use crate::{violations, Diagnostic, Settings, SourceCodeLocator}; use crate::source_code::{Locator, Stylist};
use crate::violations;
fn extract_range(body: &[&Stmt]) -> Range { fn extract_range(body: &[&Stmt]) -> Range {
let location = body.first().unwrap().location; let location = body.first().unwrap().location;
@ -29,9 +30,9 @@ fn extract_indentation_range(body: &[&Stmt]) -> Range {
/// I001 /// I001
pub fn organize_imports( pub fn organize_imports(
block: &Block, block: &Block,
locator: &SourceCodeLocator, locator: &Locator,
settings: &Settings, settings: &Settings,
stylist: &SourceCodeStyleDetector, stylist: &Stylist,
autofix: flags::Autofix, autofix: flags::Autofix,
package: Option<&Path>, package: Option<&Path>,
) -> Option<Diagnostic> { ) -> Option<Diagnostic> {

View file

@ -9,7 +9,7 @@ use rustpython_ast::{
use crate::ast::visitor::Visitor; use crate::ast::visitor::Visitor;
use crate::directives::IsortDirectives; use crate::directives::IsortDirectives;
use crate::isort::helpers; use crate::isort::helpers;
use crate::source_code_locator::SourceCodeLocator; use crate::source_code::Locator;
#[derive(Debug)] #[derive(Debug)]
pub enum Trailer { pub enum Trailer {
@ -26,7 +26,7 @@ pub struct Block<'a> {
} }
pub struct ImportTracker<'a> { pub struct ImportTracker<'a> {
locator: &'a SourceCodeLocator<'a>, locator: &'a Locator<'a>,
directives: &'a IsortDirectives, directives: &'a IsortDirectives,
pyi: bool, pyi: bool,
blocks: Vec<Block<'a>>, blocks: Vec<Block<'a>>,
@ -35,11 +35,7 @@ pub struct ImportTracker<'a> {
} }
impl<'a> ImportTracker<'a> { impl<'a> ImportTracker<'a> {
pub fn new( pub fn new(locator: &'a Locator<'a>, directives: &'a IsortDirectives, path: &'a Path) -> Self {
locator: &'a SourceCodeLocator<'a>,
directives: &'a IsortDirectives,
path: &'a Path,
) -> Self {
Self { Self {
locator, locator,
directives, directives,

View file

@ -12,21 +12,17 @@
)] )]
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
use cfg_if::cfg_if;
use crate::registry::Diagnostic;
use crate::settings::Settings;
use crate::source_code_locator::SourceCodeLocator;
mod ast; mod ast;
pub mod autofix; mod autofix;
pub mod cache; mod cache;
mod checkers; mod checkers;
pub mod cli; pub mod cli;
mod cst; mod cst;
mod directives; mod directives;
mod doc_lines;
mod docstrings; mod docstrings;
mod eradicate; mod eradicate;
pub mod fix;
mod flake8_2020; mod flake8_2020;
pub mod flake8_annotations; pub mod flake8_annotations;
pub mod flake8_bandit; pub mod flake8_bandit;
@ -40,6 +36,7 @@ mod flake8_debugger;
pub mod flake8_errmsg; pub mod flake8_errmsg;
mod flake8_implicit_str_concat; mod flake8_implicit_str_concat;
mod flake8_import_conventions; mod flake8_import_conventions;
pub mod flake8_pie;
mod flake8_print; mod flake8_print;
pub mod flake8_pytest_style; pub mod flake8_pytest_style;
pub mod flake8_quotes; pub mod flake8_quotes;
@ -71,18 +68,19 @@ pub mod resolver;
mod ruff; mod ruff;
mod rustpython_helpers; mod rustpython_helpers;
pub mod settings; pub mod settings;
pub mod source_code_generator; pub mod source_code;
pub mod source_code_locator;
pub mod source_code_style;
mod vendor; mod vendor;
mod violation; mod violation;
mod violations; mod violations;
pub mod visibility; mod visibility;
use cfg_if::cfg_if;
cfg_if! { cfg_if! {
if #[cfg(not(target_family = "wasm"))] { if #[cfg(not(target_family = "wasm"))] {
pub mod commands; pub mod commands;
mod packages; mod packaging;
#[cfg(all(feature = "update-informer"))] #[cfg(all(feature = "update-informer"))]
pub mod updates; pub mod updates;
@ -93,5 +91,3 @@ cfg_if! {
pub use lib_wasm::check; pub use lib_wasm::check;
} }
} }
pub mod doc_lines;
pub mod flake8_pie;

View file

@ -10,9 +10,8 @@ use crate::resolver::Relativity;
use crate::rustpython_helpers::tokenize; use crate::rustpython_helpers::tokenize;
use crate::settings::configuration::Configuration; use crate::settings::configuration::Configuration;
use crate::settings::{flags, pyproject, Settings}; use crate::settings::{flags, pyproject, Settings};
use crate::source_code_locator::SourceCodeLocator; use crate::source_code::{Locator, Stylist};
use crate::source_code_style::SourceCodeStyleDetector; use crate::{directives, packaging, resolver};
use crate::{directives, packages, resolver};
/// Load the relevant `Settings` for a given `Path`. /// Load the relevant `Settings` for a given `Path`.
fn resolve(path: &Path) -> Result<Settings> { fn resolve(path: &Path) -> Result<Settings> {
@ -40,10 +39,10 @@ pub fn check(path: &Path, contents: &str, autofix: bool) -> Result<Vec<Diagnosti
let tokens: Vec<LexResult> = tokenize(contents); let tokens: Vec<LexResult> = tokenize(contents);
// Map row and column locations to byte slices (lazily). // Map row and column locations to byte slices (lazily).
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
// Detect the current code style (lazily). // Detect the current code style (lazily).
let stylist = SourceCodeStyleDetector::from_contents(contents, &locator); let stylist = Stylist::from_contents(contents, &locator);
// Extract the `# noqa` and `# isort: skip` directives from the source. // Extract the `# noqa` and `# isort: skip` directives from the source.
let directives = let directives =
@ -52,7 +51,7 @@ pub fn check(path: &Path, contents: &str, autofix: bool) -> Result<Vec<Diagnosti
// Generate diagnostics. // Generate diagnostics.
let diagnostics = check_path( let diagnostics = check_path(
path, path,
packages::detect_package_root(path), packaging::detect_package_root(path),
contents, contents,
tokens, tokens,
&locator, &locator,

View file

@ -12,8 +12,7 @@ use crate::settings::configuration::Configuration;
use crate::settings::options::Options; use crate::settings::options::Options;
use crate::settings::types::PythonVersion; use crate::settings::types::PythonVersion;
use crate::settings::{flags, Settings}; use crate::settings::{flags, Settings};
use crate::source_code_locator::SourceCodeLocator; use crate::source_code::{Locator, Stylist};
use crate::source_code_style::SourceCodeStyleDetector;
use crate::{ use crate::{
directives, flake8_annotations, flake8_bandit, flake8_bugbear, flake8_errmsg, directives, flake8_annotations, flake8_bandit, flake8_bugbear, flake8_errmsg,
flake8_import_conventions, flake8_pytest_style, flake8_quotes, flake8_tidy_imports, flake8_import_conventions, flake8_pytest_style, flake8_quotes, flake8_tidy_imports,
@ -151,10 +150,10 @@ pub fn check(contents: &str, options: JsValue) -> Result<JsValue, JsValue> {
let tokens: Vec<LexResult> = tokenize(contents); let tokens: Vec<LexResult> = tokenize(contents);
// Map row and column locations to byte slices (lazily). // Map row and column locations to byte slices (lazily).
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
// Detect the current code style (lazily). // Detect the current code style (lazily).
let stylist = SourceCodeStyleDetector::from_contents(contents, &locator); let stylist = Stylist::from_contents(contents, &locator);
// Extract the `# noqa` and `# isort: skip` directives from the source. // Extract the `# noqa` and `# isort: skip` directives from the source.
let directives = directives::extract_directives(&tokens, directives::Flags::empty()); let directives = directives::extract_directives(&tokens, directives::Flags::empty());

View file

@ -11,8 +11,7 @@ use rustpython_parser::lexer::LexResult;
use similar::TextDiff; use similar::TextDiff;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::fixer; use crate::autofix::fix_file;
use crate::autofix::fixer::fix_file;
use crate::checkers::ast::check_ast; use crate::checkers::ast::check_ast;
use crate::checkers::imports::check_imports; use crate::checkers::imports::check_imports;
use crate::checkers::lines::check_lines; use crate::checkers::lines::check_lines;
@ -24,9 +23,8 @@ use crate::message::{Message, Source};
use crate::noqa::add_noqa; use crate::noqa::add_noqa;
use crate::registry::{Diagnostic, LintSource, RuleCode}; use crate::registry::{Diagnostic, LintSource, RuleCode};
use crate::settings::{flags, Settings}; use crate::settings::{flags, Settings};
use crate::source_code_locator::SourceCodeLocator; use crate::source_code::{Locator, Stylist};
use crate::source_code_style::SourceCodeStyleDetector; use crate::{cache, directives, fix, fs, rustpython_helpers, violations};
use crate::{cache, directives, fs, rustpython_helpers, violations};
const CARGO_PKG_NAME: &str = env!("CARGO_PKG_NAME"); const CARGO_PKG_NAME: &str = env!("CARGO_PKG_NAME");
const CARGO_PKG_REPOSITORY: &str = env!("CARGO_PKG_REPOSITORY"); const CARGO_PKG_REPOSITORY: &str = env!("CARGO_PKG_REPOSITORY");
@ -58,8 +56,8 @@ pub(crate) fn check_path(
package: Option<&Path>, package: Option<&Path>,
contents: &str, contents: &str,
tokens: Vec<LexResult>, tokens: Vec<LexResult>,
locator: &SourceCodeLocator, locator: &Locator,
stylist: &SourceCodeStyleDetector, stylist: &Stylist,
directives: &Directives, directives: &Directives,
settings: &Settings, settings: &Settings,
autofix: flags::Autofix, autofix: flags::Autofix,
@ -200,7 +198,7 @@ pub fn lint_path(
package: Option<&Path>, package: Option<&Path>,
settings: &Settings, settings: &Settings,
cache: flags::Cache, cache: flags::Cache,
autofix: fixer::Mode, autofix: fix::FixMode,
) -> Result<Diagnostics> { ) -> Result<Diagnostics> {
// Validate the `Settings` and return any errors. // Validate the `Settings` and return any errors.
settings.validate()?; settings.validate()?;
@ -212,7 +210,7 @@ pub fn lint_path(
// write the fixes to disk, thus invalidating the cache. But it's a bit hard // write the fixes to disk, thus invalidating the cache. But it's a bit hard
// to reason about. We need to come up with a better solution here.) // to reason about. We need to come up with a better solution here.)
let metadata = if matches!(cache, flags::Cache::Enabled) let metadata = if matches!(cache, flags::Cache::Enabled)
&& matches!(autofix, fixer::Mode::None | fixer::Mode::Generate) && matches!(autofix, fix::FixMode::None | fix::FixMode::Generate)
{ {
let metadata = path.metadata()?; let metadata = path.metadata()?;
if let Some(messages) = cache::get(path, &metadata, settings, autofix.into()) { if let Some(messages) = cache::get(path, &metadata, settings, autofix.into()) {
@ -228,12 +226,12 @@ pub fn lint_path(
let contents = fs::read_file(path)?; let contents = fs::read_file(path)?;
// Lint the file. // Lint the file.
let (messages, fixed) = if matches!(autofix, fixer::Mode::Apply | fixer::Mode::Diff) { let (messages, fixed) = if matches!(autofix, fix::FixMode::Apply | fix::FixMode::Diff) {
let (transformed, fixed, messages) = lint_fix(&contents, path, package, settings)?; let (transformed, fixed, messages) = lint_fix(&contents, path, package, settings)?;
if fixed > 0 { if fixed > 0 {
if matches!(autofix, fixer::Mode::Apply) { if matches!(autofix, fix::FixMode::Apply) {
write(path, transformed)?; write(path, transformed)?;
} else if matches!(autofix, fixer::Mode::Diff) { } else if matches!(autofix, fix::FixMode::Diff) {
let mut stdout = io::stdout().lock(); let mut stdout = io::stdout().lock();
TextDiff::from_lines(&contents, &transformed) TextDiff::from_lines(&contents, &transformed)
.unified_diff() .unified_diff()
@ -270,10 +268,10 @@ pub fn add_noqa_to_path(path: &Path, settings: &Settings) -> Result<usize> {
let tokens: Vec<LexResult> = rustpython_helpers::tokenize(&contents); let tokens: Vec<LexResult> = rustpython_helpers::tokenize(&contents);
// Map row and column locations to byte slices (lazily). // Map row and column locations to byte slices (lazily).
let locator = SourceCodeLocator::new(&contents); let locator = Locator::new(&contents);
// Detect the current code style (lazily). // Detect the current code style (lazily).
let stylist = SourceCodeStyleDetector::from_contents(&contents, &locator); let stylist = Stylist::from_contents(&contents, &locator);
// Extract the `# noqa` and `# isort: skip` directives from the source. // Extract the `# noqa` and `# isort: skip` directives from the source.
let directives = let directives =
@ -310,13 +308,13 @@ pub fn lint_stdin(
package: Option<&Path>, package: Option<&Path>,
contents: &str, contents: &str,
settings: &Settings, settings: &Settings,
autofix: fixer::Mode, autofix: fix::FixMode,
) -> Result<Diagnostics> { ) -> Result<Diagnostics> {
// Validate the `Settings` and return any errors. // Validate the `Settings` and return any errors.
settings.validate()?; settings.validate()?;
// Lint the inputs. // Lint the inputs.
let (messages, fixed) = if matches!(autofix, fixer::Mode::Apply | fixer::Mode::Diff) { let (messages, fixed) = if matches!(autofix, fix::FixMode::Apply | fix::FixMode::Diff) {
let (transformed, fixed, messages) = lint_fix( let (transformed, fixed, messages) = lint_fix(
contents, contents,
path.unwrap_or_else(|| Path::new("-")), path.unwrap_or_else(|| Path::new("-")),
@ -324,10 +322,10 @@ pub fn lint_stdin(
settings, settings,
)?; )?;
if matches!(autofix, fixer::Mode::Apply) { if matches!(autofix, fix::FixMode::Apply) {
// Write the contents to stdout, regardless of whether any errors were fixed. // Write the contents to stdout, regardless of whether any errors were fixed.
io::stdout().write_all(transformed.as_bytes())?; io::stdout().write_all(transformed.as_bytes())?;
} else if matches!(autofix, fixer::Mode::Diff) { } else if matches!(autofix, fix::FixMode::Diff) {
// But only write a diff if it's non-empty. // But only write a diff if it's non-empty.
if fixed > 0 { if fixed > 0 {
let text_diff = TextDiff::from_lines(contents, &transformed); let text_diff = TextDiff::from_lines(contents, &transformed);
@ -372,10 +370,10 @@ fn lint_only(
let tokens: Vec<LexResult> = rustpython_helpers::tokenize(contents); let tokens: Vec<LexResult> = rustpython_helpers::tokenize(contents);
// Map row and column locations to byte slices (lazily). // Map row and column locations to byte slices (lazily).
let locator = SourceCodeLocator::new(contents); let locator = Locator::new(contents);
// Detect the current code style (lazily). // Detect the current code style (lazily).
let stylist = SourceCodeStyleDetector::from_contents(contents, &locator); let stylist = Stylist::from_contents(contents, &locator);
// Extract the `# noqa` and `# isort: skip` directives from the source. // Extract the `# noqa` and `# isort: skip` directives from the source.
let directives = let directives =
@ -432,10 +430,10 @@ fn lint_fix(
let tokens: Vec<LexResult> = rustpython_helpers::tokenize(&contents); let tokens: Vec<LexResult> = rustpython_helpers::tokenize(&contents);
// Map row and column locations to byte slices (lazily). // Map row and column locations to byte slices (lazily).
let locator = SourceCodeLocator::new(&contents); let locator = Locator::new(&contents);
// Detect the current code style (lazily). // Detect the current code style (lazily).
let stylist = SourceCodeStyleDetector::from_contents(&contents, &locator); let stylist = Stylist::from_contents(&contents, &locator);
// Extract the `# noqa` and `# isort: skip` directives from the source. // Extract the `# noqa` and `# isort: skip` directives from the source.
let directives = let directives =
@ -511,8 +509,8 @@ quoting the contents of `{}`, along with the `pyproject.toml` settings and execu
pub fn test_path(path: &Path, settings: &Settings) -> Result<Vec<Diagnostic>> { pub fn test_path(path: &Path, settings: &Settings) -> Result<Vec<Diagnostic>> {
let contents = fs::read_file(path)?; let contents = fs::read_file(path)?;
let tokens: Vec<LexResult> = rustpython_helpers::tokenize(&contents); let tokens: Vec<LexResult> = rustpython_helpers::tokenize(&contents);
let locator = SourceCodeLocator::new(&contents); let locator = Locator::new(&contents);
let stylist = SourceCodeStyleDetector::from_contents(&contents, &locator); let stylist = Stylist::from_contents(&contents, &locator);
let directives = let directives =
directives::extract_directives(&tokens, directives::Flags::from_settings(settings)); directives::extract_directives(&tokens, directives::Flags::from_settings(settings));
let mut diagnostics = check_path( let mut diagnostics = check_path(
@ -540,8 +538,8 @@ pub fn test_path(path: &Path, settings: &Settings) -> Result<Vec<Diagnostic>> {
loop { loop {
let tokens: Vec<LexResult> = rustpython_helpers::tokenize(&contents); let tokens: Vec<LexResult> = rustpython_helpers::tokenize(&contents);
let locator = SourceCodeLocator::new(&contents); let locator = Locator::new(&contents);
let stylist = SourceCodeStyleDetector::from_contents(&contents, &locator); let stylist = Stylist::from_contents(&contents, &locator);
let directives = let directives =
directives::extract_directives(&tokens, directives::Flags::from_settings(settings)); directives::extract_directives(&tokens, directives::Flags::from_settings(settings));
let diagnostics = check_path( let diagnostics = check_path(

View file

@ -3,7 +3,6 @@ use std::path::{Path, PathBuf};
use std::process::ExitCode; use std::process::ExitCode;
use std::sync::mpsc::channel; use std::sync::mpsc::channel;
use ::ruff::autofix::fixer;
use ::ruff::cli::{extract_log_level, Cli, Overrides}; use ::ruff::cli::{extract_log_level, Cli, Overrides};
use ::ruff::logging::{set_up_logging, LogLevel}; use ::ruff::logging::{set_up_logging, LogLevel};
use ::ruff::printer::{Printer, Violations}; use ::ruff::printer::{Printer, Violations};
@ -13,7 +12,7 @@ use ::ruff::settings::types::SerializationFormat;
use ::ruff::settings::{pyproject, Settings}; use ::ruff::settings::{pyproject, Settings};
#[cfg(feature = "update-informer")] #[cfg(feature = "update-informer")]
use ::ruff::updates; use ::ruff::updates;
use ::ruff::{commands, warn_user_once}; use ::ruff::{commands, fix, warn_user_once};
use anyhow::Result; use anyhow::Result;
use clap::{CommandFactory, Parser}; use clap::{CommandFactory, Parser};
use colored::Colorize; use colored::Colorize;
@ -152,13 +151,13 @@ pub(crate) fn inner_main() -> Result<ExitCode> {
// but not apply fixes. That would allow us to avoid special-casing JSON // but not apply fixes. That would allow us to avoid special-casing JSON
// here. // here.
let autofix = if cli.diff { let autofix = if cli.diff {
fixer::Mode::Diff fix::FixMode::Diff
} else if fix || fix_only { } else if fix || fix_only {
fixer::Mode::Apply fix::FixMode::Apply
} else if matches!(format, SerializationFormat::Json) { } else if matches!(format, SerializationFormat::Json) {
fixer::Mode::Generate fix::FixMode::Generate
} else { } else {
fixer::Mode::None fix::FixMode::None
}; };
let violations = if cli.diff || fix_only { let violations = if cli.diff || fix_only {
Violations::Hide Violations::Hide
@ -176,7 +175,7 @@ pub(crate) fn inner_main() -> Result<ExitCode> {
let printer = Printer::new(&format, &log_level, &autofix, &violations); let printer = Printer::new(&format, &log_level, &autofix, &violations);
if cli.watch { if cli.watch {
if !matches!(autofix, fixer::Mode::None) { if !matches!(autofix, fix::FixMode::None) {
warn_user_once!("--fix is not enabled in watch mode."); warn_user_once!("--fix is not enabled in watch mode.");
} }
if format != SerializationFormat::Text { if format != SerializationFormat::Text {
@ -193,7 +192,7 @@ pub(crate) fn inner_main() -> Result<ExitCode> {
&file_strategy, &file_strategy,
&overrides, &overrides,
cache.into(), cache.into(),
fixer::Mode::None, fix::FixMode::None,
)?; )?;
printer.write_continuously(&messages)?; printer.write_continuously(&messages)?;
@ -223,7 +222,7 @@ pub(crate) fn inner_main() -> Result<ExitCode> {
&file_strategy, &file_strategy,
&overrides, &overrides,
cache.into(), cache.into(),
fixer::Mode::None, fix::FixMode::None,
)?; )?;
printer.write_continuously(&messages)?; printer.write_continuously(&messages)?;
} }
@ -263,7 +262,7 @@ pub(crate) fn inner_main() -> Result<ExitCode> {
// Always try to print violations (the printer itself may suppress output), // Always try to print violations (the printer itself may suppress output),
// unless we're writing fixes via stdin (in which case, the transformed // unless we're writing fixes via stdin (in which case, the transformed
// source code goes to stdout). // source code goes to stdout).
if !(is_stdin && matches!(autofix, fixer::Mode::Apply | fixer::Mode::Diff)) { if !(is_stdin && matches!(autofix, fix::FixMode::Apply | fix::FixMode::Diff)) {
printer.write_once(&diagnostics)?; printer.write_once(&diagnostics)?;
} }

View file

@ -1,4 +1,4 @@
pub mod rules; pub(crate) mod rules;
pub mod settings; pub mod settings;
#[cfg(test)] #[cfg(test)]
@ -9,8 +9,9 @@ mod tests {
use test_case::test_case; use test_case::test_case;
use crate::linter::test_path; use crate::linter::test_path;
use crate::mccabe;
use crate::registry::RuleCode; use crate::registry::RuleCode;
use crate::{mccabe, Settings}; use crate::settings::Settings;
#[test_case(0)] #[test_case(0)]
#[test_case(3)] #[test_case(3)]

View file

@ -2,7 +2,7 @@ use rustpython_ast::{ExcepthandlerKind, ExprKind, Stmt, StmtKind};
use crate::ast::helpers::identifier_range; use crate::ast::helpers::identifier_range;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::source_code_locator::SourceCodeLocator; use crate::source_code::Locator;
use crate::violations; use crate::violations;
fn get_complexity_number(stmts: &[Stmt]) -> usize { fn get_complexity_number(stmts: &[Stmt]) -> usize {
@ -61,7 +61,7 @@ pub fn function_is_too_complex(
name: &str, name: &str,
body: &[Stmt], body: &[Stmt],
max_complexity: usize, max_complexity: usize,
locator: &SourceCodeLocator, locator: &Locator,
) -> Option<Diagnostic> { ) -> Option<Diagnostic> {
let complexity = get_complexity_number(body) + 1; let complexity = get_complexity_number(body) + 1;
if complexity > max_complexity { if complexity > max_complexity {

View file

@ -4,9 +4,9 @@ use rustpython_parser::ast::Location;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix; use crate::fix::Fix;
use crate::registry::{Diagnostic, DiagnosticKind}; use crate::registry::{Diagnostic, DiagnosticKind};
use crate::source_code_locator::SourceCodeLocator; use crate::source_code::Locator;
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Message { pub struct Message {
@ -61,7 +61,7 @@ pub struct Source {
} }
impl Source { impl Source {
pub fn from_diagnostic(diagnostic: &Diagnostic, locator: &SourceCodeLocator) -> Self { pub fn from_diagnostic(diagnostic: &Diagnostic, locator: &Locator) -> Self {
let location = Location::new(diagnostic.location.row(), 0); let location = Location::new(diagnostic.location.row(), 0);
// Diagnostics can already extend one-past-the-end per Ropey's semantics. If // Diagnostics can already extend one-past-the-end per Ropey's semantics. If
// they do, though, then they'll end at the start of a line. We need to // they do, though, then they'll end at the start of a line. We need to

View file

@ -9,7 +9,7 @@ use regex::Regex;
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use crate::registry::{Diagnostic, RuleCode, CODE_REDIRECTS}; use crate::registry::{Diagnostic, RuleCode, CODE_REDIRECTS};
use crate::source_code_style::LineEnding; use crate::source_code::LineEnding;
static NOQA_LINE_REGEX: Lazy<Regex> = Lazy::new(|| { static NOQA_LINE_REGEX: Lazy<Regex> = Lazy::new(|| {
Regex::new( Regex::new(
@ -207,7 +207,6 @@ fn add_noqa_inner(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use nohash_hasher::IntMap; use nohash_hasher::IntMap;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use rustpython_parser::ast::Location; use rustpython_parser::ast::Location;
@ -215,7 +214,7 @@ mod tests {
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::noqa::{add_noqa_inner, NOQA_LINE_REGEX}; use crate::noqa::{add_noqa_inner, NOQA_LINE_REGEX};
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::source_code_style::LineEnding; use crate::source_code::LineEnding;
use crate::violations; use crate::violations;
#[test] #[test]

View file

@ -102,7 +102,7 @@ pub fn detect_package_roots<'a>(files: &[&'a Path]) -> FxHashMap<&'a Path, Optio
mod tests { mod tests {
use std::path::PathBuf; use std::path::PathBuf;
use crate::packages::detect_package_root; use crate::packaging::detect_package_root;
#[test] #[test]
fn package_detection() { fn package_detection() {

View file

@ -1,5 +1,5 @@
pub mod helpers; pub(crate) mod helpers;
pub mod rules; pub(crate) mod rules;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -13,16 +13,15 @@ mod tests {
use crate::linter::check_path; use crate::linter::check_path;
use crate::registry::{RuleCode, RuleCodePrefix}; use crate::registry::{RuleCode, RuleCodePrefix};
use crate::settings::flags; use crate::settings::flags;
use crate::source_code_locator::SourceCodeLocator; use crate::source_code::{Locator, Stylist};
use crate::source_code_style::SourceCodeStyleDetector;
use crate::{directives, rustpython_helpers, settings}; use crate::{directives, rustpython_helpers, settings};
fn rule_code(contents: &str, expected: &[RuleCode]) -> Result<()> { fn rule_code(contents: &str, expected: &[RuleCode]) -> Result<()> {
let contents = dedent(contents); let contents = dedent(contents);
let settings = settings::Settings::for_rules(RuleCodePrefix::PD.codes()); let settings = settings::Settings::for_rules(RuleCodePrefix::PD.codes());
let tokens: Vec<LexResult> = rustpython_helpers::tokenize(&contents); let tokens: Vec<LexResult> = rustpython_helpers::tokenize(&contents);
let locator = SourceCodeLocator::new(&contents); let locator = Locator::new(&contents);
let stylist = SourceCodeStyleDetector::from_contents(&contents, &locator); let stylist = Stylist::from_contents(&contents, &locator);
let directives = let directives =
directives::extract_directives(&tokens, directives::Flags::from_settings(&settings)); directives::extract_directives(&tokens, directives::Flags::from_settings(&settings));
let diagnostics = check_path( let diagnostics = check_path(

View file

@ -1,5 +1,5 @@
mod helpers; mod helpers;
pub mod rules; pub(crate) mod rules;
pub mod settings; pub mod settings;
#[cfg(test)] #[cfg(test)]

View file

@ -9,15 +9,11 @@ use crate::pep8_naming::helpers;
use crate::pep8_naming::settings::Settings; use crate::pep8_naming::settings::Settings;
use crate::python::string::{self}; use crate::python::string::{self};
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::source_code_locator::SourceCodeLocator; use crate::source_code::Locator;
use crate::violations; use crate::violations;
/// N801 /// N801
pub fn invalid_class_name( pub fn invalid_class_name(class_def: &Stmt, name: &str, locator: &Locator) -> Option<Diagnostic> {
class_def: &Stmt,
name: &str,
locator: &SourceCodeLocator,
) -> Option<Diagnostic> {
let stripped = name.strip_prefix('_').unwrap_or(name); let stripped = name.strip_prefix('_').unwrap_or(name);
if !stripped.chars().next().map_or(false, char::is_uppercase) || stripped.contains('_') { if !stripped.chars().next().map_or(false, char::is_uppercase) || stripped.contains('_') {
return Some(Diagnostic::new( return Some(Diagnostic::new(
@ -33,7 +29,7 @@ pub fn invalid_function_name(
func_def: &Stmt, func_def: &Stmt,
name: &str, name: &str,
ignore_names: &[String], ignore_names: &[String],
locator: &SourceCodeLocator, locator: &Locator,
) -> Option<Diagnostic> { ) -> Option<Diagnostic> {
if name.to_lowercase() != name && !ignore_names.iter().any(|ignore_name| ignore_name == name) { if name.to_lowercase() != name && !ignore_names.iter().any(|ignore_name| ignore_name == name) {
return Some(Diagnostic::new( return Some(Diagnostic::new(
@ -153,7 +149,7 @@ pub fn dunder_function_name(
scope: &Scope, scope: &Scope,
stmt: &Stmt, stmt: &Stmt,
name: &str, name: &str,
locator: &SourceCodeLocator, locator: &Locator,
) -> Option<Diagnostic> { ) -> Option<Diagnostic> {
if matches!(scope.kind, ScopeKind::Class(_)) { if matches!(scope.kind, ScopeKind::Class(_)) {
return None; return None;
@ -177,7 +173,7 @@ pub fn constant_imported_as_non_constant(
import_from: &Stmt, import_from: &Stmt,
name: &str, name: &str,
asname: &str, asname: &str,
locator: &SourceCodeLocator, locator: &Locator,
) -> Option<Diagnostic> { ) -> Option<Diagnostic> {
if string::is_upper(name) && !string::is_upper(asname) { if string::is_upper(name) && !string::is_upper(asname) {
return Some(Diagnostic::new( return Some(Diagnostic::new(
@ -193,7 +189,7 @@ pub fn lowercase_imported_as_non_lowercase(
import_from: &Stmt, import_from: &Stmt,
name: &str, name: &str,
asname: &str, asname: &str,
locator: &SourceCodeLocator, locator: &Locator,
) -> Option<Diagnostic> { ) -> Option<Diagnostic> {
if !string::is_upper(name) && string::is_lower(name) && asname.to_lowercase() != asname { if !string::is_upper(name) && string::is_lower(name) && asname.to_lowercase() != asname {
return Some(Diagnostic::new( return Some(Diagnostic::new(
@ -209,7 +205,7 @@ pub fn camelcase_imported_as_lowercase(
import_from: &Stmt, import_from: &Stmt,
name: &str, name: &str,
asname: &str, asname: &str,
locator: &SourceCodeLocator, locator: &Locator,
) -> Option<Diagnostic> { ) -> Option<Diagnostic> {
if helpers::is_camelcase(name) && string::is_lower(asname) { if helpers::is_camelcase(name) && string::is_lower(asname) {
return Some(Diagnostic::new( return Some(Diagnostic::new(
@ -225,7 +221,7 @@ pub fn camelcase_imported_as_constant(
import_from: &Stmt, import_from: &Stmt,
name: &str, name: &str,
asname: &str, asname: &str,
locator: &SourceCodeLocator, locator: &Locator,
) -> Option<Diagnostic> { ) -> Option<Diagnostic> {
if helpers::is_camelcase(name) if helpers::is_camelcase(name)
&& !string::is_lower(asname) && !string::is_lower(asname)
@ -279,7 +275,7 @@ pub fn camelcase_imported_as_acronym(
import_from: &Stmt, import_from: &Stmt,
name: &str, name: &str,
asname: &str, asname: &str,
locator: &SourceCodeLocator, locator: &Locator,
) -> Option<Diagnostic> { ) -> Option<Diagnostic> {
if helpers::is_camelcase(name) if helpers::is_camelcase(name)
&& !string::is_lower(asname) && !string::is_lower(asname)
@ -299,7 +295,7 @@ pub fn error_suffix_on_exception_name(
class_def: &Stmt, class_def: &Stmt,
bases: &[Expr], bases: &[Expr],
name: &str, name: &str,
locator: &SourceCodeLocator, locator: &Locator,
) -> Option<Diagnostic> { ) -> Option<Diagnostic> {
if !bases.iter().any(|base| { if !bases.iter().any(|base| {
if let ExprKind::Name { id, .. } = &base.node { if let ExprKind::Name { id, .. } = &base.node {

View file

@ -10,14 +10,13 @@ use rustpython_parser::ast::Location;
use serde::Serialize; use serde::Serialize;
use serde_json::json; use serde_json::json;
use crate::autofix::fixer;
use crate::fs::relativize_path; use crate::fs::relativize_path;
use crate::linter::Diagnostics; use crate::linter::Diagnostics;
use crate::logging::LogLevel; use crate::logging::LogLevel;
use crate::message::Message; use crate::message::Message;
use crate::notify_user;
use crate::registry::RuleCode; use crate::registry::RuleCode;
use crate::settings::types::SerializationFormat; use crate::settings::types::SerializationFormat;
use crate::{fix, notify_user};
/// Enum to control whether lint violations are shown to the user. /// Enum to control whether lint violations are shown to the user.
pub enum Violations { pub enum Violations {
@ -46,7 +45,7 @@ struct ExpandedMessage<'a> {
pub struct Printer<'a> { pub struct Printer<'a> {
format: &'a SerializationFormat, format: &'a SerializationFormat,
log_level: &'a LogLevel, log_level: &'a LogLevel,
autofix: &'a fixer::Mode, autofix: &'a fix::FixMode,
violations: &'a Violations, violations: &'a Violations,
} }
@ -54,7 +53,7 @@ impl<'a> Printer<'a> {
pub fn new( pub fn new(
format: &'a SerializationFormat, format: &'a SerializationFormat,
log_level: &'a LogLevel, log_level: &'a LogLevel,
autofix: &'a fixer::Mode, autofix: &'a fix::FixMode,
violations: &'a Violations, violations: &'a Violations,
) -> Self { ) -> Self {
Self { Self {
@ -84,7 +83,7 @@ impl<'a> Printer<'a> {
println!("Found {remaining} error(s)."); println!("Found {remaining} error(s).");
} }
if !matches!(self.autofix, fixer::Mode::Apply) { if !matches!(self.autofix, fix::FixMode::Apply) {
let num_fixable = diagnostics let num_fixable = diagnostics
.messages .messages
.iter() .iter()
@ -98,9 +97,9 @@ impl<'a> Printer<'a> {
Violations::Hide => { Violations::Hide => {
let fixed = diagnostics.fixed; let fixed = diagnostics.fixed;
if fixed > 0 { if fixed > 0 {
if matches!(self.autofix, fixer::Mode::Apply) { if matches!(self.autofix, fix::FixMode::Apply) {
println!("Fixed {fixed} error(s)."); println!("Fixed {fixed} error(s).");
} else if matches!(self.autofix, fixer::Mode::Diff) { } else if matches!(self.autofix, fix::FixMode::Diff) {
println!("Would fix {fixed} error(s)."); println!("Would fix {fixed} error(s).");
} }
} }

View file

@ -1,4 +1,4 @@
pub mod rules; pub(crate) mod rules;
pub mod settings; pub mod settings;
#[cfg(test)] #[cfg(test)]

View file

@ -11,13 +11,11 @@ use crate::ast::helpers::{
}; };
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::ast::whitespace::leading_space; use crate::ast::whitespace::leading_space;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::settings::Settings; use crate::settings::Settings;
use crate::source_code_generator::SourceCodeGenerator; use crate::source_code::{Generator, Locator, Stylist};
use crate::source_code_locator::SourceCodeLocator;
use crate::source_code_style::SourceCodeStyleDetector;
use crate::violations; use crate::violations;
static URL_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^https?://\S+$").unwrap()); static URL_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^https?://\S+$").unwrap());
@ -106,12 +104,7 @@ pub fn doc_line_too_long(lineno: usize, line: &str, settings: &Settings) -> Opti
} }
} }
fn compare( fn compare(left: &Expr, ops: &[Cmpop], comparators: &[Expr], stylist: &Stylist) -> String {
left: &Expr,
ops: &[Cmpop],
comparators: &[Expr],
stylist: &SourceCodeStyleDetector,
) -> String {
unparse_expr( unparse_expr(
&create_expr(ExprKind::Compare { &create_expr(ExprKind::Compare {
left: Box::new(left.clone()), left: Box::new(left.clone()),
@ -413,7 +406,7 @@ pub fn do_not_use_bare_except(
type_: Option<&Expr>, type_: Option<&Expr>,
body: &[Stmt], body: &[Stmt],
handler: &Excepthandler, handler: &Excepthandler,
locator: &SourceCodeLocator, locator: &Locator,
) -> Option<Diagnostic> { ) -> Option<Diagnostic> {
if type_.is_none() if type_.is_none()
&& !body && !body
@ -429,12 +422,7 @@ pub fn do_not_use_bare_except(
} }
} }
fn function( fn function(name: &str, args: &Arguments, body: &Expr, stylist: &Stylist) -> String {
name: &str,
args: &Arguments,
body: &Expr,
stylist: &SourceCodeStyleDetector,
) -> String {
let body = Stmt::new( let body = Stmt::new(
Location::default(), Location::default(),
Location::default(), Location::default(),
@ -454,7 +442,7 @@ fn function(
type_comment: None, type_comment: None,
}, },
); );
let mut generator: SourceCodeGenerator = stylist.into(); let mut generator: Generator = stylist.into();
generator.unparse_stmt(&func); generator.unparse_stmt(&func);
generator.generate() generator.generate()
} }
@ -585,7 +573,7 @@ fn extract_quote(text: &str) -> &str {
/// W605 /// W605
pub fn invalid_escape_sequence( pub fn invalid_escape_sequence(
locator: &SourceCodeLocator, locator: &Locator,
start: Location, start: Location,
end: Location, end: Location,
autofix: bool, autofix: bool,

View file

@ -1,5 +1,5 @@
pub mod helpers; pub(crate) mod helpers;
pub mod rules; pub(crate) mod rules;
pub mod settings; pub mod settings;
#[cfg(test)] #[cfg(test)]

View file

@ -8,12 +8,12 @@ use crate::ast::helpers::identifier_range;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::ast::whitespace::LinesWithTrailingNewline; use crate::ast::whitespace::LinesWithTrailingNewline;
use crate::ast::{cast, whitespace}; use crate::ast::{cast, whitespace};
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::docstrings::constants; use crate::docstrings::constants;
use crate::docstrings::definition::{Definition, DefinitionKind, Docstring}; use crate::docstrings::definition::{Definition, DefinitionKind, Docstring};
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::fix::Fix;
use crate::pydocstyle::helpers::{leading_quote, logical_line}; use crate::pydocstyle::helpers::{leading_quote, logical_line};
use crate::pydocstyle::settings::Convention; use crate::pydocstyle::settings::Convention;
use crate::registry::{Diagnostic, RuleCode}; use crate::registry::{Diagnostic, RuleCode};

View file

@ -5,16 +5,16 @@ use rustpython_parser::lexer;
use rustpython_parser::lexer::Tok; use rustpython_parser::lexer::Tok;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::cst::matchers::{match_expr, match_module}; use crate::cst::matchers::{match_expr, match_module};
use crate::fix::Fix;
use crate::python::string::strip_quotes_and_prefixes; use crate::python::string::strip_quotes_and_prefixes;
use crate::source_code_locator::SourceCodeLocator; use crate::source_code::Locator;
/// Generate a `Fix` to remove unused keys from format dict. /// Generate a `Fix` to remove unused keys from format dict.
pub fn remove_unused_format_arguments_from_dict( pub fn remove_unused_format_arguments_from_dict(
unused_arguments: &[&str], unused_arguments: &[&str],
stmt: &Expr, stmt: &Expr,
locator: &SourceCodeLocator, locator: &Locator,
) -> Result<Fix> { ) -> Result<Fix> {
let module_text = locator.slice_source_code_range(&Range::from_located(stmt)); let module_text = locator.slice_source_code_range(&Range::from_located(stmt));
let mut tree = match_module(&module_text)?; let mut tree = match_module(&module_text)?;
@ -60,7 +60,7 @@ pub fn remove_unused_format_arguments_from_dict(
pub fn remove_unused_keyword_arguments_from_format_call( pub fn remove_unused_keyword_arguments_from_format_call(
unused_arguments: &[&str], unused_arguments: &[&str],
location: Range, location: Range,
locator: &SourceCodeLocator, locator: &Locator,
) -> Result<Fix> { ) -> Result<Fix> {
let module_text = locator.slice_source_code_range(&location); let module_text = locator.slice_source_code_range(&location);
let mut tree = match_module(&module_text)?; let mut tree = match_module(&module_text)?;
@ -103,7 +103,7 @@ pub fn remove_unused_keyword_arguments_from_format_call(
/// Generate a `Fix` to remove the binding from an exception handler. /// Generate a `Fix` to remove the binding from an exception handler.
pub fn remove_exception_handler_assignment( pub fn remove_exception_handler_assignment(
excepthandler: &Excepthandler, excepthandler: &Excepthandler,
locator: &SourceCodeLocator, locator: &Locator,
) -> Result<Fix> { ) -> Result<Fix> {
let contents = locator.slice_source_code_range(&Range::from_located(excepthandler)); let contents = locator.slice_source_code_range(&Range::from_located(excepthandler));
let mut fix_start = None; let mut fix_start = None;

View file

@ -1,7 +1,7 @@
pub mod cformat; pub(crate) mod cformat;
pub mod fixes; pub(crate) mod fixes;
pub mod format; pub(crate) mod format;
pub mod rules; pub(crate) mod rules;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -17,8 +17,7 @@ mod tests {
use crate::linter::{check_path, test_path}; use crate::linter::{check_path, test_path};
use crate::registry::{RuleCode, RuleCodePrefix}; use crate::registry::{RuleCode, RuleCodePrefix};
use crate::settings::flags; use crate::settings::flags;
use crate::source_code_locator::SourceCodeLocator; use crate::source_code::{Locator, Stylist};
use crate::source_code_style::SourceCodeStyleDetector;
use crate::{directives, rustpython_helpers, settings}; use crate::{directives, rustpython_helpers, settings};
#[test_case(RuleCode::F401, Path::new("F401_0.py"); "F401_0")] #[test_case(RuleCode::F401, Path::new("F401_0.py"); "F401_0")]
@ -212,8 +211,8 @@ mod tests {
let contents = dedent(contents); let contents = dedent(contents);
let settings = settings::Settings::for_rules(RuleCodePrefix::F.codes()); let settings = settings::Settings::for_rules(RuleCodePrefix::F.codes());
let tokens: Vec<LexResult> = rustpython_helpers::tokenize(&contents); let tokens: Vec<LexResult> = rustpython_helpers::tokenize(&contents);
let locator = SourceCodeLocator::new(&contents); let locator = Locator::new(&contents);
let stylist = SourceCodeStyleDetector::from_contents(&contents, &locator); let stylist = Stylist::from_contents(&contents, &locator);
let directives = let directives =
directives::extract_directives(&tokens, directives::Flags::from_settings(&settings)); directives::extract_directives(&tokens, directives::Flags::from_settings(&settings));
let mut diagnostics = check_path( let mut diagnostics = check_path(

View file

@ -1,8 +1,8 @@
use rustpython_ast::{Expr, ExprKind}; use rustpython_ast::{Expr, ExprKind};
use crate::ast::helpers::find_useless_f_strings; use crate::ast::helpers::find_useless_f_strings;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::{Diagnostic, RuleCode}; use crate::registry::{Diagnostic, RuleCode};
use crate::violations; use crate::violations;

View file

@ -5,8 +5,8 @@ use rustpython_ast::{Cmpop, Expr};
use crate::ast::helpers; use crate::ast::helpers;
use crate::ast::operations::locate_cmpops; use crate::ast::operations::locate_cmpops;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::violations; use crate::violations;

View file

@ -34,7 +34,7 @@ use rustpython_parser::ast::{Excepthandler, ExcepthandlerKind, Expr, ExprKind, S
use crate::ast::helpers::except_range; use crate::ast::helpers::except_range;
use crate::ast::types::{Binding, Range, Scope, ScopeKind}; use crate::ast::types::{Binding, Range, Scope, ScopeKind};
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::source_code_locator::SourceCodeLocator; use crate::source_code::Locator;
use crate::violations; use crate::violations;
/// F821 /// F821
@ -62,7 +62,7 @@ pub fn undefined_local(name: &str, scopes: &[&Scope], bindings: &[Binding]) -> O
/// F707 /// F707
pub fn default_except_not_last( pub fn default_except_not_last(
handlers: &[Excepthandler], handlers: &[Excepthandler],
locator: &SourceCodeLocator, locator: &Locator,
) -> Option<Diagnostic> { ) -> Option<Diagnostic> {
for (idx, handler) in handlers.iter().enumerate() { for (idx, handler) in handlers.iter().enumerate() {
let ExcepthandlerKind::ExceptHandler { type_, .. } = &handler.node; let ExcepthandlerKind::ExceptHandler { type_, .. } = &handler.node;

View file

@ -1,8 +1,8 @@
use rustpython_ast::{Expr, ExprKind}; use rustpython_ast::{Expr, ExprKind};
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::Diagnostic; use crate::registry::Diagnostic;
use crate::violations; use crate::violations;

View file

@ -6,8 +6,8 @@ use rustpython_ast::{Expr, ExprKind};
use crate::ast::comparable::{ComparableConstant, ComparableExpr}; use crate::ast::comparable::{ComparableConstant, ComparableExpr};
use crate::ast::helpers::unparse_expr; use crate::ast::helpers::unparse_expr;
use crate::ast::types::Range; use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::{Diagnostic, RuleCode}; use crate::registry::{Diagnostic, RuleCode};
use crate::violations; use crate::violations;

View file

@ -3,8 +3,8 @@ use rustpython_ast::{Expr, ExprKind, Stmt, StmtKind};
use crate::ast::types::{BindingKind, Range, RefEquality, ScopeKind}; use crate::ast::types::{BindingKind, Range, RefEquality, ScopeKind};
use crate::autofix::helpers::delete_stmt; use crate::autofix::helpers::delete_stmt;
use crate::autofix::Fix;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::{Diagnostic, RuleCode}; use crate::registry::{Diagnostic, RuleCode};
use crate::violations; use crate::violations;

View file

@ -1,4 +1,4 @@
pub mod rules; pub(crate) mod rules;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -1,4 +1,4 @@
pub mod rules; pub(crate) mod rules;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -9,7 +9,7 @@ mod tests {
use crate::linter::test_path; use crate::linter::test_path;
use crate::registry::RuleCode; use crate::registry::RuleCode;
use crate::Settings; use crate::settings::Settings;
#[test_case(RuleCode::PLC0414, Path::new("import_aliasing.py"); "PLC0414")] #[test_case(RuleCode::PLC0414, Path::new("import_aliasing.py"); "PLC0414")]
#[test_case(RuleCode::PLC2201, Path::new("misplaced_comparison_constant.py"); "PLC2201")] #[test_case(RuleCode::PLC2201, Path::new("misplaced_comparison_constant.py"); "PLC2201")]

View file

@ -2,7 +2,8 @@ use rustpython_ast::Expr;
use crate::ast::types::{FunctionDef, Range, ScopeKind}; use crate::ast::types::{FunctionDef, Range, ScopeKind};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::{violations, Diagnostic}; use crate::registry::Diagnostic;
use crate::violations;
/// PLE1142 /// PLE1142
pub fn await_outside_async(checker: &mut Checker, expr: &Expr) { pub fn await_outside_async(checker: &mut Checker, expr: &Expr) {

Some files were not shown because too many files have changed in this diff Show more