Add tests, fix bug, and improve symbol constructor errors

This commit is contained in:
T0mstone 2025-07-23 14:04:17 +02:00
parent 91189f4061
commit 70f619e896
3 changed files with 44 additions and 19 deletions

View File

@ -8,7 +8,7 @@ use serde::{Serialize, Serializer};
use typst_syntax::{Span, Spanned, is_ident};
use typst_utils::hash128;
use crate::diag::{DeprecationSink, SourceResult, StrResult, bail};
use crate::diag::{DeprecationSink, SourceResult, StrResult, bail, error};
use crate::foundations::{
Array, Content, Func, NativeElement, NativeFunc, Packed, PlainText, Repr as _, cast,
elem, func, scope, ty,
@ -231,15 +231,30 @@ impl Symbol {
// A list of modifiers, cleared & reused in each iteration.
let mut modifiers = Vec::new();
let mut errors = ecow::eco_vec![];
// Validate the variants.
for (i, &Spanned { ref v, span }) in variants.iter().enumerate() {
'variants: for (i, &Spanned { ref v, span }) in variants.iter().enumerate() {
modifiers.clear();
if v.1.is_empty() {
errors.push(if v.0.is_empty() {
error!(span, "empty default variant")
} else {
error!(span, "empty variant: {}", v.0.repr())
});
}
if !v.0.is_empty() {
// Collect all modifiers.
for modifier in v.0.split('.') {
if !is_ident(modifier) {
bail!(span, "invalid symbol modifier: {}", modifier.repr());
errors.push(error!(
span,
"invalid symbol modifier: {}",
modifier.repr()
));
continue 'variants;
}
modifiers.push(modifier);
}
@ -250,37 +265,34 @@ impl Symbol {
// Ensure that there are no duplicate modifiers.
if let Some(ms) = modifiers.windows(2).find(|ms| ms[0] == ms[1]) {
bail!(
errors.push(error!(
span, "duplicate modifier within variant: {}", ms[0].repr();
hint: "modifiers are not ordered, so each one may appear only once"
)
));
continue 'variants;
}
// Check whether we had this set of modifiers before.
let hash = hash128(&modifiers);
if let Some(&i) = seen.get(&hash) {
if v.0.is_empty() {
bail!(span, "duplicate default variant");
errors.push(if v.0.is_empty() {
error!(span, "duplicate default variant")
} else if v.0 == variants[i].v.0 {
bail!(span, "duplicate variant: {}", v.0.repr());
error!(span, "duplicate variant: {}", v.0.repr())
} else {
bail!(
error!(
span, "duplicate variant: {}", v.0.repr();
hint: "variants with the same modifiers are identical, regardless of their order"
)
}
}
if v.1.is_empty() {
if v.0.is_empty() {
bail!(span, "default variant is empty");
} else {
bail!(span, "variant is empty: {}", v.0.repr());
}
});
continue 'variants;
}
seen.insert(hash, i);
}
if !errors.is_empty() {
return Err(errors);
}
let list = variants
.into_iter()
@ -394,7 +406,6 @@ pub struct SymbolVariant(EcoString, EcoString);
cast! {
SymbolVariant,
c: char => Self(EcoString::new(), c.into()),
s: EcoString => Self(EcoString::new(), s),
array: Array => {
let mut iter = array.into_iter();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 511 B

After

Width:  |  Height:  |  Size: 558 B

View File

@ -21,6 +21,10 @@
("lightning", "🖄"),
("fly", "🖅"),
)
#let one = symbol(
"1",
("emoji", "1")
)
#envelope
#envelope.stamped
@ -28,6 +32,8 @@
#envelope.stamped.pen
#envelope.lightning
#envelope.fly
#one
#one.emoji
--- symbol-constructor-empty ---
// Error: 2-10 expected at least one variant
@ -82,6 +88,14 @@
("variant.duplicate", "y"),
)
--- symbol-constructor-empty-variant ---
// Error: 2:3-2:5 empty default variant
// Error: 3:3-3:16 empty variant: "empty"
#symbol(
"",
("empty", "")
)
--- symbol-unknown-modifier ---
// Error: 13-20 unknown symbol modifier
#emoji.face.garbage