From c47721674cb256e09a23cdcf4b0c6c7ca25a10b3 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Mon, 17 Apr 2023 07:57:35 +0200 Subject: [PATCH] Show line/column numbers for syntax errors --- crates/ruff/src/linter.rs | 8 ++------ crates/ruff/src/logging.rs | 33 ++++++++++++++++++++++++++++++ crates/ruff_cli/src/diagnostics.rs | 14 ++++++------- 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/crates/ruff/src/linter.rs b/crates/ruff/src/linter.rs index 31bb9d2d3eeb53..82df045e535bd0 100644 --- a/crates/ruff/src/linter.rs +++ b/crates/ruff/src/linter.rs @@ -23,6 +23,7 @@ use crate::checkers::physical_lines::check_physical_lines; use crate::checkers::tokens::check_tokens; use crate::directives::Directives; use crate::doc_lines::{doc_lines_from_ast, doc_lines_from_tokens}; +use crate::logging::DisplayParseError; use crate::message::Message; use crate::noqa::add_noqa; use crate::registry::{AsRule, Rule}; @@ -296,12 +297,7 @@ pub fn add_noqa_to_path(path: &Path, package: Option<&Path>, settings: &Settings // Log any parse errors. if let Some(err) = error { - error!( - "{}{}{} {err:?}", - "Failed to parse ".bold(), - fs::relativize_path(path).bold(), - ":".bold() - ); + error!("{}", DisplayParseError::new(err, locator.to_source_code())); } // Add any missing `# noqa` pragmas. diff --git a/crates/ruff/src/logging.rs b/crates/ruff/src/logging.rs index 49ee5a4ed59fe7..1607508ead2039 100644 --- a/crates/ruff/src/logging.rs +++ b/crates/ruff/src/logging.rs @@ -1,10 +1,15 @@ +use std::fmt::{Display, Formatter}; +use std::path::Path; use std::sync::Mutex; +use crate::fs; use anyhow::Result; use colored::Colorize; use fern; use log::Level; use once_cell::sync::Lazy; +use ruff_python_ast::source_code::SourceCode; +use rustpython_parser::ParseError; pub(crate) static WARNINGS: Lazy>> = Lazy::new(Mutex::default); @@ -127,6 +132,34 @@ pub fn set_up_logging(level: &LogLevel) -> Result<()> { Ok(()) } +pub struct DisplayParseError<'a> { + error: ParseError, + source_code: SourceCode<'a, 'a>, +} + +impl<'a> DisplayParseError<'a> { + pub fn new(error: ParseError, source_code: SourceCode<'a, 'a>) -> Self { + Self { error, source_code } + } +} + +impl Display for DisplayParseError<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let source_location = self.source_code.source_location(self.error.location); + + write!( + f, + "{header} {path}{colon}{row}{colon}{column}{colon} {inner}", + header = "Failed to parse ".bold(), + path = fs::relativize_path(Path::new(&self.error.source_path)).bold(), + row = source_location.row, + column = source_location.column, + colon = ":".cyan(), + inner = &self.error.error + ) + } +} + #[cfg(test)] mod tests { use crate::logging::LogLevel; diff --git a/crates/ruff_cli/src/diagnostics.rs b/crates/ruff_cli/src/diagnostics.rs index 1aba61f8267be2..b77a8431457c89 100644 --- a/crates/ruff_cli/src/diagnostics.rs +++ b/crates/ruff_cli/src/diagnostics.rs @@ -16,10 +16,11 @@ use similar::TextDiff; use ruff::fs; use ruff::jupyter::{is_jupyter_notebook, JupyterIndex, JupyterNotebook}; use ruff::linter::{lint_fix, lint_only, FixTable, FixerResult, LinterResult}; +use ruff::logging::DisplayParseError; use ruff::message::Message; use ruff::settings::{flags, AllSettings, Settings}; use ruff_python_ast::imports::ImportMap; -use ruff_python_ast::source_code::SourceFileBuilder; +use ruff_python_ast::source_code::{LineIndex, SourceCode, SourceFileBuilder}; use crate::cache; @@ -200,13 +201,12 @@ pub fn lint_path( let imports = imports.unwrap_or_default(); if let Some(err) = parse_error { - // FIXME micha manually print parse errors to get line and column numbers - // Notify the user of any parse errors. error!( - "{}{}{} {err}", - "Failed to parse ".bold(), - fs::relativize_path(path).bold(), - ":".bold() + "{}", + DisplayParseError::new( + err, + SourceCode::new(&contents, &LineIndex::from_source_text(&contents)) + ) ); // Purge the cache.