-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Emit a deprecation warning on an encounter of a String
field when deriving Properties
#3382
base: master
Are you sure you want to change the base?
Emit a deprecation warning on an encounter of a String
field when deriving Properties
#3382
Conversation
Visit the preview URL for this PR (updated for commit c7bd63e): https://yew-rs-api--pr3382-add-string-prop-fiel-n2djdgpk.web.app (expires Tue, 29 Aug 2023 15:28:46 GMT) 🔥 via Firebase Hosting GitHub Action 🌎 |
Size Comparison
✅ None of the examples has changed their size significantly. |
Benchmark - SSRYew Master
Pull Request
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also add a test case for it?
Type::Path(TypePath { qself: None, path }) if is_path_a_string(path) => { | ||
emit_warning!( | ||
path.span(), | ||
"storing string values with `String` is deprecated, use `AttrValue` \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not deprecated, just not recommended. We don't intend to take away the ability to do so. What is deprecated is the implicit conversion from String
to AttrValue
, but unfortunately we don't have a way to emit a warning for it.
Can you change the message accordingly? A link to documentation where downsides of using String is mentioned would be great to have as well
I couldn't find a way to explicitly test if a warning is emitted by a derive macro, but there are already some tests that raise this warning, for example here |
iter.next().map_or(false, |first| { | ||
first == "String" | ||
|| (first == "std" || first == "alloc") | ||
&& iter.next().map_or(false, |x| x == "string") | ||
&& iter.next().map_or(false, |x| x == "String") | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think adding an early return might help to make the conditions chain easier to read.
iter.next().map_or(false, |first| { | |
first == "String" | |
|| (first == "std" || first == "alloc") | |
&& iter.next().map_or(false, |x| x == "string") | |
&& iter.next().map_or(false, |x| x == "String") | |
}) | |
iter.next().map_or(false, |first| { | |
if first == "String" { | |
return true; | |
} | |
(first == "std" || first == "alloc") | |
&& iter.next().map_or(false, |x| x == "string") | |
&& iter.next().map_or(false, |x| x == "String") | |
}) |
Also, do you think that could be a good idea to add some names like second
and third
?
iter.next().map_or(false, |first| { | |
first == "String" | |
|| (first == "std" || first == "alloc") | |
&& iter.next().map_or(false, |x| x == "string") | |
&& iter.next().map_or(false, |x| x == "String") | |
}) | |
iter.next().map_or(false, |first| { | |
first == "String" | |
|| (first == "std" || first == "alloc") | |
&& iter.next().map_or(false, |second| second == "string") | |
&& iter.next().map_or(false, |third| third == "String") | |
}) |
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the ||
operator is perfectly readable and makes the function concise, but I do agree that giving descriptive names to closures' arguments is a worthwhile measure, I'll do that right away
pub(crate) fn is_path_a_string(path: &Path) -> bool { | ||
is_path_segments_a_string(path.segments.iter().map(|ps| &ps.ident)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pub(crate) fn is_path_a_string(path: &Path) -> bool { | |
is_path_segments_a_string(path.segments.iter().map(|ps| &ps.ident)) | |
pub(crate) fn is_path_a_string(path: &Path) -> bool { | |
use syn::parse_quote as q; | |
path == q!(String) | |
|| path == q!(alloc::string::String) | |
|| path == q!(std::string::String) | |
|| path == q!(::alloc::string::String) | |
|| path == q!(::std::string::String) |
I think is_path_segments_a_string is only used for testing purpose, so we can simply check the path here instead.
This should also help with adding more variants in the future should we need it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided to benchmark both variants, here's the code of the benchmarks:
#![feature(test)]
extern crate test;
use syn::parse_quote as q;
use syn::Path;
use test::Bencher;
fn is_path_segments_a_string<'a, T>(mut iter: impl Iterator<Item = &'a T>) -> bool
where
T: 'a + ?Sized + PartialEq<str>,
{
iter.next().map_or(false, |first| {
first == "String"
|| (first == "std" || first == "alloc")
&& iter.next().map_or(false, |second| second == "string")
&& iter.next().map_or(false, |third| third == "String")
})
}
fn is_path_a_string_1(path: &Path) -> bool {
is_path_segments_a_string(path.segments.iter().map(|ps| &ps.ident))
}
fn is_path_a_string_2(path: &Path) -> bool {
path == &q!(String)
|| path == &q!(alloc::string::String)
|| path == &q!(std::string::String)
|| path == &q!(::alloc::string::String)
|| path == &q!(::std::string::String)
}
#[bench]
fn bench_1(b: &mut Bencher) {
b.iter(|| {
is_path_a_string_1(&q!(String));
is_path_a_string_1(&q!(::std::String));
is_path_a_string_1(&q!(std::string::String));
is_path_a_string_1(&q!(Vec));
})
}
#[bench]
fn bench_2(b: &mut Bencher) {
b.iter(|| {
is_path_a_string_2(&q!(String));
is_path_a_string_2(&q!(::std::String));
is_path_a_string_2(&q!(std::string::String));
is_path_a_string_2(&q!(Vec));
})
}
here's the output of cargo bench
:
running 2 tests
test bench_1 ... bench: 4,079 ns/iter (+/- 137)
test bench_2 ... bench: 23,215 ns/iter (+/- 541)
The first approach is as readable as the second one, but is much faster
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whilst I do not believe the readability is the same (imagine if we need to add Rc<str>
), if the first approach demonstrates better performance at this moment and it is not difficult to understand, I am fine with the current approach as well.
You can write a failing test in the You can add |
That's a great idea! I'll do that right away |
Wait, it doesn't emit a |
Does this mean users will have no way to suppress this warning even if they want to? |
On the other hand, I'm not sure about emitting a |
The lints are supposed to be opt-in: #2882 |
If this is not a deprecation, then IMO, it should be classified as a lint (undesirable usage). Which means it is not in the scope of a procedural macro to do it but a tool like clippy. |
There's something wrong with the way lints are configured, yew-macro's Makefile seemed to have seen no changes after #2882 , and the lints themselves wouldn't appear no matter what I tried |
Description
Makes the
derive(Properties)
macro emit a warning when encountering a field of typeString
in the struct, which suggests to useAttrValue
instead.Adsing this warning raises 2 questions:
Checklist