diff --git a/crates/typst/src/eval/access.rs b/crates/typst/src/eval/access.rs index e3bf2bf0d..6b6dff157 100644 --- a/crates/typst/src/eval/access.rs +++ b/crates/typst/src/eval/access.rs @@ -56,7 +56,7 @@ impl Access for ast::FuncCall<'_> { if is_accessor_method(&method) { let span = self.span(); let world = vm.world(); - let args = self.args().eval(vm)?; + let args = self.args().eval(vm)?.spanned(span); let value = access.target().access(vm)?; let result = call_method_access(value, &method, args, span); let point = || Tracepoint::Call(Some(method.get().clone())); diff --git a/crates/typst/src/eval/call.rs b/crates/typst/src/eval/call.rs index 26626b1e9..3f7224d80 100644 --- a/crates/typst/src/eval/call.rs +++ b/crates/typst/src/eval/call.rs @@ -12,7 +12,7 @@ use crate::introspection::{Introspector, Locator}; use crate::math::{Accent, AccentElem, LrElem}; use crate::symbols::Symbol; use crate::syntax::ast::{self, AstNode}; -use crate::syntax::{Spanned, SyntaxNode}; +use crate::syntax::{Span, Spanned, SyntaxNode}; use crate::text::TextElem; use crate::util::LazyHash; use crate::World; @@ -40,7 +40,7 @@ impl Eval for ast::FuncCall<'_> { let field_span = field.span(); let target = if is_mutating_method(&field) { - let mut args = args.eval(vm)?; + let mut args = args.eval(vm)?.spanned(span); let target = target.access(vm)?; // Only arrays and dictionaries have mutable methods. @@ -59,7 +59,7 @@ impl Eval for ast::FuncCall<'_> { access.target().eval(vm)? }; - let mut args = args.eval(vm)?; + let mut args = args.eval(vm)?.spanned(span); // Handle plugins. if let Value::Plugin(plugin) = &target { @@ -126,7 +126,7 @@ impl Eval for ast::FuncCall<'_> { bail!(error); } } else { - (callee.eval(vm)?, args.eval(vm)?) + (callee.eval(vm)?, args.eval(vm)?.spanned(span)) }; // Handle math special cases for non-functions: @@ -222,7 +222,9 @@ impl Eval for ast::Args<'_> { } } - Ok(Args { span: self.span(), items }) + // We do *not* use the `self.span()` here because we want the callsite + // span to be one level higher (the whole function call). + Ok(Args { span: Span::detached(), items }) } } diff --git a/crates/typst/src/eval/rules.rs b/crates/typst/src/eval/rules.rs index e34fe5cd1..f5d7da178 100644 --- a/crates/typst/src/eval/rules.rs +++ b/crates/typst/src/eval/rules.rs @@ -23,7 +23,7 @@ impl Eval for ast::SetRule<'_> { }) }) .at(target.span())?; - let args = self.args().eval(vm)?; + let args = self.args().eval(vm)?.spanned(self.span()); Ok(target.set(&mut vm.engine, args)?.spanned(self.span())) } } diff --git a/crates/typst/src/foundations/args.rs b/crates/typst/src/foundations/args.rs index 548114375..4624ae02f 100644 --- a/crates/typst/src/foundations/args.rs +++ b/crates/typst/src/foundations/args.rs @@ -42,7 +42,8 @@ use crate::syntax::{Span, Spanned}; #[derive(Clone, Hash)] #[allow(clippy::derived_hash_with_manual_eq)] pub struct Args { - /// The span of the whole argument list. + /// The callsite span for the function. This is not the span of the argument + /// list itself, but of the whole function call. pub span: Span, /// The positional and named arguments. pub items: EcoVec, @@ -62,6 +63,14 @@ impl Args { Self { span, items } } + /// Attach a span to these arguments if they don't already have one. + pub fn spanned(mut self, span: Span) -> Self { + if self.span.is_detached() { + self.span = span; + } + self + } + /// Returns the number of remaining positional arguments. pub fn remaining(&self) -> usize { self.items.iter().filter(|slot| slot.name.is_none()).count() @@ -345,8 +354,8 @@ pub trait IntoArgs { } impl IntoArgs for Args { - fn into_args(self, _: Span) -> Args { - self + fn into_args(self, fallback: Span) -> Args { + self.spanned(fallback) } } diff --git a/crates/typst/src/foundations/calc.rs b/crates/typst/src/foundations/calc.rs index c65102ced..eb0b59dd9 100644 --- a/crates/typst/src/foundations/calc.rs +++ b/crates/typst/src/foundations/calc.rs @@ -114,11 +114,9 @@ pub fn pow( }; let result = match (base, exponent.v) { - (Num::Int(a), Num::Int(b)) if b >= 0 => a - .checked_pow(b as u32) - .map(Num::Int) - .ok_or("the result is too large") - .at(span)?, + (Num::Int(a), Num::Int(b)) if b >= 0 => { + a.checked_pow(b as u32).map(Num::Int).ok_or_else(too_large).at(span)? + } (a, b) => Num::Float(if a.float() == std::f64::consts::E { b.float().exp() } else if a.float() == 2.0 { @@ -469,7 +467,7 @@ pub fn fact( /// The number whose factorial to calculate. Must be non-negative. number: u64, ) -> StrResult { - Ok(fact_impl(1, number).ok_or("the result is too large")?) + Ok(fact_impl(1, number).ok_or_else(too_large)?) } /// Calculates a permutation. @@ -493,7 +491,7 @@ pub fn perm( return Ok(0); } - Ok(fact_impl(base - numbers + 1, base).ok_or("the result is too large")?) + Ok(fact_impl(base - numbers + 1, base).ok_or_else(too_large)?) } /// Calculates the product of a range of numbers. Used to calculate @@ -528,7 +526,7 @@ pub fn binom( /// The lower coefficient. Must be non-negative. k: u64, ) -> StrResult { - Ok(binom_impl(n, k).ok_or("the result is too large")?) + Ok(binom_impl(n, k).ok_or_else(too_large)?) } /// Calculates a binomial coefficient, with `n` the upper coefficient and `k` @@ -594,7 +592,7 @@ pub fn lcm( Ok(a.checked_div(gcd(a, b)) .and_then(|gcd| gcd.checked_mul(b)) .map(|v| v.abs()) - .ok_or("the return value is too large")?) + .ok_or_else(too_large)?) } /// Rounds a number down to the nearest integer. @@ -974,3 +972,9 @@ cast! { v: f64 => Self::Float(v), v: Angle => Self::Angle(v), } + +/// The error message when the result is too large to be represented. +#[cold] +fn too_large() -> &'static str { + "the result is too large" +} diff --git a/tests/typ/bugs/parenthesized.typ b/tests/typ/bugs/parenthesized.typ index 99488a676..6901158e6 100644 --- a/tests/typ/bugs/parenthesized.typ +++ b/tests/typ/bugs/parenthesized.typ @@ -31,7 +31,7 @@ #test(f(a: _ => 5), 6) --- -// Error: 18-20 missing argument: pattern parameter +// Error: 17-20 missing argument: pattern parameter #let f(a: 10) = a() + 1 #f(a: _ => 5) diff --git a/tests/typ/compiler/closure.typ b/tests/typ/compiler/closure.typ index ef4e7df04..29c092b7c 100644 --- a/tests/typ/compiler/closure.typ +++ b/tests/typ/compiler/closure.typ @@ -118,7 +118,7 @@ let types(x, y) = "[" + str(type(x)) + ", " + str(type(y)) + "]" test(types(14%, 12pt), "[ratio, length]") - // Error: 13-21 missing argument: y + // Error: 8-21 missing argument: y test(types("nope"), "[string, none]") } diff --git a/tests/typ/compiler/delayed-error.typ b/tests/typ/compiler/delayed-error.typ index c0bfba4c4..eff6b85ba 100644 --- a/tests/typ/compiler/delayed-error.typ +++ b/tests/typ/compiler/delayed-error.typ @@ -1,10 +1,10 @@ // Test that errors in show rules are delayed: There can be multiple at once. --- -// Error: 26-34 panicked with: "hey1" +// Error: 21-34 panicked with: "hey1" #show heading: _ => panic("hey1") -// Error: 25-33 panicked with: "hey2" +// Error: 20-33 panicked with: "hey2" #show strong: _ => panic("hey2") = Hello diff --git a/tests/typ/compiler/spread.typ b/tests/typ/compiler/spread.typ index 23cd587bc..db6584532 100644 --- a/tests/typ/compiler/spread.typ +++ b/tests/typ/compiler/spread.typ @@ -128,6 +128,6 @@ #{ let f(..a, b, c, d) = none - // Error: 4-10 missing argument: d + // Error: 3-10 missing argument: d f(1, 2) } diff --git a/tests/typ/compute/calc.typ b/tests/typ/compute/calc.typ index db03af493..acd9b2a8d 100644 --- a/tests/typ/compute/calc.typ +++ b/tests/typ/compute/calc.typ @@ -211,7 +211,7 @@ #1.bit-rshift(-1) --- -// Error: 10-16 zero to the power of zero is undefined +// Error: 2-16 zero to the power of zero is undefined #calc.pow(0, 0) --- @@ -219,7 +219,7 @@ #calc.pow(2, 10000000000000000) --- -// Error: 10-25 the result is too large +// Error: 2-25 the result is too large #calc.pow(2, 2147483647) --- @@ -227,7 +227,7 @@ #calc.pow(2, calc.pow(2.0, 10000.0)) --- -// Error: 10-19 the result is not a real number +// Error: 2-19 the result is not a real number #calc.pow(-1, 0.5) --- @@ -259,7 +259,7 @@ #calc.log(1, base: 0) --- -// Error: 10-24 the result is not a real number +// Error: 2-24 the result is not a real number #calc.log(10, base: -1) --- @@ -268,7 +268,7 @@ #test(calc.fact(5), 120) --- -// Error: 11-15 the result is too large +// Error: 2-15 the result is too large #calc.fact(21) --- @@ -279,7 +279,7 @@ #test(calc.perm(5, 6), 0) --- -// Error: 11-19 the result is too large +// Error: 2-19 the result is too large #calc.perm(21, 21) --- @@ -311,11 +311,11 @@ #test(calc.lcm(8, 0), 0) --- -// Error: 10-41 the return value is too large +// Error: 2-41 the result is too large #calc.lcm(15486487489457, 4874879896543) --- -// Error: 10-12 expected at least one value +// Error: 2-12 expected at least one value #calc.min() --- @@ -339,7 +339,7 @@ #test(range(10, 0, step: -3), (10, 7, 4, 1)) --- -// Error: 7-9 missing argument: end +// Error: 2-9 missing argument: end #range() --- diff --git a/tests/typ/compute/construct.typ b/tests/typ/compute/construct.typ index f37669b5f..dcf597558 100644 --- a/tests/typ/compute/construct.typ +++ b/tests/typ/compute/construct.typ @@ -35,7 +35,7 @@ // Mix in hue-based space. #test(rgb(color.mix(red, blue, space: color.hsl)), rgb("#c408ff")) #test(rgb(color.mix((red, 50%), (blue, 100%), space: color.hsl)), rgb("#5100f8")) -// Error: 15-51 cannot mix more than two colors in a hue-based space +// Error: 6-51 cannot mix more than two colors in a hue-based space #rgb(color.mix(red, blue, white, space: color.hsl)) --- @@ -127,11 +127,11 @@ #rgb("lol") --- -// Error: 5-7 missing argument: red component +// Error: 2-7 missing argument: red component #rgb() --- -// Error: 5-11 missing argument: blue component +// Error: 2-11 missing argument: blue component #rgb(0, 1) --- @@ -181,7 +181,7 @@ #envelope.fly --- -// Error: 8-10 expected at least one variant +// Error: 2-10 expected at least one variant #symbol() --- @@ -223,7 +223,7 @@ #str.from-unicode(-1) --- -// Error: 18-28 0x110000 is not a valid codepoint +// Error: 2-28 0x110000 is not a valid codepoint #str.from-unicode(0x110000) // 0x10ffff is the highest valid code point --- @@ -284,15 +284,15 @@ #test(datetime.today(offset: 2).display(), "1970-01-01") --- -// Error: 10-12 at least one of date or time must be fully specified +// Error: 2-12 at least one of date or time must be fully specified #datetime() --- -// Error: 10-42 time is invalid +// Error: 2-42 time is invalid #datetime(hour: 25, minute: 0, second: 0) --- -// Error: 10-41 date is invalid +// Error: 2-41 date is invalid #datetime(year: 2000, month: 2, day: 30) --- diff --git a/tests/typ/compute/foundations.typ b/tests/typ/compute/foundations.typ index e4b7ce6ab..eb50c0723 100644 --- a/tests/typ/compute/foundations.typ +++ b/tests/typ/compute/foundations.typ @@ -12,27 +12,27 @@ --- // Test panic. -// Error: 7-9 panicked +// Error: 2-9 panicked #panic() --- // Test panic. -// Error: 7-12 panicked with: 123 +// Error: 2-12 panicked with: 123 #panic(123) --- // Test panic. -// Error: 7-24 panicked with: "this is wrong" +// Error: 2-24 panicked with: "this is wrong" #panic("this is wrong") --- // Test failing assertions. -// Error: 8-16 assertion failed +// Error: 2-16 assertion failed #assert(1 == 2) --- // Test failing assertions. -// Error: 8-51 assertion failed: two is smaller than one +// Error: 2-51 assertion failed: two is smaller than one #assert(2 < 1, message: "two is smaller than one") --- @@ -42,22 +42,22 @@ --- // Test failing assertions. -// Error: 11-19 equality assertion failed: value 10 was not equal to 11 +// Error: 2-19 equality assertion failed: value 10 was not equal to 11 #assert.eq(10, 11) --- // Test failing assertions. -// Error: 11-55 equality assertion failed: 10 and 12 are not equal +// Error: 2-55 equality assertion failed: 10 and 12 are not equal #assert.eq(10, 12, message: "10 and 12 are not equal") --- // Test failing assertions. -// Error: 11-19 inequality assertion failed: value 11 was equal to 11 +// Error: 2-19 inequality assertion failed: value 11 was equal to 11 #assert.ne(11, 11) --- // Test failing assertions. -// Error: 11-57 inequality assertion failed: must be different from 11 +// Error: 2-57 inequality assertion failed: must be different from 11 #assert.ne(11, 11, message: "must be different from 11") --- diff --git a/tests/typ/layout/spacing.typ b/tests/typ/layout/spacing.typ index f4e595908..faba60b48 100644 --- a/tests/typ/layout/spacing.typ +++ b/tests/typ/layout/spacing.typ @@ -42,5 +42,5 @@ A #h(1fr) B --- // Missing spacing. -// Error: 11-13 missing argument: amount +// Error: 10-13 missing argument: amount Totally #h() ignored diff --git a/tests/typ/math/frac.typ b/tests/typ/math/frac.typ index 0252f430c..fc7dd14b7 100644 --- a/tests/typ/math/frac.typ +++ b/tests/typ/math/frac.typ @@ -21,7 +21,7 @@ $ binom(circle, square) $ $ binom(n, k_1, k_2, k_3) $ --- -// Error: 8-13 missing argument: lower +// Error: 3-13 missing argument: lower $ binom(x^2) $ --- diff --git a/tests/typ/meta/document.typ b/tests/typ/meta/document.typ index 33c15dbce..b058bd96f 100644 --- a/tests/typ/meta/document.typ +++ b/tests/typ/meta/document.typ @@ -27,7 +27,7 @@ Hello #set document(title: [Hello]) --- -// Error: 10-12 can only be used in set rules +// Error: 2-12 can only be used in set rules #document() --- diff --git a/tests/typ/text/lorem.typ b/tests/typ/text/lorem.typ index 92dfbba41..804f804df 100644 --- a/tests/typ/text/lorem.typ +++ b/tests/typ/text/lorem.typ @@ -28,5 +28,5 @@ } --- -// Error: 7-9 missing argument: words +// Error: 2-9 missing argument: words #lorem() diff --git a/tests/typ/visualize/image.typ b/tests/typ/visualize/image.typ index 093fecc1c..0e256eb86 100644 --- a/tests/typ/visualize/image.typ +++ b/tests/typ/visualize/image.typ @@ -66,7 +66,7 @@ A #box(image("/files/tiger.jpg", height: 1cm, width: 80%)) B #image.decode(``.text, format: "svg") --- -// Error: 14-168 failed to parse SVG (missing root node) +// Error: 2-168 failed to parse SVG (missing root node) #image.decode(``.text, format: "svg") --- @@ -78,5 +78,5 @@ A #box(image("/files/tiger.jpg", height: 1cm, width: 80%)) B #image.decode(read("/files/tiger.jpg", encoding: none), format: "jpg", width: 80%) --- -// Error: 14-83 failed to decode image (Format error decoding Png: Invalid PNG signature.) +// Error: 2-83 failed to decode image (Format error decoding Png: Invalid PNG signature.) #image.decode(read("/files/tiger.jpg", encoding: none), format: "png", width: 80%) diff --git a/tests/typ/visualize/pattern-small.typ b/tests/typ/visualize/pattern-small.typ index 8a63c374c..1d289f921 100644 --- a/tests/typ/visualize/pattern-small.typ +++ b/tests/typ/visualize/pattern-small.typ @@ -14,6 +14,6 @@ ) --- -// Error: 22-52 pattern tile size must be non-zero -// Hint: 22-52 try setting the size manually +// Error: 15-52 pattern tile size must be non-zero +// Hint: 15-52 try setting the size manually #line(stroke: pattern(path((0pt, 0pt), (1em, 0pt))))