diff --git a/resources/test/fixtures/F821.py b/resources/test/fixtures/F821.py index f356ad66a8..7ac7795af7 100644 --- a/resources/test/fixtures/F821.py +++ b/resources/test/fixtures/F821.py @@ -54,3 +54,5 @@ except Exception as e: y: int = 1 + +x: "Bar" = 1 diff --git a/src/cache.rs b/src/cache.rs index eaf1ade7ee..c686f129cf 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -3,12 +3,12 @@ use std::fs::Metadata; use std::hash::{Hash, Hasher}; use std::path::Path; -use crate::autofix; use cacache::Error::EntryNotFound; use filetime::FileTime; use log::error; use serde::{Deserialize, Serialize}; +use crate::autofix; use crate::message::Message; use crate::settings::Settings; diff --git a/src/check_ast.rs b/src/check_ast.rs index 5d9464af8e..98669ce4f7 100644 --- a/src/check_ast.rs +++ b/src/check_ast.rs @@ -13,6 +13,7 @@ use crate::ast_ops::{ }; use crate::builtins::{BUILTINS, MAGIC_GLOBALS}; use crate::checks::{Check, CheckCode, CheckKind, Fix, RejectedCmpop}; +use crate::relocator::relocate_expr; use crate::settings::Settings; use crate::visitor::{walk_excepthandler, Visitor}; use crate::{autofix, fixer, visitor}; @@ -34,7 +35,7 @@ struct Checker<'a> { scopes: Vec, scope_stack: Vec, dead_scopes: Vec, - deferred_annotations: Vec<&'a str>, + deferred_annotations: Vec<(Location, &'a str)>, deferred_functions: Vec<(&'a Stmt, Vec, Vec)>, deferred_lambdas: Vec<(&'a Expr, Vec, Vec)>, // Derivative state. @@ -759,7 +760,7 @@ where value: Constant::Str(value), .. } if self.in_annotation && !self.in_literal => { - self.deferred_annotations.push(value); + self.deferred_annotations.push((expr.location, value)); } _ => {} }; @@ -1074,7 +1075,6 @@ impl<'a> Checker<'a> { }) .unwrap_or_default() { - // Really need parent here. self.add_binding( id.to_string(), Binding { @@ -1115,8 +1115,9 @@ impl<'a> Checker<'a> { 'b: 'a, { while !self.deferred_annotations.is_empty() { - let value = self.deferred_annotations.pop().unwrap(); - if let Ok(expr) = parser::parse_expression(value, path) { + let (location, expression) = self.deferred_annotations.pop().unwrap(); + if let Ok(mut expr) = parser::parse_expression(expression, path) { + relocate_expr(&mut expr, location); allocator.push(expr); } } diff --git a/src/fixer.rs b/src/fixer.rs index 2d925ba9a3..bf0074c915 100644 --- a/src/fixer.rs +++ b/src/fixer.rs @@ -1,8 +1,8 @@ -use crate::ast_ops::SourceCodeLocator; use rustpython_parser::ast::{Expr, Keyword, Location}; use rustpython_parser::lexer; use rustpython_parser::token::Tok; +use crate::ast_ops::SourceCodeLocator; use crate::checks::Fix; /// Convert a location within a file (relative to `base`) to an absolute position. diff --git a/src/lib.rs b/src/lib.rs index 3f072b9f7d..6d9ef229c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,5 +13,6 @@ pub mod linter; pub mod logging; pub mod message; mod pyproject; +mod relocator; pub mod settings; mod visitor; diff --git a/src/linter.rs b/src/linter.rs index eb2070a879..c14f325d77 100644 --- a/src/linter.rs +++ b/src/linter.rs @@ -664,6 +664,11 @@ mod tests { location: Location::new(21, 12), fix: None, }, + Check { + kind: CheckKind::UndefinedName("Bar".to_string()), + location: Location::new(58, 5), + fix: None, + }, ]; assert_eq!(actual.len(), expected.len()); for i in 0..actual.len() { diff --git a/src/relocator.rs b/src/relocator.rs new file mode 100644 index 0000000000..e07594e285 --- /dev/null +++ b/src/relocator.rs @@ -0,0 +1,137 @@ +use rustpython_parser::ast::{Expr, ExprKind, Keyword, Location}; + +fn relocate_keyword(keyword: &mut Keyword, location: Location) { + keyword.location = location; + relocate_expr(&mut keyword.node.value, location); +} + +/// Change an expression's location (recursively) to match a desired, fixed location. +pub fn relocate_expr(expr: &mut Expr, location: Location) { + expr.location = location; + match &mut expr.node { + ExprKind::BoolOp { values, .. } => { + for expr in values { + relocate_expr(expr, location); + } + } + ExprKind::NamedExpr { target, value } => { + relocate_expr(target, location); + relocate_expr(value, location); + } + ExprKind::BinOp { left, right, .. } => { + relocate_expr(left, location); + relocate_expr(right, location); + } + ExprKind::UnaryOp { operand, .. } => { + relocate_expr(operand, location); + } + ExprKind::Lambda { body, .. } => { + relocate_expr(body, location); + } + ExprKind::IfExp { test, body, orelse } => { + relocate_expr(test, location); + relocate_expr(body, location); + relocate_expr(orelse, location); + } + ExprKind::Dict { keys, values } => { + for expr in keys { + relocate_expr(expr, location); + } + for expr in values { + relocate_expr(expr, location); + } + } + ExprKind::Set { elts } => { + for expr in elts { + relocate_expr(expr, location); + } + } + ExprKind::ListComp { elt, .. } => { + relocate_expr(elt, location); + } + ExprKind::SetComp { elt, .. } => { + relocate_expr(elt, location); + } + ExprKind::DictComp { key, value, .. } => { + relocate_expr(key, location); + relocate_expr(value, location); + } + ExprKind::GeneratorExp { elt, .. } => { + relocate_expr(elt, location); + } + ExprKind::Await { value } => relocate_expr(value, location), + ExprKind::Yield { value } => { + if let Some(expr) = value { + relocate_expr(expr, location); + } + } + ExprKind::YieldFrom { value } => relocate_expr(value, location), + ExprKind::Compare { + left, comparators, .. + } => { + relocate_expr(left, location); + for expr in comparators { + relocate_expr(expr, location); + } + } + ExprKind::Call { + func, + args, + keywords, + } => { + relocate_expr(func, location); + for expr in args { + relocate_expr(expr, location); + } + for keyword in keywords { + relocate_keyword(keyword, location); + } + } + ExprKind::FormattedValue { + value, format_spec, .. + } => { + relocate_expr(value, location); + if let Some(expr) = format_spec { + relocate_expr(expr, location); + } + } + ExprKind::JoinedStr { values } => { + for expr in values { + relocate_expr(expr, location); + } + } + ExprKind::Constant { .. } => {} + ExprKind::Attribute { value, .. } => { + relocate_expr(value, location); + } + ExprKind::Subscript { value, slice, .. } => { + relocate_expr(value, location); + relocate_expr(slice, location); + } + ExprKind::Starred { value, .. } => { + relocate_expr(value, location); + } + ExprKind::Name { .. } => {} + ExprKind::List { elts, .. } => { + for expr in elts { + relocate_expr(expr, location); + } + } + ExprKind::Tuple { elts, .. } => { + for expr in elts { + relocate_expr(expr, location); + } + } + ExprKind::Slice { lower, upper, step } => { + if let Some(expr) = lower { + relocate_expr(expr, location); + } + if let Some(expr) = upper { + relocate_expr(expr, location); + } + if let Some(expr) = step { + relocate_expr(expr, location); + } + } + } +}