diff --git a/askama_parser/src/expr.rs b/askama_parser/src/expr.rs index e70a8042..a9c42b8a 100644 --- a/askama_parser/src/expr.rs +++ b/askama_parser/src/expr.rs @@ -42,61 +42,47 @@ struct Allowed { fn check_expr<'a>(expr: &WithSpan<'a, Expr<'a>>, allowed: Allowed) -> Result<(), ParseErr<'a>> { match &expr.inner { - Expr::Var(name) => { + &Expr::Var(name) => { // List can be found in rust compiler "can_be_raw" function (although in our case, it's // also used in cases like `match`, so `self` is allowed in this case). - if (!allowed.super_keyword && *name == "super") || matches!(*name, "crate" | "Self") { - Err(winnow::error::ErrMode::Cut(ErrorContext::new( - format!("`{name}` cannot be used as an identifier"), - expr.span, - ))) - } else if !allowed.underscore && *name == "_" { - Err(winnow::error::ErrMode::Cut(ErrorContext::new( - "reserved keyword `_` cannot be used here", - expr.span, - ))) + if (!allowed.super_keyword && name == "super") || matches!(name, "crate" | "Self") { + Err(err_reserved_identifier(name)) + } else if !allowed.underscore && name == "_" { + Err(err_underscore_identifier(name)) } else { Ok(()) } } - Expr::IsDefined(var) | Expr::IsNotDefined(var) => { - if *var == "_" { - Err(winnow::error::ErrMode::Cut(ErrorContext::new( - "reserved keyword `_` cannot be used here", - expr.span, - ))) + &Expr::IsDefined(var) | &Expr::IsNotDefined(var) => { + if var == "_" { + Err(err_underscore_identifier(var)) } else { Ok(()) } } Expr::Path(path) => { - if let [name] = path.as_slice() { + if let &[name] = path.as_slice() { if !crate::can_be_variable_name(name) { - return Err(winnow::error::ErrMode::Cut(ErrorContext::new( - format!("`{name}` cannot be used as an identifier"), - *name, - ))); + return Err(err_reserved_identifier(name)); } } Ok(()) } - Expr::BoolLit(_) - | Expr::NumLit(_, _) - | Expr::StrLit(_) - | Expr::CharLit(_) - | Expr::Attr(_, _) - | Expr::Filter(_) - | Expr::NamedArgument(_, _) - | Expr::RustMacro(_, _) - | Expr::Try(_) - | Expr::FilterSource - | Expr::LetCond(_) => Ok(()), Expr::Array(elems) | Expr::Tuple(elems) | Expr::Concat(elems) => { for elem in elems { check_expr(elem, allowed)?; } Ok(()) } + Expr::Attr(elem, attr) => { + if attr.name == "_" { + Err(err_underscore_identifier(attr.name)) + } else if !crate::can_be_variable_name(attr.name) { + Err(err_reserved_identifier(attr.name)) + } else { + check_expr(elem, Allowed::default()) + } + } Expr::Index(elem1, elem2) | Expr::BinOp(_, elem1, elem2) => { check_expr(elem1, Allowed::default())?; check_expr(elem2, Allowed::default()) @@ -130,9 +116,33 @@ fn check_expr<'a>(expr: &WithSpan<'a, Expr<'a>>, allowed: Allowed) -> Result<(), "unreachable", expr.span, ))), + Expr::BoolLit(_) + | Expr::NumLit(_, _) + | Expr::StrLit(_) + | Expr::CharLit(_) + | Expr::Filter(_) + | Expr::NamedArgument(_, _) + | Expr::RustMacro(_, _) + | Expr::Try(_) + | Expr::FilterSource + | Expr::LetCond(_) => Ok(()), } } +fn err_underscore_identifier(name: &str) -> winnow::error::ErrMode> { + winnow::error::ErrMode::Cut(ErrorContext::new( + "reserved keyword `_` cannot be used here", + name, + )) +} + +fn err_reserved_identifier(name: &str) -> winnow::error::ErrMode> { + winnow::error::ErrMode::Cut(ErrorContext::new( + format!("`{name}` cannot be used as an identifier"), + name, + )) +} + #[derive(Clone, Debug, PartialEq)] pub enum Expr<'a> { BoolLit(bool), diff --git a/fuzzing/fuzz/artifacts/derive/clusterfuzz-testcase-minimized-derive-6617490908315648 b/fuzzing/fuzz/artifacts/derive/clusterfuzz-testcase-minimized-derive-6617490908315648 new file mode 100644 index 00000000..734ddd00 --- /dev/null +++ b/fuzzing/fuzz/artifacts/derive/clusterfuzz-testcase-minimized-derive-6617490908315648 @@ -0,0 +1 @@ +ÿÿÿ{{crate.4}}ÿÿÿÿeÿ \ No newline at end of file diff --git a/testing/tests/ui/crate_identifier.rs b/testing/tests/ui/crate_identifier.rs index 69fa9dd2..a68f4b9d 100644 --- a/testing/tests/ui/crate_identifier.rs +++ b/testing/tests/ui/crate_identifier.rs @@ -148,4 +148,32 @@ struct PathElemSelfType2 { a: u8, } +#[derive(Template)] +#[template(ext = "html", source = "{{ Self.4 }}")] +struct InvalidRawIdentifierSelfTyInAttr1; + +#[derive(Template)] +#[template(ext = "html", source = "{{ super.4 }}")] +struct InvalidRawIdentifierSuperInAttr1; + +#[derive(Template)] +#[template(ext = "html", source = "{{ crate.4 }}")] +struct InvalidRawIdentifierCrateInAttr1; + +#[derive(Template)] +#[template(ext = "html", source = "{{ self.Self.4 }}")] +struct InvalidRawIdentifierSelfTyInAttr2; + +#[derive(Template)] +#[template(ext = "html", source = "{{ self.super.4 }}")] +struct InvalidRawIdentifierSuperInAttr2; + +#[derive(Template)] +#[template(ext = "html", source = "{{ self.crate.4 }}")] +struct InvalidRawIdentifierCrateInAttr2; + +#[derive(Template)] +#[template(ext = "html", source = "{{ self.self.4 }}")] +struct InvalidRawIdentifierSelfInAttr; + fn main() {} diff --git a/testing/tests/ui/crate_identifier.stderr b/testing/tests/ui/crate_identifier.stderr index 4bb17b99..f4dcb44c 100644 --- a/testing/tests/ui/crate_identifier.stderr +++ b/testing/tests/ui/crate_identifier.stderr @@ -237,3 +237,59 @@ error: `Self` cannot be used as an identifier | 146 | #[template(ext = "html", source = "{% match a %}{% when self::a::b::Self %}{% endmatch %}")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `Self` cannot be used as an identifier + --> :1:3 + "Self.4 }}" + --> tests/ui/crate_identifier.rs:152:35 + | +152 | #[template(ext = "html", source = "{{ Self.4 }}")] + | ^^^^^^^^^^^^^^ + +error: `super` cannot be used as an identifier + --> :1:3 + "super.4 }}" + --> tests/ui/crate_identifier.rs:156:35 + | +156 | #[template(ext = "html", source = "{{ super.4 }}")] + | ^^^^^^^^^^^^^^^ + +error: `crate` cannot be used as an identifier + --> :1:3 + "crate.4 }}" + --> tests/ui/crate_identifier.rs:160:35 + | +160 | #[template(ext = "html", source = "{{ crate.4 }}")] + | ^^^^^^^^^^^^^^^ + +error: `Self` cannot be used as an identifier + --> :1:8 + "Self.4 }}" + --> tests/ui/crate_identifier.rs:164:35 + | +164 | #[template(ext = "html", source = "{{ self.Self.4 }}")] + | ^^^^^^^^^^^^^^^^^^^ + +error: `super` cannot be used as an identifier + --> :1:8 + "super.4 }}" + --> tests/ui/crate_identifier.rs:168:35 + | +168 | #[template(ext = "html", source = "{{ self.super.4 }}")] + | ^^^^^^^^^^^^^^^^^^^^ + +error: `crate` cannot be used as an identifier + --> :1:8 + "crate.4 }}" + --> tests/ui/crate_identifier.rs:172:35 + | +172 | #[template(ext = "html", source = "{{ self.crate.4 }}")] + | ^^^^^^^^^^^^^^^^^^^^ + +error: `self` cannot be used as an identifier + --> :1:8 + "self.4 }}" + --> tests/ui/crate_identifier.rs:176:35 + | +176 | #[template(ext = "html", source = "{{ self.self.4 }}")] + | ^^^^^^^^^^^^^^^^^^^