rott/rottlib/src/parser/errors.rs
dkanus 588790b9b4 Refactor everything
Huge dump of refactored code. Still in the middle of the changes that
are to be squashed later in a one huge monster commit, because there is
no value in anything atomic here.
2026-04-05 20:32:11 +07:00

371 lines
15 KiB
Rust

//! Submodule with parsing related errors.
use crate::{ast::AstSpan, lexer::TokenPosition};
/// Internal parse error kinds.
///
/// Used by the parser as a compact signal for later construction of user-facing
/// diagnostics.
///
/// Naming convention:
/// - Prefix identifies the syntactic construct
/// (`Expression`, `For`, `Switch`, etc.).
/// - Suffix describes the exact problem (`MissingClosingParenthesis`,
/// `UnexpectedToken`, `MultipleDefaults`, etc.).
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum ParseErrorKind {
// ================== New errors that are 100% used! ==================
// headline: empty parenthesized expression
// primary label on ): expected an expression before this \)'`
// secondary label on (: parenthesized expression starts here
// Remove the parentheses or put an expression inside them.
ParenthesizedExpressionEmpty {
left_parenthesis_position: TokenPosition,
},
// headline: missing type argument in \class<...>``
// primary label on > or insertion site: expected a type name here
// secondary label on < or on class: type argument list starts here
// help: Write a type name, for example \class<Pawn>`.`
ClassTypeMissingTypeArgument {
left_angle_bracket_position: TokenPosition,
},
// headline: missing closing \>` in `class<...>``
// primary label on offending following token or EOF: expected \>` before this token` or at EOF: expected \>` here`
// secondary label on <: this \<` starts the type argument`
// help: Add \>` to close the class type expression.`
ClassTypeMissingClosingAngleBracket {
left_angle_bracket_position: TokenPosition,
},
// headline: missing closing \)'`
// primary label on the point where ) was expected: expected \)' here` or, if you have a real token there, expected \)' before this token`
// secondary label on the opening (: this \(` starts the parenthesized expression`
// help: Add \)' to close the expression.`
ParenthesizedExpressionMissingClosingParenthesis {
left_parenthesis_position: TokenPosition,
},
// headline: expected expression
// primary label: this token cannot start an expression
// optional help: Expressions can start with literals, identifiers, \(`, `{`, or expression keywords.`
ExpressionExpected,
// headline: invalid type argument in \class<...>``
// primary label on the bad token inside the angle brackets: expected a qualified type name here
// secondary label on class or <: while parsing this class type expression
// note: Only a type name is accepted between \<` and `>` here.`
ClassTypeInvalidTypeArgument {
left_angle_bracket_position: TokenPosition,
},
// headline: too many arguments in \new(...)``
// primary label on the fourth argument, or on the comma before it if that is easier: unexpected extra argument
// secondary label on the opening (: this argument list accepts at most three arguments
// note: The three slots are \outer`, `name`, and `flags`.`
// help: Remove the extra argument.
NewTooManyArguments {
left_parenthesis_position: TokenPosition,
},
// headline: missing closing \)' in `new(...)``
// primary label: expected \)' here`
// secondary label on the opening (: this argument list starts here
// help: Add \)' to close the argument list.`
NewMissingClosingParenthesis {
left_parenthesis_position: TokenPosition,
},
// missing class specifier in \new` expression`
// Primary label on the first token where a class specifier should have started: expected a class specifier here
// Secondary label on new: \new` expression starts here` If there was an argument list, an additional secondary on ( is also reasonable: optional \new(...)` arguments end here`
// Help: Add the class or expression to instantiate after \new` or `new(...)`.`
NewMissingClassSpecifier {
new_keyword_position: TokenPosition,
},
// ================== Old errors to be thrown away! ==================
/// Expression inside `(...)` could not be parsed and no closing `)`
/// was found.
FunctionCallMissingClosingParenthesis,
/// A `do` block was not followed by a matching `until`.
DoMissingUntil,
/// Found an unexpected token while parsing an expression.
ExpressionUnexpectedToken,
DeclEmptyVariableDeclarations,
DeclNoSeparatorBetweenVariableDeclarations,
DeclExpectedRightBracketAfterArraySize,
DeclExpectedCommaAfterVariableDeclarator,
TypeSpecExpectedType,
TypeSpecInvalidNamedTypeName,
TypeSpecArrayMissingOpeningAngle,
TypeSpecArrayMissingInnerType,
TypeSpecArrayMissingClosingAngle,
TypeSpecClassMissingInnerType,
TypeSpecClassMissingClosingAngle,
/// A `for` loop is missing its opening `(`.
ForMissingOpeningParenthesis,
/// The first `;` in `for (init; cond; step)` is missing.
ForMissingInitializationSemicolon,
/// The second `;` in `for (init; cond; step)` is missing.
ForMissingConditionSemicolon,
/// The closing `)` of a `for` loop is missing.
ForMissingClosingParenthesis,
/// An expression inside a block is not terminated with `;`.
BlockMissingSemicolonAfterExpression,
/// A statement inside a block is not terminated with `;`.
BlockMissingSemicolonAfterStatement,
BlockMissingClosingBrace,
/// `switch` has no body (missing matching braces).
SwitchMissingBody,
/// The first top-level item in a `switch` body is not a `case`.
SwitchTopLevelItemNotCase,
/// A `case` arm is missing the trailing `:`.
SwitchCaseMissingColon,
/// Found more than one `default` branch.
SwitchDuplicateDefault,
/// Found `case` arms after a `default` branch.
SwitchCasesAfterDefault,
SwitchMissingClosingBrace,
/// A `goto` was not followed by a label.
GotoMissingLabel,
/// Unexpected end of input while parsing.
UnexpectedEndOfFile,
/// Token looked like a numeric literal but could not be parsed as one.
InvalidNumericLiteral,
/// A bare expression appeared in a `switch` arm but was not the final arm.
///
/// Such an expression must be terminated with `;` or be the final arm.
SwitchBareExpressionBeforeNextArm,
/// A `local` declaration is missing its first identifier.
///
/// At least one variable name must follow the type.
LocalMissingIdentifier,
/// A `local` declaration was followed by a token that cannot serve
/// as a type name.
LocalInvalidTypeName,
/// Invalid variable name identifier in `local` variable definition.
LocalBadVariableIdentifier,
/// An initializer appears in a `local` variable declaration.
LocalInitializerNotAllowed,
/// A non-`local` variable declaration is missing its first identifier.
///
/// At least one variable name must follow the type.
DeclMissingIdentifier,
/// Invalid variable name identifier in non-`local` variable definition.
DeclBadVariableIdentifier,
/// Found an unexpected token while parsing a declaration literal.
///
/// Expected one of: integer, float, string, `true`, `false`, `none`
/// or an identifier.
DeclarationLiteralUnexpectedToken,
/// A class name was expected, but the current token is not an identifier.
///
/// Emitted when parsing `class Foo` and the token after `class` is not an
/// identifier (so its string value cannot be extracted).
ClassNameNotIdentifier,
/// A parent class name after `extends` was expected, but the token is not
/// an identifier.
///
/// Emitted when parsing `class Foo extends Bar` and the token after
/// `extends` is not an identifier.
ClassParentNameNotIdentifier,
/// A class declaration was not terminated with `;`.
///
/// Emitted when the parser reaches the end of a class definition but
/// does not encounter the required semicolon.
ClassMissingSemicolon,
/// An identifier was expected inside optional parentheses, but the token
/// is not an identifier.
///
/// Emitted by helpers that parse either `(<Ident>)` or bare `<Ident>`.
ParenthesisedIdentifierNameNotIdentifier,
/// A `(` was seen before an identifier, but the matching `)` was not found.
///
/// Emitted when parsing a parenthesised identifier like `(Foo)`.
ParenthesisedIdentifierMissingClosingParenthesis,
/// `HideCategories` is missing the opening `(` before the category list.
///
/// Expected syntax: `HideCategories(CategoryA, CategoryB, ...)`.
HideCategoriesMissingOpeningParenthesis,
/// `HideCategories` is missing the closing `)` after the category list.
HideCategoriesMissingClosingParenthesis,
/// `HideCategories` is missing the opening `(` before the category list.
///
/// Expected syntax: `HideCategories(CategoryA, CategoryB, ...)`.
ShowCategoriesMissingOpeningParenthesis,
/// `HideCategories` is missing the closing `)` after the category list.
ShowCategoriesMissingClosingParenthesis,
/// `Within` must be followed by a class or package name identifier.
///
/// Example: `Within(MyOuterClass)`.
WithinNameNotIdentifier,
/// `operator` modifier is missing the opening `(` before
/// the precedence rank.
///
/// Expected syntax: `operator(<integer>)`.
OperatorMissingOpeningParenthesis,
/// `operator(<...>)` must contain an integer literal precedence rank.
///
/// Emitted when the token inside parentheses is not an integer literal.
OperatorPrecedenceNotIntegerLiteral,
/// `operator(<integer>` is missing the closing `)`.
OperatorMissingClosingParenthesis,
ParamInvalidTypeName,
ParamMissingIdentifier,
FunctionReturnTypeNotTypeName,
FunctionNameNotIdentifier,
FunctionParamsMissingOpeningParenthesis,
FunctionParamsMissingClosingParenthesis,
ClassUnexpectedItem,
EnumMissingLeftBrace,
EnumBadVariant,
StructFieldMissingName,
StructFieldMissingSemicolon,
StructMissingRightBrace,
// Named enum/struct typedefs
EnumMissingKeyword, // class member: expected `enum`
EnumExpectedNameOrBrace, // after `enum`, expected identifier
EnumNoClosingBrace,
EnumEmptyVariants,
EnumNoSeparatorBetweenVariants,
EnumMissingLBrace,
StructMissingKeyword, // class member: expected `struct`
StructExpectedNameOrBrace, // after `struct`, expected identifier
StructExpectedExtendsOrBrace,
StructMissingLeftBrace,
StructExpectedBaseName,
StructBodyUnexpectedItem,
CppDirectiveMissingCppBlock,
// var(...) field decls
VarMissingKeyword, // class member: expected `var`
VarSpecsMissingOpeningParenthesis, // after `var`, expected '('
VarSpecNotIdentifier, // inside var(...), expected identifier
VarSpecsMissingClosingParenthesis, // var(...) missing ')'
// Generic decl end
DeclMissingSemicolon, // class-level declaration missing `;`
// --- Replication ---
ReplicationMissingReliability,
ReplicationIfMissingOpeningParenthesis,
ReplicationIfMissingClosingParenthesis,
ReplicationMemberNotIdentifier,
ReplicationMemberMissingClosingParenthesis,
ReplicationRuleMissingSemicolon,
ReplicationMissingKeyword,
ReplicationMissingLBrace,
ReplicationMissingRBrace,
// --- DefaultProperties ---
DefaultPropPathExpectedIdentifier,
DefaultPropIndexNotIntegerLiteral,
DefaultPropIndexMissingClosingParenthesis,
DefaultPropAssignMissingEq,
DefaultPropsMissingKeyword,
DefaultPropsMissingLBrace,
DefaultPropsMissingRBrace,
// --- Begin/End Object headers ---
ObjectBeginMissingKeyword,
ObjectMissingKeyword,
ObjectHeaderKeyNotIdentifier,
ObjectHeaderMissingEq,
// --- State / ignores ---
IgnoresItemNotIdentifier,
IgnoresMissingSemicolon,
StateMissingKeyword,
StateNameNotIdentifier,
StateParentNameNotIdentifier,
StateMissingLBrace,
StateMissingRBrace,
ClassMissingKeyword,
TypeMissingLT,
TypeMissingGT,
StateParensMissingRParen,
BadTypeInClassTypeDeclaration,
IdentifierExpected,
// --- Generic list diagnostics (comma-separated, closed by `)`) ---
/// Saw `)` immediately after `(`, or closed the list without any items.
/// Use when a construct requires at least one item: e.g. `HideCategories(...)`.
ListEmpty,
/// Parser was positioned where an item was required but found neither an
/// item nor a terminator. Typical triggers:
/// - Leading comma: `(, Foo)`
/// - Double comma: `(Foo,, Bar)`
/// - Garbage in place of an item: `(@@, Foo)`
///
/// Recovery: skip to next comma or `)`.
ListMissingIdentifierBeforeSeparator,
/// Parser was positioned where an item was required but found neither an
/// item nor a terminator. Typical triggers:
/// - Leading comma: `(, Foo)`
/// - Double comma: `(Foo,, Bar)`
/// - Garbage in place of an item: `(@@, Foo)`
///
/// Recovery: skip to next comma or `)`.
ListInvalidIdentifier,
/// Two items without a comma (or some token after an item where a comma
/// was required). Typical triggers:
/// - Adjacent identifiers: `(Foo Bar)`
/// - Token after an item where only `,` or `)` are valid.
///
/// Recovery: behave as if a comma were present; continue with the next item.
ListMissingSeparator,
/// Comma directly before `)`: `(Foo, )`.
/// Treat as a soft error or warning, depending on your policy.
ListTrailingSeparator,
FunctionArgumentMissingComma,
// Expression was required, but none started
MissingExpression,
MissingBranchBody,
CallableExpectedHeader,
CallableExpectedKind,
CallableOperatorInvalidPrecedence,
CallableMissingBodyOrSemicolon,
CallableNameNotIdentifier,
CallablePrefixOperatorInvalidSymbol,
CallableInfixOperatorInvalidSymbol,
CallablePostfixOperatorInvalidSymbol,
CallableParamsMissingOpeningParenthesis,
CallableParamsMissingClosingParenthesis,
NativeModifierIdNotIntegerLiteral,
}
/// Enumerates all specific kinds of parsing errors that the parser can emit.
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
#[must_use]
pub struct ParseError {
/// The specific kind of parse error that occurred.
pub kind: ParseErrorKind,
pub anchor: TokenPosition,
/// Where the user should look first.
pub blame_span: AstSpan,
/// The source span in which the error was detected.
pub covered_span: AstSpan,
pub related_span: Option<AstSpan>,
}
pub type ParseResult<'src, 'arena, T> = Result<T, ParseError>;
impl crate::parser::Parser<'_, '_> {
pub(crate) fn make_error_here(&self, error_kind: ParseErrorKind) -> ParseError {
self.make_error_at(error_kind, self.last_consumed_position_or_start())
}
pub(crate) fn make_error_at(
&self,
error_kind: ParseErrorKind,
position: TokenPosition,
) -> ParseError {
ParseError {
kind: error_kind,
anchor: position,
blame_span: AstSpan::new(position),
covered_span: AstSpan::new(position),
related_span: None,
}
}
}