Better errors for array/dictionary method calls that return mutable (#3370)

This commit is contained in:
Leedehai 2024-02-11 08:08:43 -05:00 committed by GitHub
parent a1f111dfa6
commit 17d687b6a2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 126 additions and 9 deletions

View File

@ -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>;
}

View File

@ -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.

View File

@ -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<T: IntoValue> IntoResult for StrResult<T> {
}
}
impl<T: IntoValue> IntoResult for HintedStrResult<T> {
fn into_result(self, span: Span) -> SourceResult<Value> {
self.map(IntoValue::into_value).at(span)
}
}
impl<T: IntoValue> IntoResult for SourceResult<T> {
fn into_result(self, _: Span) -> SourceResult<Value> {
self.map(IntoValue::into_value)

View File

@ -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!(

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}