Add a warning for keys after the first node which do nothing (#4978)
* add a warning for keys after the first node which do nothing * warnings on stable * better warnings on nightly
This commit is contained in:
1 parent
c58526a36b
commit
386153672e
5 files changed
+57
-5
No files matched your search
Generated
+1
@@ -6311,6 +6311,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"proc-macro2-diagnostics",
|
"proc-macro2-diagnostics",
|
||||||
"quote",
|
"quote",
|
||||||
|
"rustversion",
|
||||||
"syn 2.0.108",
|
"syn 2.0.108",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ keywords = ["dom", "ui", "gui", "react"]
|
|||||||
proc-macro2 = { workspace = true, features = ["span-locations"] }
|
proc-macro2 = { workspace = true, features = ["span-locations"] }
|
||||||
proc-macro2-diagnostics = { workspace = true }
|
proc-macro2-diagnostics = { workspace = true }
|
||||||
quote = { workspace = true }
|
quote = { workspace = true }
|
||||||
|
rustversion.workspace = true
|
||||||
syn = { workspace = true, features = ["full", "extra-traits", "visit", "visit-mut"] }
|
syn = { workspace = true, features = ["full", "extra-traits", "visit", "visit-mut"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|||||||
@@ -53,3 +53,29 @@ impl ToTokens for Diagnostics {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Ideally this would be integrated into the existing diagnostics struct, but currently all fields are public so adding
|
||||||
|
// new fields would be a breaking change. Diagnostics also doesn't expose the message directly so we can't just modify
|
||||||
|
// the expansion
|
||||||
|
pub(crate) mod new_diagnostics {
|
||||||
|
use proc_macro2_diagnostics::SpanDiagnosticExt;
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||||
|
use quote::quote_spanned;
|
||||||
|
|
||||||
|
pub(crate) fn warning_diagnostic(span: Span, message: impl Display) -> TokenStream2 {
|
||||||
|
let note = message.to_string();
|
||||||
|
// If we are compiling on nightly, use diagnostics directly which supports proper warnings through new span apis
|
||||||
|
if rustversion::cfg!(nightly) {
|
||||||
|
return span.warning(note).emit_as_item_tokens();
|
||||||
|
}
|
||||||
|
quote_spanned! { span =>
|
||||||
|
const _: () = {
|
||||||
|
#[deprecated(note = #note)]
|
||||||
|
struct Warning;
|
||||||
|
_ = Warning;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -176,6 +176,14 @@ impl BodyNode {
|
|||||||
_ => panic!("Element name not available for this node"),
|
_ => panic!("Element name not available for this node"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn key(&self) -> Option<&AttributeValue> {
|
||||||
|
match self {
|
||||||
|
Self::Element(el) => el.key(),
|
||||||
|
Self::Component(comp) => comp.get_key(),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -109,6 +109,8 @@ impl ToTokens for TemplateBody {
|
|||||||
None => quote! { None },
|
None => quote! { None },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let key_warnings = self.check_for_duplicate_keys();
|
||||||
|
|
||||||
let roots = node.quote_roots();
|
let roots = node.quote_roots();
|
||||||
|
|
||||||
// Print paths is easy - just print the paths
|
// Print paths is easy - just print the paths
|
||||||
@@ -138,6 +140,8 @@ impl ToTokens for TemplateBody {
|
|||||||
dioxus_core::Element::Ok({
|
dioxus_core::Element::Ok({
|
||||||
#diagnostics
|
#diagnostics
|
||||||
|
|
||||||
|
#key_warnings
|
||||||
|
|
||||||
// Components pull in the dynamic literal pool and template in debug mode, so they need to be defined before dynamic nodes
|
// Components pull in the dynamic literal pool and template in debug mode, so they need to be defined before dynamic nodes
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
fn __original_template() -> &'static dioxus_core::internal::HotReloadedTemplate {
|
fn __original_template() -> &'static dioxus_core::internal::HotReloadedTemplate {
|
||||||
@@ -273,11 +277,7 @@ impl TemplateBody {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn implicit_key(&self) -> Option<&AttributeValue> {
|
pub fn implicit_key(&self) -> Option<&AttributeValue> {
|
||||||
match self.roots.first() {
|
self.roots.first().and_then(BodyNode::key)
|
||||||
Some(BodyNode::Element(el)) => el.key(),
|
|
||||||
Some(BodyNode::Component(comp)) => comp.get_key(),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensure only one key and that the key is not a static str
|
/// Ensure only one key and that the key is not a static str
|
||||||
@@ -304,6 +304,22 @@ impl TemplateBody {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_for_duplicate_keys(&self) -> TokenStream2 {
|
||||||
|
let mut warnings = TokenStream2::new();
|
||||||
|
|
||||||
|
// Make sure there are not multiple keys or keys on nodes other than the first in the block
|
||||||
|
for root in self.roots.iter().skip(1) {
|
||||||
|
if let Some(key) = root.key() {
|
||||||
|
warnings.extend(new_diagnostics::warning_diagnostic(
|
||||||
|
key.span(),
|
||||||
|
"Keys are only allowed on the first node in the block.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
warnings
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_dyn_node(&self, path: &[u8]) -> &BodyNode {
|
pub fn get_dyn_node(&self, path: &[u8]) -> &BodyNode {
|
||||||
let mut node = self.roots.get(path[0] as usize).unwrap();
|
let mut node = self.roots.get(path[0] as usize).unwrap();
|
||||||
for idx in path.iter().skip(1) {
|
for idx in path.iter().skip(1) {
|
||||||
|
|||||||
Reference in new issue
Block a user