diff --git a/crates/ruff/src/checkers/ast.rs b/crates/ruff/src/checkers/ast.rs index 662d0c1183ed08..e9ecc6350671da 100644 --- a/crates/ruff/src/checkers/ast.rs +++ b/crates/ruff/src/checkers/ast.rs @@ -6,8 +6,6 @@ use std::path::Path; use itertools::Itertools; use log::error; use nohash_hasher::IntMap; -use ruff_python::builtins::{BUILTINS, MAGIC_GLOBALS}; -use ruff_python::typing::TYPING_EXTENSIONS; use rustc_hash::{FxHashMap, FxHashSet}; use rustpython_common::cformat::{CFormatError, CFormatErrorType}; use rustpython_parser::ast::{ @@ -17,6 +15,9 @@ use rustpython_parser::ast::{ use rustpython_parser::parser; use smallvec::smallvec; +use ruff_python::builtins::{BUILTINS, MAGIC_GLOBALS}; +use ruff_python::typing::TYPING_EXTENSIONS; + use crate::ast::helpers::{ binding_range, collect_call_path, extract_handler_names, from_relative_import, to_module_path, }; @@ -30,7 +31,6 @@ use crate::ast::typing::{match_annotated_subscript, Callable, SubscriptKind}; use crate::ast::visitor::{walk_excepthandler, Visitor}; use crate::ast::{branch_detection, cast, helpers, operations, typing, visitor}; use crate::docstrings::definition::{Definition, DefinitionKind, Docstring, Documentable}; -use crate::noqa::Directive; use crate::registry::{Diagnostic, Rule}; use crate::rules::{ flake8_2020, flake8_annotations, flake8_bandit, flake8_blind_except, flake8_boolean_trap, @@ -282,7 +282,7 @@ impl<'a> Checker<'a> { } /// Return `true` if a `Rule` is disabled by a `noqa` directive. - pub fn is_ignored(&self, code: &Rule, lineno: usize) -> bool { + pub fn rule_is_ignored(&self, code: &Rule, lineno: usize) -> bool { // TODO(charlie): `noqa` directives are mostly enforced in `check_lines.rs`. // However, in rare cases, we need to check them here. For example, when // removing unused imports, we create a single fix that's applied to all @@ -293,16 +293,7 @@ impl<'a> Checker<'a> { if matches!(self.noqa, flags::Noqa::Disabled) { return false; } - let noqa_lineno = self.noqa_line_for.get(&lineno).unwrap_or(&lineno); - let line = self.locator.slice_source_code_range(&Range::new( - Location::new(*noqa_lineno, 0), - Location::new(noqa_lineno + 1, 0), - )); - match noqa::extract_noqa_directive(line) { - Directive::None => false, - Directive::All(..) => true, - Directive::Codes(.., codes) => noqa::includes(code, &codes), - } + noqa::rule_is_ignored(code, lineno, self.noqa_line_for, self.locator) } } @@ -4866,9 +4857,9 @@ impl<'a> Checker<'a> { None }; - if self.is_ignored(&Rule::UnusedImport, diagnostic_lineno) + if self.rule_is_ignored(&Rule::UnusedImport, diagnostic_lineno) || parent_lineno.map_or(false, |parent_lineno| { - self.is_ignored(&Rule::UnusedImport, parent_lineno) + self.rule_is_ignored(&Rule::UnusedImport, parent_lineno) }) { ignored diff --git a/crates/ruff/src/linter.rs b/crates/ruff/src/linter.rs index 28cc35f3148b66..e93fd782f877be 100644 --- a/crates/ruff/src/linter.rs +++ b/crates/ruff/src/linter.rs @@ -18,7 +18,7 @@ use crate::checkers::tokens::check_tokens; use crate::directives::Directives; use crate::doc_lines::{doc_lines_from_ast, doc_lines_from_tokens}; use crate::message::{Message, Source}; -use crate::noqa::add_noqa; +use crate::noqa::{add_noqa, rule_is_ignored}; use crate::registry::{Diagnostic, LintSource, Rule}; use crate::rules::pycodestyle; use crate::settings::{flags, Settings}; @@ -149,7 +149,17 @@ pub fn check_path( if settings.rules.enabled(&Rule::SyntaxError) { pycodestyle::rules::syntax_error(&mut diagnostics, &parse_error); } - error = Some(parse_error); + + // If the syntax error is ignored, suppress it (regardless of whether + // `Rule::SyntaxError` is enabled). + if !rule_is_ignored( + &Rule::SyntaxError, + parse_error.location.row(), + &directives.noqa_line_for, + locator, + ) { + error = Some(parse_error); + } } } } diff --git a/crates/ruff/src/noqa.rs b/crates/ruff/src/noqa.rs index 8f65a1e462ec02..cdc524be7bba0f 100644 --- a/crates/ruff/src/noqa.rs +++ b/crates/ruff/src/noqa.rs @@ -7,10 +7,12 @@ use nohash_hasher::IntMap; use once_cell::sync::Lazy; use regex::Regex; use rustc_hash::{FxHashMap, FxHashSet}; +use rustpython_parser::ast::Location; +use crate::ast::types::Range; use crate::registry::{Diagnostic, Rule}; use crate::rule_redirects::get_redirect_target; -use crate::source_code::LineEnding; +use crate::source_code::{LineEnding, Locator}; static NOQA_LINE_REGEX: Lazy = Lazy::new(|| { Regex::new( @@ -76,6 +78,25 @@ pub fn includes(needle: &Rule, haystack: &[&str]) -> bool { .any(|candidate| needle == get_redirect_target(candidate).unwrap_or(candidate)) } +/// Returns `true` if the given [`Rule`] is ignored at the specified `lineno`. +pub fn rule_is_ignored( + code: &Rule, + lineno: usize, + noqa_line_for: &IntMap, + locator: &Locator, +) -> bool { + let noqa_lineno = noqa_line_for.get(&lineno).unwrap_or(&lineno); + let line = locator.slice_source_code_range(&Range::new( + Location::new(*noqa_lineno, 0), + Location::new(noqa_lineno + 1, 0), + )); + match extract_noqa_directive(line) { + Directive::None => false, + Directive::All(..) => true, + Directive::Codes(.., codes) => includes(code, &codes), + } +} + pub fn add_noqa( path: &Path, diagnostics: &[Diagnostic],