Prevent crate, Self and super to be used as identifiers

This commit is contained in:
Guillaume Gomez 2025-05-19 21:49:19 +02:00
parent 6ac39d24d9
commit 244980e0e9
3 changed files with 49 additions and 2 deletions

View File

@ -39,6 +39,14 @@ fn check_expr<'a>(
allow_underscore: bool,
) -> Result<(), ParseErr<'a>> {
match &expr.inner {
// 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).
Expr::Var(name @ ("crate" | "super" | "Self")) => {
Err(winnow::error::ErrMode::Cut(ErrorContext::new(
format!("`{name}` cannot be used as an identifier"),
expr.span,
)))
}
Expr::Var("_") if !allow_underscore => Err(winnow::error::ErrMode::Cut(ErrorContext::new(
"reserved keyword `_` cannot be used here",
expr.span,
@ -53,11 +61,21 @@ fn check_expr<'a>(
Ok(())
}
}
Expr::Path(path) => {
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"),
expr.span,
)));
}
}
Ok(())
}
Expr::BoolLit(_)
| Expr::NumLit(_, _)
| Expr::StrLit(_)
| Expr::CharLit(_)
| Expr::Path(_)
| Expr::Attr(_, _)
| Expr::Filter(_)
| Expr::NamedArgument(_, _)
@ -702,7 +720,17 @@ impl<'a> Suffix<'a> {
preceded(
ws(('.', not('.'))),
cut_err((
alt((digit1, identifier)),
|i: &mut _| {
let name = alt((digit1, identifier)).parse_next(i)?;
if !crate::can_be_variable_name(name) {
Err(winnow::error::ErrMode::Cut(ErrorContext::new(
format!("`{name}` cannot be used as an identifier"),
*i,
)))
} else {
Ok(name)
}
},
opt(|i: &mut _| call_generics(i, level)),
)),
)

View File

@ -1077,6 +1077,11 @@ pub fn strip_common(base: &Path, path: &Path) -> String {
}
}
#[inline]
pub(crate) fn can_be_variable_name(name: &str) -> bool {
!matches!(name, "self" | "Self" | "super" | "crate")
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IntKind {
I8,

View File

@ -108,6 +108,15 @@ impl<'a> Target<'a> {
return Ok(Self::Struct(path, targets));
}
// If the path only contains one item, we need to check the name.
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"),
i_before_matching_with,
)));
}
}
*i = i_before_matching_with;
return Ok(Self::Path(path));
}
@ -204,6 +213,11 @@ fn verify_name<'a>(
format!("cannot use `{name}` as a name: it is a rust keyword"),
input,
)))
} else if !crate::can_be_variable_name(name) {
Err(winnow::error::ErrMode::Cut(ErrorContext::new(
format!("`{name}` cannot be used as an identifier"),
input,
)))
} else if name.starts_with("__askama") {
Err(winnow::error::ErrMode::Cut(ErrorContext::new(
format!("cannot use `{name}` as a name: it is reserved for `askama`"),