Skip to main content

ruff_formatter/
diagnostics.rs

1use crate::GroupId;
2use crate::prelude::TagKind;
3use ruff_text_size::TextRange;
4use std::error::Error;
5
6#[derive(Debug, PartialEq, Eq, Copy, Clone)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8/// Series of errors encountered during formatting
9pub enum FormatError {
10    /// In case a node can't be formatted because it either misses a require child element or
11    /// a child is present that should not (e.g. a trailing comma after a rest element).
12    SyntaxError { message: &'static str },
13    /// In case range formatting failed because the provided range was larger
14    /// than the formatted syntax tree
15    RangeError { input: TextRange, tree: TextRange },
16
17    /// In case printing the document failed because it has an invalid structure.
18    InvalidDocument(InvalidDocumentError),
19
20    /// Formatting failed because some content encountered a situation where a layout
21    /// choice by an enclosing [`crate::Format`] resulted in a poor layout for a child [`crate::Format`].
22    ///
23    /// It's up to an enclosing [`crate::Format`] to handle the error and pick another layout.
24    /// This error should not be raised if there's no outer [`crate::Format`] handling the poor layout error,
25    /// avoiding that formatting of the whole document fails.
26    PoorLayout,
27}
28
29impl std::fmt::Display for FormatError {
30    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        match self {
32            FormatError::SyntaxError { message } => {
33                std::write!(fmt, "syntax error: {message}")
34            }
35            FormatError::RangeError { input, tree } => std::write!(
36                fmt,
37                "formatting range {input:?} is larger than syntax tree {tree:?}"
38            ),
39            FormatError::InvalidDocument(error) => std::write!(
40                fmt,
41                "Invalid document: {error}\n\n This is an internal Ruff error. Please report if necessary."
42            ),
43            FormatError::PoorLayout => {
44                std::write!(
45                    fmt,
46                    "Poor layout: The formatter wasn't able to pick a good layout for your document. This is an internal Ruff error. Please report if necessary."
47                )
48            }
49        }
50    }
51}
52
53impl Error for FormatError {}
54
55impl From<PrintError> for FormatError {
56    fn from(error: PrintError) -> Self {
57        FormatError::from(&error)
58    }
59}
60
61impl From<&PrintError> for FormatError {
62    fn from(error: &PrintError) -> Self {
63        match error {
64            PrintError::InvalidDocument(reason) => FormatError::InvalidDocument(*reason),
65        }
66    }
67}
68
69impl FormatError {
70    pub fn syntax_error(message: &'static str) -> Self {
71        Self::SyntaxError { message }
72    }
73}
74
75#[derive(Debug, Copy, Clone, Eq, PartialEq)]
76#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
77pub enum InvalidDocumentError {
78    /// Mismatching start/end kinds
79    ///
80    /// ```plain
81    /// StartIndent
82    /// ...
83    /// EndGroup
84    /// ```
85    StartEndTagMismatch {
86        start_kind: TagKind,
87        end_kind: TagKind,
88    },
89
90    /// End tag without a corresponding start tag.
91    ///
92    /// ```plain
93    /// Text
94    /// EndGroup
95    /// ```
96    StartTagMissing {
97        kind: TagKind,
98    },
99
100    /// Expected a specific start tag but instead is:
101    /// - at the end of the document
102    /// - at another start tag
103    /// - at an end tag
104    ExpectedStart {
105        expected_start: TagKind,
106        actual: ActualStart,
107    },
108
109    UnknownGroupId {
110        group_id: GroupId,
111    },
112}
113
114#[derive(Debug, Copy, Clone, Eq, PartialEq)]
115#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
116pub enum ActualStart {
117    /// The actual element is not a tag.
118    Content,
119
120    /// The actual element was a start tag of another kind.
121    Start(TagKind),
122
123    /// The actual element is an end tag instead of a start tag.
124    End(TagKind),
125
126    /// Reached the end of the document
127    EndOfDocument,
128}
129
130impl std::fmt::Display for InvalidDocumentError {
131    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132        match self {
133            InvalidDocumentError::StartEndTagMismatch {
134                start_kind,
135                end_kind,
136            } => {
137                std::write!(
138                    f,
139                    "Expected end tag of kind {start_kind:?} but found {end_kind:?}."
140                )
141            }
142            InvalidDocumentError::StartTagMissing { kind } => {
143                std::write!(f, "End tag of kind {kind:?} without matching start tag.")
144            }
145            InvalidDocumentError::ExpectedStart {
146                expected_start,
147                actual,
148            } => match actual {
149                ActualStart::EndOfDocument => {
150                    std::write!(
151                        f,
152                        "Expected start tag of kind {expected_start:?} but at the end of document."
153                    )
154                }
155                ActualStart::Start(start) => {
156                    std::write!(
157                        f,
158                        "Expected start tag of kind {expected_start:?} but found start tag of kind {start:?}."
159                    )
160                }
161                ActualStart::End(end) => {
162                    std::write!(
163                        f,
164                        "Expected start tag of kind {expected_start:?} but found end tag of kind {end:?}."
165                    )
166                }
167                ActualStart::Content => {
168                    std::write!(
169                        f,
170                        "Expected start tag of kind {expected_start:?} but found non-tag element."
171                    )
172                }
173            },
174            InvalidDocumentError::UnknownGroupId { group_id } => {
175                std::write!(
176                    f,
177                    "Encountered unknown group id {group_id:?}. Ensure that the group with the id {group_id:?} exists and that the group is a parent of or comes before the element referring to it."
178                )
179            }
180        }
181    }
182}
183
184#[derive(Debug, Clone, Eq, PartialEq)]
185pub enum PrintError {
186    InvalidDocument(InvalidDocumentError),
187}
188
189impl Error for PrintError {}
190
191impl std::fmt::Display for PrintError {
192    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193        match self {
194            PrintError::InvalidDocument(inner) => {
195                std::write!(f, "Invalid document: {inner}")
196            }
197        }
198    }
199}