mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Error on duplicate symbol variant with modifiers in different orders (#5587)
Co-authored-by: Laurenz <laurmaedje@gmail.com>
This commit is contained in:
parent
6953685358
commit
39706fe42f
@ -1,11 +1,12 @@
|
|||||||
use std::cmp::Reverse;
|
use std::cmp::Reverse;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::{BTreeSet, HashMap};
|
||||||
use std::fmt::{self, Debug, Display, Formatter, Write};
|
use std::fmt::{self, Debug, Display, Formatter, Write};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use ecow::{eco_format, EcoString};
|
use ecow::{eco_format, EcoString};
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
use typst_syntax::{is_ident, Span, Spanned};
|
use typst_syntax::{is_ident, Span, Spanned};
|
||||||
|
use typst_utils::hash128;
|
||||||
|
|
||||||
use crate::diag::{bail, SourceResult, StrResult};
|
use crate::diag::{bail, SourceResult, StrResult};
|
||||||
use crate::foundations::{cast, func, scope, ty, Array, Func, NativeFunc, Repr as _};
|
use crate::foundations::{cast, func, scope, ty, Array, Func, NativeFunc, Repr as _};
|
||||||
@ -198,24 +199,62 @@ impl Symbol {
|
|||||||
#[variadic]
|
#[variadic]
|
||||||
variants: Vec<Spanned<SymbolVariant>>,
|
variants: Vec<Spanned<SymbolVariant>>,
|
||||||
) -> SourceResult<Symbol> {
|
) -> SourceResult<Symbol> {
|
||||||
let mut list = Vec::new();
|
|
||||||
if variants.is_empty() {
|
if variants.is_empty() {
|
||||||
bail!(span, "expected at least one variant");
|
bail!(span, "expected at least one variant");
|
||||||
}
|
}
|
||||||
for Spanned { v, span } in variants {
|
|
||||||
if list.iter().any(|(prev, _)| &v.0 == prev) {
|
// Maps from canonicalized 128-bit hashes to indices of variants we've
|
||||||
bail!(span, "duplicate variant");
|
// seen before.
|
||||||
}
|
let mut seen = HashMap::<u128, usize>::new();
|
||||||
|
|
||||||
|
// A list of modifiers, cleared & reused in each iteration.
|
||||||
|
let mut modifiers = Vec::new();
|
||||||
|
|
||||||
|
// Validate the variants.
|
||||||
|
for (i, &Spanned { ref v, span }) in variants.iter().enumerate() {
|
||||||
|
modifiers.clear();
|
||||||
|
|
||||||
if !v.0.is_empty() {
|
if !v.0.is_empty() {
|
||||||
|
// Collect all modifiers.
|
||||||
for modifier in v.0.split('.') {
|
for modifier in v.0.split('.') {
|
||||||
if !is_ident(modifier) {
|
if !is_ident(modifier) {
|
||||||
bail!(span, "invalid symbol modifier: {}", modifier.repr());
|
bail!(span, "invalid symbol modifier: {}", modifier.repr());
|
||||||
}
|
}
|
||||||
|
modifiers.push(modifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
list.push((v.0, v.1));
|
|
||||||
|
// Canonicalize the modifier order.
|
||||||
|
modifiers.sort();
|
||||||
|
|
||||||
|
// Ensure that there are no duplicate modifiers.
|
||||||
|
if let Some(ms) = modifiers.windows(2).find(|ms| ms[0] == ms[1]) {
|
||||||
|
bail!(
|
||||||
|
span, "duplicate modifier within variant: {}", ms[0].repr();
|
||||||
|
hint: "modifiers are not ordered, so each one may appear only once"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
} else if v.0 == variants[i].v.0 {
|
||||||
|
bail!(span, "duplicate variant: {}", v.0.repr());
|
||||||
|
} else {
|
||||||
|
bail!(
|
||||||
|
span, "duplicate variant: {}", v.0.repr();
|
||||||
|
hint: "variants with the same modifiers are identical, regardless of their order"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
seen.insert(hash, i);
|
||||||
}
|
}
|
||||||
Ok(Symbol::runtime(list.into_boxed_slice()))
|
|
||||||
|
let list = variants.into_iter().map(|s| (s.v.0, s.v.1)).collect();
|
||||||
|
Ok(Symbol::runtime(list))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,13 +39,49 @@
|
|||||||
("invalid. id!", "x")
|
("invalid. id!", "x")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
--- symbol-constructor-duplicate-modifier ---
|
||||||
|
// Error: 2:3-2:31 duplicate modifier within variant: "duplicate"
|
||||||
|
// Hint: 2:3-2:31 modifiers are not ordered, so each one may appear only once
|
||||||
|
#symbol(
|
||||||
|
("duplicate.duplicate", "x"),
|
||||||
|
)
|
||||||
|
|
||||||
|
--- symbol-constructor-duplicate-default-variant ---
|
||||||
|
// Error: 3:3-3:6 duplicate default variant
|
||||||
|
#symbol(
|
||||||
|
"x",
|
||||||
|
"y",
|
||||||
|
)
|
||||||
|
|
||||||
|
--- symbol-constructor-duplicate-empty-variant ---
|
||||||
|
// Error: 3:3-3:12 duplicate default variant
|
||||||
|
#symbol(
|
||||||
|
("", "x"),
|
||||||
|
("", "y"),
|
||||||
|
)
|
||||||
|
|
||||||
|
--- symbol-constructor-default-and-empty-variants ---
|
||||||
|
// Error: 3:3-3:12 duplicate default variant
|
||||||
|
#symbol(
|
||||||
|
"x",
|
||||||
|
("", "y"),
|
||||||
|
)
|
||||||
|
|
||||||
--- symbol-constructor-duplicate-variant ---
|
--- symbol-constructor-duplicate-variant ---
|
||||||
// Error: 3:3-3:29 duplicate variant
|
// Error: 3:3-3:29 duplicate variant: "duplicate.variant"
|
||||||
#symbol(
|
#symbol(
|
||||||
("duplicate.variant", "x"),
|
("duplicate.variant", "x"),
|
||||||
("duplicate.variant", "y"),
|
("duplicate.variant", "y"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
--- symbol-constructor-duplicate-variant-different-order ---
|
||||||
|
// Error: 3:3-3:29 duplicate variant: "variant.duplicate"
|
||||||
|
// Hint: 3:3-3:29 variants with the same modifiers are identical, regardless of their order
|
||||||
|
#symbol(
|
||||||
|
("duplicate.variant", "x"),
|
||||||
|
("variant.duplicate", "y"),
|
||||||
|
)
|
||||||
|
|
||||||
--- symbol-unknown-modifier ---
|
--- symbol-unknown-modifier ---
|
||||||
// Error: 13-20 unknown symbol modifier
|
// Error: 13-20 unknown symbol modifier
|
||||||
#emoji.face.garbage
|
#emoji.face.garbage
|
||||||
|
Loading…
x
Reference in New Issue
Block a user