From 13aa1e1326d2473c23eb2b3ecd94e86638edcef3 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Tue, 27 Feb 2024 18:42:06 +0100 Subject: [PATCH] wip: add a way to validate the fields error recovery --- .../src/compiler/analysis/definitions.rs | 38 +++++++++++++++- .../internal_macros/src/derive/spanned.rs | 3 +- .../recovered_only_for_delimited/test.rs | 44 +++++++++++++++++++ .../recovered_only_for_delimited/test.stderr | 5 +++ 4 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 crates/codegen/language/tests/src/fail/references/recovered_only_for_delimited/test.rs create mode 100644 crates/codegen/language/tests/src/fail/references/recovered_only_for_delimited/test.stderr diff --git a/crates/codegen/language/definition/src/compiler/analysis/definitions.rs b/crates/codegen/language/definition/src/compiler/analysis/definitions.rs index f13f2a1076..f07e1facf1 100644 --- a/crates/codegen/language/definition/src/compiler/analysis/definitions.rs +++ b/crates/codegen/language/definition/src/compiler/analysis/definitions.rs @@ -3,13 +3,14 @@ use std::collections::HashSet; use crate::compiler::analysis::{Analysis, ItemMetadata}; use crate::compiler::version_set::VersionSet; use crate::internals::Spanned; -use crate::model::{Identifier, SpannedItem, SpannedVersionSpecifier}; +use crate::model::{Identifier, SpannedFieldsErrorRecovery, SpannedItem, SpannedVersionSpecifier}; pub(crate) fn analyze_definitions(analysis: &mut Analysis) { collect_top_level_items(analysis); check_enum_items(analysis); check_precedence_items(analysis); + check_field_error_recovery(analysis); } fn collect_top_level_items(analysis: &mut Analysis) { @@ -97,6 +98,39 @@ fn check_precedence_items(analysis: &mut Analysis) { } } +fn check_field_error_recovery(analysis: &mut Analysis) { + let mut visit_item = |item: &Spanned| { + if item.recover_from_no_match.is_some() && item.delimiters.is_none() { + analysis + .errors + .add(item, &Errors::RecoverFromNoMatchWithoutDelimiters); + } + }; + + // Visit PrecedenceOperators and SpannedItem::Structs: + for item in analysis.language.clone().items() { + match item { + SpannedItem::Precedence { item } => { + for er in item + .precedence_expressions + .iter() + .flat_map(|x| &x.operators) + .filter_map(|x| x.error_recovery.as_ref()) + { + visit_item(er); + } + } + + SpannedItem::Struct { item } => { + if let Some(abc) = item.error_recovery.as_ref() { + visit_item(abc); + } + } + _ => {} + } + } +} + fn get_item_name(item: &SpannedItem) -> &Spanned { match item { SpannedItem::Struct { item } => &item.name, @@ -168,4 +202,6 @@ enum Errors<'err> { ExistingVariant(&'err Identifier), #[error("An expression with the name '{0}' already exists.")] ExistingExpression(&'err Identifier), + #[error("The field 'recover_from_no_match' can only be defined if 'delimiters' is defined.")] + RecoverFromNoMatchWithoutDelimiters, } diff --git a/crates/codegen/language/internal_macros/src/derive/spanned.rs b/crates/codegen/language/internal_macros/src/derive/spanned.rs index f83fa812a3..fece99204d 100644 --- a/crates/codegen/language/internal_macros/src/derive/spanned.rs +++ b/crates/codegen/language/internal_macros/src/derive/spanned.rs @@ -114,7 +114,6 @@ fn get_spanned_type(input: Type) -> Type { | "EnumVariant" | "Field" | "FieldDelimiters" - | "FieldsErrorRecovery" | "FragmentItem" | "InputItem" | "Item" @@ -143,7 +142,7 @@ fn get_spanned_type(input: Type) -> Type { // These are model Types that have a derived 'SpannedXXX' type. // Let's use that instead, but also wrap it in 'Spanned' because we want to capture its complete span for validation: - "OperatorModel" | "VersionSpecifier" => { + "OperatorModel" | "VersionSpecifier" | "FieldsErrorRecovery" => { let spanned_type = format_ident!("{}", add_spanned_prefix(type_name)); parse_quote! { crate::internals::Spanned diff --git a/crates/codegen/language/tests/src/fail/references/recovered_only_for_delimited/test.rs b/crates/codegen/language/tests/src/fail/references/recovered_only_for_delimited/test.rs new file mode 100644 index 0000000000..24917d8e7a --- /dev/null +++ b/crates/codegen/language/tests/src/fail/references/recovered_only_for_delimited/test.rs @@ -0,0 +1,44 @@ +#![allow(unused_crate_dependencies)] + +codegen_language_macros::compile!(Language( + name = Foo, + documentation_dir = "foo/bar", + root_item = Bar, + leading_trivia = Sequence([]), + trailing_trivia = Sequence([]), + versions = ["1.0.0", "2.0.0", "3.0.0"], + sections = [Section( + title = "Section One", + topics = [Topic( + title = "Topic One", + items = [ + Struct( + name = Bar, + error_recovery = + FieldsErrorRecovery(terminator = semicolon, recover_from_no_match = true), + fields = ( + field_4 = Optional( + // correct + reference = Baz, + enabled = Range(from = "2.0.0", till = "3.0.0") + ), + semicolon = Required(Semicolon) + ) + ), + Token( + name = Semicolon, + definitions = [TokenDefinition(scanner = Atom(";"))] + ), + Token( + name = Baz, + definitions = [TokenDefinition( + enabled = Range(from = "2.0.0", till = "3.0.0"), + scanner = Atom("baz") + )] + ) + ] + )] + )] +)); + +fn main() {} diff --git a/crates/codegen/language/tests/src/fail/references/recovered_only_for_delimited/test.stderr b/crates/codegen/language/tests/src/fail/references/recovered_only_for_delimited/test.stderr new file mode 100644 index 0000000000..42575bc0a2 --- /dev/null +++ b/crates/codegen/language/tests/src/fail/references/recovered_only_for_delimited/test.stderr @@ -0,0 +1,5 @@ +error: The field 'recover_from_no_match' can only be defined if 'delimiters' is defined. + --> src/fail/references/recovered_only_for_delimited/test.rs:18:25 + | +18 | FieldsErrorRecovery(terminator = semicolon, recover_from_no_match = true), + | ^^^^^^^^^^^^^^^^^^^