Skip to content

Commit 788cc44

Browse files
yew-macro:
- Removed most of `.to_string()` calls - Factored out the check for a component-like name
1 parent 3b5c891 commit 788cc44

File tree

13 files changed

+178
-58
lines changed

13 files changed

+178
-58
lines changed

packages/yew-macro/src/function_component.rs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use syn::{
99
};
1010

1111
use crate::hook::BodyRewriter;
12+
use crate::DisplayExt;
1213

1314
#[derive(Clone)]
1415
pub struct FunctionComponent {
@@ -157,29 +158,25 @@ impl FunctionComponent {
157158
fn filter_attrs_for_component_struct(&self) -> Vec<Attribute> {
158159
self.attrs
159160
.iter()
160-
.filter_map(|m| {
161+
.filter(|m| {
161162
m.path()
162163
.get_ident()
163-
.and_then(|ident| match ident.to_string().as_str() {
164-
"doc" | "allow" => Some(m.clone()),
165-
_ => None,
166-
})
164+
.is_some_and(|ident| (ident.repr_eq("doc") || ident.repr_eq("allow")))
167165
})
166+
.cloned()
168167
.collect()
169168
}
170169

171170
/// Filters attributes that should be copied to the component impl block.
172171
fn filter_attrs_for_component_impl(&self) -> Vec<Attribute> {
173172
self.attrs
174173
.iter()
175-
.filter_map(|m| {
174+
.filter(|m| {
176175
m.path()
177176
.get_ident()
178-
.and_then(|ident| match ident.to_string().as_str() {
179-
"allow" => Some(m.clone()),
180-
_ => None,
181-
})
177+
.is_some_and(|ident| ident.repr_eq("allow"))
182178
})
179+
.cloned()
183180
.collect()
184181
}
185182

packages/yew-macro/src/hook/body.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use syn::{
88
ExprMatch, ExprWhile, Ident, Item,
99
};
1010

11+
use crate::DisplayExt;
12+
1113
#[derive(Debug)]
1214
pub struct BodyRewriter {
1315
branch_lock: Arc<Mutex<()>>,
@@ -43,7 +45,7 @@ impl VisitMut for BodyRewriter {
4345
// Only rewrite hook calls.
4446
if let Expr::Path(ref m) = &*i.func {
4547
if let Some(m) = m.path.segments.last().as_ref().map(|m| &m.ident) {
46-
if m.to_string().starts_with("use_") {
48+
if m.starts_with("use_") {
4749
if self.is_branched() {
4850
emit_error!(
4951
m,
@@ -69,7 +71,7 @@ impl VisitMut for BodyRewriter {
6971
match &mut *i {
7072
Expr::Macro(m) => {
7173
if let Some(ident) = m.mac.path.segments.last().as_ref().map(|m| &m.ident) {
72-
if ident.to_string().starts_with("use_") {
74+
if ident.starts_with("use_") {
7375
if self.is_branched() {
7476
emit_error!(
7577
ident,

packages/yew-macro/src/hook/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ mod signature;
1515
pub use body::BodyRewriter;
1616
use signature::HookSignature;
1717

18+
use crate::DisplayExt;
19+
1820
#[derive(Clone)]
1921
pub struct HookFn {
2022
inner: ItemFn,
@@ -42,7 +44,7 @@ impl Parse for HookFn {
4244
emit_error!(sig.unsafety, "unsafe functions can't be hooks");
4345
}
4446

45-
if !sig.ident.to_string().starts_with("use_") {
47+
if !sig.ident.starts_with("use_") {
4648
emit_error!(sig.ident, "hooks must have a name starting with `use_`");
4749
}
4850

packages/yew-macro/src/html_tree/html_component.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::fmt::{Display, Write};
2+
13
use proc_macro2::Span;
24
use quote::{quote, quote_spanned, ToTokens};
35
use syn::parse::discouraged::Speculative;
@@ -9,6 +11,41 @@ use super::{HtmlChildrenTree, TagTokens};
911
use crate::is_ide_completion;
1012
use crate::props::ComponentProps;
1113

14+
/// Returns `true` if `s` looks like a component name
15+
pub fn is_component_name(s: impl Display) -> bool {
16+
struct X {
17+
is_ide_completion: bool,
18+
empty: bool,
19+
}
20+
21+
impl Write for X {
22+
fn write_str(&mut self, chunk: &str) -> std::fmt::Result {
23+
if self.empty {
24+
self.empty = chunk.is_empty();
25+
if !self.is_ide_completion
26+
&& chunk
27+
.bytes()
28+
.next()
29+
.is_some_and(|b| !b.is_ascii_uppercase())
30+
{
31+
return Err(std::fmt::Error);
32+
}
33+
}
34+
chunk
35+
.bytes()
36+
.any(|b| b.is_ascii_uppercase())
37+
.then_some(())
38+
.ok_or(std::fmt::Error)
39+
}
40+
}
41+
42+
let mut writer = X {
43+
is_ide_completion: is_ide_completion(),
44+
empty: true,
45+
};
46+
write!(writer, "{s}").is_ok_and(|_| !writer.empty)
47+
}
48+
1249
pub struct HtmlComponent {
1350
ty: Type,
1451
props: ComponentProps,

packages/yew-macro/src/html_tree/html_dashed_name.rs

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use syn::spanned::Spanned;
99
use syn::{LitStr, Token};
1010

1111
use crate::stringify::Stringify;
12-
use crate::{non_capitalized_ascii, Peek};
12+
use crate::{DisplayExt, Peek};
1313

1414
#[derive(Clone, PartialEq, Eq)]
1515
pub struct HtmlDashedName {
@@ -18,17 +18,6 @@ pub struct HtmlDashedName {
1818
}
1919

2020
impl HtmlDashedName {
21-
/// Checks if this name is equal to the provided item (which can be anything implementing
22-
/// `Into<String>`).
23-
pub fn eq_ignore_ascii_case<S>(&self, other: S) -> bool
24-
where
25-
S: Into<String>,
26-
{
27-
let mut s = other.into();
28-
s.make_ascii_lowercase();
29-
s == self.to_ascii_lowercase_string()
30-
}
31-
3221
pub fn to_ascii_lowercase_string(&self) -> String {
3322
let mut s = self.to_string();
3423
s.make_ascii_lowercase();
@@ -53,7 +42,7 @@ impl fmt::Display for HtmlDashedName {
5342
impl Peek<'_, Self> for HtmlDashedName {
5443
fn peek(cursor: Cursor) -> Option<(Self, Cursor)> {
5544
let (name, cursor) = cursor.ident()?;
56-
if !non_capitalized_ascii(&name.to_string()) {
45+
if !name.is_non_capitalized_ascii() {
5746
return None;
5847
}
5948

packages/yew-macro/src/html_tree/html_element.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use syn::{Expr, Ident, Lit, LitStr, Token};
99
use super::{HtmlChildrenTree, HtmlDashedName, TagTokens};
1010
use crate::props::{ElementProps, Prop, PropDirective};
1111
use crate::stringify::{Stringify, Value};
12-
use crate::{is_ide_completion, non_capitalized_ascii, Peek, PeekValue};
12+
use crate::{is_ide_completion, DisplayExt, Peek, PeekValue};
1313

1414
fn is_normalised_element_name(name: &str) -> bool {
1515
match name {
@@ -457,8 +457,7 @@ impl ToTokens for HtmlElement {
457457
#[cfg(nightly_yew)]
458458
let invalid_void_tag_msg_start = {
459459
let span = vtag.span().unwrap();
460-
let source_file = span.source_file().path();
461-
let source_file = source_file.display();
460+
let source_file = span.file();
462461
let start = span.start();
463462
format!("[{}:{}:{}] ", source_file, start.line(), start.column())
464463
};
@@ -668,13 +667,13 @@ impl PeekValue<TagKey> for HtmlElementOpen {
668667
let (tag_key, cursor) = TagName::peek(cursor)?;
669668
if let TagKey::Lit(name) = &tag_key {
670669
// Avoid parsing `<key=[...]>` as an element. It needs to be parsed as an `HtmlList`.
671-
if name.to_string() == "key" {
670+
if name.repr_eq("key") {
672671
let (punct, _) = cursor.punct()?;
673672
// ... unless it isn't followed by a '='. `<key></key>` is a valid element!
674673
if punct.as_char() == '=' {
675674
return None;
676675
}
677-
} else if !non_capitalized_ascii(&name.to_string()) {
676+
} else if !name.is_non_capitalized_ascii() {
678677
return None;
679678
}
680679
}
@@ -743,7 +742,7 @@ impl PeekValue<TagKey> for HtmlElementClose {
743742
}
744743

745744
let (tag_key, cursor) = TagName::peek(cursor)?;
746-
if matches!(&tag_key, TagKey::Lit(name) if !non_capitalized_ascii(&name.to_string())) {
745+
if matches!(&tag_key, TagKey::Lit(name) if !name.is_non_capitalized_ascii()) {
747746
return None;
748747
}
749748

packages/yew-macro/src/html_tree/html_node.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use syn::Lit;
77

88
use super::ToNodeIterator;
99
use crate::stringify::Stringify;
10-
use crate::PeekValue;
10+
use crate::{DisplayExt, PeekValue};
1111

1212
pub enum HtmlNode {
1313
Literal(Box<Lit>),
@@ -44,10 +44,7 @@ impl PeekValue<()> for HtmlNode {
4444
fn peek(cursor: Cursor) -> Option<()> {
4545
cursor.literal().map(|_| ()).or_else(|| {
4646
let (ident, _) = cursor.ident()?;
47-
match ident.to_string().as_str() {
48-
"true" | "false" => Some(()),
49-
_ => None,
50-
}
47+
(ident.repr_eq("true") || ident.repr_eq("false")).then_some(())
5148
})
5249
}
5350
}

packages/yew-macro/src/html_tree/lint/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use syn::spanned::Spanned;
77
use super::html_element::{HtmlElement, TagName};
88
use super::HtmlTree;
99
use crate::props::{ElementProps, Prop};
10+
use crate::DisplayExt;
1011

1112
/// Lints HTML elements to check if they are well formed. If the element is not well-formed, then
1213
/// use `proc-macro-error` (and the `emit_warning!` macro) to produce a warning. At present, these
@@ -49,7 +50,7 @@ fn get_attribute<'a>(props: &'a ElementProps, name: &str) -> Option<&'a Prop> {
4950
props
5051
.attributes
5152
.iter()
52-
.find(|item| item.label.eq_ignore_ascii_case(name))
53+
.find(|item| item.label.repr_eq_ignore_ascii_case(name))
5354
}
5455

5556
/// Lints to check if anchor (`<a>`) tags have valid `href` attributes defined.
@@ -58,7 +59,7 @@ pub struct AHrefLint;
5859
impl Lint for AHrefLint {
5960
fn lint(element: &HtmlElement) {
6061
if let TagName::Lit(ref tag_name) = element.name {
61-
if !tag_name.eq_ignore_ascii_case("a") {
62+
if !tag_name.repr_eq_ignore_ascii_case("a") {
6263
return;
6364
};
6465
if let Some(prop) = get_attribute(&element.props, "href") {
@@ -96,7 +97,7 @@ pub struct ImgAltLint;
9697
impl Lint for ImgAltLint {
9798
fn lint(element: &HtmlElement) {
9899
if let super::html_element::TagName::Lit(ref tag_name) = element.name {
99-
if !tag_name.eq_ignore_ascii_case("img") {
100+
if !tag_name.repr_eq_ignore_ascii_case("img") {
100101
return;
101102
};
102103
if get_attribute(&element.props, "alt").is_none() {

packages/yew-macro/src/html_tree/mod.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use syn::parse::{Parse, ParseStream};
66
use syn::spanned::Spanned;
77
use syn::{braced, token, Token};
88

9-
use crate::{is_ide_completion, PeekValue};
9+
use crate::PeekValue;
1010

1111
mod html_block;
1212
mod html_component;
@@ -20,7 +20,7 @@ mod lint;
2020
mod tag;
2121

2222
use html_block::HtmlBlock;
23-
use html_component::HtmlComponent;
23+
use html_component::{is_component_name, HtmlComponent};
2424
pub use html_dashed_name::HtmlDashedName;
2525
use html_element::HtmlElement;
2626
use html_if::HtmlIf;
@@ -97,14 +97,10 @@ impl HtmlTree {
9797
Some(HtmlType::Component)
9898
} else if input.peek(Ident::peek_any) {
9999
let ident = Ident::parse_any(&input).ok()?;
100-
let ident_str = ident.to_string();
101100

102101
if input.peek(Token![=]) || (input.peek(Token![?]) && input.peek2(Token![=])) {
103102
Some(HtmlType::List)
104-
} else if ident_str.chars().next().unwrap().is_ascii_uppercase()
105-
|| input.peek(Token![::])
106-
|| is_ide_completion() && ident_str.chars().any(|c| c.is_ascii_uppercase())
107-
{
103+
} else if input.peek(Token![::]) || is_component_name(&ident) {
108104
Some(HtmlType::Component)
109105
} else {
110106
Some(HtmlType::Element)

packages/yew-macro/src/html_tree/tag.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ fn span_eq_hack(a: &Span, b: &Span) -> bool {
1414
fn error_replace_span(err: syn::Error, from: Span, to: impl ToTokens) -> syn::Error {
1515
let err_it = err.into_iter().map(|err| {
1616
if span_eq_hack(&err.span(), &from) {
17-
syn::Error::new_spanned(&to, err.to_string())
17+
syn::Error::new_spanned(&to, err)
1818
} else {
1919
err
2020
}

0 commit comments

Comments
 (0)