From 39b3d94beecf9348b41b2983dd8c96e153b2555a Mon Sep 17 00:00:00 2001 From: Noam Zaks Date: Sat, 14 Jun 2025 23:35:59 +0300 Subject: [PATCH] Error on imports with keyword names --- crates/typst-eval/src/import.rs | 4 ++++ crates/typst-syntax/src/ast.rs | 10 ++++++++-- crates/typst-syntax/src/lexer.rs | 6 ++++++ crates/typst-syntax/src/lib.rs | 4 ++-- tests/suite/scripting/import.typ | 5 +++++ tests/suite/scripting/modules/set.typ | 1 + 6 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 tests/suite/scripting/modules/set.typ diff --git a/crates/typst-eval/src/import.rs b/crates/typst-eval/src/import.rs index 1b1641487..0749596c7 100644 --- a/crates/typst-eval/src/import.rs +++ b/crates/typst-eval/src/import.rs @@ -85,6 +85,10 @@ impl Eval for ast::ModuleImport<'_> { source_span, "module name would not be a valid identifier"; hint: "you can rename the import with `as`", ), + Err(BareImportError::Keyword(name)) => bail!( + source_span, "module `{name}` would be overridden as a keyword"; + hint: "you can rename the import with `as`", + ), // Bad package spec would have failed the import already. Err(BareImportError::PackageInvalid) => unreachable!(), } diff --git a/crates/typst-syntax/src/ast.rs b/crates/typst-syntax/src/ast.rs index 7b211bfc1..ab6ceb32f 100644 --- a/crates/typst-syntax/src/ast.rs +++ b/crates/typst-syntax/src/ast.rs @@ -86,7 +86,7 @@ use ecow::EcoString; use unscanny::Scanner; use crate::package::PackageSpec; -use crate::{is_ident, is_newline, Span, SyntaxKind, SyntaxNode}; +use crate::{is_ident, is_keyword, is_newline, Span, SyntaxKind, SyntaxNode}; /// A typed AST node. pub trait AstNode<'a>: Sized { @@ -2223,6 +2223,10 @@ impl<'a> ModuleImport<'a> { return Err(BareImportError::PathInvalid); } + if is_keyword(&name) { + return Err(BareImportError::Keyword(name)); + } + Ok(name) } _ => Err(BareImportError::Dynamic), @@ -2240,13 +2244,15 @@ impl<'a> ModuleImport<'a> { } /// Reasons why a bare name cannot be determined for an import source. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[derive(Debug, Clone, Eq, PartialEq, Hash)] pub enum BareImportError { /// There is no statically resolvable binding name. Dynamic, /// The import source is not a valid path or the path stem not a valid /// identifier. PathInvalid, + /// The import source is a valid path but the path stem is a keyword. + Keyword(EcoString), /// The import source is not a valid package spec. PackageInvalid, } diff --git a/crates/typst-syntax/src/lexer.rs b/crates/typst-syntax/src/lexer.rs index 74f14cfeb..ee2bc11cc 100644 --- a/crates/typst-syntax/src/lexer.rs +++ b/crates/typst-syntax/src/lexer.rs @@ -1063,6 +1063,12 @@ pub fn is_ident(string: &str) -> bool { .is_some_and(|c| is_id_start(c) && chars.all(is_id_continue)) } +/// Whether a string is a Typst keyword. +#[inline] +pub fn is_keyword(string: &str) -> bool { + keyword(string).is_some() +} + /// Whether a character can start an identifier. #[inline] pub fn is_id_start(c: char) -> bool { diff --git a/crates/typst-syntax/src/lib.rs b/crates/typst-syntax/src/lib.rs index 4741506c5..50f2d7d3b 100644 --- a/crates/typst-syntax/src/lib.rs +++ b/crates/typst-syntax/src/lib.rs @@ -20,8 +20,8 @@ pub use self::file::FileId; pub use self::highlight::{highlight, highlight_html, Tag}; pub use self::kind::SyntaxKind; pub use self::lexer::{ - is_id_continue, is_id_start, is_ident, is_newline, is_valid_label_literal_id, - link_prefix, split_newlines, + is_id_continue, is_id_start, is_ident, is_keyword, is_newline, + is_valid_label_literal_id, link_prefix, split_newlines, }; pub use self::lines::Lines; pub use self::node::{LinkedChildren, LinkedNode, Side, SyntaxError, SyntaxNode}; diff --git a/tests/suite/scripting/import.typ b/tests/suite/scripting/import.typ index 382e444cc..4fc793bb1 100644 --- a/tests/suite/scripting/import.typ +++ b/tests/suite/scripting/import.typ @@ -483,3 +483,8 @@ This is never reached. --- import-from-file-package-lookalike --- // Error: 9-28 file not found (searched at tests/suite/scripting/#test/mypkg:1.0.0) #import "#test/mypkg:1.0.0": * + +--- import-keyword --- +// Error: 9-26 module `set` would be overridden as a keyword +// Hint: 9-26 you can rename the import with `as` +#import "modules/set.typ" diff --git a/tests/suite/scripting/modules/set.typ b/tests/suite/scripting/modules/set.typ new file mode 100644 index 000000000..9138f3c3f --- /dev/null +++ b/tests/suite/scripting/modules/set.typ @@ -0,0 +1 @@ +// SKIP