Skip to content

Commit 65bf05f

Browse files
authored
Explicitly disallow dynamic attribute names (#464)
They were already disallowed before, but this also removes them from the AST.
1 parent 1f76200 commit 65bf05f

File tree

4 files changed

+50
-39
lines changed

4 files changed

+50
-39
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
use maud::html;
2+
3+
fn main() {
4+
let name = "href";
5+
html! {
6+
a (name)="about:blank" {}
7+
};
8+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
error: expected one of: identifier, literal, `.`, `#`, curly braces, `;`
2+
--> tests/warnings/dynamic-attribute-names.rs:6:11
3+
|
4+
6 | a (name)="about:blank" {}
5+
| ^

maud_macros/src/ast.rs

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -335,15 +335,15 @@ impl<E: ToTokens> ToTokens for Block<E> {
335335
pub enum Attribute {
336336
Class {
337337
dot_token: Dot,
338-
name: AttributeName,
338+
name: HtmlNameOrMarkup,
339339
toggler: Option<Toggler>,
340340
},
341341
Id {
342342
pound_token: Pound,
343-
name: AttributeName,
343+
name: HtmlNameOrMarkup,
344344
},
345345
Named {
346-
name: AttributeName,
346+
name: HtmlName,
347347
attr_type: AttributeType,
348348
},
349349
}
@@ -375,8 +375,12 @@ impl DiagnosticParse for Attribute {
375375
name: input.diagnostic_parse(diagnostics)?,
376376
})
377377
} else {
378-
let name = input.diagnostic_parse::<AttributeName>(diagnostics)?;
379-
let name_display = name.to_string();
378+
let name = input.diagnostic_parse::<HtmlName>(diagnostics)?;
379+
380+
if input.peek(Question) {
381+
input.parse::<Question>()?;
382+
}
383+
380384
let fork = input.fork();
381385

382386
let attr = Self::Named {
@@ -388,8 +392,8 @@ impl DiagnosticParse for Attribute {
388392
diagnostics.push(
389393
attr.span()
390394
.error("attribute value must be a string")
391-
.help(format!("to declare an empty attribute, omit the equals sign: `{name_display}`"))
392-
.help(format!("to toggle the attribute, use square brackets: `{name_display}[some_boolean_flag]`"))
395+
.help(format!("to declare an empty attribute, omit the equals sign: `{name}`"))
396+
.help(format!("to toggle the attribute, use square brackets: `{name}[some_boolean_flag]`"))
393397
);
394398
}
395399

@@ -425,49 +429,43 @@ impl ToTokens for Attribute {
425429
}
426430

427431
#[derive(Debug, Clone)]
428-
pub enum AttributeName {
429-
Normal(HtmlName),
432+
pub enum HtmlNameOrMarkup {
433+
HtmlName(HtmlName),
430434
Markup(Markup<NoElement>),
431435
}
432436

433-
impl DiagnosticParse for AttributeName {
437+
impl DiagnosticParse for HtmlNameOrMarkup {
434438
fn diagnostic_parse(
435439
input: ParseStream,
436440
diagnostics: &mut Vec<Diagnostic>,
437441
) -> syn::Result<Self> {
438-
let name = if input.peek(Ident::peek_any) || input.peek(Lit) {
439-
input.diagnostic_parse(diagnostics).map(Self::Normal)
442+
if input.peek(Ident::peek_any) || input.peek(Lit) {
443+
input.diagnostic_parse(diagnostics).map(Self::HtmlName)
440444
} else {
441445
input.diagnostic_parse(diagnostics).map(Self::Markup)
442-
};
443-
444-
if input.peek(Question) {
445-
input.parse::<Question>()?;
446446
}
447-
448-
name
449447
}
450448
}
451449

452-
impl Parse for AttributeName {
450+
impl Parse for HtmlNameOrMarkup {
453451
fn parse(input: ParseStream) -> syn::Result<Self> {
454452
Self::diagnostic_parse(input, &mut Vec::new())
455453
}
456454
}
457455

458-
impl ToTokens for AttributeName {
456+
impl ToTokens for HtmlNameOrMarkup {
459457
fn to_tokens(&self, tokens: &mut TokenStream) {
460458
match self {
461-
Self::Normal(name) => name.to_tokens(tokens),
459+
Self::HtmlName(name) => name.to_tokens(tokens),
462460
Self::Markup(markup) => markup.to_tokens(tokens),
463461
}
464462
}
465463
}
466464

467-
impl Display for AttributeName {
465+
impl Display for HtmlNameOrMarkup {
468466
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
469467
match self {
470-
Self::Normal(name) => name.fmt(f),
468+
Self::HtmlName(name) => name.fmt(f),
471469
Self::Markup(markup) => markup.to_token_stream().fmt(f),
472470
}
473471
}

maud_macros/src/generate.rs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -87,18 +87,18 @@ impl Generator {
8787
build.push_escaped(&name.to_string());
8888
}
8989

90-
fn attr_name(&self, name: AttributeName, build: &mut Builder) {
90+
fn name_or_markup(&self, name: HtmlNameOrMarkup, build: &mut Builder) {
9191
match name {
92-
AttributeName::Normal(name) => self.name(name, build),
93-
AttributeName::Markup(markup) => self.markup(markup, build),
92+
HtmlNameOrMarkup::HtmlName(name) => self.name(name, build),
93+
HtmlNameOrMarkup::Markup(markup) => self.markup(markup, build),
9494
}
9595
}
9696

97-
fn attr(&self, name: AttributeName, value: AttributeType, build: &mut Builder) {
97+
fn attr(&self, name: HtmlName, value: AttributeType, build: &mut Builder) {
9898
match value {
9999
AttributeType::Normal { value, .. } => {
100100
build.push_str(" ");
101-
self.attr_name(name, build);
101+
self.name(name, build);
102102
build.push_str("=\"");
103103
self.markup(value, build);
104104
build.push_str("\"");
@@ -112,7 +112,7 @@ impl Generator {
112112
let body = {
113113
let mut build = self.builder();
114114
build.push_str(" ");
115-
self.attr_name(name, &mut build);
115+
self.name(name, &mut build);
116116
build.push_str("=\"");
117117
self.splice(inner_value.clone(), &mut build);
118118
build.push_str("\"");
@@ -122,13 +122,13 @@ impl Generator {
122122
}
123123
AttributeType::Empty(None) => {
124124
build.push_str(" ");
125-
self.attr_name(name, build);
125+
self.name(name, build);
126126
}
127127
AttributeType::Empty(Some(Toggler { cond, .. })) => {
128128
let body = {
129129
let mut build = self.builder();
130130
build.push_str(" ");
131-
self.attr_name(name, &mut build);
131+
self.name(name, &mut build);
132132
build.finish()
133133
};
134134
build.push_tokens(quote!(if (#cond) { #body }));
@@ -143,7 +143,7 @@ impl Generator {
143143
let mut toggle_class_exprs = vec![];
144144

145145
build.push_str(" ");
146-
self.attr_name(parse_quote!(class), build);
146+
self.name(parse_quote!(class), build);
147147
build.push_str("=\"");
148148
for (i, (name, toggler)) in classes.into_iter().enumerate() {
149149
if let Some(toggler) = toggler {
@@ -152,7 +152,7 @@ impl Generator {
152152
if i > 0 {
153153
build.push_str(" ");
154154
}
155-
self.attr_name(name, build);
155+
self.name_or_markup(name, build);
156156
}
157157
}
158158

@@ -162,7 +162,7 @@ impl Generator {
162162
if not_first {
163163
build.push_str(" ");
164164
}
165-
self.attr_name(name, &mut build);
165+
self.name_or_markup(name, &mut build);
166166
build.finish()
167167
};
168168
build.push_tokens(quote!(if (#toggler) { #body }));
@@ -173,9 +173,9 @@ impl Generator {
173173

174174
if let Some(id) = id {
175175
build.push_str(" ");
176-
self.attr_name(parse_quote!(id), build);
176+
self.name(parse_quote!(id), build);
177177
build.push_str("=\"");
178-
self.attr_name(id, build);
178+
self.name_or_markup(id, build);
179179
build.push_str("\"");
180180
}
181181

@@ -311,9 +311,9 @@ impl Generator {
311311
fn split_attrs(
312312
attrs: Vec<Attribute>,
313313
) -> (
314-
Vec<(AttributeName, Option<Expr>)>,
315-
Option<AttributeName>,
316-
Vec<(AttributeName, AttributeType)>,
314+
Vec<(HtmlNameOrMarkup, Option<Expr>)>,
315+
Option<HtmlNameOrMarkup>,
316+
Vec<(HtmlName, AttributeType)>,
317317
) {
318318
let mut classes = vec![];
319319
let mut id = None;

0 commit comments

Comments
 (0)