diff --git a/crates/typst/src/eval/access.rs b/crates/typst/src/eval/access.rs index 99b2f72ed..e3bf2bf0d 100644 --- a/crates/typst/src/eval/access.rs +++ b/crates/typst/src/eval/access.rs @@ -7,7 +7,7 @@ use crate::syntax::ast::{self, AstNode}; /// Access an expression mutably. pub(crate) trait Access { - /// Access the value. + /// Access the expression's evaluated value mutably. fn access<'a>(self, vm: &'a mut Vm) -> SourceResult<&'a mut Value>; } diff --git a/crates/typst/src/foundations/array.rs b/crates/typst/src/foundations/array.rs index 6f7c63475..27f56194f 100644 --- a/crates/typst/src/foundations/array.rs +++ b/crates/typst/src/foundations/array.rs @@ -113,7 +113,7 @@ impl Array { let len = self.len(); self.locate_opt(index, false) .and_then(move |i| self.0.make_mut().get_mut(i)) - .ok_or_else(|| out_of_bounds_no_default(index, len)) + .ok_or_else(|| out_of_bounds(index, len)) } /// Resolve an index or throw an out of bounds error. diff --git a/crates/typst/src/foundations/cast.rs b/crates/typst/src/foundations/cast.rs index 6a40ceccf..716eae9d4 100644 --- a/crates/typst/src/foundations/cast.rs +++ b/crates/typst/src/foundations/cast.rs @@ -8,7 +8,7 @@ use ecow::{eco_format, EcoString}; use smallvec::SmallVec; use unicode_math_class::MathClass; -use crate::diag::{At, SourceResult, StrResult}; +use crate::diag::{At, HintedStrResult, SourceResult, StrResult}; use crate::foundations::{array, repr, NativeElement, Packed, Repr, Str, Type, Value}; use crate::syntax::{Span, Spanned}; @@ -233,6 +233,12 @@ impl IntoResult for StrResult { } } +impl IntoResult for HintedStrResult { + fn into_result(self, span: Span) -> SourceResult { + self.map(IntoValue::into_value).at(span) + } +} + impl IntoResult for SourceResult { fn into_result(self, _: Span) -> SourceResult { self.map(IntoValue::into_value) diff --git a/crates/typst/src/foundations/dict.rs b/crates/typst/src/foundations/dict.rs index 341504cb4..59dd85c7b 100644 --- a/crates/typst/src/foundations/dict.rs +++ b/crates/typst/src/foundations/dict.rs @@ -7,7 +7,7 @@ use ecow::{eco_format, EcoString}; use indexmap::IndexMap; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use crate::diag::StrResult; +use crate::diag::{Hint, HintedStrResult, StrResult}; use crate::foundations::{array, func, repr, scope, ty, Array, Repr, Str, Value}; use crate::syntax::is_ident; use crate::util::ArcExt; @@ -83,10 +83,11 @@ impl Dict { } /// Mutably borrow the value the given `key` maps to. - pub fn at_mut(&mut self, key: &str) -> StrResult<&mut Value> { + pub fn at_mut(&mut self, key: &str) -> HintedStrResult<&mut Value> { Arc::make_mut(&mut self.0) .get_mut(key) - .ok_or_else(|| missing_key_no_default(key)) + .ok_or_else(|| missing_key(key)) + .hint("use `insert` to add or update values") } /// Remove the value if the dictionary contains the given key. @@ -354,7 +355,7 @@ fn missing_key(key: &str) -> EcoString { eco_format!("dictionary does not contain key {}", key.repr()) } -/// The missing key access error message when no default was fiven. +/// The missing key access error message when no default was given. #[cold] fn missing_key_no_default(key: &str) -> EcoString { eco_format!( diff --git a/tests/typ/bugs/3154-array-dict-mut-entry.typ b/tests/typ/bugs/3154-array-dict-mut-entry.typ new file mode 100644 index 000000000..b5a52814b --- /dev/null +++ b/tests/typ/bugs/3154-array-dict-mut-entry.typ @@ -0,0 +1,109 @@ +// Issue #3154: Confusing errors from methods supposed to return a mutable entry +// https://github.com/typst/typst/issues/3154 +// Ref: false + +--- +#{ + let array = () + // Error: 3-16 array is empty + array.first() +} + +--- +#{ + let array = () + // Error: 3-16 array is empty + array.first() = 9 +} + +--- +#{ + let array = () + // Error: 3-15 array is empty + array.last() +} + +--- +#{ + let array = () + // Error: 3-15 array is empty + array.last() = 9 +} + +--- +#{ + let array = (1,) + // Error: 3-14 array index out of bounds (index: 1, len: 1) and no default value was specified + array.at(1) +} + +--- +#{ + let array = (1,) + test(array.at(1, default: 0), 0) +} + +--- +#{ + let array = (1,) + // Error: 3-14 array index out of bounds (index: 1, len: 1) + array.at(1) = 9 +} + +--- +#{ + let array = (1,) + // Error: 3-26 array index out of bounds (index: 1, len: 1) + array.at(1, default: 0) = 9 +} + +--- +#{ + let dict = (a: 1) + // Error: 3-15 dictionary does not contain key "b" and no default value was specified + dict.at("b") +} + +--- +#{ + let dict = (a: 1) + test(dict.at("b", default: 0), 0) +} + +--- +#{ + let dict = (a: 1) + // Error: 3-15 dictionary does not contain key "b" + // Hint: 3-15 use `insert` to add or update values + dict.at("b") = 9 +} + +--- +#{ + let dict = (a: 1) + // Error: 3-27 dictionary does not contain key "b" + // Hint: 3-27 use `insert` to add or update values + dict.at("b", default: 0) = 9 +} + +--- +#{ + let dict = (a: 1) + // Error: 8-9 dictionary does not contain key "b" + dict.b +} + +--- +#{ + let dict = (a: 1) + dict.b = 9 + test(dict, (a: 1, b: 9)) +} + +--- +#{ + let dict = (a: 1) + // Error: 3-9 dictionary does not contain key "b" + // Hint: 3-9 use `insert` to add or update values + dict.b += 9 +} diff --git a/tests/typ/compiler/array.typ b/tests/typ/compiler/array.typ index 67b8abc35..9ff294804 100644 --- a/tests/typ/compiler/array.typ +++ b/tests/typ/compiler/array.typ @@ -54,7 +54,7 @@ // Test lvalue out of bounds. #{ let array = (1, 2, 3) - // Error: 3-14 array index out of bounds (index: 3, len: 3) and no default value was specified + // Error: 3-14 array index out of bounds (index: 3, len: 3) array.at(3) = 5 } diff --git a/tests/typ/compiler/dict.typ b/tests/typ/compiler/dict.typ index 92cacdb39..eca1c4582 100644 --- a/tests/typ/compiler/dict.typ +++ b/tests/typ/compiler/dict.typ @@ -66,7 +66,8 @@ // Missing lvalue is not automatically none-initialized. #{ let dict = (:) - // Error: 3-9 dictionary does not contain key "b" and no default value was specified + // Error: 3-9 dictionary does not contain key "b" + // Hint: 3-9 use `insert` to add or update values dict.b += 1 }