Add row and column numbers to formatted parse errors (#9321)

## Summary

We now render parse errors in the formatter identically to those in the
linter, e.g.:

```
❯ cargo run -p ruff_cli -- format foo.py
error: Failed to parse foo.py:1:17: Unexpected token '='
```

Closes https://github.com/astral-sh/ruff/issues/8338.

Closes https://github.com/astral-sh/ruff/issues/9311.
This commit is contained in:
Charlie Marsh 2023-12-31 08:10:45 -04:00 committed by GitHub
parent e80260a3c5
commit 48e04cc2c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 88 additions and 59 deletions

View file

@ -6,8 +6,7 @@ use ruff_formatter::{format, FormatError, Formatted, PrintError, Printed, Source
use ruff_python_ast::AstNode;
use ruff_python_ast::Mod;
use ruff_python_index::tokens_and_ranges;
use ruff_python_parser::lexer::LexicalError;
use ruff_python_parser::{parse_ok_tokens, AsMode, ParseError};
use ruff_python_parser::{parse_ok_tokens, AsMode, ParseError, ParseErrorType};
use ruff_python_trivia::CommentRanges;
use ruff_source_file::Locator;
@ -108,35 +107,25 @@ where
#[derive(Error, Debug)]
pub enum FormatModuleError {
#[error("source contains syntax errors: {0}")]
LexError(LexicalError),
#[error("source contains syntax errors: {0}")]
ParseError(ParseError),
#[error(transparent)]
ParseError(#[from] ParseError),
#[error(transparent)]
FormatError(#[from] FormatError),
#[error(transparent)]
PrintError(#[from] PrintError),
}
impl From<LexicalError> for FormatModuleError {
fn from(value: LexicalError) -> Self {
Self::LexError(value)
}
}
impl From<ParseError> for FormatModuleError {
fn from(value: ParseError) -> Self {
Self::ParseError(value)
}
}
#[tracing::instrument(name = "format", level = Level::TRACE, skip_all)]
pub fn format_module_source(
source: &str,
options: PyFormatOptions,
) -> Result<Printed, FormatModuleError> {
let source_type = options.source_type();
let (tokens, comment_ranges) = tokens_and_ranges(source, source_type)?;
let (tokens, comment_ranges) =
tokens_and_ranges(source, source_type).map_err(|err| ParseError {
offset: err.location,
error: ParseErrorType::Lexical(err.error),
})?;
let module = parse_ok_tokens(tokens, source, source_type.as_mode())?;
let formatted = format_module_ast(&module, &comment_ranges, source, options)?;
Ok(formatted.print()?)
@ -180,7 +169,6 @@ mod tests {
use ruff_python_ast::PySourceType;
use ruff_python_index::tokens_and_ranges;
use ruff_python_parser::{parse_ok_tokens, AsMode};
use crate::{format_module_ast, format_module_source, PyFormatOptions};

View file

@ -4,8 +4,8 @@
use std::{borrow::Cow, collections::VecDeque};
use ruff_python_parser::ParseError;
use {once_cell::sync::Lazy, regex::Regex};
use {
ruff_formatter::{write, FormatOptions, IndentStyle, LineWidth, Printed},
ruff_python_trivia::{is_python_whitespace, PythonWhitespace},
@ -499,11 +499,7 @@ impl<'ast, 'buf, 'fmt, 'src> DocstringLinePrinter<'ast, 'buf, 'fmt, 'src> {
let printed = match docstring_format_source(options, self.quote_char, &codeblob) {
Ok(printed) => printed,
Err(FormatModuleError::FormatError(err)) => return Err(err),
Err(
FormatModuleError::LexError(_)
| FormatModuleError::ParseError(_)
| FormatModuleError::PrintError(_),
) => {
Err(FormatModuleError::ParseError(_) | FormatModuleError::PrintError(_)) => {
return Ok(None);
}
};
@ -1518,7 +1514,8 @@ fn docstring_format_source(
use ruff_python_parser::AsMode;
let source_type = options.source_type();
let (tokens, comment_ranges) = ruff_python_index::tokens_and_ranges(source, source_type)?;
let (tokens, comment_ranges) =
ruff_python_index::tokens_and_ranges(source, source_type).map_err(ParseError::from)?;
let module = ruff_python_parser::parse_ok_tokens(tokens, source, source_type.as_mode())?;
let source_code = ruff_formatter::SourceCode::new(source);
let comments = crate::Comments::from_ast(&module, source_code, &comment_ranges);