@@ -129,37 +129,57 @@ impl DiagnosticKind {
129129 matches ! ( self , Self :: UnnamedDef )
130130 }
131131
132+ /// Default hint for this kind, automatically included in diagnostics.
133+ /// Call sites can add additional hints for context-specific information.
134+ pub fn default_hint ( & self ) -> Option < & ' static str > {
135+ match self {
136+ Self :: ExpectedSubtype => Some ( "e.g., `expression/binary_expression`" ) ,
137+ Self :: ExpectedTypeName => Some ( "e.g., `::MyType` or `::string`" ) ,
138+ Self :: ExpectedFieldName => Some ( "e.g., `!value`" ) ,
139+ Self :: EmptyTree => Some ( "use `(_)` to match any named node, or `_` for any node" ) ,
140+ Self :: TreeSitterSequenceSyntax => Some ( "use `{...}` for sequences" ) ,
141+ Self :: MixedAltBranches => {
142+ Some ( "use all labels for a tagged union, or none for a merged struct" )
143+ }
144+ Self :: RecursionNoEscape => {
145+ Some ( "add a non-recursive branch to terminate: `[Base: ... Rec: (Self)]`" )
146+ }
147+ Self :: DirectRecursion => {
148+ Some ( "recursive references must consume input before recursing" )
149+ }
150+ _ => None ,
151+ }
152+ }
153+
132154 /// Base message for this diagnostic kind, used when no custom message is provided.
133155 pub fn fallback_message ( & self ) -> & ' static str {
134156 match self {
135- // Unclosed delimiters - clear about what's missing
157+ // Unclosed delimiters
136158 Self :: UnclosedTree => "missing closing `)`" ,
137159 Self :: UnclosedSequence => "missing closing `}`" ,
138160 Self :: UnclosedAlternation => "missing closing `]`" ,
139161
140- // Expected token errors - specific about what's needed
162+ // Expected token errors
141163 Self :: ExpectedExpression => "expected an expression" ,
142- Self :: ExpectedTypeName => "expected type name after `::` " ,
143- Self :: ExpectedCaptureName => "expected name after `@` " ,
164+ Self :: ExpectedTypeName => "expected type name" ,
165+ Self :: ExpectedCaptureName => "expected capture name " ,
144166 Self :: ExpectedFieldName => "expected field name" ,
145- Self :: ExpectedSubtype => "expected subtype after `/` " ,
146-
147- // Invalid syntax - explain what's wrong
148- Self :: EmptyTree => "empty parentheses are not allowed" ,
149- Self :: BareIdentifier => "bare identifier is not a valid expression " ,
150- Self :: InvalidSeparator => "separators are not needed " ,
151- Self :: InvalidFieldEquals => "use `:` for field constraints, not `=`" ,
152- Self :: InvalidSupertypeSyntax => "supertype syntax not allowed on references " ,
153- Self :: InvalidTypeAnnotationSyntax => "use `::` for type annotations, not `:` " ,
154- Self :: ErrorTakesNoArguments => "`(ERROR)` cannot have child nodes " ,
167+ Self :: ExpectedSubtype => "expected subtype name " ,
168+
169+ // Invalid syntax
170+ Self :: EmptyTree => "empty `()` is not allowed" ,
171+ Self :: BareIdentifier => "bare identifier is not valid" ,
172+ Self :: InvalidSeparator => "unexpected separator " ,
173+ Self :: InvalidFieldEquals => "use `:` instead of `=`" ,
174+ Self :: InvalidSupertypeSyntax => "references cannot have supertypes " ,
175+ Self :: InvalidTypeAnnotationSyntax => "use `::` for type annotations" ,
176+ Self :: ErrorTakesNoArguments => "`(ERROR)` cannot have children " ,
155177 Self :: RefCannotHaveChildren => "references cannot have children" ,
156- Self :: ErrorMissingOutsideParens => {
157- "`ERROR` and `MISSING` must be wrapped in parentheses"
158- }
159- Self :: UnsupportedPredicate => "predicates like `#match?` are not supported" ,
178+ Self :: ErrorMissingOutsideParens => "special node requires parentheses" ,
179+ Self :: UnsupportedPredicate => "predicates are not supported" ,
160180 Self :: UnexpectedToken => "unexpected token" ,
161- Self :: CaptureWithoutTarget => "`@` must follow an expression to capture " ,
162- Self :: LowercaseBranchLabel => "branch labels must be capitalized " ,
181+ Self :: CaptureWithoutTarget => "capture has no target " ,
182+ Self :: LowercaseBranchLabel => "branch label must start with uppercase " ,
163183
164184 // Naming convention violations
165185 Self :: CaptureNameHasDots => "capture names cannot contain `.`" ,
@@ -172,23 +192,25 @@ impl DiagnosticKind {
172192 Self :: FieldNameHasHyphens => "field names cannot contain `-`" ,
173193 Self :: FieldNameUppercase => "field names must be lowercase" ,
174194 Self :: TypeNameInvalidChars => "type names cannot contain `.` or `-`" ,
175- Self :: TreeSitterSequenceSyntax => "Tree -sitter sequence syntax" ,
195+ Self :: TreeSitterSequenceSyntax => "tree -sitter sequence syntax" ,
176196
177197 // Semantic errors
178- Self :: DuplicateDefinition => "name already defined " ,
198+ Self :: DuplicateDefinition => "duplicate definition " ,
179199 Self :: UndefinedReference => "undefined reference" ,
180200 Self :: MixedAltBranches => "cannot mix labeled and unlabeled branches" ,
181- Self :: RecursionNoEscape => "infinite recursion: cycle has no escape path" ,
201+ Self :: RecursionNoEscape => "infinite recursion: no escape path" ,
182202 Self :: DirectRecursion => "infinite recursion: cycle consumes no input" ,
183- Self :: FieldSequenceValue => "field must match exactly one node " ,
203+ Self :: FieldSequenceValue => "field cannot match a sequence " ,
184204
185205 // Type inference
186- Self :: IncompatibleTypes => "incompatible types in alternation branches " ,
206+ Self :: IncompatibleTypes => "incompatible types" ,
187207 Self :: MultiCaptureQuantifierNoName => {
188- "quantified expression with multiple captures requires `@name` "
208+ "quantified expression with multiple captures requires a struct capture "
189209 }
190210 Self :: UnusedBranchLabels => "branch labels have no effect without capture" ,
191- Self :: StrictDimensionalityViolation => "quantifier requires row capture" ,
211+ Self :: StrictDimensionalityViolation => {
212+ "quantifier with captures requires a struct capture"
213+ }
192214 Self :: DuplicateCaptureInScope => "duplicate capture in scope" ,
193215 Self :: IncompatibleCaptureTypes => "incompatible capture types" ,
194216 Self :: IncompatibleStructShapes => "incompatible struct shapes" ,
@@ -201,7 +223,7 @@ impl DiagnosticKind {
201223 Self :: InvalidChildType => "node type not valid as child" ,
202224
203225 // Structural
204- Self :: UnnamedDef => "definitions must be named" ,
226+ Self :: UnnamedDef => "definition must be named" ,
205227 }
206228 }
207229
@@ -212,9 +234,7 @@ impl DiagnosticKind {
212234 Self :: RefCannotHaveChildren => {
213235 "`{}` is a reference and cannot have children" . to_string ( )
214236 }
215- Self :: FieldSequenceValue => {
216- "field `{}` must match exactly one node, not a sequence" . to_string ( )
217- }
237+ Self :: FieldSequenceValue => "field `{}` cannot match a sequence" . to_string ( ) ,
218238
219239 // Semantic errors with name context
220240 Self :: DuplicateDefinition => "`{}` is already defined" . to_string ( ) ,
@@ -249,15 +269,13 @@ impl DiagnosticKind {
249269 }
250270
251271 // Type annotation specifics
252- Self :: InvalidTypeAnnotationSyntax => {
253- "type annotations use `::`, not `:` — {}" . to_string ( )
254- }
272+ Self :: InvalidTypeAnnotationSyntax => "use `::` for type annotations: {}" . to_string ( ) ,
255273
256- // Named def
257- Self :: UnnamedDef => "definitions must be named — {}" . to_string ( ) ,
274+ // Named def (no custom message needed; suggestion goes in hint)
275+ Self :: UnnamedDef => self . fallback_message ( ) . to_string ( ) ,
258276
259277 // Standard pattern: fallback + context
260- _ => format ! ( "{}; {{}}" , self . fallback_message( ) ) ,
278+ _ => format ! ( "{}: {{}}" , self . fallback_message( ) ) ,
261279 }
262280 }
263281
0 commit comments