From 63ed7a31e84c9f572d3c903fa6796660fcbc6303 Mon Sep 17 00:00:00 2001 From: konsti Date: Wed, 19 Jul 2023 17:15:26 +0200 Subject: [PATCH] Add message to formatter SyntaxError (#5881) **Summary** Add a static string error message to the formatter syntax error so we can disambiguate where the syntax error came from **Test Plan** No fixed tests, we don't expect this to occur, but it helped with transformers syntax error debugging: ``` Error: Failed to format node Caused by: syntax error: slice first colon token was not a colon ``` --- crates/ruff_formatter/src/diagnostics.rs | 12 ++++++++++-- .../ruff_python_formatter/src/comments/format.rs | 4 +++- .../src/expression/expr_slice.rs | 14 +++++++++----- .../ruff_python_formatter/src/expression/string.rs | 8 ++++++-- .../src/statement/stmt_assign.rs | 4 +++- .../src/statement/stmt_with.rs | 9 +++++++-- 6 files changed, 38 insertions(+), 13 deletions(-) diff --git a/crates/ruff_formatter/src/diagnostics.rs b/crates/ruff_formatter/src/diagnostics.rs index a5fd6f9632..9228975a7e 100644 --- a/crates/ruff_formatter/src/diagnostics.rs +++ b/crates/ruff_formatter/src/diagnostics.rs @@ -8,7 +8,7 @@ use std::error::Error; pub enum FormatError { /// In case a node can't be formatted because it either misses a require child element or /// a child is present that should not (e.g. a trailing comma after a rest element). - SyntaxError, + SyntaxError { message: &'static str }, /// In case range formatting failed because the provided range was larger /// than the formatted syntax tree RangeError { input: TextRange, tree: TextRange }, @@ -28,7 +28,9 @@ pub enum FormatError { impl std::fmt::Display for FormatError { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - FormatError::SyntaxError => fmt.write_str("syntax error"), + FormatError::SyntaxError {message} => { + std::write!(fmt, "syntax error: {message}") + }, FormatError::RangeError { input, tree } => std::write!( fmt, "formatting range {input:?} is larger than syntax tree {tree:?}" @@ -57,6 +59,12 @@ impl From<&PrintError> for FormatError { } } +impl FormatError { + pub fn syntax_error(message: &'static str) -> Self { + Self::SyntaxError { message } + } +} + #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum InvalidDocumentError { diff --git a/crates/ruff_python_formatter/src/comments/format.rs b/crates/ruff_python_formatter/src/comments/format.rs index aa7d7296f1..7376f9288b 100644 --- a/crates/ruff_python_formatter/src/comments/format.rs +++ b/crates/ruff_python_formatter/src/comments/format.rs @@ -251,7 +251,9 @@ impl Format> for FormatComment<'_> { let trailing_whitespace_len = comment_text.text_len() - trimmed.text_len(); let Some(content) = trimmed.strip_prefix('#') else { - return Err(FormatError::SyntaxError); + return Err(FormatError::syntax_error( + "Didn't find expected comment token `#`", + )); }; // Fast path for correctly formatted comments: diff --git a/crates/ruff_python_formatter/src/expression/expr_slice.rs b/crates/ruff_python_formatter/src/expression/expr_slice.rs index 93434b8777..be5f8396b7 100644 --- a/crates/ruff_python_formatter/src/expression/expr_slice.rs +++ b/crates/ruff_python_formatter/src/expression/expr_slice.rs @@ -162,18 +162,22 @@ pub(crate) fn find_colons( let after_lower = lower .as_ref() .map_or(range.start(), |lower| lower.range().end()); - let first_colon = - first_non_trivia_token(after_lower, contents).ok_or(FormatError::SyntaxError)?; + let first_colon = first_non_trivia_token(after_lower, contents).ok_or( + FormatError::syntax_error("Din't find any token for slice first colon"), + )?; if first_colon.kind != TokenKind::Colon { - return Err(FormatError::SyntaxError); + return Err(FormatError::syntax_error( + "slice first colon token was not a colon", + )); } let after_upper = upper .as_ref() .map_or(first_colon.end(), |upper| upper.range().end()); // At least the closing bracket must exist, so there must be a token there - let next_token = - first_non_trivia_token(after_upper, contents).ok_or(FormatError::SyntaxError)?; + let next_token = first_non_trivia_token(after_upper, contents).ok_or( + FormatError::syntax_error("Din't find any token for slice second colon"), + )?; let second_colon = if next_token.kind == TokenKind::Colon { debug_assert!( next_token.range.start() < range.end(), diff --git a/crates/ruff_python_formatter/src/expression/string.rs b/crates/ruff_python_formatter/src/expression/string.rs index a162a9a76a..1970b72163 100644 --- a/crates/ruff_python_formatter/src/expression/string.rs +++ b/crates/ruff_python_formatter/src/expression/string.rs @@ -91,7 +91,9 @@ impl Format> for FormatStringContinuation<'_> { continue; } Err(_) => { - return Err(FormatError::SyntaxError); + return Err(FormatError::syntax_error( + "Unexpected lexer error in string formatting", + )); } }; @@ -167,7 +169,9 @@ impl Format> for FormatStringPart { let prefix = StringPrefix::parse(string_content); let after_prefix = &string_content[usize::from(prefix.text_len())..]; - let quotes = StringQuotes::parse(after_prefix).ok_or(FormatError::SyntaxError)?; + let quotes = StringQuotes::parse(after_prefix).ok_or(FormatError::syntax_error( + "Didn't find string quotes after prefix", + ))?; let relative_raw_content_range = TextRange::new( prefix.text_len() + quotes.text_len(), string_content.text_len() - quotes.text_len(), diff --git a/crates/ruff_python_formatter/src/statement/stmt_assign.rs b/crates/ruff_python_formatter/src/statement/stmt_assign.rs index 55ff07d949..4a2d6538e4 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_assign.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_assign.rs @@ -20,7 +20,9 @@ impl FormatNodeRule for FormatStmtAssign { type_comment: _, } = item; - let (first, rest) = targets.split_first().ok_or(FormatError::SyntaxError)?; + let (first, rest) = targets.split_first().ok_or(FormatError::syntax_error( + "Expected at least on assignment target", + ))?; write!( f, diff --git a/crates/ruff_python_formatter/src/statement/stmt_with.rs b/crates/ruff_python_formatter/src/statement/stmt_with.rs index 2c610f029d..95465e9ec5 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_with.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_with.rs @@ -117,14 +117,19 @@ fn are_with_items_parenthesized( with: &AnyStatementWith, context: &PyFormatContext, ) -> FormatResult { - let first_with_item = with.items().first().ok_or(FormatError::SyntaxError)?; + let first_with_item = with + .items() + .first() + .ok_or(FormatError::syntax_error("Expected at least one with item"))?; let before_first_with_item = TextRange::new(with.start(), first_with_item.start()); let mut tokenizer = SimpleTokenizer::new(context.source(), before_first_with_item) .skip_trivia() .skip_while(|t| t.kind() == TokenKind::Async); - let with_keyword = tokenizer.next().ok_or(FormatError::SyntaxError)?; + let with_keyword = tokenizer.next().ok_or(FormatError::syntax_error( + "Expected a with keyword, didn't find any token", + ))?; debug_assert_eq!( with_keyword.kind(),