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
```
This commit is contained in:
konsti 2023-07-19 17:15:26 +02:00 committed by GitHub
parent 46a17d11f3
commit 63ed7a31e8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 38 additions and 13 deletions

View file

@ -8,7 +8,7 @@ use std::error::Error;
pub enum FormatError { pub enum FormatError {
/// In case a node can't be formatted because it either misses a require child element or /// 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). /// 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 /// In case range formatting failed because the provided range was larger
/// than the formatted syntax tree /// than the formatted syntax tree
RangeError { input: TextRange, tree: TextRange }, RangeError { input: TextRange, tree: TextRange },
@ -28,7 +28,9 @@ pub enum FormatError {
impl std::fmt::Display for FormatError { impl std::fmt::Display for FormatError {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
FormatError::SyntaxError => fmt.write_str("syntax error"), FormatError::SyntaxError {message} => {
std::write!(fmt, "syntax error: {message}")
},
FormatError::RangeError { input, tree } => std::write!( FormatError::RangeError { input, tree } => std::write!(
fmt, fmt,
"formatting range {input:?} is larger than syntax tree {tree:?}" "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)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum InvalidDocumentError { pub enum InvalidDocumentError {

View file

@ -251,7 +251,9 @@ impl Format<PyFormatContext<'_>> for FormatComment<'_> {
let trailing_whitespace_len = comment_text.text_len() - trimmed.text_len(); let trailing_whitespace_len = comment_text.text_len() - trimmed.text_len();
let Some(content) = trimmed.strip_prefix('#') else { 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: // Fast path for correctly formatted comments:

View file

@ -162,18 +162,22 @@ pub(crate) fn find_colons(
let after_lower = lower let after_lower = lower
.as_ref() .as_ref()
.map_or(range.start(), |lower| lower.range().end()); .map_or(range.start(), |lower| lower.range().end());
let first_colon = let first_colon = first_non_trivia_token(after_lower, contents).ok_or(
first_non_trivia_token(after_lower, contents).ok_or(FormatError::SyntaxError)?; FormatError::syntax_error("Din't find any token for slice first colon"),
)?;
if first_colon.kind != TokenKind::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 let after_upper = upper
.as_ref() .as_ref()
.map_or(first_colon.end(), |upper| upper.range().end()); .map_or(first_colon.end(), |upper| upper.range().end());
// At least the closing bracket must exist, so there must be a token there // At least the closing bracket must exist, so there must be a token there
let next_token = let next_token = first_non_trivia_token(after_upper, contents).ok_or(
first_non_trivia_token(after_upper, contents).ok_or(FormatError::SyntaxError)?; FormatError::syntax_error("Din't find any token for slice second colon"),
)?;
let second_colon = if next_token.kind == TokenKind::Colon { let second_colon = if next_token.kind == TokenKind::Colon {
debug_assert!( debug_assert!(
next_token.range.start() < range.end(), next_token.range.start() < range.end(),

View file

@ -91,7 +91,9 @@ impl Format<PyFormatContext<'_>> for FormatStringContinuation<'_> {
continue; continue;
} }
Err(_) => { Err(_) => {
return Err(FormatError::SyntaxError); return Err(FormatError::syntax_error(
"Unexpected lexer error in string formatting",
));
} }
}; };
@ -167,7 +169,9 @@ impl Format<PyFormatContext<'_>> for FormatStringPart {
let prefix = StringPrefix::parse(string_content); let prefix = StringPrefix::parse(string_content);
let after_prefix = &string_content[usize::from(prefix.text_len())..]; 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( let relative_raw_content_range = TextRange::new(
prefix.text_len() + quotes.text_len(), prefix.text_len() + quotes.text_len(),
string_content.text_len() - quotes.text_len(), string_content.text_len() - quotes.text_len(),

View file

@ -20,7 +20,9 @@ impl FormatNodeRule<StmtAssign> for FormatStmtAssign {
type_comment: _, type_comment: _,
} = item; } = 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!( write!(
f, f,

View file

@ -117,14 +117,19 @@ fn are_with_items_parenthesized(
with: &AnyStatementWith, with: &AnyStatementWith,
context: &PyFormatContext, context: &PyFormatContext,
) -> FormatResult<bool> { ) -> FormatResult<bool> {
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 before_first_with_item = TextRange::new(with.start(), first_with_item.start());
let mut tokenizer = SimpleTokenizer::new(context.source(), before_first_with_item) let mut tokenizer = SimpleTokenizer::new(context.source(), before_first_with_item)
.skip_trivia() .skip_trivia()
.skip_while(|t| t.kind() == TokenKind::Async); .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!( debug_assert_eq!(
with_keyword.kind(), with_keyword.kind(),