use std::collections::{BTreeMap, BTreeSet}; use std::ops::Deref; use std::path::Path; use log::error; use once_cell::unsync::OnceCell; use rustpython_parser::ast::{ Arg, Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind, KeywordData, Operator, Stmt, StmtKind, Suite, }; use rustpython_parser::parser; use crate::ast::helpers::{extract_handler_names, match_name_or_attr, SubscriptKind}; use crate::ast::operations::{extract_all_names, SourceCodeLocator}; use crate::ast::relocate::relocate_expr; use crate::ast::types::{ Binding, BindingContext, BindingKind, CheckLocator, FunctionScope, ImportKind, Range, Scope, ScopeKind, }; use crate::ast::visitor::{walk_excepthandler, Visitor}; use crate::ast::{helpers, operations, visitor}; use crate::autofix::fixer; use crate::checks::{Check, CheckCode, CheckKind}; use crate::docstrings::definition::{Definition, DefinitionKind, Documentable}; use crate::python::builtins::{BUILTINS, MAGIC_GLOBALS}; use crate::python::future::ALL_FEATURE_NAMES; use crate::settings::{PythonVersion, Settings}; use crate::visibility::{module_visibility, transition_scope, Modifier, Visibility, VisibleScope}; use crate::{ docstrings, flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_print, pep8_naming, pycodestyle, pydocstyle, pyflakes, pyupgrade, }; pub const GLOBAL_SCOPE_INDEX: usize = 0; pub struct Checker<'a> { // Input data. path: &'a Path, content: &'a str, autofix: &'a fixer::Mode, pub(crate) settings: &'a Settings, // Computed checks. checks: Vec, // Efficient source-code slicing. locator: OnceCell>, // Docstring tracking. docstrings: Vec<(Definition<'a>, Visibility)>, // Edit tracking. // TODO(charlie): Instead of exposing deletions, wrap in a public API. pub(crate) deletions: BTreeSet, // Retain all scopes and parent nodes, along with a stack of indexes to track which are active // at various points in time. pub(crate) parents: Vec<&'a Stmt>, pub(crate) parent_stack: Vec, scopes: Vec, scope_stack: Vec, dead_scopes: Vec, deferred_string_annotations: Vec<(Range, &'a str)>, deferred_annotations: Vec<(&'a Expr, Vec, Vec)>, deferred_functions: Vec<(&'a Stmt, Vec, Vec, VisibleScope)>, deferred_lambdas: Vec<(&'a Expr, Vec, Vec)>, deferred_assignments: Vec, // Internal, derivative state. visible_scope: VisibleScope, in_f_string: Option, in_annotation: bool, in_literal: bool, seen_import_boundary: bool, futures_allowed: bool, annotations_future_enabled: bool, except_handlers: Vec>, } impl<'a> Checker<'a> { pub fn new( settings: &'a Settings, autofix: &'a fixer::Mode, path: &'a Path, content: &'a str, ) -> Checker<'a> { Checker { settings, autofix, path, content, locator: OnceCell::new(), checks: Default::default(), docstrings: Default::default(), deletions: Default::default(), parents: Default::default(), parent_stack: Default::default(), scopes: Default::default(), scope_stack: Default::default(), dead_scopes: Default::default(), deferred_string_annotations: Default::default(), deferred_annotations: Default::default(), deferred_functions: Default::default(), deferred_lambdas: Default::default(), deferred_assignments: Default::default(), visible_scope: VisibleScope { modifier: Modifier::Module, visibility: module_visibility(path), }, in_f_string: None, in_annotation: Default::default(), in_literal: Default::default(), seen_import_boundary: Default::default(), futures_allowed: true, annotations_future_enabled: Default::default(), except_handlers: Default::default(), } } /// Get access to a lazily-initialized `SourceCodeLocator` for the file contents. pub fn get_locator(&self) -> &SourceCodeLocator { self.locator .get_or_init(|| SourceCodeLocator::new(self.content)) } /// Return `true` if a patch should be generated under the given autofix `Mode`. pub fn patch(&self) -> bool { self.autofix.patch() } } impl<'a, 'b> Visitor<'b> for Checker<'a> where 'b: 'a, { fn visit_stmt(&mut self, stmt: &'b Stmt) { self.push_parent(stmt); // Track whether we've seen docstrings, non-imports, etc. match &stmt.node { StmtKind::ImportFrom { module, .. } => { // Allow __future__ imports until we see a non-__future__ import. if self.futures_allowed { if let Some(module) = module { if module != "__future__" { self.futures_allowed = false; } } } } StmtKind::Import { .. } => { self.futures_allowed = false; } node => { self.futures_allowed = false; if !self.seen_import_boundary && !helpers::is_assignment_to_a_dunder(node) && !operations::in_nested_block(&self.parent_stack, &self.parents) { self.seen_import_boundary = true; } } } // Pre-visit. match &stmt.node { StmtKind::Global { names } | StmtKind::Nonlocal { names } => { let global_scope_id = self.scopes[GLOBAL_SCOPE_INDEX].id; let current_scope = self.current_scope(); let current_scope_id = current_scope.id; if current_scope_id != global_scope_id { for name in names { for scope in self.scopes.iter_mut().skip(GLOBAL_SCOPE_INDEX + 1) { scope.values.insert( name.to_string(), Binding { kind: BindingKind::Assignment, used: Some((global_scope_id, Range::from_located(stmt))), range: Range::from_located(stmt), }, ); } } } if self.settings.enabled.contains(&CheckCode::E741) { let location = self.locate_check(Range::from_located(stmt)); self.checks.extend(names.iter().filter_map(|name| { pycodestyle::checks::ambiguous_variable_name(name, location) })); } } StmtKind::Break => { if self.settings.enabled.contains(&CheckCode::F701) { if let Some(check) = pyflakes::checks::break_outside_loop( stmt, &self.parents, &self.parent_stack, self, ) { self.checks.push(check); } } } StmtKind::Continue => { if self.settings.enabled.contains(&CheckCode::F702) { if let Some(check) = pyflakes::checks::continue_outside_loop( stmt, &self.parents, &self.parent_stack, self, ) { self.checks.push(check); } } } StmtKind::FunctionDef { name, decorator_list, returns, args, .. } | StmtKind::AsyncFunctionDef { name, decorator_list, returns, args, .. } => { if self.settings.enabled.contains(&CheckCode::E743) { if let Some(check) = pycodestyle::checks::ambiguous_function_name( name, self.locate_check(Range::from_located(stmt)), ) { self.checks.push(check); } } if self.settings.enabled.contains(&CheckCode::N802) { if let Some(check) = pep8_naming::checks::invalid_function_name(stmt, name) { self.checks.push(check); } } if self.settings.enabled.contains(&CheckCode::N804) { if let Some(check) = pep8_naming::checks::invalid_first_argument_name_for_class_method( self.current_scope(), decorator_list, args, ) { self.checks.push(check); } } if self.settings.enabled.contains(&CheckCode::N805) { if let Some(check) = pep8_naming::checks::invalid_first_argument_name_for_method( self.current_scope(), decorator_list, args, ) { self.checks.push(check); } } if self.settings.enabled.contains(&CheckCode::N807) { if let Some(check) = pep8_naming::checks::dunder_function_name(stmt, self.current_scope(), name) { self.checks.push(check); } } self.check_builtin_shadowing(name, Range::from_located(stmt), true); // Visit the decorators and arguments, but avoid the body, which will be deferred. for expr in decorator_list { self.visit_expr(expr); } for arg in &args.posonlyargs { if let Some(expr) = &arg.node.annotation { self.visit_annotation(expr); } } for arg in &args.args { if let Some(expr) = &arg.node.annotation { self.visit_annotation(expr); } } if let Some(arg) = &args.vararg { if let Some(expr) = &arg.node.annotation { self.visit_annotation(expr); } } for arg in &args.kwonlyargs { if let Some(expr) = &arg.node.annotation { self.visit_annotation(expr); } } if let Some(arg) = &args.kwarg { if let Some(expr) = &arg.node.annotation { self.visit_annotation(expr); } } for expr in returns { self.visit_annotation(expr); } for expr in &args.kw_defaults { self.visit_expr(expr); } for expr in &args.defaults { self.visit_expr(expr); } self.add_binding( name.to_string(), Binding { kind: BindingKind::Definition, used: None, range: Range::from_located(stmt), }, ); } StmtKind::Return { .. } => { if self.settings.enabled.contains(&CheckCode::F706) { if let Some(scope_index) = self.scope_stack.last().cloned() { match self.scopes[scope_index].kind { ScopeKind::Class | ScopeKind::Module => { self.checks.push(Check::new( CheckKind::ReturnOutsideFunction, self.locate_check(Range::from_located(stmt)), )); } _ => {} } } } } StmtKind::ClassDef { name, bases, keywords, decorator_list, .. } => { if self.settings.enabled.contains(&CheckCode::U004) { pyupgrade::plugins::useless_object_inheritance( self, stmt, name, bases, keywords, ); } if self.settings.enabled.contains(&CheckCode::E742) { if let Some(check) = pycodestyle::checks::ambiguous_class_name( name, self.locate_check(Range::from_located(stmt)), ) { self.checks.push(check); } } if self.settings.enabled.contains(&CheckCode::N801) { if let Some(check) = pep8_naming::checks::invalid_class_name(stmt, name) { self.checks.push(check); } } self.check_builtin_shadowing( name, self.locate_check(Range::from_located(stmt)), false, ); for expr in bases { self.visit_expr(expr) } for keyword in keywords { self.visit_keyword(keyword) } for expr in decorator_list { self.visit_expr(expr) } self.push_scope(Scope::new(ScopeKind::Class)) } StmtKind::Import { names } => { if self.settings.enabled.contains(&CheckCode::E402) { if self.seen_import_boundary && stmt.location.column() == 1 { self.checks.push(Check::new( CheckKind::ModuleImportNotAtTopOfFile, self.locate_check(Range::from_located(stmt)), )); } } for alias in names { if alias.node.name.contains('.') && alias.node.asname.is_none() { // Given `import foo.bar`, `name` would be "foo", and `full_name` would be // "foo.bar". let name = alias.node.name.split('.').next().unwrap(); let full_name = &alias.node.name; self.add_binding( name.to_string(), Binding { kind: BindingKind::SubmoduleImportation( name.to_string(), full_name.to_string(), self.binding_context(), ), used: None, range: Range::from_located(stmt), }, ) } else { if let Some(asname) = &alias.node.asname { self.check_builtin_shadowing(asname, Range::from_located(stmt), false); } // Given `import foo`, `name` and `full_name` would both be `foo`. // Given `import foo as bar`, `name` would be `bar` and `full_name` would // be `foo`. let name = alias.node.asname.as_ref().unwrap_or(&alias.node.name); let full_name = &alias.node.name; self.add_binding( name.to_string(), Binding { kind: BindingKind::Importation( name.to_string(), full_name.to_string(), self.binding_context(), ), used: None, range: Range::from_located(stmt), }, ) } if let Some(asname) = &alias.node.asname { let name = alias.node.name.split('.').last().unwrap(); if self.settings.enabled.contains(&CheckCode::N811) { if let Some(check) = pep8_naming::checks::constant_imported_as_non_constant( stmt, name, asname, ) { self.checks.push(check); } } if self.settings.enabled.contains(&CheckCode::N812) { if let Some(check) = pep8_naming::checks::lowercase_imported_as_non_lowercase( stmt, name, asname, ) { self.checks.push(check); } } if self.settings.enabled.contains(&CheckCode::N813) { if let Some(check) = pep8_naming::checks::camelcase_imported_as_lowercase( stmt, name, asname, ) { self.checks.push(check); } } if self.settings.enabled.contains(&CheckCode::N814) { if let Some(check) = pep8_naming::checks::camelcase_imported_as_constant( stmt, name, asname, ) { self.checks.push(check); } } if self.settings.enabled.contains(&CheckCode::N817) { if let Some(check) = pep8_naming::checks::camelcase_imported_as_acronym( stmt, name, asname, ) { self.checks.push(check); } } } } } StmtKind::ImportFrom { names, module, level, } => { if self.settings.enabled.contains(&CheckCode::E402) { if self.seen_import_boundary && stmt.location.column() == 1 { self.checks.push(Check::new( CheckKind::ModuleImportNotAtTopOfFile, self.locate_check(Range::from_located(stmt)), )); } } for alias in names { if let Some("__future__") = module.as_deref() { let name = alias.node.asname.as_ref().unwrap_or(&alias.node.name); self.add_binding( name.to_string(), Binding { kind: BindingKind::FutureImportation, used: Some(( self.scopes[*(self .scope_stack .last() .expect("No current scope found."))] .id, Range::from_located(stmt), )), range: Range::from_located(stmt), }, ); if alias.node.name == "annotations" { self.annotations_future_enabled = true; } if self.settings.enabled.contains(&CheckCode::F407) { if !ALL_FEATURE_NAMES.contains(&alias.node.name.deref()) { self.checks.push(Check::new( CheckKind::FutureFeatureNotDefined(alias.node.name.to_string()), self.locate_check(Range::from_located(stmt)), )); } } if self.settings.enabled.contains(&CheckCode::F404) && !self.futures_allowed { self.checks.push(Check::new( CheckKind::LateFutureImport, self.locate_check(Range::from_located(stmt)), )); } } else if alias.node.name == "*" { let module_name = format!( "{}{}", ".".repeat(level.unwrap_or_default()), module.clone().unwrap_or_else(|| "module".to_string()), ); self.add_binding( module_name.to_string(), Binding { kind: BindingKind::StarImportation, used: None, range: Range::from_located(stmt), }, ); if self.settings.enabled.contains(&CheckCode::F406) { let scope = &self.scopes [*(self.scope_stack.last().expect("No current scope found."))]; if !matches!(scope.kind, ScopeKind::Module) { self.checks.push(Check::new( CheckKind::ImportStarNotPermitted(module_name.to_string()), self.locate_check(Range::from_located(stmt)), )); } } if self.settings.enabled.contains(&CheckCode::F403) { self.checks.push(Check::new( CheckKind::ImportStarUsed(module_name.to_string()), self.locate_check(Range::from_located(stmt)), )); } let scope = &mut self.scopes[*(self .scope_stack .last_mut() .expect("No current scope found."))]; scope.import_starred = true; } else { if let Some(asname) = &alias.node.asname { self.check_builtin_shadowing(asname, Range::from_located(stmt), false); } // Given `from foo import bar`, `name` would be "bar" and `full_name` would // be "foo.bar". Given `from foo import bar as baz`, `name` would be "baz" // and `full_name` would be "foo.bar". let name = alias.node.asname.as_ref().unwrap_or(&alias.node.name); let full_name = match module { None => alias.node.name.to_string(), Some(parent) => format!("{}.{}", parent, alias.node.name), }; self.add_binding( name.to_string(), Binding { kind: BindingKind::FromImportation( name.to_string(), full_name, self.binding_context(), ), used: None, range: Range::from_located(stmt), }, ) } if let Some(asname) = &alias.node.asname { if self.settings.enabled.contains(&CheckCode::N811) { if let Some(check) = pep8_naming::checks::constant_imported_as_non_constant( stmt, &alias.node.name, asname, ) { self.checks.push(check); } } if self.settings.enabled.contains(&CheckCode::N812) { if let Some(check) = pep8_naming::checks::lowercase_imported_as_non_lowercase( stmt, &alias.node.name, asname, ) { self.checks.push(check); } } if self.settings.enabled.contains(&CheckCode::N813) { if let Some(check) = pep8_naming::checks::camelcase_imported_as_lowercase( stmt, &alias.node.name, asname, ) { self.checks.push(check); } } if self.settings.enabled.contains(&CheckCode::N814) { if let Some(check) = pep8_naming::checks::camelcase_imported_as_constant( stmt, &alias.node.name, asname, ) { self.checks.push(check); } } if self.settings.enabled.contains(&CheckCode::N817) { if let Some(check) = pep8_naming::checks::camelcase_imported_as_acronym( stmt, &alias.node.name, asname, ) { self.checks.push(check); } } } } } StmtKind::Raise { exc, .. } => { if self.settings.enabled.contains(&CheckCode::F901) { if let Some(expr) = exc { if let Some(check) = pyflakes::checks::raise_not_implemented(expr) { self.checks.push(check); } } } } StmtKind::AugAssign { target, .. } => { self.handle_node_load(target); } StmtKind::If { test, .. } => { if self.settings.enabled.contains(&CheckCode::F634) { pyflakes::plugins::if_tuple(self, stmt, test); } } StmtKind::Assert { test, msg } => { if self.settings.enabled.contains(&CheckCode::F631) { pyflakes::plugins::assert_tuple(self, stmt, test); } if self.settings.enabled.contains(&CheckCode::B011) { flake8_bugbear::plugins::assert_false(self, stmt, test, msg); } } StmtKind::With { items, .. } | StmtKind::AsyncWith { items, .. } => { if self.settings.enabled.contains(&CheckCode::B017) { flake8_bugbear::plugins::assert_raises_exception(self, stmt, items); } } StmtKind::Try { handlers, .. } => { if self.settings.enabled.contains(&CheckCode::F707) { if let Some(check) = pyflakes::checks::default_except_not_last(handlers) { self.checks.push(check); } } if self.settings.enabled.contains(&CheckCode::B014) || self.settings.enabled.contains(&CheckCode::B025) { flake8_bugbear::plugins::duplicate_exceptions(self, stmt, handlers); } } StmtKind::Assign { targets, value, .. } => { if self.settings.enabled.contains(&CheckCode::E731) { if let Some(check) = pycodestyle::checks::do_not_assign_lambda( value, self.locate_check(Range::from_located(stmt)), ) { self.checks.push(check); } } if self.settings.enabled.contains(&CheckCode::U001) { pyupgrade::plugins::useless_metaclass_type(self, stmt, value, targets); } } StmtKind::AnnAssign { value, .. } => { if self.settings.enabled.contains(&CheckCode::E731) { if let Some(value) = value { if let Some(check) = pycodestyle::checks::do_not_assign_lambda( value, self.locate_check(Range::from_located(stmt)), ) { self.checks.push(check); } } } } StmtKind::Delete { .. } => {} _ => {} } // Recurse. let prev_visible_scope = self.visible_scope.clone(); match &stmt.node { StmtKind::FunctionDef { body, .. } | StmtKind::AsyncFunctionDef { body, .. } => { let definition = docstrings::extraction::extract( &self.visible_scope, stmt, body, &Documentable::Function, ); let scope = transition_scope(&self.visible_scope, stmt, &Documentable::Function); self.docstrings.push((definition, scope.visibility.clone())); self.visible_scope = scope; self.deferred_functions.push(( stmt, self.scope_stack.clone(), self.parent_stack.clone(), self.visible_scope.clone(), )); } StmtKind::ClassDef { body, .. } => { let definition = docstrings::extraction::extract( &self.visible_scope, stmt, body, &Documentable::Class, ); let scope = transition_scope(&self.visible_scope, stmt, &Documentable::Class); self.docstrings.push((definition, scope.visibility.clone())); self.visible_scope = scope; for stmt in body { self.visit_stmt(stmt); } } StmtKind::Try { body, handlers, orelse, finalbody, } => { self.except_handlers.push(extract_handler_names(handlers)); for stmt in body { self.visit_stmt(stmt); } self.except_handlers.pop(); for excepthandler in handlers { self.visit_excepthandler(excepthandler) } for stmt in orelse { self.visit_stmt(stmt); } for stmt in finalbody { self.visit_stmt(stmt); } } _ => visitor::walk_stmt(self, stmt), }; self.visible_scope = prev_visible_scope; // Post-visit. if let StmtKind::ClassDef { name, .. } = &stmt.node { self.pop_scope(); self.add_binding( name.to_string(), Binding { kind: BindingKind::ClassDefinition, used: None, range: Range::from_located(stmt), }, ); }; self.pop_parent(); } fn visit_annotation(&mut self, expr: &'b Expr) { let prev_in_annotation = self.in_annotation; self.in_annotation = true; self.visit_expr(expr); self.in_annotation = prev_in_annotation; } fn visit_expr(&mut self, expr: &'b Expr) { let prev_in_f_string = self.in_f_string; let prev_in_literal = self.in_literal; let prev_in_annotation = self.in_annotation; if self.in_annotation && self.annotations_future_enabled { if let ExprKind::Constant { value: Constant::Str(value), .. } = &expr.node { self.deferred_string_annotations .push((Range::from_located(expr), value)); } else { self.deferred_annotations.push(( expr, self.scope_stack.clone(), self.parent_stack.clone(), )); } return; } // Pre-visit. match &expr.node { ExprKind::Subscript { value, slice, .. } => { // Ex) typing.List[...] if self.settings.enabled.contains(&CheckCode::U007) && self.settings.target_version >= PythonVersion::Py39 { pyupgrade::plugins::use_pep604_annotation(self, expr, value, slice); } if match_name_or_attr(value, "Literal") { self.in_literal = true; } } ExprKind::Tuple { elts, ctx } | ExprKind::List { elts, ctx } => { if matches!(ctx, ExprContext::Store) { let check_too_many_expressions = self.settings.enabled.contains(&CheckCode::F621); let check_two_starred_expressions = self.settings.enabled.contains(&CheckCode::F622); if let Some(check) = pyflakes::checks::starred_expressions( elts, check_too_many_expressions, check_two_starred_expressions, self.locate_check(Range::from_located(expr)), ) { self.checks.push(check); } } } ExprKind::Name { id, ctx } => match ctx { ExprContext::Load => { // Ex) List[...] if self.settings.enabled.contains(&CheckCode::U006) && self.settings.target_version >= PythonVersion::Py39 { pyupgrade::plugins::use_pep585_annotation(self, expr, id); } self.handle_node_load(expr); } ExprContext::Store => { if self.settings.enabled.contains(&CheckCode::E741) { if let Some(check) = pycodestyle::checks::ambiguous_variable_name( id, self.locate_check(Range::from_located(expr)), ) { self.checks.push(check); } } self.check_builtin_shadowing(id, Range::from_located(expr), true); self.handle_node_store(expr, self.current_parent()); } ExprContext::Del => self.handle_node_delete(expr), }, ExprKind::Attribute { value, attr, .. } => { // Ex) typing.List[...] if self.settings.enabled.contains(&CheckCode::U006) && self.settings.target_version >= PythonVersion::Py39 { if let ExprKind::Name { id, .. } = &value.node { if id == "typing" { pyupgrade::plugins::use_pep585_annotation(self, expr, attr); } } } } ExprKind::Call { func, args, keywords, .. } => { if self.settings.enabled.contains(&CheckCode::U005) { pyupgrade::plugins::deprecated_unittest_alias(self, func); } // flake8-super if self.settings.enabled.contains(&CheckCode::U008) { pyupgrade::plugins::super_call_with_parameters(self, expr, func, args); } // flake8-print if self.settings.enabled.contains(&CheckCode::T201) || self.settings.enabled.contains(&CheckCode::T203) { flake8_print::plugins::print_call(self, expr, func); } // flake8-comprehensions if self.settings.enabled.contains(&CheckCode::C400) { if let Some(check) = flake8_comprehensions::checks::unnecessary_generator_list(expr, func, args) { self.checks.push(check); }; } if self.settings.enabled.contains(&CheckCode::C401) { if let Some(check) = flake8_comprehensions::checks::unnecessary_generator_set(expr, func, args) { self.checks.push(check); }; } if self.settings.enabled.contains(&CheckCode::C402) { if let Some(check) = flake8_comprehensions::checks::unnecessary_generator_dict(expr, func, args) { self.checks.push(check); }; } if self.settings.enabled.contains(&CheckCode::C403) { if let Some(check) = flake8_comprehensions::checks::unnecessary_list_comprehension_set( expr, func, args, ) { self.checks.push(check); }; } if self.settings.enabled.contains(&CheckCode::C404) { if let Some(check) = flake8_comprehensions::checks::unnecessary_list_comprehension_dict( expr, func, args, ) { self.checks.push(check); }; } if self.settings.enabled.contains(&CheckCode::C405) { if let Some(check) = flake8_comprehensions::checks::unnecessary_literal_set(expr, func, args) { self.checks.push(check); }; } if self.settings.enabled.contains(&CheckCode::C406) { if let Some(check) = flake8_comprehensions::checks::unnecessary_literal_dict(expr, func, args) { self.checks.push(check); }; } if self.settings.enabled.contains(&CheckCode::C408) { if let Some(check) = flake8_comprehensions::checks::unnecessary_collection_call( expr, func, args, keywords, ) { self.checks.push(check); }; } if self.settings.enabled.contains(&CheckCode::C409) { if let Some(check) = flake8_comprehensions::checks::unnecessary_literal_within_tuple_call( expr, func, args, ) { self.checks.push(check); }; } if self.settings.enabled.contains(&CheckCode::C410) { if let Some(check) = flake8_comprehensions::checks::unnecessary_literal_within_list_call( expr, func, args, ) { self.checks.push(check); }; } if self.settings.enabled.contains(&CheckCode::C411) { if let Some(check) = flake8_comprehensions::checks::unnecessary_list_call(expr, func, args) { self.checks.push(check); }; } if self.settings.enabled.contains(&CheckCode::C413) { if let Some(check) = flake8_comprehensions::checks::unnecessary_call_around_sorted( expr, func, args, ) { self.checks.push(check); }; } if self.settings.enabled.contains(&CheckCode::C414) { if let Some(check) = flake8_comprehensions::checks::unnecessary_double_cast_or_process( expr, func, args, ) { self.checks.push(check); }; } if self.settings.enabled.contains(&CheckCode::C415) { if let Some(check) = flake8_comprehensions::checks::unnecessary_subscript_reversal( expr, func, args, ) { self.checks.push(check); }; } if self.settings.enabled.contains(&CheckCode::C417) { if let Some(check) = flake8_comprehensions::checks::unnecessary_map(expr, func, args) { self.checks.push(check); }; } // pyupgrade if self.settings.enabled.contains(&CheckCode::U002) && self.settings.target_version >= PythonVersion::Py310 { pyupgrade::plugins::unnecessary_abspath(self, expr, func, args); } if self.settings.enabled.contains(&CheckCode::U003) { pyupgrade::plugins::type_of_primitive(self, expr, func, args); } if let ExprKind::Name { id, ctx } = &func.node { if id == "locals" && matches!(ctx, ExprContext::Load) { let scope = &mut self.scopes[*(self .scope_stack .last_mut() .expect("No current scope found."))]; if matches!( scope.kind, ScopeKind::Function(FunctionScope { uses_locals: false }) ) { scope.kind = ScopeKind::Function(FunctionScope { uses_locals: true }); } } } } ExprKind::Dict { keys, .. } => { let check_repeated_literals = self.settings.enabled.contains(&CheckCode::F601); let check_repeated_variables = self.settings.enabled.contains(&CheckCode::F602); if check_repeated_literals || check_repeated_variables { self.checks.extend(pyflakes::checks::repeated_keys( keys, check_repeated_literals, check_repeated_variables, self, )); } } ExprKind::Yield { .. } | ExprKind::YieldFrom { .. } | ExprKind::Await { .. } => { let scope = self.current_scope(); if self.settings.enabled.contains(&CheckCode::F704) { if matches!(scope.kind, ScopeKind::Class | ScopeKind::Module) { self.checks.push(Check::new( CheckKind::YieldOutsideFunction, self.locate_check(Range::from_located(expr)), )); } } } ExprKind::JoinedStr { values } => { if self.settings.enabled.contains(&CheckCode::F541) { if self.in_f_string.is_none() && !values .iter() .any(|value| matches!(value.node, ExprKind::FormattedValue { .. })) { self.checks.push(Check::new( CheckKind::FStringMissingPlaceholders, self.locate_check(Range::from_located(expr)), )); } } self.in_f_string = Some(Range::from_located(expr)); } ExprKind::BinOp { left, op: Operator::RShift, .. } => { if self.settings.enabled.contains(&CheckCode::F633) { pyflakes::plugins::invalid_print_syntax(self, left); } } ExprKind::UnaryOp { op, operand } => { let check_not_in = self.settings.enabled.contains(&CheckCode::E713); let check_not_is = self.settings.enabled.contains(&CheckCode::E714); if check_not_in || check_not_is { self.checks.extend(pycodestyle::checks::not_tests( op, operand, check_not_in, check_not_is, self, )); } if self.settings.enabled.contains(&CheckCode::B002) { flake8_bugbear::plugins::unary_prefix_increment(self, expr, op, operand); } } ExprKind::Compare { left, ops, comparators, } => { let check_none_comparisons = self.settings.enabled.contains(&CheckCode::E711); let check_true_false_comparisons = self.settings.enabled.contains(&CheckCode::E712); if check_none_comparisons || check_true_false_comparisons { self.checks.extend(pycodestyle::checks::literal_comparisons( left, ops, comparators, check_none_comparisons, check_true_false_comparisons, self, )); } if self.settings.enabled.contains(&CheckCode::F632) { self.checks.extend(pyflakes::checks::is_literal( left, ops, comparators, self.locate_check(Range::from_located(expr)), )); } if self.settings.enabled.contains(&CheckCode::E721) { self.checks.extend(pycodestyle::checks::type_comparison( ops, comparators, self.locate_check(Range::from_located(expr)), )); } } ExprKind::Constant { value: Constant::Str(value), .. } => { if self.in_annotation && !self.in_literal { self.deferred_string_annotations .push((Range::from_located(expr), value)); } } ExprKind::Lambda { args, .. } => { // Visit the arguments, but avoid the body, which will be deferred. for arg in &args.posonlyargs { if let Some(expr) = &arg.node.annotation { self.visit_annotation(expr); } } for arg in &args.args { if let Some(expr) = &arg.node.annotation { self.visit_annotation(expr); } } if let Some(arg) = &args.vararg { if let Some(expr) = &arg.node.annotation { self.visit_annotation(expr); } } for arg in &args.kwonlyargs { if let Some(expr) = &arg.node.annotation { self.visit_annotation(expr); } } if let Some(arg) = &args.kwarg { if let Some(expr) = &arg.node.annotation { self.visit_annotation(expr); } } for expr in &args.kw_defaults { self.visit_expr(expr); } for expr in &args.defaults { self.visit_expr(expr); } } ExprKind::ListComp { elt, generators } | ExprKind::SetComp { elt, generators } => { if self.settings.enabled.contains(&CheckCode::C416) { if let Some(check) = flake8_comprehensions::checks::unnecessary_comprehension( expr, elt, generators, ) { self.checks.push(check); }; } self.push_scope(Scope::new(ScopeKind::Generator)) } ExprKind::GeneratorExp { .. } | ExprKind::DictComp { .. } => { self.push_scope(Scope::new(ScopeKind::Generator)) } _ => {} }; // Recurse. match &expr.node { ExprKind::Lambda { .. } => { self.deferred_lambdas.push(( expr, self.scope_stack.clone(), self.parent_stack.clone(), )); } ExprKind::Call { func, args, keywords, } => { if match_name_or_attr(func, "ForwardRef") { self.visit_expr(func); for expr in args { self.visit_annotation(expr); } } else if match_name_or_attr(func, "cast") { self.visit_expr(func); if !args.is_empty() { self.visit_annotation(&args[0]); } for expr in args.iter().skip(1) { self.visit_expr(expr); } } else if match_name_or_attr(func, "NewType") { self.visit_expr(func); for expr in args.iter().skip(1) { self.visit_annotation(expr); } } else if match_name_or_attr(func, "TypeVar") { self.visit_expr(func); for expr in args.iter().skip(1) { self.visit_annotation(expr); } for keyword in keywords { let KeywordData { arg, value } = &keyword.node; if let Some(id) = arg { if id == "bound" { self.visit_annotation(value); } else { self.in_annotation = false; self.visit_expr(value); self.in_annotation = prev_in_annotation; } } } } else if match_name_or_attr(func, "NamedTuple") { self.visit_expr(func); // Ex) NamedTuple("a", [("a", int)]) if args.len() > 1 { match &args[1].node { ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => { for elt in elts { match &elt.node { ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => { if elts.len() == 2 { self.in_annotation = false; self.visit_expr(&elts[0]); self.in_annotation = prev_in_annotation; self.visit_annotation(&elts[1]); } } _ => {} } } } _ => {} } } // Ex) NamedTuple("a", a=int) for keyword in keywords { let KeywordData { value, .. } = &keyword.node; self.visit_annotation(value); } } else if match_name_or_attr(func, "TypedDict") { self.visit_expr(func); // Ex) TypedDict("a", {"a": int}) if args.len() > 1 { if let ExprKind::Dict { keys, values } = &args[1].node { for key in keys { self.in_annotation = false; self.visit_expr(key); self.in_annotation = prev_in_annotation; } for value in values { self.visit_annotation(value); } } } // Ex) TypedDict("a", a=int) for keyword in keywords { let KeywordData { value, .. } = &keyword.node; self.visit_annotation(value); } } else { visitor::walk_expr(self, expr); } } ExprKind::Subscript { value, slice, ctx } => { match helpers::match_annotated_subscript(value) { Some(subscript) => match subscript { // Ex) Optional[int] SubscriptKind::AnnotatedSubscript => { self.visit_expr(value); self.visit_annotation(slice); self.visit_expr_context(ctx); } // Ex) Annotated[int, "Hello, world!"] SubscriptKind::PEP593AnnotatedSubscript => { // First argument is a type (including forward references); the rest are // arbitrary Python objects. self.visit_expr(value); if let ExprKind::Tuple { elts, ctx } = &slice.node { if let Some(expr) = elts.first() { self.visit_expr(expr); self.in_annotation = false; for expr in elts.iter().skip(1) { self.visit_expr(expr); } self.in_annotation = true; self.visit_expr_context(ctx); } } else { error!("Found non-ExprKind::Tuple argument to PEP 593 Annotation.") } } }, None => visitor::walk_expr(self, expr), } } _ => visitor::walk_expr(self, expr), } // Post-visit. match &expr.node { ExprKind::GeneratorExp { .. } | ExprKind::ListComp { .. } | ExprKind::DictComp { .. } | ExprKind::SetComp { .. } => { self.pop_scope(); } _ => {} }; self.in_annotation = prev_in_annotation; self.in_literal = prev_in_literal; self.in_f_string = prev_in_f_string; } fn visit_excepthandler(&mut self, excepthandler: &'b Excepthandler) { match &excepthandler.node { ExcepthandlerKind::ExceptHandler { type_, name, .. } => { if self.settings.enabled.contains(&CheckCode::E722) && type_.is_none() { self.checks.push(Check::new( CheckKind::DoNotUseBareExcept, Range::from_located(excepthandler), )); } match name { Some(name) => { if self.settings.enabled.contains(&CheckCode::E741) { if let Some(check) = pycodestyle::checks::ambiguous_variable_name( name, self.locate_check(Range::from_located(excepthandler)), ) { self.checks.push(check); } } self.check_builtin_shadowing( name, Range::from_located(excepthandler), false, ); if self.current_scope().values.contains_key(name) { self.handle_node_store( &Expr::new( excepthandler.location, excepthandler.end_location.unwrap(), ExprKind::Name { id: name.to_string(), ctx: ExprContext::Store, }, ), self.current_parent(), ); } let definition = self.current_scope().values.get(name).cloned(); self.handle_node_store( &Expr::new( excepthandler.location, excepthandler.end_location.unwrap(), ExprKind::Name { id: name.to_string(), ctx: ExprContext::Store, }, ), self.current_parent(), ); walk_excepthandler(self, excepthandler); let scope = &mut self.scopes [*(self.scope_stack.last().expect("No current scope found."))]; if let Some(binding) = &scope.values.remove(name) { if binding.used.is_none() { if self.settings.enabled.contains(&CheckCode::F841) { self.checks.push(Check::new( CheckKind::UnusedVariable(name.to_string()), Range::from_located(excepthandler), )); } } } if let Some(binding) = definition { scope.values.insert(name.to_string(), binding); } } None => walk_excepthandler(self, excepthandler), } } } } fn visit_arguments(&mut self, arguments: &'b Arguments) { if self.settings.enabled.contains(&CheckCode::F831) { self.checks .extend(pyflakes::checks::duplicate_arguments(arguments)); } // Bind, but intentionally avoid walking default expressions, as we handle them upstream. for arg in &arguments.posonlyargs { self.visit_arg(arg); } for arg in &arguments.args { self.visit_arg(arg); } if let Some(arg) = &arguments.vararg { self.visit_arg(arg); } for arg in &arguments.kwonlyargs { self.visit_arg(arg); } if let Some(arg) = &arguments.kwarg { self.visit_arg(arg); } } fn visit_arg(&mut self, arg: &'b Arg) { // Bind, but intentionally avoid walking the annotation, as we handle it upstream. self.add_binding( arg.node.arg.to_string(), Binding { kind: BindingKind::Argument, used: None, range: Range::from_located(arg), }, ); if self.settings.enabled.contains(&CheckCode::E741) { if let Some(check) = pycodestyle::checks::ambiguous_variable_name( &arg.node.arg, self.locate_check(Range::from_located(arg)), ) { self.checks.push(check); } } if self.settings.enabled.contains(&CheckCode::N803) { if let Some(check) = pep8_naming::checks::invalid_argument_name(Range::from_located(arg), &arg.node.arg) { self.checks.push(check); } } self.check_builtin_arg_shadowing(&arg.node.arg, Range::from_located(arg)); } } impl CheckLocator for Checker<'_> { fn locate_check(&self, default: Range) -> Range { self.in_f_string.unwrap_or(default) } } fn try_mark_used(scope: &mut Scope, scope_id: usize, id: &str, expr: &Expr) -> bool { let alias = if let Some(binding) = scope.values.get_mut(id) { // Mark the binding as used. binding.used = Some((scope_id, Range::from_located(expr))); // If the name of the sub-importation is the same as an alias of another importation and the // alias is used, that sub-importation should be marked as used too. // // This handles code like: // import pyarrow as pa // import pyarrow.csv // print(pa.csv.read_csv("test.csv")) if let BindingKind::Importation(name, full_name, _) | BindingKind::FromImportation(name, full_name, _) | BindingKind::SubmoduleImportation(name, full_name, _) = &binding.kind { let has_alias = full_name .split('.') .last() .map(|segment| segment != name) .unwrap_or_default(); if has_alias { // Clone the alias. (We'll mutate it below.) full_name.to_string() } else { return true; } } else { return true; } } else { return false; }; // Mark the sub-importation as used. if let Some(binding) = scope.values.get_mut(&alias) { binding.used = Some((scope_id, Range::from_located(expr))); } true } impl<'a> Checker<'a> { pub fn add_check(&mut self, check: Check) { self.checks.push(check); } fn push_parent(&mut self, parent: &'a Stmt) { self.parent_stack.push(self.parents.len()); self.parents.push(parent); } fn pop_parent(&mut self) { self.parent_stack .pop() .expect("Attempted to pop without scope."); } fn push_scope(&mut self, scope: Scope) { self.scope_stack.push(self.scopes.len()); self.scopes.push(scope); } fn pop_scope(&mut self) { self.dead_scopes.push( self.scope_stack .pop() .expect("Attempted to pop without scope."), ); } fn bind_builtins(&mut self) { let scope = &mut self.scopes[*(self.scope_stack.last().expect("No current scope found."))]; for builtin in BUILTINS { scope.values.insert( (*builtin).to_string(), Binding { kind: BindingKind::Builtin, range: Default::default(), used: None, }, ); } for builtin in MAGIC_GLOBALS { scope.values.insert( (*builtin).to_string(), Binding { kind: BindingKind::Builtin, range: Default::default(), used: None, }, ); } } pub fn current_scope(&self) -> &Scope { &self.scopes[*(self.scope_stack.last().expect("No current scope found."))] } pub fn current_parent(&self) -> &'a Stmt { self.parents[*(self.parent_stack.last().expect("No parent found."))] } pub fn binding_context(&self) -> BindingContext { let mut rev = self.parent_stack.iter().rev().fuse(); let defined_by = *rev.next().expect("Expected to bind within a statement."); let defined_in = rev.next().cloned(); BindingContext { defined_by, defined_in, } } fn add_binding(&mut self, name: String, binding: Binding) { let scope = &mut self.scopes[*(self.scope_stack.last().expect("No current scope found."))]; // TODO(charlie): Don't treat annotations as assignments if there is an existing value. let binding = match scope.values.get(&name) { None => binding, Some(existing) => { if self.settings.enabled.contains(&CheckCode::F402) { if matches!(binding.kind, BindingKind::LoopVar) && matches!( existing.kind, BindingKind::Importation(_, _, _) | BindingKind::FromImportation(_, _, _) | BindingKind::SubmoduleImportation(_, _, _) | BindingKind::StarImportation | BindingKind::FutureImportation ) { self.checks.push(Check::new( CheckKind::ImportShadowedByLoopVar( name.clone(), existing.range.location.row(), ), binding.range, )); } } Binding { kind: binding.kind, range: binding.range, used: existing.used, } } }; scope.values.insert(name, binding); } fn handle_node_load(&mut self, expr: &Expr) { if let ExprKind::Name { id, .. } = &expr.node { let scope_id = self.scopes[*(self.scope_stack.last().expect("No current scope found."))].id; let mut first_iter = true; let mut in_generator = false; let mut import_starred = false; for scope_index in self.scope_stack.iter().rev() { let scope = &mut self.scopes[*scope_index]; if matches!(scope.kind, ScopeKind::Class) { if id == "__class__" { return; } else if !first_iter && !in_generator { continue; } } if try_mark_used(scope, scope_id, id, expr) { return; } first_iter = false; in_generator = matches!(scope.kind, ScopeKind::Generator); import_starred = import_starred || scope.import_starred; } if import_starred { if self.settings.enabled.contains(&CheckCode::F405) { let mut from_list = vec![]; for scope_index in self.scope_stack.iter().rev() { let scope = &self.scopes[*scope_index]; for (name, binding) in scope.values.iter() { if matches!(binding.kind, BindingKind::StarImportation) { from_list.push(name.to_string()); } } } from_list.sort(); self.checks.push(Check::new( CheckKind::ImportStarUsage(id.clone(), from_list), self.locate_check(Range::from_located(expr)), )); } return; } if self.settings.enabled.contains(&CheckCode::F821) { // Allow __path__. if self.path.ends_with("__init__.py") && id == "__path__" { return; } // Avoid flagging if NameError is handled. if let Some(handler_names) = self.except_handlers.last() { if handler_names.contains(&"NameError".to_string()) { return; } } self.checks.push(Check::new( CheckKind::UndefinedName(id.clone()), self.locate_check(Range::from_located(expr)), )) } } } fn handle_node_store(&mut self, expr: &Expr, parent: &Stmt) { if let ExprKind::Name { id, .. } = &expr.node { let current = &self.scopes[*(self.scope_stack.last().expect("No current scope found."))]; if self.settings.enabled.contains(&CheckCode::F823) { if matches!(current.kind, ScopeKind::Function(_)) && !current.values.contains_key(id) { for scope in self.scopes.iter().rev().skip(1) { if matches!(scope.kind, ScopeKind::Function(_) | ScopeKind::Module) { if let Some(binding) = scope.values.get(id) { if let Some((scope_id, location)) = binding.used { if scope_id == current.id { self.checks.push(Check::new( CheckKind::UndefinedLocal(id.clone()), self.locate_check(location), )); } } } } } } } if matches!(parent.node, StmtKind::AnnAssign { value: None, .. }) { self.add_binding( id.to_string(), Binding { kind: BindingKind::Annotation, used: None, range: Range::from_located(expr), }, ); return; } // TODO(charlie): Include comprehensions here. if matches!( parent.node, StmtKind::For { .. } | StmtKind::AsyncFor { .. } ) { self.add_binding( id.to_string(), Binding { kind: BindingKind::LoopVar, used: None, range: Range::from_located(expr), }, ); return; } if operations::is_unpacking_assignment(parent) { self.add_binding( id.to_string(), Binding { kind: BindingKind::Binding, used: None, range: Range::from_located(expr), }, ); return; } if id == "__all__" && matches!(current.kind, ScopeKind::Module) && matches!( parent.node, StmtKind::Assign { .. } | StmtKind::AugAssign { .. } | StmtKind::AnnAssign { .. } ) { self.add_binding( id.to_string(), Binding { kind: BindingKind::Export(extract_all_names(parent, current)), used: None, range: Range::from_located(expr), }, ); return; } self.add_binding( id.to_string(), Binding { kind: BindingKind::Assignment, used: None, range: Range::from_located(expr), }, ); } } fn handle_node_delete(&mut self, expr: &Expr) { if let ExprKind::Name { id, .. } = &expr.node { if operations::on_conditional_branch(&self.parent_stack, &self.parents) { return; } let scope = &mut self.scopes[*(self.scope_stack.last().expect("No current scope found."))]; if scope.values.remove(id).is_none() && self.settings.enabled.contains(&CheckCode::F821) { self.checks.push(Check::new( CheckKind::UndefinedName(id.clone()), self.locate_check(Range::from_located(expr)), )) } } } fn visit_docstring<'b>(&mut self, python_ast: &'b Suite) -> bool where 'b: 'a, { let docstring = docstrings::extraction::docstring_from(python_ast); self.docstrings.push(( Definition { kind: if self.path.ends_with("__init__.py") { DefinitionKind::Package } else { DefinitionKind::Module }, docstring, }, self.visible_scope.visibility.clone(), )); docstring.is_some() } fn check_deferred_annotations(&mut self) { while let Some((expr, scopes, parents)) = self.deferred_annotations.pop() { self.parent_stack = parents; self.scope_stack = scopes; self.visit_expr(expr); } } fn check_deferred_string_annotations<'b>(&mut self, allocator: &'b mut Vec) where 'b: 'a, { while let Some((range, expression)) = self.deferred_string_annotations.pop() { if let Ok(mut expr) = parser::parse_expression(expression, "") { relocate_expr(&mut expr, range); allocator.push(expr); } else { if self.settings.enabled.contains(&CheckCode::F722) { self.checks.push(Check::new( CheckKind::ForwardAnnotationSyntaxError(expression.to_string()), self.locate_check(range), )); } } } for expr in allocator { self.visit_expr(expr); } } fn check_deferred_functions(&mut self) { while let Some((stmt, scopes, parents, visibility)) = self.deferred_functions.pop() { self.parent_stack = parents; self.scope_stack = scopes; self.visible_scope = visibility; self.push_scope(Scope::new(ScopeKind::Function(Default::default()))); match &stmt.node { StmtKind::FunctionDef { body, args, .. } | StmtKind::AsyncFunctionDef { body, args, .. } => { self.visit_arguments(args); for stmt in body { self.visit_stmt(stmt); } } _ => {} } self.deferred_assignments .push(*self.scope_stack.last().expect("No current scope found.")); self.pop_scope(); } } fn check_deferred_lambdas(&mut self) { while let Some((expr, scopes, parents)) = self.deferred_lambdas.pop() { self.parent_stack = parents; self.scope_stack = scopes; self.push_scope(Scope::new(ScopeKind::Function(Default::default()))); if let ExprKind::Lambda { args, body } = &expr.node { self.visit_arguments(args); self.visit_expr(body); } self.deferred_assignments .push(*self.scope_stack.last().expect("No current scope found.")); self.pop_scope(); } } fn check_deferred_assignments(&mut self) { if self.settings.enabled.contains(&CheckCode::F841) { while let Some(index) = self.deferred_assignments.pop() { self.checks.extend(pyflakes::checks::unused_variables( &self.scopes[index], self, &self.settings.dummy_variable_rgx, )); } } } fn check_dead_scopes(&mut self) { if !self.settings.enabled.contains(&CheckCode::F401) && !self.settings.enabled.contains(&CheckCode::F405) && !self.settings.enabled.contains(&CheckCode::F822) { return; } for index in self.dead_scopes.iter().copied() { let scope = &self.scopes[index]; let all_binding = scope.values.get("__all__"); let all_names = all_binding.and_then(|binding| match &binding.kind { BindingKind::Export(names) => Some(names), _ => None, }); if self.settings.enabled.contains(&CheckCode::F822) { if !scope.import_starred && !self.path.ends_with("__init__.py") { if let Some(all_binding) = all_binding { if let Some(names) = all_names { for name in names { if !scope.values.contains_key(name) { self.checks.push(Check::new( CheckKind::UndefinedExport(name.to_string()), self.locate_check(all_binding.range), )); } } } } } } if self.settings.enabled.contains(&CheckCode::F405) && scope.import_starred { if let Some(all_binding) = all_binding { if let Some(names) = all_names { let mut from_list = vec![]; for (name, binding) in scope.values.iter() { if matches!(binding.kind, BindingKind::StarImportation) { from_list.push(name.to_string()); } } from_list.sort(); for name in names { if !scope.values.contains_key(name) { self.checks.push(Check::new( CheckKind::ImportStarUsage(name.clone(), from_list.clone()), self.locate_check(all_binding.range), )); } } } } } if self.settings.enabled.contains(&CheckCode::F401) { // Collect all unused imports by location. (Multiple unused imports at the same // location indicates an `import from`.) let mut unused: BTreeMap<(ImportKind, usize, Option), Vec<&str>> = BTreeMap::new(); for (name, binding) in scope.values.iter().rev() { let used = binding.used.is_some() || all_names .map(|names| names.contains(name)) .unwrap_or_default(); if !used { match &binding.kind { BindingKind::FromImportation(_, full_name, context) => { let full_names = unused .entry(( ImportKind::ImportFrom, context.defined_by, context.defined_in, )) .or_default(); full_names.push(full_name); } BindingKind::Importation(_, full_name, context) | BindingKind::SubmoduleImportation(_, full_name, context) => { let full_names = unused .entry(( ImportKind::Import, context.defined_by, context.defined_in, )) .or_default(); full_names.push(full_name); } _ => {} } } } for ((kind, defined_by, defined_in), full_names) in unused { let child = self.parents[defined_by]; let parent = defined_in.map(|defined_in| self.parents[defined_in]); let fix = if self.patch() { let deleted: Vec<&Stmt> = self .deletions .iter() .map(|index| self.parents[*index]) .collect(); let removal_fn = match kind { ImportKind::Import => pyflakes::fixes::remove_unused_imports, ImportKind::ImportFrom => pyflakes::fixes::remove_unused_import_froms, }; match removal_fn(self.get_locator(), &full_names, child, parent, &deleted) { Ok(fix) => Some(fix), Err(e) => { error!("Failed to fix unused imports: {}", e); None } } } else { None }; let mut check = Check::new( CheckKind::UnusedImport(full_names.into_iter().map(String::from).collect()), self.locate_check(Range::from_located(child)), ); if let Some(fix) = fix { check.amend(fix); } self.checks.push(check); } } } } fn check_docstrings(&mut self) { while let Some((docstring, visibility)) = self.docstrings.pop() { if !pydocstyle::plugins::not_empty(self, &docstring) { continue; } if !pydocstyle::plugins::not_missing(self, &docstring, &visibility) { continue; } if self.settings.enabled.contains(&CheckCode::D200) { pydocstyle::plugins::one_liner(self, &docstring); } if self.settings.enabled.contains(&CheckCode::D201) || self.settings.enabled.contains(&CheckCode::D202) { pydocstyle::plugins::blank_before_after_function(self, &docstring); } if self.settings.enabled.contains(&CheckCode::D203) || self.settings.enabled.contains(&CheckCode::D204) || self.settings.enabled.contains(&CheckCode::D211) { pydocstyle::plugins::blank_before_after_class(self, &docstring); } if self.settings.enabled.contains(&CheckCode::D205) { pydocstyle::plugins::blank_after_summary(self, &docstring); } if self.settings.enabled.contains(&CheckCode::D206) || self.settings.enabled.contains(&CheckCode::D207) || self.settings.enabled.contains(&CheckCode::D208) { pydocstyle::plugins::indent(self, &docstring); } if self.settings.enabled.contains(&CheckCode::D209) { pydocstyle::plugins::newline_after_last_paragraph(self, &docstring); } if self.settings.enabled.contains(&CheckCode::D210) { pydocstyle::plugins::no_surrounding_whitespace(self, &docstring); } if self.settings.enabled.contains(&CheckCode::D212) || self.settings.enabled.contains(&CheckCode::D213) { pydocstyle::plugins::multi_line_summary_start(self, &docstring); } if self.settings.enabled.contains(&CheckCode::D300) { pydocstyle::plugins::triple_quotes(self, &docstring); } if self.settings.enabled.contains(&CheckCode::D400) { pydocstyle::plugins::ends_with_period(self, &docstring); } if self.settings.enabled.contains(&CheckCode::D402) { pydocstyle::plugins::no_signature(self, &docstring); } if self.settings.enabled.contains(&CheckCode::D403) { pydocstyle::plugins::capitalized(self, &docstring); } if self.settings.enabled.contains(&CheckCode::D404) { pydocstyle::plugins::starts_with_this(self, &docstring); } if self.settings.enabled.contains(&CheckCode::D415) { pydocstyle::plugins::ends_with_punctuation(self, &docstring); } if self.settings.enabled.contains(&CheckCode::D418) { pydocstyle::plugins::if_needed(self, &docstring); } if self.settings.enabled.contains(&CheckCode::D212) || self.settings.enabled.contains(&CheckCode::D214) || self.settings.enabled.contains(&CheckCode::D215) || self.settings.enabled.contains(&CheckCode::D405) || self.settings.enabled.contains(&CheckCode::D406) || self.settings.enabled.contains(&CheckCode::D407) || self.settings.enabled.contains(&CheckCode::D408) || self.settings.enabled.contains(&CheckCode::D409) || self.settings.enabled.contains(&CheckCode::D410) || self.settings.enabled.contains(&CheckCode::D411) || self.settings.enabled.contains(&CheckCode::D412) || self.settings.enabled.contains(&CheckCode::D413) || self.settings.enabled.contains(&CheckCode::D414) || self.settings.enabled.contains(&CheckCode::D416) || self.settings.enabled.contains(&CheckCode::D417) { pydocstyle::plugins::sections(self, &docstring); } } } fn check_builtin_shadowing(&mut self, name: &str, location: Range, is_attribute: bool) { if is_attribute && matches!(self.current_scope().kind, ScopeKind::Class) { if self.settings.enabled.contains(&CheckCode::A003) { if let Some(check) = flake8_builtins::checks::builtin_shadowing( name, self.locate_check(location), flake8_builtins::types::ShadowingType::Attribute, ) { self.checks.push(check); } } } else { if self.settings.enabled.contains(&CheckCode::A001) { if let Some(check) = flake8_builtins::checks::builtin_shadowing( name, self.locate_check(location), flake8_builtins::types::ShadowingType::Variable, ) { self.checks.push(check); } } } } fn check_builtin_arg_shadowing(&mut self, name: &str, location: Range) { if self.settings.enabled.contains(&CheckCode::A002) { if let Some(check) = flake8_builtins::checks::builtin_shadowing( name, self.locate_check(location), flake8_builtins::types::ShadowingType::Argument, ) { self.checks.push(check); } } } } pub fn check_ast( python_ast: &Suite, contents: &str, settings: &Settings, autofix: &fixer::Mode, path: &Path, ) -> Vec { let mut checker = Checker::new(settings, autofix, path, contents); checker.push_scope(Scope::new(ScopeKind::Module)); checker.bind_builtins(); // Check for module docstring. let python_ast = if checker.visit_docstring(python_ast) { &python_ast[1..] } else { python_ast }; // Iterate over the AST. for stmt in python_ast { checker.visit_stmt(stmt); } // Check any deferred statements. checker.check_deferred_functions(); checker.check_deferred_lambdas(); checker.check_deferred_assignments(); checker.check_deferred_annotations(); let mut allocator = vec![]; checker.check_deferred_string_annotations(&mut allocator); // Reset the scope to module-level, and check all consumed scopes. checker.scope_stack = vec![GLOBAL_SCOPE_INDEX]; checker.pop_scope(); checker.check_dead_scopes(); // Check docstrings. checker.check_docstrings(); checker.checks }