///|
/// Where an incorrect name was found (the compiler's
/// `IncorrectNamePosition`).
pub(all) enum IncorrectNamePosition {
AsPattern
Module
Function
Attribute
Constant
Target
Variable
} derive(Eq, Debug)
///|
/// The compiler's `ParseErrorType`, one variant each, plus `TooMuchNesting`
/// (gleam-parser's own limit). `LexError` carries the scanner's diagnostic
/// for a lexical error met by a snippet parser.
pub(all) enum ParseErrorType {
ExpectedEqual
ExpectedExpr
ExpectedName
ExpectedPattern
ExpectedType
ExpectedUpName
ExpectedValue
ExpectedDefinition
ExpectedDeprecationMessage
ExpectedExternalArguments
ExpectedFunctionDefinition
ExpectedTargetName
ExprLparStart
ExtraSeparator
IncorrectName(IncorrectNamePosition)
IncorrectUpName
InvalidBitArraySegment
InvalidBitArrayUnit
InvalidTailPattern
InvalidTupleAccess
LexError(@diagnostic.Diagnostic)
NestedBitArrayPattern
NoLetBinding
NoValueAfterEqual
NotConstType
OpNakedRight
OpaqueTypeAlias
TooManyArgHoles
DuplicateAttribute
UnknownAttribute
UnknownTarget
ListSpreadWithoutElements
ListSpreadFollowedByElements
ListSpreadWithAnotherSpread(first_spread_location~ : @span.SrcSpan)
UnexpectedLabel
UnexpectedEof
UnexpectedReservedWord
UnexpectedToken(
token~ : @scanner.TokenKind,
expected~ : ArrayView[String],
hint~ : String?
)
UnexpectedFunction
ConcatPatternVariableLeftHandSide
ConcatPatternVariableWithSuffix(name~ : String)
ListSpreadWithoutTail
ExpectedFunctionBody
RedundantInternalAttribute
InvalidModuleTypePattern
ListPatternSpreadFollowedByElements
ExpectedRecordConstructor(
type_name~ : String,
public~ : Bool,
opaque_~ : Bool,
fields~ : ArrayView[@ast.RecordConstructorArg]
)
CallInClauseGuard
IfExpression
TypeDefinitionNoArguments
UnknownAttributeRecordVariant
IncorrectImportModuleSeparator(module_~ : String, item~ : String)
EmptyGuardBlock
ConstantInsideFunction
FunctionDefinitionAngleGenerics
TypeUsageAngleGenerics(
module_~ : String?,
name~ : String,
arguments~ : ArrayView[@ast.TypeAst]
)
TypeDefinitionAngleGenerics(name~ : String, arguments~ : ArrayView[String])
MissingConstantAsMessage
RecordUpdateAfterFields
TooMuchNesting(budget~ : @fuel.Budget, spent~ : Array[@fuel.Spent])
} derive(Eq, Debug)
///|
/// A syntax error: the compiler's error and its span.
pub(all) suberror ParseError {
ParseError(error~ : ParseErrorType, location~ : @span.SrcSpan)
} derive(Eq, Debug)
///|
/// The compiler's `ParseErrorDetails`: the body text, the primary label
/// text, the extra labels and the hint.
pub(all) struct ParseErrorDetails {
text : String
label_text : String
extra_labels : ArrayView[@diagnostic.Label]
hint : String?
} derive(Eq, Debug)
///|
/// The variant name (`UnexpectedToken`, …), as the oracle writes it.
pub fn ParseErrorType::kind(self : ParseErrorType) -> String {
match self {
ExpectedEqual => "ExpectedEqual"
ExpectedExpr => "ExpectedExpr"
ExpectedName => "ExpectedName"
ExpectedPattern => "ExpectedPattern"
ExpectedType => "ExpectedType"
ExpectedUpName => "ExpectedUpName"
ExpectedValue => "ExpectedValue"
ExpectedDefinition => "ExpectedDefinition"
ExpectedDeprecationMessage => "ExpectedDeprecationMessage"
ExpectedExternalArguments => "ExpectedExternalArguments"
ExpectedFunctionDefinition => "ExpectedFunctionDefinition"
ExpectedTargetName => "ExpectedTargetName"
ExprLparStart => "ExprLparStart"
ExtraSeparator => "ExtraSeparator"
IncorrectName(_) => "IncorrectName"
IncorrectUpName => "IncorrectUpName"
InvalidBitArraySegment => "InvalidBitArraySegment"
InvalidBitArrayUnit => "InvalidBitArrayUnit"
InvalidTailPattern => "InvalidTailPattern"
InvalidTupleAccess => "InvalidTupleAccess"
LexError(_) => "LexError"
NestedBitArrayPattern => "NestedBitArrayPattern"
NoLetBinding => "NoLetBinding"
NoValueAfterEqual => "NoValueAfterEqual"
NotConstType => "NotConstType"
OpNakedRight => "OpNakedRight"
OpaqueTypeAlias => "OpaqueTypeAlias"
TooManyArgHoles => "TooManyArgHoles"
DuplicateAttribute => "DuplicateAttribute"
UnknownAttribute => "UnknownAttribute"
UnknownTarget => "UnknownTarget"
ListSpreadWithoutElements => "ListSpreadWithoutElements"
ListSpreadFollowedByElements => "ListSpreadFollowedByElements"
ListSpreadWithAnotherSpread(..) => "ListSpreadWithAnotherSpread"
UnexpectedLabel => "UnexpectedLabel"
UnexpectedEof => "UnexpectedEof"
UnexpectedReservedWord => "UnexpectedReservedWord"
UnexpectedToken(..) => "UnexpectedToken"
UnexpectedFunction => "UnexpectedFunction"
ConcatPatternVariableLeftHandSide => "ConcatPatternVariableLeftHandSide"
ConcatPatternVariableWithSuffix(..) => "ConcatPatternVariableWithSuffix"
ListSpreadWithoutTail => "ListSpreadWithoutTail"
ExpectedFunctionBody => "ExpectedFunctionBody"
RedundantInternalAttribute => "RedundantInternalAttribute"
InvalidModuleTypePattern => "InvalidModuleTypePattern"
ListPatternSpreadFollowedByElements => "ListPatternSpreadFollowedByElements"
ExpectedRecordConstructor(..) => "ExpectedRecordConstructor"
CallInClauseGuard => "CallInClauseGuard"
IfExpression => "IfExpression"
TypeDefinitionNoArguments => "TypeDefinitionNoArguments"
UnknownAttributeRecordVariant => "UnknownAttributeRecordVariant"
IncorrectImportModuleSeparator(..) => "IncorrectImportModuleSeparator"
EmptyGuardBlock => "EmptyGuardBlock"
ConstantInsideFunction => "ConstantInsideFunction"
FunctionDefinitionAngleGenerics => "FunctionDefinitionAngleGenerics"
TypeUsageAngleGenerics(..) => "TypeUsageAngleGenerics"
TypeDefinitionAngleGenerics(..) => "TypeDefinitionAngleGenerics"
MissingConstantAsMessage => "MissingConstantAsMessage"
RecordUpdateAfterFields => "RecordUpdateAfterFields"
TooMuchNesting(..) => "TooMuchNesting"
}
}
///|
/// The compiler's `Display` for a token: its text in backquotes. A name,
/// number or string shows its own text.
pub fn token_text(kind : @scanner.TokenKind) -> String {
let s = match kind {
Name(n) | UpName(n) | DiscardName(n) => n
Int(value~, ..) | Float(value~, ..) => value
String(v) => v
LeftParen => "("
RightParen => ")"
LeftSquare => "["
RightSquare => "]"
LeftBrace => "{"
RightBrace => "}"
Plus => "+"
Minus => "-"
Star => "*"
Slash => "/"
Less => "<"
Greater => ">"
LessEqual => "<="
GreaterEqual => ">="
Percent => "%"
PlusDot => "+."
MinusDot => "-."
StarDot => "*."
SlashDot => "/."
LessDot => "<."
GreaterDot => ">."
LessEqualDot => "<=."
GreaterEqualDot => ">=."
Concatenate => "<>"
Colon => ":"
Comma => ","
Hash => "#"
Bang => "!"
Equal => "="
EqualEqual => "=="
NotEqual => "!="
Vbar => "|"
VbarVbar => "||"
AmperAmper => "&&"
LtLt => "<<"
GtGt => ">>"
Pipe => "|>"
Dot => "."
RArrow => "->"
LArrow => "<-"
DotDot => ".."
At => "@"
Keyword(k) => k.text()
Error => "?"
}
"`\{s}`"
}
///|
/// What the compiler says it found (`UnexpectedToken` text).
fn found_text(kind : @scanner.TokenKind) -> String {
match kind {
Int(..) => "an Int"
Float(..) => "a Float"
String(_) => "a String"
DiscardName(_) => "a discard name"
Name(_) | UpName(_) => "a name"
Keyword(_) => "the keyword \{token_text(kind)}"
_ => token_text(kind)
}
}
///|
/// The source form of a type annotation (`TypeAst::print`), for messages.
pub fn type_ast_text(t : @ast.TypeAst) -> String {
let sb = StringBuilder()
write_type_ast(sb, t)
sb.to_string()
}
///|
fn write_type_ast(sb : StringBuilder, t : @ast.TypeAst) -> Unit {
fn list(xs : ArrayView[@ast.TypeAst]) {
for i, x in xs {
if i > 0 {
sb.write_string(", ")
}
write_type_ast(sb, x)
}
}
match t {
Var(v) => sb.write_string(v.name)
Hole(h) => sb.write_string(h.name)
Tuple(tuple) => {
sb.write_string("#(")
list(tuple.elements)
sb.write_string(")")
}
Fn(f) => {
sb.write_string("fn(")
list(f.arguments)
sb.write_string(") -> ")
write_type_ast(sb, f.return_)
}
Constructor(c) => {
match c.name {
Unqualified(name~, ..) => sb.write_string(name)
Qualified(module_~, name~, ..) => {
sb.write_string(module_)
sb.write_string(".")
if name is Some(n) {
sb.write_string(n.name)
}
}
}
if c.arguments.length() > 0 {
sb.write_string("(")
list(c.arguments)
sb.write_string(")")
}
}
}
}
///|
fn details(
text : String,
label_text : String,
hint? : String,
extra_labels~ : Array[@diagnostic.Label] = [],
) -> ParseErrorDetails {
{ text, label_text, extra_labels: extra_labels[:], hint, }
}
///|
/// The compiler's `ParseErrorType::details()`: body text, primary label,
/// extra labels and hint, with its exact wording and `wrap` at 75 bytes.
pub fn ParseErrorType::details(self : ParseErrorType) -> ParseErrorDetails {
match self {
ExpectedEqual => details("", "I was expecting a '=' after this")
ExpectedExpr => details("", "I was expecting an expression after this")
ExpectedName => details("", "I was expecting a name here")
ExpectedPattern => details("", "I was expecting a pattern after this")
ExpectedType =>
details(
"See: https://tour.gleam.run/basics/assignments/", "I was expecting a type after this",
)
ExpectedUpName => details("", "I was expecting a type name here")
ExpectedValue => details("", "I was expecting a value after this")
ExpectedDefinition => details("", "I was expecting a definition after this")
ExpectedDeprecationMessage =>
details(
"See: https://tour.gleam.run/functions/deprecations/", "A deprecation attribute must have a string message.",
)
ExpectedExternalArguments =>
details(
"",
"This attribute is incomplete",
hint="See https://tour.gleam.run/advanced-features/externals/",
)
ExpectedFunctionDefinition =>
details("", "I was expecting a function definition after this")
ExpectedTargetName =>
details(
"Try `erlang`, `javascript`.", "I was expecting a target name after this",
)
ExtraSeparator =>
details("", "This is an extra delimiter", hint="Try removing it?")
ExprLparStart =>
details(
"",
"This parenthesis cannot be understood here",
hint="To group expressions in Gleam, use \"{\" and \"}\"; tuples are created with `#(` and `)`.",
)
IncorrectName(kind) => {
let subject = match kind {
AsPattern => "A name after `as`"
Module => "A module name"
Function => "A function name"
Attribute => "An attribute name"
Constant => "A constant name"
Target => "A target name"
Variable => "A variable name"
}
details(
@scanner.wrap(
"\{subject} must start with a lowercase letter, and can contain a-z, 0-9, or _.",
),
"I'm expecting a lowercase name here",
)
}
IncorrectUpName =>
details(
"",
"I'm expecting a type name here",
hint=@scanner.wrap(
"Type names start with a uppercase letter, and can contain a-z, A-Z, or 0-9.",
),
)
InvalidBitArraySegment =>
details(
"See: https://tour.gleam.run/data-types/bit-arrays/",
"This is not a valid BitArray segment option",
hint="Valid BitArray segment options are:\n" +
@scanner.wrap(
"bits, bytes, int, float, utf8, utf16, utf32, utf8_codepoint, utf16_codepoint, utf32_codepoint, signed, unsigned, big, little, native, size, unit.",
),
)
InvalidBitArrayUnit =>
details(
"See: https://tour.gleam.run/data-types/bit-arrays/",
"This is not a valid BitArray unit value",
hint="Unit must be an integer literal >= 1 and <= 256.",
)
InvalidTailPattern =>
details("", "This part of a list pattern can only be a name or a discard")
InvalidTupleAccess =>
details(
"",
"This integer is not valid for tuple access",
hint="Only non negative integer literals like 0, or 1_000 can be used.",
)
LexError(d) => details(d.text, d.message)
NestedBitArrayPattern => details("", "BitArray patterns cannot be nested")
NotConstType =>
details(
"See: https://tour.gleam.run/basics/constants/", "This type is not allowed in module constants",
)
NoLetBinding =>
details(
"See: https://tour.gleam.run/basics/assignments/",
"There must be a 'let' to bind variable to value",
hint="Use let for binding.",
)
NoValueAfterEqual =>
details("", "I was expecting to see a value after this equals sign")
OpaqueTypeAlias =>
details(
"See: https://tour.gleam.run/basics/type-aliases/", "Type Aliases cannot be opaque",
)
OpNakedRight =>
details(
"",
"This operator has no value on its right side",
hint="Remove it or put a value after it.",
)
TooManyArgHoles =>
details(
"See: https://tour.gleam.run/functions/functions/",
"There is more than 1 argument hole in this function call",
hint="Function calls can have at most one argument hole.",
)
UnexpectedEof => details("", "The module ended unexpectedly")
ListSpreadWithoutElements =>
details(
"See: https://tour.gleam.run/basics/lists/",
"This spread does nothing",
hint="Try prepending some elements [1, 2, ..list].",
)
ListSpreadWithAnotherSpread(first_spread_location~) =>
details(
[
"Lists are immutable and singly-linked, so to join two or more lists",
"all the elements of the lists would need to be copied into a new list.",
"This would be slow, so there is no built-in syntax for it.",
].join("\n"),
"I wasn't expecting a second list here",
extra_labels=[
{ span: first_spread_location, text: "You're using a list here", },
],
)
ListSpreadFollowedByElements =>
details(
[
"Lists are immutable and singly-linked, so to append items to them", "all the elements of a list would need to be copied into a new list.",
"This would be slow, so there is no built-in syntax for it.",
].join("\n"),
"I wasn't expecting elements after this",
hint="Prepend items to the list and then reverse it once you are done.",
)
ListPatternSpreadFollowedByElements =>
details(
[
"Lists are immutable and singly-linked, so to match on the end", "of a list would require the whole list to be traversed. This",
"would be slow, so there is no built-in syntax for it. Pattern", "match on the start of the list instead.",
].join("\n"),
"I wasn't expecting elements after this",
)
UnexpectedReservedWord =>
details(
"",
"This is a reserved word",
hint="I was expecting to see a name here.",
)
UnexpectedLabel =>
details(
"Please remove the argument label.", "Argument labels are not allowed for anonymous functions",
)
UnexpectedToken(token~, expected~, hint~) => {
let lines = ["Found \{found_text(token)}, expected one of: "]
for s in expected {
lines.push("- \{s}")
}
details(lines.join("\n"), "I was not expecting this", hint?)
}
ConcatPatternVariableLeftHandSide =>
details(
[
"We can't tell what size this prefix should be so we don't know", "how to handle this pattern.",
"", "If you want to match one character consider using `pop_grapheme`",
"from the stdlib's `gleam/string` module.",
].join("\n"),
"This must be a string literal",
)
ConcatPatternVariableWithSuffix(name~) =>
details(
[
"A string pattern can only match on a literal string prefix.",
"",
@scanner.wrap(
"Matching on a literal suffix is not possible, because `\{name}` would have an unknown size.",
),
].join("\n"),
"This pattern is not allowed",
)
UnexpectedFunction =>
details("", "Functions can only be called within other functions")
ListSpreadWithoutTail =>
details(
"If a list expression has a spread then a tail must also be given.", "I was expecting a value after this spread",
)
UnknownAttribute =>
details(
"",
"I don't recognise this attribute",
hint="Try `deprecated`, `external` or `internal` instead.",
)
DuplicateAttribute =>
details("This attribute has already been given.", "Duplicate attribute")
UnknownTarget =>
details("Try `erlang`, `javascript`.", "I don't recognise this target")
ExpectedFunctionBody => details("", "This function does not have a body")
RedundantInternalAttribute =>
details(
"Only a public definition can be annotated as internal.",
"Redundant internal attribute",
hint="Remove the `@internal` annotation.",
)
InvalidModuleTypePattern =>
details(
[
"I'm expecting a pattern here,", "or a variable to bind a value to, etc.",
].join("\n"),
"Invalid pattern",
hint="A pattern can be a constructor name, a literal value\nSee: https://tour.gleam.run/flow-control/case-expressions/",
)
ExpectedRecordConstructor(type_name~, public~, opaque_~, fields~) => {
let accessor = if public { "pub " } else { "" }
let opaque_text = if public && opaque_ { "opaque " } else { "" }
let field_lines = fields.map(field => {
match field.label {
Some(label) => " \{label.name}: \{type_ast_text(field.ast)},"
None => " \{type_ast_text(field.ast)},"
}
})
details(
"Each custom type variant must have a constructor:\n\n\{accessor}\{opaque_text}type \{type_name} {\n \{type_name}(\n\{field_lines.join("\n")}\n )\n}",
"I was not expecting this",
)
}
CallInClauseGuard =>
details(
"Functions cannot be called in clause guards.", "Unsupported expression",
)
IfExpression =>
details(
[
"If you want to write a conditional expression you can use a `case`:",
"", " case condition {", " True -> todo", " False -> todo",
" }", "", "See: https://tour.gleam.run/flow-control/case-expressions/",
].join("\n"),
"Gleam doesn't have if expressions",
)
TypeDefinitionNoArguments =>
details(
"A generic type must have at least a generic parameter.",
"I was expecting generic parameters here",
hint="If a type is not generic you should omit the `()`.",
)
UnknownAttributeRecordVariant =>
details(
"",
"This attribute cannot be used on a variant.",
hint="Did you mean `@deprecated`?",
)
IncorrectImportModuleSeparator(module_~, item~) =>
details(
[
"Perhaps you meant one of:",
"",
" import \{module_}/\{item}",
" import \{module_}.{item}",
].join("\n"),
"I was expecting either `/` or `.{` here.",
)
EmptyGuardBlock => details("", "A clause guard block cannot be empty")
MissingConstantAsMessage =>
details(
"", "I was expecting to see a constant expression after this `as`",
)
ConstantInsideFunction =>
details(
@scanner.wrap(
"All variables are immutable in Gleam, so constants inside functions are not necessary.",
),
"Constants are not allowed inside functions",
hint="Either move this into the global scope or use `let` binding instead.",
)
FunctionDefinitionAngleGenerics =>
details(
[
"Generic function type variables do not need to be predeclared like they",
"would be in some other languages, instead they are written with lowercase",
"names.", "", " fn example(argument: generic) -> generic", "", "See: https://tour.gleam.run/functions/generic-functions/",
].join("\n"),
"I was expecting `(` here.",
)
TypeUsageAngleGenerics(module_~, name~, arguments~) => {
let type_arguments = arguments.map(type_ast_text).join(", ")
let replacement = match module_ {
Some(m) => "\{m}.\{name}(\{type_arguments})"
None => "\{name}(\{type_arguments})"
}
details(
[
"Type parameters use lowercase names and are surrounded by parentheses.",
"",
" \{replacement}",
"",
"See: https://tour.gleam.run/data-types/generic-custom-types/",
].join("\n"),
"I was expecting `(` here.",
)
}
TypeDefinitionAngleGenerics(name~, arguments~) =>
details(
[
"Type parameters use lowercase names and are surrounded by parentheses.",
"",
" type \{name}(\{arguments.join(", ")}) {",
"",
"See: https://tour.gleam.run/data-types/generic-custom-types/",
].join("\n"),
"I was expecting `(` here.",
)
RecordUpdateAfterFields =>
details(
[
"When updating a record, the previous record comes before the fields to change",
"", " let record = Record(field1:, field2:)", " let updated_record = Record(..record, field1: \"new_value\")",
"", "See: https://tour.gleam.run/data-types/record-updates/",
].join("\n"),
"I wasn't expecting a record update here",
)
TooMuchNesting(budget~, spent~) => {
let stack = spent
.map(s => "\{s.operation.name()} ×\{s.levels}")
.join(", ")
details(
"The fuel budget of \{budget.units()} ran out while nesting \{stack}. gleam-parser spends one unit of fuel per nested level of syntax; the Gleam compiler has no such limit.",
"Recursion limit exceeded",
hint="Split the code into smaller functions or constants, or give the parser a Config with more fuel on a target with a large stack (native, js).",
)
}
}
}
///|
/// The diagnostic for a parse error: `GL-PARSE-001` with the compiler's
/// title "Syntax error" and wording; `GL-PARSE-004` for `TooMuchNesting`;
/// the scanner's own diagnostic for `LexError`. `UnexpectedEof` keeps the
/// compiler's raw span `0..0`; the renderer moves it to the last byte as
/// `error.rs` does.
pub fn ParseError::to_diagnostic(self : ParseError) -> @diagnostic.Diagnostic {
let ParseError(error~, location~) = self
match error {
LexError(d) => d
_ => {
let details = error.details()
@diagnostic.Diagnostic::new(
code=if error is TooMuchNesting(..) {
"GL-PARSE-004"
} else {
"GL-PARSE-001"
},
kind=error.kind(),
severity=Error,
title="Syntax error",
message=details.label_text,
span=location,
labels=details.extra_labels.to_owned(),
text=details.text,
hint=details.hint,
)
}
}
}
///|
/// The diagnostic for a parser warning, with the compiler's title, text and
/// label (`warning.rs`): `GL-PARSE-002` for deprecated syntax,
/// `GL-PARSE-003` for a detached doc comment.
pub fn warning_diagnostic(w : @ast.Warning) -> @diagnostic.Diagnostic {
fn deprecated(
kind : String,
location : @span.SrcSpan,
title : String,
text : String,
label : String,
) -> @diagnostic.Diagnostic {
@diagnostic.Diagnostic::new(
code="GL-PARSE-002",
kind~,
severity=Warning,
title~,
message=label,
span=location,
text=@scanner.wrap(text),
)
}
match w {
DeprecatedListPrepend(location~) =>
deprecated(
"DeprecatedListPrepend", location, "Deprecated prepend syntax", "This syntax for prepending to a list is deprecated.\nWhen prepending an item to a list it should be preceded by a comma, like this: `[item, ..list]`.",
"This spread should be preceded by a comma",
)
DeprecatedListPattern(location~) =>
deprecated(
"DeprecatedListPattern", location, "Deprecated list pattern matching syntax",
"This syntax for pattern matching on a list is deprecated.\nWhen matching on the rest of a list it should always be preceded by a comma, like this: `[item, ..list]`.",
"This spread should be preceded by a comma",
)
DeprecatedRecordSpreadPattern(location~) =>
deprecated(
"DeprecatedRecordSpreadPattern", location, "Deprecated record pattern matching syntax",
"This syntax for pattern matching on a record is deprecated.", "This should be preceded by a comma",
)
DeprecatedListCatchAllPattern(location~) =>
deprecated(
"DeprecatedListCatchAllPattern", location, "Deprecated list pattern matching syntax",
"This syntax for pattern matching on lists is deprecated.\nTo match on all possible lists, use the `_` catch-all pattern instead.",
"Replace this with `_`",
)
DeprecatedListSpreadAsVariablePattern(location~, name~) =>
deprecated(
"DeprecatedListSpreadAsVariablePattern",
location,
"Deprecated list pattern matching syntax",
"This syntax for pattern matching on lists is deprecated.\nThis spread matches the entire list, so the variable \{name} can be used as the pattern directly instead.",
"Replace this with `\{name}`",
)
DeprecatedEmptyClauseGuard(location~) =>
deprecated(
"DeprecatedEmptyClauseGuard", location, "Deprecated empty guard syntax",
"This syntax for an empty guard is deprecated. To have a clause without a guard, remove this.",
"This can be removed.",
)
DeprecatedTargetShorthand(target~, location~) => {
let full_name = target.name()
deprecated(
"DeprecatedTargetShorthand",
location,
"Deprecated target shorthand syntax",
"This shorthand target name is deprecated. Use the full name: `\{full_name}` instead.",
"This should be replaced with `\{full_name}`",
)
}
DetachedDocComment(location~) =>
@diagnostic.Diagnostic::new(
code="GL-PARSE-003",
kind="DetachedDocComment",
severity=Warning,
title="Detached doc comment",
message="This is not attached to a definition",
span=location,
text=@scanner.wrap(
"This doc comment is followed by a regular comment so it is not attached to any definition.",
),
hint=Some("Move the comment above the doc comment"),
)
}
}