155 lines
6.0 KiB
Rust
155 lines
6.0 KiB
Rust
//! Conversion from parser errors to user-facing diagnostics.
|
|
//!
|
|
//! This module maps [`ParseError`] values produced by the parser to structured
|
|
//! [`Diagnostic`] values suitable for rendering.
|
|
//!
|
|
//! It owns the top-level dispatch by [`crate::parser::ParseErrorKind`] and
|
|
//! keeps small shared utilities used by parse-error diagnostic constructors.
|
|
//!
|
|
//! Concrete diagnostic constructors are grouped into submodules that mirror
|
|
//! parser areas or grammar families.
|
|
|
|
use super::{Diagnostic, DiagnosticBuilder};
|
|
use crate::diagnostics::parse_error_diagnostics::block_items::{
|
|
diagnostic_block_expected_item, diagnostic_block_missing_closing_brace,
|
|
diagnostic_block_missing_semicolon_after_expression,
|
|
};
|
|
use crate::lexer::{TokenPosition, TokenSpan, TokenizedFile};
|
|
use crate::parser::{ParseError, ParseErrorKind};
|
|
|
|
mod block_items;
|
|
mod control_flow_expressions;
|
|
mod primary_expressions;
|
|
|
|
#[derive(Clone, Copy)]
|
|
enum FoundAt<'src> {
|
|
Token(&'src str),
|
|
EndOfFile,
|
|
Unknown,
|
|
}
|
|
|
|
fn found_at<'src>(file: &TokenizedFile<'src>, position: TokenPosition) -> FoundAt<'src> {
|
|
if let Some(token_text) = file.token_text(position) {
|
|
FoundAt::Token(token_text)
|
|
} else if file.is_eof(&position) {
|
|
FoundAt::EndOfFile
|
|
} else {
|
|
FoundAt::Unknown
|
|
}
|
|
}
|
|
|
|
fn collapse_span_to_end_on_same_line(file: &TokenizedFile<'_>, mut span: TokenSpan) -> TokenSpan {
|
|
if file.same_line(span.start, span.end) {
|
|
span.start = span.end;
|
|
}
|
|
span
|
|
}
|
|
|
|
fn should_show_context_label<'src>(
|
|
file: &TokenizedFile<'src>,
|
|
context_span: TokenSpan,
|
|
blame_span: TokenSpan,
|
|
) -> bool {
|
|
!file.same_line(context_span.start, blame_span.end)
|
|
}
|
|
|
|
fn primary_span_with_optional_multiline_context<'src>(
|
|
file: &TokenizedFile<'src>,
|
|
context_span: Option<TokenSpan>,
|
|
blame_span: TokenSpan,
|
|
) -> TokenSpan {
|
|
if let Some(context_span) = context_span
|
|
&& should_show_context_label(file, context_span, blame_span)
|
|
{
|
|
TokenSpan {
|
|
start: context_span.start,
|
|
end: blame_span.end,
|
|
}
|
|
} else {
|
|
collapse_span_to_end_on_same_line(file, blame_span)
|
|
}
|
|
}
|
|
|
|
pub(crate) fn diagnostic_from_parse_error<'src>(
|
|
error: ParseError,
|
|
file: &TokenizedFile<'src>,
|
|
) -> Diagnostic {
|
|
use control_flow_expressions::*;
|
|
use primary_expressions::*;
|
|
match error.kind {
|
|
// primary_expressions.rs
|
|
ParseErrorKind::ParenthesizedExpressionInvalidStart => {
|
|
diagnostic_parenthesized_expression_invalid_start(error, file)
|
|
}
|
|
ParseErrorKind::ExpressionExpected => diagnostic_expression_expected(error, file),
|
|
ParseErrorKind::ParenthesizedExpressionMissingClosingParenthesis => {
|
|
diagnostic_parenthesized_expression_missing_closing_parenthesis(error, file)
|
|
}
|
|
ParseErrorKind::ClassTypeMissingTypeArgument => {
|
|
diagnostic_class_type_missing_type_argument(error, file)
|
|
}
|
|
ParseErrorKind::ClassTypeExpectedQualifiedTypeName => {
|
|
diagnostic_class_type_expected_qualified_type_name(error, file)
|
|
}
|
|
ParseErrorKind::ClassTypeInvalidStart => diagnostic_class_type_invalid_start(error, file),
|
|
ParseErrorKind::ClassTypeMissingClosingAngleBracket => {
|
|
diagnostic_class_type_missing_closing_angle_bracket(error, file)
|
|
}
|
|
ParseErrorKind::NewMissingClassSpecifier => {
|
|
diagnostic_new_missing_class_specifier(error, file)
|
|
}
|
|
ParseErrorKind::NewTooManyArguments => diagnostic_new_too_many_arguments(error, file),
|
|
|
|
ParseErrorKind::NewMissingClosingParenthesis => {
|
|
diagnostic_new_missing_closing_parenthesis(error, file)
|
|
}
|
|
ParseErrorKind::NewArgumentMissingComma => {
|
|
diagnostic_new_argument_missing_comma(error, file)
|
|
}
|
|
// control_flow_expressions.rs
|
|
ParseErrorKind::ConditionExpected => diagnostic_condition_expected(error, file),
|
|
ParseErrorKind::ControlFlowBodyExpected => {
|
|
diagnostic_control_flow_body_expected(error, file)
|
|
}
|
|
ParseErrorKind::DoMissingUntil => diagnostic_do_missing_until(error, file),
|
|
ParseErrorKind::ForEachIteratorExpressionExpected => {
|
|
diagnostic_for_each_iterator_expression_expected(error, file)
|
|
}
|
|
ParseErrorKind::ForLoopHeaderInitializerInvalidStart => {
|
|
diagnostic_for_loop_header_initializer_invalid_start(error, file)
|
|
}
|
|
ParseErrorKind::ForLoopHeaderMissingSemicolonAfterInitializer => {
|
|
diagnostic_for_loop_header_missing_semicolon_after_initializer(error, file)
|
|
}
|
|
ParseErrorKind::ForLoopHeaderConditionInvalidStart => {
|
|
diagnostic_for_loop_header_condition_invalid_start(error, file)
|
|
}
|
|
ParseErrorKind::ForLoopHeaderMissingSemicolonAfterCondition => {
|
|
diagnostic_for_loop_header_missing_semicolon_after_condition(error, file)
|
|
}
|
|
ParseErrorKind::ForLoopHeaderStepInvalidStart => {
|
|
diagnostic_for_loop_header_step_invalid_start(error, file)
|
|
}
|
|
ParseErrorKind::ForLoopHeaderMissingClosingParenthesis => {
|
|
diagnostic_for_loop_header_missing_closing_parenthesis(error, file)
|
|
}
|
|
ParseErrorKind::ReturnValueInvalidStart => {
|
|
diagnostic_return_value_invalid_start(error, file)
|
|
}
|
|
ParseErrorKind::BreakValueInvalidStart => diagnostic_break_value_invalid_start(error, file),
|
|
ParseErrorKind::GotoMissingLabel => diagnostic_goto_missing_label(error, file),
|
|
// block_items.rs
|
|
ParseErrorKind::BlockMissingSemicolonAfterExpression => {
|
|
diagnostic_block_missing_semicolon_after_expression(error, file)
|
|
}
|
|
ParseErrorKind::BlockMissingClosingBrace => {
|
|
diagnostic_block_missing_closing_brace(error, file)
|
|
}
|
|
ParseErrorKind::BlockExpectedItem => diagnostic_block_expected_item(error, file),
|
|
|
|
_ => DiagnosticBuilder::error(format!("error {:?} while parsing", error.kind))
|
|
.primary_label(error.covered_span, "happened here")
|
|
.build(),
|
|
}
|
|
}
|