mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 10:22:24 +00:00
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:
parent
46a17d11f3
commit
63ed7a31e8
6 changed files with 38 additions and 13 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue