summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYip Coekjan <69834864+Coekjan@users.noreply.github.com>2024-06-22 17:02:53 +0800
committerGitHub <noreply@github.com>2024-06-22 09:02:53 +0000
commit0fbec820355f9f61161b56dc7b757e8a2f5aef4f (patch)
treedc5fcfa8fa04eeebc8f61a33fbf2698096f70973
parent49f1c85c18c847ec2c4e6f5d9fbf13fb84f34f9a (diff)
Hint for shadowed std functions (#4402)
Co-authored-by: Laurenz <laurmaedje@gmail.com>
-rw-r--r--crates/typst/src/eval/call.rs18
-rw-r--r--crates/typst/src/foundations/scope.rs8
-rw-r--r--tests/suite/scripting/call.typ7
3 files changed, 31 insertions, 2 deletions
diff --git a/crates/typst/src/eval/call.rs b/crates/typst/src/eval/call.rs
index 12d024ad..a143c8ac 100644
--- a/crates/typst/src/eval/call.rs
+++ b/crates/typst/src/eval/call.rs
@@ -128,7 +128,7 @@ impl Eval for ast::FuncCall<'_> {
(callee.eval(vm)?, args.eval(vm)?.spanned(span))
};
- let func_result = callee.clone().cast::<Func>().at(callee_span);
+ let func_result = callee.clone().cast::<Func>();
if in_math && func_result.is_err() {
// For non-functions in math, we wrap the arguments in parentheses.
let mut body = Content::empty();
@@ -148,7 +148,21 @@ impl Eval for ast::FuncCall<'_> {
));
}
- let func = func_result?;
+ let func = func_result
+ .map_err(|mut err| {
+ if let ast::Expr::Ident(ident) = self.callee() {
+ let ident = ident.get();
+ if vm.scopes.check_std_shadowed(ident) {
+ err.hint(eco_format!(
+ "use `std.{}` to access the shadowed standard library function",
+ ident,
+ ));
+ }
+ }
+ err
+ })
+ .at(callee_span)?;
+
let point = || Tracepoint::Call(func.name().map(Into::into));
let f = || {
func.call(&mut vm.engine, vm.context, args)
diff --git a/crates/typst/src/foundations/scope.rs b/crates/typst/src/foundations/scope.rs
index e0ef7a72..0313df7a 100644
--- a/crates/typst/src/foundations/scope.rs
+++ b/crates/typst/src/foundations/scope.rs
@@ -87,6 +87,14 @@ impl<'a> Scopes<'a> {
}
})?
}
+
+ /// Check if an std variable is shadowed.
+ pub fn check_std_shadowed(&self, var: &str) -> bool {
+ self.base.is_some_and(|base| base.global.scope().get(var).is_some())
+ && std::iter::once(&self.top)
+ .chain(self.scopes.iter().rev())
+ .any(|scope| scope.get(var).is_some())
+ }
}
#[cold]
diff --git a/tests/suite/scripting/call.typ b/tests/suite/scripting/call.typ
index 34344608..5a5fb326 100644
--- a/tests/suite/scripting/call.typ
+++ b/tests/suite/scripting/call.typ
@@ -49,6 +49,13 @@
// Error: 2-3 expected function, found string
#x()
+--- call-shadowed-builtin-function ---
+#let image = "image"
+
+// Error: 2-7 expected function, found string
+// Hint: 2-7 use `std.image` to access the shadowed standard library function
+#image("image")
+
--- call-bad-type-int-expr ---
#let f(x) = x