1use rowan::TextRange;
2
3use super::{SourceId, Span};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
18pub enum DiagnosticKind {
19 UnclosedTree,
21 UnclosedSequence,
22 UnclosedAlternation,
23
24 ExpectedExpression,
26 ExpectedTypeName,
27 ExpectedCaptureName,
28 ExpectedFieldName,
29 ExpectedSubtype,
30
31 EmptyTree,
33 EmptyAnonymousNode,
34 EmptySequence,
35 EmptyAlternation,
36 BareIdentifier,
37 InvalidSeparator,
38 AnchorInAlternation,
39 InvalidFieldEquals,
40 InvalidSupertypeSyntax,
41 InvalidTypeAnnotationSyntax,
42 ErrorTakesNoArguments,
43 RefCannotHaveChildren,
44 ErrorMissingOutsideParens,
45 UnsupportedPredicate,
46 UnexpectedToken,
47 CaptureWithoutTarget,
48 LowercaseBranchLabel,
49
50 CaptureNameHasDots,
52 CaptureNameHasHyphens,
53 CaptureNameUppercase,
54 DefNameLowercase,
55 DefNameHasSeparators,
56 BranchLabelHasSeparators,
57 FieldNameHasDots,
58 FieldNameHasHyphens,
59 FieldNameUppercase,
60 TypeNameInvalidChars,
61 TreeSitterSequenceSyntax,
62 NegationSyntaxDeprecated,
63
64 DuplicateDefinition,
66 UndefinedReference,
67 MixedAltBranches,
68 RecursionNoEscape,
69 DirectRecursion,
70 FieldSequenceValue,
71 AnchorWithoutContext,
72
73 IncompatibleTypes,
75 MultiCaptureQuantifierNoName,
76 UnusedBranchLabels,
77 StrictDimensionalityViolation,
78 UncapturedOutputWithCaptures,
79 AmbiguousUncapturedOutputs,
80 DuplicateCaptureInScope,
81 IncompatibleCaptureTypes,
82 IncompatibleStructShapes,
83
84 UnknownNodeType,
86 UnknownField,
87 FieldNotOnNodeType,
88 InvalidFieldChildType,
89 InvalidChildType,
90
91 UnnamedDef,
93}
94
95impl DiagnosticKind {
96 pub fn default_severity(&self) -> Severity {
98 match self {
99 Self::UnusedBranchLabels
100 | Self::TreeSitterSequenceSyntax
101 | Self::NegationSyntaxDeprecated => Severity::Warning,
102 _ => Severity::Error,
103 }
104 }
105
106 pub fn suppresses(&self, other: &DiagnosticKind) -> bool {
111 self < other
112 }
113
114 pub fn is_structural_error(&self) -> bool {
117 matches!(
118 self,
119 Self::UnclosedTree | Self::UnclosedSequence | Self::UnclosedAlternation
120 )
121 }
122
123 pub fn is_root_cause_error(&self) -> bool {
126 matches!(
127 self,
128 Self::ExpectedExpression
129 | Self::ExpectedTypeName
130 | Self::ExpectedCaptureName
131 | Self::ExpectedFieldName
132 | Self::ExpectedSubtype
133 )
134 }
135
136 pub fn is_consequence_error(&self) -> bool {
139 matches!(self, Self::UnnamedDef)
140 }
141
142 pub fn default_hint(&self) -> Option<&'static str> {
145 match self {
146 Self::ExpectedSubtype => Some("e.g., `expression/binary_expression`"),
147 Self::ExpectedTypeName => Some("e.g., `::MyType` or `::string`"),
148 Self::ExpectedFieldName => Some("e.g., `-value`"),
149 Self::EmptyTree => Some("use `(_)` to match any named node, or `_` for any node"),
150 Self::EmptyAnonymousNode => Some("use a valid anonymous node or remove it"),
151 Self::EmptySequence => Some("sequences must contain at least one expression"),
152 Self::EmptyAlternation => Some("alternations must contain at least one branch"),
153 Self::TreeSitterSequenceSyntax => Some("use `{...}` for sequences"),
154 Self::NegationSyntaxDeprecated => Some("use `-field` instead of `!field`"),
155 Self::MixedAltBranches => {
156 Some("use all labels for a tagged union, or none for a merged struct")
157 }
158 Self::RecursionNoEscape => {
159 Some("add a non-recursive branch to terminate: `[Base: ... Rec: (Self)]`")
160 }
161 Self::DirectRecursion => {
162 Some("recursive references must consume input before recursing")
163 }
164 Self::AnchorWithoutContext => Some("wrap in a named node: `(parent . (child))`"),
165 Self::AnchorInAlternation => Some("use `[{(a) . (b)} (c)]` to anchor within a branch"),
166 Self::UncapturedOutputWithCaptures => Some("add `@name` to capture the output"),
167 Self::AmbiguousUncapturedOutputs => {
168 Some("capture each expression explicitly: `(X) @x (Y) @y`")
169 }
170 _ => None,
171 }
172 }
173
174 pub fn fallback_message(&self) -> &'static str {
176 match self {
177 Self::UnclosedTree => "missing closing `)`",
179 Self::UnclosedSequence => "missing closing `}`",
180 Self::UnclosedAlternation => "missing closing `]`",
181
182 Self::ExpectedExpression => "expected an expression",
184 Self::ExpectedTypeName => "expected type name",
185 Self::ExpectedCaptureName => "expected capture name",
186 Self::ExpectedFieldName => "expected field name",
187 Self::ExpectedSubtype => "expected subtype name",
188
189 Self::EmptyTree => "empty `()` is not allowed",
191 Self::EmptyAnonymousNode => "empty anonymous node",
192 Self::EmptySequence => "empty `{}` is not allowed",
193 Self::EmptyAlternation => "empty `[]` is not allowed",
194 Self::BareIdentifier => "bare identifier is not valid",
195 Self::InvalidSeparator => "unexpected separator",
196 Self::AnchorInAlternation => "anchors cannot appear directly in alternations",
197 Self::InvalidFieldEquals => "use `:` instead of `=`",
198 Self::InvalidSupertypeSyntax => "references cannot have supertypes",
199 Self::InvalidTypeAnnotationSyntax => "use `::` for type annotations",
200 Self::ErrorTakesNoArguments => "`(ERROR)` cannot have children",
201 Self::RefCannotHaveChildren => "references cannot have children",
202 Self::ErrorMissingOutsideParens => "special node requires parentheses",
203 Self::UnsupportedPredicate => "predicates are not supported",
204 Self::UnexpectedToken => "unexpected token",
205 Self::CaptureWithoutTarget => "capture has no target",
206 Self::LowercaseBranchLabel => "branch label must start with uppercase",
207
208 Self::CaptureNameHasDots => "capture names cannot contain `.`",
210 Self::CaptureNameHasHyphens => "capture names cannot contain `-`",
211 Self::CaptureNameUppercase => "capture names must be lowercase",
212 Self::DefNameLowercase => "definition names must start uppercase",
213 Self::DefNameHasSeparators => "definition names must be PascalCase",
214 Self::BranchLabelHasSeparators => "branch labels must be PascalCase",
215 Self::FieldNameHasDots => "field names cannot contain `.`",
216 Self::FieldNameHasHyphens => "field names cannot contain `-`",
217 Self::FieldNameUppercase => "field names must be lowercase",
218 Self::TypeNameInvalidChars => "type names cannot contain `.` or `-`",
219 Self::TreeSitterSequenceSyntax => "tree-sitter sequence syntax",
220 Self::NegationSyntaxDeprecated => "deprecated negation syntax",
221
222 Self::DuplicateDefinition => "duplicate definition",
224 Self::UndefinedReference => "undefined reference",
225 Self::MixedAltBranches => "cannot mix labeled and unlabeled branches",
226 Self::RecursionNoEscape => "infinite recursion: no escape path",
227 Self::DirectRecursion => "infinite recursion: cycle consumes no input",
228 Self::FieldSequenceValue => "field cannot match a sequence",
229 Self::AnchorWithoutContext => "boundary anchor requires parent node context",
230
231 Self::IncompatibleTypes => "incompatible types",
233 Self::MultiCaptureQuantifierNoName => {
234 "quantified expression with multiple captures requires a struct capture"
235 }
236 Self::UnusedBranchLabels => "branch labels have no effect without capture",
237 Self::StrictDimensionalityViolation => {
238 "quantifier with captures requires a struct capture"
239 }
240 Self::UncapturedOutputWithCaptures => {
241 "output-producing expression requires capture when siblings have captures"
242 }
243 Self::AmbiguousUncapturedOutputs => {
244 "multiple expressions produce output without capture"
245 }
246 Self::DuplicateCaptureInScope => "duplicate capture in scope",
247 Self::IncompatibleCaptureTypes => "incompatible capture types",
248 Self::IncompatibleStructShapes => "incompatible struct shapes",
249
250 Self::UnknownNodeType => "unknown node type",
252 Self::UnknownField => "unknown field",
253 Self::FieldNotOnNodeType => "field not valid on this node type",
254 Self::InvalidFieldChildType => "node type not valid for this field",
255 Self::InvalidChildType => "node type not valid as child",
256
257 Self::UnnamedDef => "definition must be named",
259 }
260 }
261
262 pub fn custom_message(&self) -> String {
264 match self {
265 Self::RefCannotHaveChildren => {
267 "`{}` is a reference and cannot have children".to_string()
268 }
269 Self::FieldSequenceValue => "field `{}` cannot match a sequence".to_string(),
270
271 Self::DuplicateDefinition => "`{}` is already defined".to_string(),
273 Self::UndefinedReference => "`{}` is not defined".to_string(),
274 Self::IncompatibleTypes => "incompatible types: {}".to_string(),
275
276 Self::StrictDimensionalityViolation => "{}".to_string(),
278 Self::DuplicateCaptureInScope => {
279 "capture `@{}` already defined in this scope".to_string()
280 }
281 Self::IncompatibleCaptureTypes => {
282 "capture `@{}` has incompatible types across branches".to_string()
283 }
284 Self::IncompatibleStructShapes => {
285 "capture `@{}` has incompatible struct fields across branches".to_string()
286 }
287
288 Self::UnknownNodeType => "`{}` is not a valid node type".to_string(),
290 Self::UnknownField => "`{}` is not a valid field".to_string(),
291 Self::FieldNotOnNodeType => "field `{}` is not valid on this node type".to_string(),
292 Self::InvalidFieldChildType => "node type `{}` is not valid for this field".to_string(),
293 Self::InvalidChildType => "`{}` cannot be a child of this node".to_string(),
294
295 Self::MixedAltBranches => "cannot mix labeled and unlabeled branches: {}".to_string(),
297
298 Self::UnclosedTree | Self::UnclosedSequence | Self::UnclosedAlternation => {
300 format!("{}; {{}}", self.fallback_message())
301 }
302
303 Self::InvalidTypeAnnotationSyntax => "use `::` for type annotations: {}".to_string(),
305
306 Self::UnnamedDef => self.fallback_message().to_string(),
308
309 _ => format!("{}: {{}}", self.fallback_message()),
311 }
312 }
313
314 pub fn message(&self, msg: Option<&str>) -> String {
319 match msg {
320 None => self.fallback_message().to_string(),
321 Some(detail) => self.custom_message().replace("{}", detail),
322 }
323 }
324}
325
326#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
327pub enum Severity {
328 #[default]
329 Error,
330 Warning,
331}
332
333impl std::fmt::Display for Severity {
334 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
335 match self {
336 Severity::Error => write!(f, "error"),
337 Severity::Warning => write!(f, "warning"),
338 }
339 }
340}
341
342#[derive(Debug, Clone, PartialEq, Eq)]
343pub struct Fix {
344 pub(crate) replacement: String,
345 pub(crate) description: String,
346}
347
348impl Fix {
349 pub fn new(replacement: impl Into<String>, description: impl Into<String>) -> Self {
350 Self {
351 replacement: replacement.into(),
352 description: description.into(),
353 }
354 }
355}
356
357#[derive(Debug, Clone, PartialEq, Eq)]
358pub struct RelatedInfo {
359 pub(crate) span: Span,
360 pub(crate) message: String,
361}
362
363impl RelatedInfo {
364 pub fn new(source: SourceId, range: TextRange, message: impl Into<String>) -> Self {
365 Self {
366 span: Span::new(source, range),
367 message: message.into(),
368 }
369 }
370}
371
372#[derive(Debug, Clone, PartialEq, Eq)]
373pub(crate) struct DiagnosticMessage {
374 pub(crate) kind: DiagnosticKind,
375 pub(crate) source: SourceId,
377 pub(crate) range: TextRange,
379 pub(crate) suppression_range: TextRange,
384 pub(crate) message: String,
385 pub(crate) fix: Option<Fix>,
386 pub(crate) related: Vec<RelatedInfo>,
387 pub(crate) hints: Vec<String>,
388}
389
390impl DiagnosticMessage {
391 pub(crate) fn new(
392 source: SourceId,
393 kind: DiagnosticKind,
394 range: TextRange,
395 message: impl Into<String>,
396 ) -> Self {
397 Self {
398 kind,
399 source,
400 range,
401 suppression_range: range,
402 message: message.into(),
403 fix: None,
404 related: Vec::new(),
405 hints: Vec::new(),
406 }
407 }
408
409 pub(crate) fn with_default_message(
410 source: SourceId,
411 kind: DiagnosticKind,
412 range: TextRange,
413 ) -> Self {
414 Self::new(source, kind, range, kind.fallback_message())
415 }
416
417 pub(crate) fn severity(&self) -> Severity {
418 self.kind.default_severity()
419 }
420
421 pub(crate) fn is_error(&self) -> bool {
422 self.severity() == Severity::Error
423 }
424
425 pub(crate) fn is_warning(&self) -> bool {
426 self.severity() == Severity::Warning
427 }
428}
429
430impl std::fmt::Display for DiagnosticMessage {
431 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
432 write!(
433 f,
434 "{} at {}..{}: {}",
435 self.severity(),
436 u32::from(self.range.start()),
437 u32::from(self.range.end()),
438 self.message
439 )?;
440 if let Some(fix) = &self.fix {
441 write!(f, " (fix: {})", fix.description)?;
442 }
443 for related in &self.related {
444 write!(
445 f,
446 " (related: {} at {}..{})",
447 related.message,
448 u32::from(related.span.range.start()),
449 u32::from(related.span.range.end())
450 )?;
451 }
452 for hint in &self.hints {
453 write!(f, " (hint: {})", hint)?;
454 }
455 Ok(())
456 }
457}