Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for @rust-attr on enums #2935

Merged
merged 1 commit into from
Jun 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 8 additions & 1 deletion api/rs/slint/tests/simple_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,20 @@ fn empty_stuff() {
fn test_serialize_deserialize_struct() {
i_slint_backend_testing::init();
slint! {

@rust-attr(derive(serde::Serialize, serde::Deserialize))
export enum TestEnum {
hello, world, xxx
}

@rust-attr(derive(serde::Serialize, serde::Deserialize))
export struct TestStruct {
enum: TestEnum,
foo: int,
}
export component Test { }
}
let data = TestStruct { foo: 1 };
let data = TestStruct { foo: 1, r#enum: TestEnum::World };
let serialized = serde_json::to_string(&data).unwrap();
let deserialized: TestStruct = serde_json::from_str(&serialized).unwrap();
assert_eq!(data, deserialized);
Expand Down
11 changes: 10 additions & 1 deletion internal/compiler/generator/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ fn generate_struct(

let attributes = if let Some(feature) = rust_attributes {
let attr =
feature.iter().map(|f| match TokenStream::from_str(format!(r#"#[{}]"#, f).as_str()) {
feature.iter().map(|f| match TokenStream::from_str(format!(r#"#[{f}]"#).as_str()) {
Ok(eval) => eval,
Err(_) => quote! {},
});
Expand Down Expand Up @@ -475,8 +475,17 @@ fn generate_enum(en: &std::rc::Rc<Enumeration>) -> TokenStream {
quote!(#i)
}
});
let rust_attr = en.node.as_ref().and_then(|node| {
node.AtRustAttr().map(|attr| {
match TokenStream::from_str(format!(r#"#[{}]"#, attr.text()).as_str()) {
Ok(eval) => eval,
Err(_) => quote! {},
}
})
});
quote! {
#[derive(Default, Copy, Clone, PartialEq, Debug)]
#rust_attr
pub enum #enum_name {
#(#enum_values,)*
}
Expand Down
15 changes: 3 additions & 12 deletions internal/compiler/object_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@

// cSpell: ignore qualname

use itertools::{Either, Itertools};

use crate::diagnostics::{BuildDiagnostics, SourceLocation, Spanned};
use crate::expression_tree::{self, BindingExpression, Expression, Unit};
use crate::langtype::{BuiltinElement, Enumeration, NativeClass, Type};
Expand All @@ -19,6 +17,7 @@ use crate::parser;
use crate::parser::{syntax_nodes, SyntaxKind, SyntaxNode};
use crate::typeloader::ImportedTypes;
use crate::typeregister::TypeRegister;
use itertools::Either;
use std::cell::{Cell, RefCell};
use std::collections::btree_map::Entry;
use std::collections::{BTreeMap, HashMap, HashSet};
Expand Down Expand Up @@ -77,17 +76,9 @@ impl Document {
diag: &mut BuildDiagnostics,
local_registry: &mut TypeRegister,
inner_types: &mut Vec<Type>| {
let rust_attributes: Vec<String> = n
.children()
.filter(|child| child.kind() == SyntaxKind::AtRustAttr)
.map(|child| {
let mut text = child.text().to_string();
text.pop();
text
})
.collect_vec();
let rust_attributes = n.AtRustAttr().map(|child| vec![child.text().to_string()]);
let mut ty =
type_struct_from_node(n.ObjectType(), diag, local_registry, Some(rust_attributes));
type_struct_from_node(n.ObjectType(), diag, local_registry, rust_attributes);
if let Type::Struct { name, .. } = &mut ty {
*name = parser::identifier_text(&n.DeclaredIdentifier());
} else {
Expand Down
47 changes: 37 additions & 10 deletions internal/compiler/parser-test-macro/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
extern crate proc_macro;
use core::iter::IntoIterator;
use core::str::FromStr;
use proc_macro::{TokenStream, TokenTree};
use proc_macro::{Delimiter, TokenStream, TokenTree};

fn error(e: &str) -> String {
format!("::core::compile_error!{{\"{}\"}}", e)
}

fn generate_test(fn_name: &str, doc: &str) -> String {
fn generate_test(fn_name: &str, doc: &str, extra_args: usize) -> String {
if fn_name.is_empty() {
return error("Could not parse function name");
}
Expand Down Expand Up @@ -61,20 +61,23 @@ fn generate_test(fn_name: &str, doc: &str) -> String {
if line.is_empty() {
continue;
}
tests += &format!(r#"
#[test] fn parser_test_{fn}_{i}()
let follow_args = ", Default::default()".repeat(extra_args);
tests += &format!(
r#"
#[test] fn parser_test_{fn_name}_{i}()
{{
let mut diag = Default::default();
let mut p = DefaultParser::new("{source}", &mut diag);
{fn}(&mut p);
let mut p = DefaultParser::new("{line}", &mut diag);
{fn_name}(&mut p{follow_args});
let has_error = p.diags.has_error();
//#[cfg(feature = "display-diagnostics")]
//p.diags.print();
assert!(!has_error);
assert_eq!(p.cursor, p.tokens.len());
{verify}
}}
"#, fn = fn_name, i = i, source = line, verify = verify)
"#,
)
}
tests
}
Expand All @@ -87,6 +90,7 @@ pub fn parser_test(_attr: TokenStream, item: TokenStream) -> TokenStream {
let mut item = item.into_iter();

let mut fn_name = String::new();
let mut extra_args = 0;

// Extract the doc comment.
// Bail out once we find a token that does not fit the doc comment pattern
Expand Down Expand Up @@ -146,9 +150,32 @@ pub fn parser_test(_attr: TokenStream, item: TokenStream) -> TokenStream {
}
}

let test_function = TokenStream::from_str(&generate_test(&fn_name, &doc)).unwrap_or_else(|e| {
TokenStream::from_str(&error(&format!("Lex error in generated test: {:?}", e))).unwrap()
});
loop {
match item.next() {
None => break,
Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => {
let mut had_coma = false;
for tt in g.stream().into_iter() {
match tt {
TokenTree::Punct(p) if p.as_char() == ',' => {
had_coma = true;
}
TokenTree::Punct(p) if p.as_char() == ':' && had_coma => {
extra_args += 1;
had_coma = false;
}
_ => {}
}
}
}
_ => (),
}
}

let test_function = TokenStream::from_str(&generate_test(&fn_name, &doc, extra_args))
.unwrap_or_else(|e| {
TokenStream::from_str(&error(&format!("Lex error in generated test: {:?}", e))).unwrap()
});

result.extend(test_function);
result
Expand Down
10 changes: 7 additions & 3 deletions internal/compiler/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ declare_syntax! {
/// `struct Foo { ... }`
StructDeclaration -> [DeclaredIdentifier, ObjectType, ?AtRustAttr],
/// `enum Foo { bli, bla, blu }`
EnumDeclaration -> [DeclaredIdentifier, *EnumValue],
EnumDeclaration -> [DeclaredIdentifier, *EnumValue, ?AtRustAttr],
/// The value is a Identifier
EnumValue -> [],
/// `@rust-attr(...)`
Expand Down Expand Up @@ -497,8 +497,12 @@ mod parser_trait {
#[must_use = "use start_node_at to use this checkpoint"]
fn checkpoint(&mut self) -> Self::Checkpoint;
#[must_use = "The node will be finished when it is dropped"]
fn start_node_at(&mut self, checkpoint: Self::Checkpoint, kind: SyntaxKind) -> Node<Self> {
self.start_node_impl(kind, Some(checkpoint), NodeToken(()));
fn start_node_at(
&mut self,
checkpoint: impl Into<Option<Self::Checkpoint>>,
kind: SyntaxKind,
) -> Node<Self> {
self.start_node_impl(kind, checkpoint.into(), NodeToken(()));
Node(self)
}

Expand Down
59 changes: 29 additions & 30 deletions internal/compiler/parser/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use super::r#type::{parse_enum_declaration, parse_rustattr, parse_struct_declara
/// import { Base } from "somewhere"; Type := Base {}
/// struct Foo { foo: foo }
/// enum Foo { hello }
/// @rust-attr(...) struct X {}
/// /* empty */
/// ```
pub fn parse_document(p: &mut impl Parser) -> bool {
Expand All @@ -32,7 +33,7 @@ pub fn parse_document(p: &mut impl Parser) -> bool {

match p.peek().as_str() {
"export" => {
if !parse_export(&mut *p) {
if !parse_export(&mut *p, None) {
break;
}
}
Expand All @@ -42,42 +43,39 @@ pub fn parse_document(p: &mut impl Parser) -> bool {
}
}
"struct" => {
if !parse_struct_declaration(&mut *p) {
if !parse_struct_declaration(&mut *p, None) {
break;
}
}
"enum" => {
if !parse_enum_declaration(&mut *p) {
if !parse_enum_declaration(&mut *p, None) {
break;
}
}
"@" if p.nth(1).as_str() == "rust-attr" => {
let mut is_export = false;
let mut i = 0;
loop {
let value = p.nth(i);
if value.as_str() == ")" && p.nth(i + 1).as_str() == "export" {
is_export = true;
break;
} else if (value.as_str() == ")"
&& p.nth(i + 1).as_str() != "struct"
&& p.nth(i + 1).as_str() != "export"
&& p.nth(i + 1).as_str() != ")")
|| (value.as_str() == ")" && p.nth(i + 1).as_str() == "struct")
{
break;
}
i += 1;
let checkpoint = p.checkpoint();
if !parse_rustattr(&mut *p) {
break;
}
if is_export {
let mut p = p.start_node(SyntaxKind::ExportsList);
if !parse_rustattr(&mut *p) {
break;
}
let is_export = p.nth(0).as_str() == "export";
let i = if is_export { 1 } else { 0 };
if !matches!(p.nth(i).as_str(), "enum" | "struct") {
p.error("Expected enum or struct after @rust-attr");
continue;
}
let r = if is_export {
parse_export(&mut *p, Some(checkpoint))
} else {
if !parse_rustattr(&mut *p) {
break;
if p.nth(0).as_str() == "struct" {
parse_struct_declaration(&mut *p, Some(checkpoint))
} else if p.nth(0).as_str() == "enum" {
parse_enum_declaration(&mut *p, Some(checkpoint))
} else {
false
}
};
if !r {
break;
}
}
_ => {
Expand Down Expand Up @@ -193,9 +191,10 @@ pub fn parse_qualified_name(p: &mut impl Parser) -> bool {
/// export enum Foo { bar }
/// export * from "foo";
/// ```
fn parse_export(p: &mut impl Parser) -> bool {
fn parse_export<P: Parser>(p: &mut P, checkpoint: Option<P::Checkpoint>) -> bool {
debug_assert_eq!(p.peek().as_str(), "export");
let mut p = p.start_node(SyntaxKind::ExportsList);
let mut p = p.start_node_at(checkpoint.clone(), SyntaxKind::ExportsList);

p.expect(SyntaxKind::Identifier); // "export"
if p.test(SyntaxKind::LBrace) {
loop {
Expand All @@ -219,9 +218,9 @@ fn parse_export(p: &mut impl Parser) -> bool {
}
}
} else if p.peek().as_str() == "struct" {
parse_struct_declaration(&mut *p)
parse_struct_declaration(&mut *p, checkpoint)
} else if p.peek().as_str() == "enum" {
parse_enum_declaration(&mut *p)
parse_enum_declaration(&mut *p, checkpoint)
} else if p.peek().kind == SyntaxKind::Star {
let mut p = p.start_node(SyntaxKind::ExportModule);
p.consume(); // *
Expand Down