parser: reject illegal raw identifiers in attribute access

This commit is contained in:
René Kijewski 2025-06-23 15:50:21 +02:00 committed by René Kijewski
parent 76b26cd5fa
commit 3f380721a2
4 changed files with 128 additions and 33 deletions

View File

@ -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<ErrorContext<'_>> {
winnow::error::ErrMode::Cut(ErrorContext::new(
"reserved keyword `_` cannot be used here",
name,
))
}
fn err_reserved_identifier(name: &str) -> winnow::error::ErrMode<ErrorContext<'_>> {
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),

View File

@ -0,0 +1 @@
˙˙˙{{crate.4}}˙˙˙˙e˙

View File

@ -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() {}

View File

@ -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
--> <source attribute>: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
--> <source attribute>: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
--> <source attribute>: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
--> <source attribute>: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
--> <source attribute>: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
--> <source attribute>: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
--> <source attribute>:1:8
"self.4 }}"
--> tests/ui/crate_identifier.rs:176:35
|
176 | #[template(ext = "html", source = "{{ self.self.4 }}")]
| ^^^^^^^^^^^^^^^^^^^