Skip to content

Commit

Permalink
fix: correctly check aliases and imports unique identifiers
Browse files Browse the repository at this point in the history
Aliases couldn't be checked to a non-optimal struct format in its first
version and imports were skipped when then don't have a final type
element.
  • Loading branch information
dnaka91 committed Oct 27, 2023
1 parent d09e182 commit 9c88e32
Show file tree
Hide file tree
Showing 18 changed files with 226 additions and 67 deletions.
2 changes: 1 addition & 1 deletion crates/stef-build/src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
}
}
Expand Down
15 changes: 9 additions & 6 deletions crates/stef-build/src/definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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;
}
}

Expand All @@ -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 {
Expand Down Expand Up @@ -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,)*> }
Expand Down
4 changes: 2 additions & 2 deletions crates/stef-compiler/src/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
});
}
Expand All @@ -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());
}
});
}
Expand Down
7 changes: 2 additions & 5 deletions crates/stef-compiler/src/names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
19 changes: 11 additions & 8 deletions crates/stef-parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
}
Expand All @@ -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};")
}
}

Expand Down Expand Up @@ -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<Name<'a>>,
/// 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<DataType<'a>>,
}
Expand Down Expand Up @@ -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<Name<'a>>,
/// 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<Name<'a>>,
Expand All @@ -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 {
Expand Down
52 changes: 45 additions & 7 deletions crates/stef-parser/src/parser/aliases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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),
Expand All @@ -46,7 +66,8 @@ pub(super) fn parse<'i>(input: &mut Input<'i>) -> Result<TypeAlias<'i>, 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)),
Expand All @@ -55,9 +76,10 @@ pub(super) fn parse<'i>(input: &mut Input<'i>) -> Result<TypeAlias<'i>, ParseErr
)),
)
.parse_next(input)
.map(|(alias, target)| TypeAlias {
.map(|(name, generics, target)| TypeAlias {
comment: Comment::default(),
alias,
name,
generics,
target,
})
.map_err(|e| {
Expand All @@ -67,3 +89,19 @@ pub(super) fn parse<'i>(input: &mut Input<'i>) -> Result<TypeAlias<'i>, ParseErr
})
})
}

fn parse_name<'i>(input: &mut Input<'i>) -> Result<Name<'i>, 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(),
})
})
}
9 changes: 7 additions & 2 deletions crates/stef-parser/src/parser/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -91,13 +91,18 @@ pub(super) fn parse<'i>(input: &mut Input<'i>) -> Result<Import<'i>, 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<Name<'i>, 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(),
Expand Down
4 changes: 2 additions & 2 deletions crates/stef-parser/src/parser/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
9 changes: 7 additions & 2 deletions crates/stef-parser/src/parser/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -171,8 +171,13 @@ fn parse_external<'i>(input: &mut Input<'i>) -> Result<ExternalType<'i>, Cause>
})
}

fn parse_external_name<'i>(input: &mut Input<'i>) -> Result<&'i str, Cause> {
fn parse_external_name<'i>(input: &mut Input<'i>) -> Result<Name<'i>, Cause> {
(one_of('A'..='Z'), alphanumeric0)
.recognize()
.with_span()
.parse_next(input)
.map(|(value, span)| Name {
value,
span: span.into(),
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,22 @@ 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
╭────
1type sImple = Simple;
· ▲
· ╰── In this declaration
╰────
help: Expected type alias declaration in the form `❬B❭type <Alias> = <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❭<Name>❬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❭)
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down
Loading

0 comments on commit 9c88e32

Please sign in to comment.