diff --git a/crates/stef-build/src/decode.rs b/crates/stef-build/src/decode.rs index b3f671d..87e6761 100644 --- a/crates/stef-build/src/decode.rs +++ b/crates/stef-build/src/decode.rs @@ -322,7 +322,7 @@ fn compile_data_type(ty: &DataType<'_>) -> TokenStream { quote! { ::stef::buf::decode_array(r, |r| { #ty }) } } DataType::External(ty) => { - let ty = Ident::new(ty.name, Span::call_site()); + let ty = Ident::new(ty.name.get(), Span::call_site()); quote! { #ty::decode(r) } } } diff --git a/crates/stef-build/src/definition.rs b/crates/stef-build/src/definition.rs index c276d80..95b80a9 100644 --- a/crates/stef-build/src/definition.rs +++ b/crates/stef-build/src/definition.rs @@ -2,7 +2,7 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote, ToTokens}; use stef_parser::{ Comment, Const, DataType, Definition, Enum, ExternalType, Fields, Generics, Import, Literal, - Module, NamedField, Schema, Struct, TypeAlias, UnnamedField, Variant, + Module, NamedField, Schema, Struct, TypeAlias, UnnamedField, Variant, Name, }; use super::{decode, encode}; @@ -141,18 +141,20 @@ fn compile_variant( fn compile_alias( TypeAlias { comment, - alias, + name, + generics, target, }: &TypeAlias<'_>, ) -> TokenStream { let comment = compile_comment(comment); - let alias = compile_data_type(alias); + let name = Ident::new(name.get(), Span::call_site()); + let generics = compile_generics(generics); let target = compile_data_type(target); quote! { #comment #[allow(dead_code, clippy::module_name_repetitions, clippy::option_option)] - pub type #alias = #target; + pub type #name #generics = #target; } } @@ -178,7 +180,7 @@ fn compile_const( fn compile_import(Import { segments, element }: &Import<'_>) -> TokenStream { let segments = segments.iter().enumerate().map(|(i, segment)| { - let segment = Ident::new(segment, Span::call_site()); + let segment = Ident::new(segment.get(), Span::call_site()); if i > 0 { quote! {::#segment} } else { @@ -335,7 +337,8 @@ pub(super) fn compile_data_type(ty: &DataType<'_>) -> TokenStream { name, generics, }) => { - let name = Ident::new(name, Span::call_site()); + let path = path.iter().map(Name::get); + let name = Ident::new(name.get(), Span::call_site()); let generics = (!generics.is_empty()).then(|| { let types = generics.iter().map(compile_data_type); quote! { <#(#types,)*> } diff --git a/crates/stef-compiler/src/generics.rs b/crates/stef-compiler/src/generics.rs index 2c123ea..2026642 100644 --- a/crates/stef-compiler/src/generics.rs +++ b/crates/stef-compiler/src/generics.rs @@ -106,7 +106,7 @@ fn validate_field_generics(value: &Fields<'_>, unvisited: &mut HashMap<&str, Spa for field in named { visit_externals(&field.ty, &mut |external| { if external.path.is_empty() && external.generics.is_empty() { - unvisited.remove(external.name); + unvisited.remove(external.name.get()); } }); } @@ -115,7 +115,7 @@ fn validate_field_generics(value: &Fields<'_>, unvisited: &mut HashMap<&str, Spa for field in unnamed { visit_externals(&field.ty, &mut |external| { if external.path.is_empty() && external.generics.is_empty() { - unvisited.remove(external.name); + unvisited.remove(external.name.get()); } }); } diff --git a/crates/stef-compiler/src/names.rs b/crates/stef-compiler/src/names.rs index 5cc23a9..2418ff4 100644 --- a/crates/stef-compiler/src/names.rs +++ b/crates/stef-compiler/src/names.rs @@ -117,16 +117,13 @@ pub(crate) fn validate_names_in_module(value: &[Definition<'_>]) -> Result<(), D Definition::Module(m) => &m.name, Definition::Struct(s) => &s.name, Definition::Enum(e) => &e.name, - Definition::TypeAlias(_) => { - eprintln!("TODO: implement name check for type aliases"); - return None; - } + Definition::TypeAlias(a) => &a.name, Definition::Const(c) => &c.name, Definition::Import(Import { element: Some(name), .. }) => name, - Definition::Import(_) => return None, + Definition::Import(Import { segments, .. }) => segments.last()?, }; visited.insert(name.get(), name.span()).map(|first| { DuplicateNameInModule { diff --git a/crates/stef-parser/src/lib.rs b/crates/stef-parser/src/lib.rs index cfae0f2..71ce768 100644 --- a/crates/stef-parser/src/lib.rs +++ b/crates/stef-parser/src/lib.rs @@ -417,8 +417,10 @@ impl<'a> Display for Variant<'a> { pub struct TypeAlias<'a> { /// Optional comment. pub comment: Comment<'a>, - /// New data type definition. - pub alias: DataType<'a>, + /// Unique name of the type alias within the current scope. + pub name: Name<'a>, + /// Potential generic type arguments. + pub generics: Generics<'a>, /// Original type that is being aliased. pub target: DataType<'a>, } @@ -427,14 +429,15 @@ impl<'a> Print for TypeAlias<'a> { fn print(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result { let Self { comment, - alias, + name, + generics, target, } = self; comment.print(f, level)?; Self::indent(f, level)?; - write!(f, "type {alias} = {target};") + write!(f, "type {name}{generics} = {target};") } } @@ -779,9 +782,9 @@ impl<'a> Display for DataType<'a> { #[derive(Clone, Debug, Eq, PartialEq)] pub struct ExternalType<'a> { /// Optional path, if the type wasn't fully imported with a `use` statement. - pub path: Vec<&'a str>, + pub path: Vec>, /// Unique name of the type within the current scope (or the module if prefixed with a path). - pub name: &'a str, + pub name: Name<'a>, /// Potential generic type arguments. pub generics: Vec>, } @@ -949,7 +952,7 @@ impl Display for Literal { #[derive(Debug, PartialEq)] pub struct Import<'a> { /// Individual elements that form the import path. - pub segments: Vec<&'a str>, + pub segments: Vec>, /// Optional final element that allows to fully import the type, making it look as it would be /// defined in the current schema. pub element: Option>, @@ -966,7 +969,7 @@ impl<'a> Print for Import<'a> { if i > 0 { f.write_str("::")?; } - f.write_str(segment)?; + f.write_str(segment.get())?; } if let Some(element) = element { diff --git a/crates/stef-parser/src/parser/aliases.rs b/crates/stef-parser/src/parser/aliases.rs index 4a38001..207b29b 100644 --- a/crates/stef-parser/src/parser/aliases.rs +++ b/crates/stef-parser/src/parser/aliases.rs @@ -2,15 +2,16 @@ use std::ops::Range; use stef_derive::{ParserError, ParserErrorCause}; use winnow::{ - ascii::{space0, space1}, - combinator::{cut_err, delimited, preceded}, + ascii::{alphanumeric0, space0, space1}, + combinator::{cut_err, delimited, opt, preceded}, error::ErrorKind, stream::Location, + token::one_of, Parser, }; -use super::{types, Input, ParserExt, Result}; -use crate::{highlight, Comment, TypeAlias}; +use super::{generics, types, Input, ParserExt, Result}; +use crate::{highlight, Comment, Name, TypeAlias}; /// Encountered an invalid `type` alias declaration. #[derive(Debug, ParserError)] @@ -37,6 +38,25 @@ pub struct ParseError { pub enum Cause { /// Non-specific general parser error. Parser(ErrorKind), + /// Defined name is not considered valid. + #[err( + msg("Invalid alias name"), + code(stef::parse::alias_def::invalid_name), + help( + "Alias names must start with an uppercase letter ({}), followed by zero or more \ + alphanumeric characters ({})", + highlight::value("A-Z"), + highlight::value("A-Z, a-z, 0-9"), + ) + )] + InvalidName { + /// Source location of the character. + #[err(label("Problematic character"))] + at: usize, + }, + /// Invalid alias generics declaration. + #[forward] + Generics(generics::ParseError), /// Invalid type declaration. #[forward] Type(types::ParseError), @@ -46,7 +66,8 @@ pub(super) fn parse<'i>(input: &mut Input<'i>) -> Result, ParseErr preceded( ("type", space1), cut_err(( - types::parse.map_err(Cause::from), + parse_name, + opt(generics::parse.map_err(Cause::Generics)).map(Option::unwrap_or_default), delimited( (space0, '='), preceded(space0, types::parse.map_err(Cause::from)), @@ -55,9 +76,10 @@ pub(super) fn parse<'i>(input: &mut Input<'i>) -> Result, ParseErr )), ) .parse_next(input) - .map(|(alias, target)| TypeAlias { + .map(|(name, generics, target)| TypeAlias { comment: Comment::default(), - alias, + name, + generics, target, }) .map_err(|e| { @@ -67,3 +89,19 @@ pub(super) fn parse<'i>(input: &mut Input<'i>) -> Result, ParseErr }) }) } + +fn parse_name<'i>(input: &mut Input<'i>) -> Result, Cause> { + (one_of('A'..='Z'), alphanumeric0) + .recognize() + .with_span() + .parse_next(input) + .map(|(value, span)| Name { + value, + span: span.into(), + }) + .map_err(|e| { + e.map(|()| Cause::InvalidName { + at: input.location(), + }) + }) +} diff --git a/crates/stef-parser/src/parser/imports.rs b/crates/stef-parser/src/parser/imports.rs index a80267f..039a1de 100644 --- a/crates/stef-parser/src/parser/imports.rs +++ b/crates/stef-parser/src/parser/imports.rs @@ -11,7 +11,7 @@ use winnow::{ }; use super::{enums, structs, Input, ParserExt, Result}; -use crate::{highlight, location, Import}; +use crate::{highlight, location, Import, Name}; /// Encountered an invalid `use` declaration. #[derive(Debug, ParserError)] @@ -91,13 +91,18 @@ pub(super) fn parse<'i>(input: &mut Input<'i>) -> Result, ParseError> }) } -pub(super) fn parse_segment<'i>(input: &mut Input<'i>) -> Result<&'i str, Cause> { +pub(super) fn parse_segment<'i>(input: &mut Input<'i>) -> Result, Cause> { ( one_of('a'..='z'), take_while(0.., ('a'..='z', '0'..='9', '_')), ) .recognize() + .with_span() .parse_next(input) + .map(|(value, span)| Name { + value, + span: span.into(), + }) .map_err(|e| { e.map(|()| Cause::InvalidSegmentName { at: input.location(), diff --git a/crates/stef-parser/src/parser/structs.rs b/crates/stef-parser/src/parser/structs.rs index 4c8ebfb..2dddda8 100644 --- a/crates/stef-parser/src/parser/structs.rs +++ b/crates/stef-parser/src/parser/structs.rs @@ -54,9 +54,9 @@ pub enum Cause { #[err(label("Problematic character"))] at: usize, }, - /// Invalid sturct generics declaration. + /// Invalid struct generics declaration. #[forward] - Generics(super::generics::ParseError), + Generics(generics::ParseError), /// Invalid declaration of struct fields. #[forward] Fields(fields::ParseError), diff --git a/crates/stef-parser/src/parser/types.rs b/crates/stef-parser/src/parser/types.rs index cb0fca0..9f31d9b 100644 --- a/crates/stef-parser/src/parser/types.rs +++ b/crates/stef-parser/src/parser/types.rs @@ -14,7 +14,7 @@ use winnow::{ }; use super::{imports, ws, Input, ParserExt, Result}; -use crate::{highlight, DataType, ExternalType}; +use crate::{highlight, DataType, ExternalType, Name}; /// Encountered an invalid type definition. #[derive(Debug, ParserError)] @@ -171,8 +171,13 @@ fn parse_external<'i>(input: &mut Input<'i>) -> Result, Cause> }) } -fn parse_external_name<'i>(input: &mut Input<'i>) -> Result<&'i str, Cause> { +fn parse_external_name<'i>(input: &mut Input<'i>) -> Result, Cause> { (one_of('A'..='Z'), alphanumeric0) .recognize() + .with_span() .parse_next(input) + .map(|(value, span)| Name { + value, + span: span.into(), + }) } diff --git a/crates/stef-parser/tests/snapshots/parser__error@alias_name.stef.snap b/crates/stef-parser/tests/snapshots/parser__error@alias_name.stef.snap index ed53f7f..7552375 100644 --- a/crates/stef-parser/tests/snapshots/parser__error@alias_name.stef.snap +++ b/crates/stef-parser/tests/snapshots/parser__error@alias_name.stef.snap @@ -6,8 +6,7 @@ input_file: crates/stef-parser/tests/inputs/invalid/alias_name.stef stef::parse::alias_def (https://docs.rs/stef-parser/0.1.0/stef_parser/error/struct.ParseAliasError.html) × Failed to parse type alias declaration - ├─▶ Failed to parse type definition - ╰─▶ error Verify + ╰─▶ Invalid alias name ╭──── 1 │ type sImple = Simple; · ▲ @@ -15,16 +14,14 @@ stef::parse::alias_def (https://docs.rs/stef-parser/0.1.0/stef_parser/error/stru ╰──── help: Expected type alias declaration in the form `❬B❭type = ;❬B❭` -Error: stef::parse::type_def (https://docs.rs/stef-parser/0.1.0/stef_parser/error/struct.ParseTypeError.html) +Error: stef::parse::alias_def::invalid_name (https://docs.rs/stef-parser/0.1.0/stef_parser/error/enum.ParseAliasCause.html#variant.InvalidName) - × Failed to parse type definition - ╰─▶ error Verify + × Invalid alias name ╭──── 1 │ type sImple = Simple; · ▲ - · ╰── In this declaration + · ╰── Problematic character ╰──── - help: Expected type definition in the form `❬B❭❬B❭` - -Error: × error Verify + help: Alias names must start with an uppercase letter (❬Y❭A-Z❬Y❭), followed by zero or more alphanumeric + characters (❬Y❭A-Z, a-z, 0-9❬Y❭) diff --git a/crates/stef-parser/tests/snapshots/parser__parse@alias_basic.stef.snap b/crates/stef-parser/tests/snapshots/parser__parse@alias_basic.stef.snap index c699425..4f9ae95 100644 --- a/crates/stef-parser/tests/snapshots/parser__parse@alias_basic.stef.snap +++ b/crates/stef-parser/tests/snapshots/parser__parse@alias_basic.stef.snap @@ -12,12 +12,15 @@ Schema { "Sample type alias.", ], ), - alias: External( - ExternalType { - path: [], - name: "Sample", - generics: [], + name: Name { + value: "Sample", + span: Span { + start: 28, + end: 34, }, + }, + generics: Generics( + [], ), target: U32, }, diff --git a/crates/stef-parser/tests/snapshots/parser__parse@enum_generics.stef.snap b/crates/stef-parser/tests/snapshots/parser__parse@enum_generics.stef.snap index 3d8f3bd..8f963f4 100644 --- a/crates/stef-parser/tests/snapshots/parser__parse@enum_generics.stef.snap +++ b/crates/stef-parser/tests/snapshots/parser__parse@enum_generics.stef.snap @@ -96,7 +96,13 @@ Schema { ty: External( ExternalType { path: [], - name: "A", + name: Name { + value: "A", + span: Span { + start: 70, + end: 71, + }, + }, generics: [], }, ), @@ -116,7 +122,13 @@ Schema { ty: External( ExternalType { path: [], - name: "B", + name: Name { + value: "B", + span: Span { + start: 76, + end: 77, + }, + }, generics: [], }, ), @@ -173,7 +185,13 @@ Schema { ty: External( ExternalType { path: [], - name: "C", + name: Name { + value: "C", + span: Span { + start: 114, + end: 115, + }, + }, generics: [], }, ), @@ -203,7 +221,13 @@ Schema { ty: External( ExternalType { path: [], - name: "D", + name: Name { + value: "D", + span: Span { + start: 136, + end: 137, + }, + }, generics: [], }, ), diff --git a/crates/stef-parser/tests/snapshots/parser__parse@enum_min_ws.stef.snap b/crates/stef-parser/tests/snapshots/parser__parse@enum_min_ws.stef.snap index 8e92508..cf79c57 100644 --- a/crates/stef-parser/tests/snapshots/parser__parse@enum_min_ws.stef.snap +++ b/crates/stef-parser/tests/snapshots/parser__parse@enum_min_ws.stef.snap @@ -101,7 +101,13 @@ Schema { ty: External( ExternalType { path: [], - name: "T", + name: Name { + value: "T", + span: Span { + start: 37, + end: 38, + }, + }, generics: [], }, ), @@ -206,7 +212,13 @@ Schema { ty: External( ExternalType { path: [], - name: "T", + name: Name { + value: "T", + span: Span { + start: 84, + end: 85, + }, + }, generics: [], }, ), diff --git a/crates/stef-parser/tests/snapshots/parser__parse@import_basic.stef.snap b/crates/stef-parser/tests/snapshots/parser__parse@import_basic.stef.snap index a297d9f..8930035 100644 --- a/crates/stef-parser/tests/snapshots/parser__parse@import_basic.stef.snap +++ b/crates/stef-parser/tests/snapshots/parser__parse@import_basic.stef.snap @@ -8,8 +8,20 @@ Schema { Import( Import { segments: [ - "other", - "schema", + Name { + value: "other", + span: Span { + start: 4, + end: 9, + }, + }, + Name { + value: "schema", + span: Span { + start: 11, + end: 17, + }, + }, ], element: Some( Name { @@ -25,8 +37,20 @@ Schema { Import( Import { segments: [ - "second", - "submodule", + Name { + value: "second", + span: Span { + start: 31, + end: 37, + }, + }, + Name { + value: "submodule", + span: Span { + start: 39, + end: 48, + }, + }, ], element: None, }, diff --git a/crates/stef-parser/tests/snapshots/parser__parse@struct_generics.stef.snap b/crates/stef-parser/tests/snapshots/parser__parse@struct_generics.stef.snap index a6860a4..5624876 100644 --- a/crates/stef-parser/tests/snapshots/parser__parse@struct_generics.stef.snap +++ b/crates/stef-parser/tests/snapshots/parser__parse@struct_generics.stef.snap @@ -56,7 +56,13 @@ Schema { ty: External( ExternalType { path: [], - name: "K", + name: Name { + value: "K", + span: Span { + start: 61, + end: 62, + }, + }, generics: [], }, ), @@ -86,7 +92,13 @@ Schema { ty: External( ExternalType { path: [], - name: "V", + name: Name { + value: "V", + span: Span { + start: 78, + end: 79, + }, + }, generics: [], }, ), diff --git a/crates/stef-parser/tests/snapshots/parser__parse@struct_many_ws.stef.snap b/crates/stef-parser/tests/snapshots/parser__parse@struct_many_ws.stef.snap index ab24df1..7bddd99 100644 --- a/crates/stef-parser/tests/snapshots/parser__parse@struct_many_ws.stef.snap +++ b/crates/stef-parser/tests/snapshots/parser__parse@struct_many_ws.stef.snap @@ -97,7 +97,13 @@ Schema { ty: External( ExternalType { path: [], - name: "T", + name: Name { + value: "T", + span: Span { + start: 132, + end: 133, + }, + }, generics: [], }, ), diff --git a/crates/stef-parser/tests/snapshots/parser__parse@struct_min_ws.stef.snap b/crates/stef-parser/tests/snapshots/parser__parse@struct_min_ws.stef.snap index 4662cbb..7410f3a 100644 --- a/crates/stef-parser/tests/snapshots/parser__parse@struct_min_ws.stef.snap +++ b/crates/stef-parser/tests/snapshots/parser__parse@struct_min_ws.stef.snap @@ -95,7 +95,13 @@ Schema { ty: External( ExternalType { path: [], - name: "T", + name: Name { + value: "T", + span: Span { + start: 36, + end: 37, + }, + }, generics: [], }, ), diff --git a/crates/stef-parser/tests/snapshots/parser__parse@types_ref.stef.snap b/crates/stef-parser/tests/snapshots/parser__parse@types_ref.stef.snap index 416e594..034bd99 100644 --- a/crates/stef-parser/tests/snapshots/parser__parse@types_ref.stef.snap +++ b/crates/stef-parser/tests/snapshots/parser__parse@types_ref.stef.snap @@ -39,7 +39,13 @@ Schema { ty: External( ExternalType { path: [], - name: "Test123", + name: Name { + value: "Test123", + span: Span { + start: 27, + end: 34, + }, + }, generics: [], }, ), @@ -69,7 +75,13 @@ Schema { ty: External( ExternalType { path: [], - name: "KeyValue", + name: Name { + value: "KeyValue", + span: Span { + start: 58, + end: 66, + }, + }, generics: [ U32, Bool, @@ -187,7 +199,13 @@ Schema { ty: External( ExternalType { path: [], - name: "K", + name: Name { + value: "K", + span: Span { + start: 150, + end: 151, + }, + }, generics: [], }, ), @@ -217,7 +235,13 @@ Schema { ty: External( ExternalType { path: [], - name: "V", + name: Name { + value: "V", + span: Span { + start: 167, + end: 168, + }, + }, generics: [], }, ),