Added `required` tests, fixed `required` bugs.
This commit is contained in:
parent
0cda0be5f6
commit
051989cf2e
|
@ -874,6 +874,91 @@ mod widget_tests {
|
|||
assert!(util::traced(&required, "required!"));
|
||||
assert!(util::traced(&required, "required2!"));
|
||||
}
|
||||
|
||||
// Mixin used in inherit required tests.
|
||||
#[widget_mixin2($crate::widget_tests::required_mixin)]
|
||||
pub mod required_mixin {
|
||||
properties! {
|
||||
super::util::trace as required_trace = required!;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests inheriting a required property.
|
||||
*/
|
||||
#[widget2($crate::widget_tests::required_inherited_wgt)]
|
||||
pub mod required_inherited_wgt {
|
||||
inherit!(super::required_mixin);
|
||||
}
|
||||
#[test]
|
||||
pub fn wgt_required_inherited() {
|
||||
let mut required = required_inherited_wgt! {
|
||||
required_trace = "required!";// removing this line must cause a compile error.
|
||||
};
|
||||
required.test_init(&mut TestWidgetContext::wait_new());
|
||||
|
||||
assert!(util::traced(&required, "required!"))
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests inheriting a required property and setting it with a default value.
|
||||
*/
|
||||
#[widget2($crate::widget_tests::required_inherited_default_wgt)]
|
||||
pub mod required_inherited_default_wgt {
|
||||
inherit!(super::required_mixin);
|
||||
|
||||
properties! {
|
||||
//required_trace = unset!; // this line must cause a compile error.
|
||||
required_trace = "required_inherited_default_wgt";
|
||||
}
|
||||
}
|
||||
#[widget2($crate::widget_tests::required_inherited_default_depth2_wgt)]
|
||||
pub mod required_inherited_default_depth2_wgt {
|
||||
inherit!(super::required_inherited_default_wgt);
|
||||
|
||||
properties! {
|
||||
//required_trace = unset!; // this line must cause a compile error.
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
pub fn wgt_required_inherited_default() {
|
||||
let mut required = required_inherited_default_wgt! {
|
||||
// TODO expected compile error here, got panic.
|
||||
//required_trace = unset!; // uncommenting this line must cause a compile error.
|
||||
};
|
||||
required.test_init(&mut TestWidgetContext::wait_new());
|
||||
|
||||
assert!(util::traced(&required, "required_inherited_default_wgt"));
|
||||
|
||||
let mut required2 = required_inherited_default_depth2_wgt! {
|
||||
//required_trace = unset!; // uncommenting this line must cause a compile error.
|
||||
};
|
||||
required2.test_init(&mut TestWidgetContext::wait_new());
|
||||
|
||||
assert!(util::traced(&required2, "required_inherited_default_wgt"));
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests inheriting a required property with default value changing to required without value.
|
||||
*/
|
||||
#[widget2($crate::widget_tests::required_inherited_default_required_wgt)]
|
||||
pub mod required_inherited_default_required_wgt {
|
||||
inherit!(super::required_inherited_default_wgt);
|
||||
|
||||
properties! {
|
||||
required_trace = required!;
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
pub fn wgt_required_inherited_default_required() {
|
||||
let mut required = required_inherited_default_required_wgt! {
|
||||
required_trace = "required!"; // commenting this line must cause a compile error.
|
||||
};
|
||||
required.test_init(&mut TestWidgetContext::wait_new());
|
||||
|
||||
assert!(util::traced(&required, "required!"))
|
||||
}
|
||||
|
||||
mod util {
|
||||
use std::collections::HashSet;
|
||||
|
||||
|
|
|
@ -201,7 +201,11 @@ pub fn expand(mixin: bool, args: proc_macro::TokenStream, input: proc_macro::Tok
|
|||
continue;
|
||||
}
|
||||
// the final inherit validation is done in "widget_2_declare.rs".
|
||||
p_ident.to_tokens(&mut property_unsets);
|
||||
property_unsets.extend(quote! {
|
||||
#p_ident {
|
||||
#sp // span sample
|
||||
}
|
||||
});
|
||||
continue;
|
||||
} else if sp == "required" {
|
||||
required = true;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use syn::{parse::Parse, Ident, LitBool};
|
||||
use syn::{parse::Parse, spanned::Spanned, Ident, LitBool};
|
||||
|
||||
use crate::{
|
||||
util::{self, parse_all},
|
||||
util::{self, parse_all, Errors},
|
||||
widget_new2::{BuiltProperty, BuiltWhen, BuiltWhenAssign},
|
||||
};
|
||||
|
||||
|
@ -29,10 +29,11 @@ pub fn expand(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|||
mut new,
|
||||
mod_items,
|
||||
} = widget;
|
||||
let properties_unset: HashSet<_> = properties_unset.into_iter().collect();
|
||||
let properties_unset: HashMap<_, _> = properties_unset.into_iter().map(|u| (u.property, u.unset.span())).collect();
|
||||
let properties_declared: HashSet<_> = properties_declared.into_iter().collect();
|
||||
|
||||
let crate_core = util::crate_core();
|
||||
let mut errors = Errors::default();
|
||||
|
||||
// inherits `new_child` and `new`.
|
||||
let mut new_child_reexport = TokenStream::default();
|
||||
|
@ -85,24 +86,49 @@ pub fn expand(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|||
let mut inherited_props = vec![];
|
||||
for inherited in inherits.iter().rev() {
|
||||
for p_child in inherited.properties_child.iter().rev() {
|
||||
if !properties_unset.contains(&p_child.ident) && inherited_properties.insert(&p_child.ident, &inherited.module).is_none() {
|
||||
if inherited_properties.insert(&p_child.ident, &inherited.module).is_none() {
|
||||
inherited_props_child.push(p_child);
|
||||
}
|
||||
}
|
||||
for p in inherited.properties.iter().rev() {
|
||||
if !properties_unset.contains(&p.ident) && inherited_properties.insert(&p.ident, &inherited.module).is_none() {
|
||||
if inherited_properties.insert(&p.ident, &inherited.module).is_none() {
|
||||
inherited_props.push(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
inherited_props_child.reverse();
|
||||
inherited_props.reverse();
|
||||
|
||||
// inherited properties that are required!
|
||||
let inherited_required: HashSet<_> = inherited_props_child
|
||||
.iter()
|
||||
.chain(inherited_props.iter())
|
||||
.filter(|p| p.required)
|
||||
.map(|p| &p.ident)
|
||||
.collect();
|
||||
|
||||
// apply unsets.
|
||||
for (unset, &unset_span) in &properties_unset {
|
||||
if inherited_required.contains(unset) {
|
||||
// cannot unset
|
||||
errors.push(format_args!("property `{}` is required", unset), unset_span);
|
||||
} else if inherited_properties.remove(unset).is_some() {
|
||||
// can unset
|
||||
if let Some(i) = inherited_props_child.iter().position(|p| &p.ident == unset) {
|
||||
inherited_props_child.remove(i);
|
||||
} else if let Some(i) = inherited_props.iter().position(|p| &p.ident == unset) {
|
||||
inherited_props.remove(i);
|
||||
}
|
||||
}
|
||||
// else was unset in a new property that must be a warning when that is stable
|
||||
}
|
||||
|
||||
let inherited_properties = inherited_properties;
|
||||
let inherited_props_child = inherited_props_child;
|
||||
let inherited_props = inherited_props;
|
||||
|
||||
// properties that are assigned (not in when blocks) or declared in the new widget.
|
||||
let wgt_used_properties: HashSet<_> = properties.iter().map(|p| &p.ident).collect();
|
||||
let wgt_used_properties: HashSet<_> = properties_child.iter().chain(properties.iter()).map(|p| &p.ident).collect();
|
||||
// properties data for widget macros.
|
||||
let mut wgt_properties_child = TokenStream::default();
|
||||
let mut wgt_properties = TokenStream::default();
|
||||
|
@ -125,9 +151,11 @@ pub fn expand(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|||
docs,
|
||||
cfg,
|
||||
default,
|
||||
required,
|
||||
mut required,
|
||||
} = ip;
|
||||
|
||||
required |= inherited_required.contains(ident);
|
||||
|
||||
// collect property data for macros.
|
||||
let wgt_props = if is_child { &mut wgt_properties_child } else { &mut wgt_properties };
|
||||
wgt_props.extend(quote! {
|
||||
|
@ -184,7 +212,7 @@ pub fn expand(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|||
required,
|
||||
..
|
||||
} = p;
|
||||
let required = *required || captured_properties.contains(ident);
|
||||
let required = *required || inherited_required.contains(ident);
|
||||
|
||||
// collect property data for macros.
|
||||
let wgt_props = if is_child { &mut wgt_properties_child } else { &mut wgt_properties };
|
||||
|
@ -265,7 +293,7 @@ pub fn expand(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|||
let mut assigns_tt = TokenStream::default();
|
||||
let mut defaults_tt = TokenStream::default();
|
||||
for BuiltWhenAssign { property, cfg, value_fn } in assigns {
|
||||
if properties_unset.contains(&property) {
|
||||
if properties_unset.contains_key(&property) {
|
||||
continue; // inherited removed by unset!.
|
||||
}
|
||||
|
||||
|
@ -429,6 +457,8 @@ pub fn expand(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|||
};
|
||||
|
||||
let r = quote! {
|
||||
#errors
|
||||
|
||||
#attrs
|
||||
#gen_docs
|
||||
#cfg
|
||||
|
@ -520,7 +550,7 @@ struct WidgetItem {
|
|||
ident: Ident,
|
||||
mixin: bool,
|
||||
|
||||
properties_unset: Vec<Ident>,
|
||||
properties_unset: Vec<UnsetItem>,
|
||||
properties_declared: Vec<Ident>,
|
||||
|
||||
properties_child: Vec<PropertyItem>,
|
||||
|
@ -574,6 +604,19 @@ impl Parse for WidgetItem {
|
|||
})
|
||||
}
|
||||
}
|
||||
struct UnsetItem {
|
||||
property: Ident,
|
||||
/// for the span of the unset keyword.
|
||||
unset: TokenStream,
|
||||
}
|
||||
impl Parse for UnsetItem {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
Ok(Self {
|
||||
property: input.parse().unwrap_or_else(|e| non_user_error!(e)),
|
||||
unset: non_user_braced!(input).parse().unwrap(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A property declaration
|
||||
struct PropertyItem {
|
||||
|
|
Loading…
Reference in New Issue