mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Remove SpanlessEq 🎃
The new solution is slightly hacky, but way more flexible. All types that implement PartialEq can now be compared spanlessly in tests by manipulating a thread-local boolean that is read in Span's PartialEq implementation.
This commit is contained in:
parent
eb9c4b1a49
commit
84f30fb735
@ -3,9 +3,23 @@
|
|||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
use std::ops::{Add, Sub};
|
use std::ops::{Add, Sub};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
thread_local! {
|
||||||
|
static CMP_SPANS: Cell<bool> = Cell::new(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// When set to `false` comparisons with `PartialEq` ignore spans.
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) fn set_cmp(cmp: bool) {
|
||||||
|
CMP_SPANS.with(|cell| cell.set(cmp));
|
||||||
|
}
|
||||||
|
|
||||||
/// Span offsetting.
|
/// Span offsetting.
|
||||||
pub trait Offset {
|
pub trait Offset {
|
||||||
/// Offset all spans contained in `Self` by the given position.
|
/// Offset all spans contained in `Self` by the given position.
|
||||||
@ -85,7 +99,7 @@ impl<T: Debug> Debug for Spanned<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Locates a slice of source code.
|
/// Locates a slice of source code.
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Hash)]
|
||||||
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
||||||
pub struct Span {
|
pub struct Span {
|
||||||
/// The inclusive start position.
|
/// The inclusive start position.
|
||||||
@ -131,6 +145,19 @@ impl Offset for Span {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Eq for Span {}
|
||||||
|
|
||||||
|
impl PartialEq for Span {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
#[cfg(test)]
|
||||||
|
if !CMP_SPANS.with(Cell::get) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.start == other.start && self.end == other.end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Debug for Span {
|
impl Debug for Span {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "<{:?}-{:?}>", self.start, self.end)
|
write!(f, "<{:?}-{:?}>", self.start, self.end)
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use crate::func::prelude::*;
|
use crate::func::prelude::*;
|
||||||
use super::decoration::Decoration;
|
use super::tree::SyntaxNode;
|
||||||
use super::expr::{Expr, Ident, NamedTuple, Object, Pair, Tuple};
|
use super::span;
|
||||||
use super::parsing::{FuncArg, FuncArgs};
|
|
||||||
use super::span::Spanned;
|
|
||||||
use super::tokens::Token;
|
|
||||||
use super::tree::{DynamicNode, SyntaxNode};
|
|
||||||
|
|
||||||
pub fn check<T>(src: &str, exp: T, found: T, cmp_spans: bool)
|
pub fn check<T>(src: &str, exp: T, found: T, cmp_spans: bool)
|
||||||
where
|
where
|
||||||
T: Debug + PartialEq + SpanlessEq,
|
T: Debug + PartialEq,
|
||||||
{
|
{
|
||||||
let cmp = if cmp_spans { PartialEq::eq } else { SpanlessEq::spanless_eq };
|
span::set_cmp(cmp_spans);
|
||||||
if !cmp(&exp, &found) {
|
let equal = exp == found;
|
||||||
|
span::set_cmp(true);
|
||||||
|
|
||||||
|
if !equal {
|
||||||
println!("source: {:?}", src);
|
println!("source: {:?}", src);
|
||||||
println!("expected: {:#?}", exp);
|
println!("expected: {:#?}", exp);
|
||||||
println!("found: {:#?}", found);
|
println!("found: {:#?}", found);
|
||||||
@ -72,142 +71,3 @@ impl Layout for DebugNode {
|
|||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compares elements by only looking at values and ignoring spans.
|
|
||||||
pub trait SpanlessEq<Rhs = Self> {
|
|
||||||
fn spanless_eq(&self, other: &Rhs) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpanlessEq for SyntaxNode {
|
|
||||||
fn spanless_eq(&self, other: &Self) -> bool {
|
|
||||||
fn downcast<'a>(func: &'a (dyn DynamicNode + 'static)) -> &'a DebugNode {
|
|
||||||
func.downcast::<DebugNode>().expect("not a debug node")
|
|
||||||
}
|
|
||||||
|
|
||||||
match (self, other) {
|
|
||||||
(Self::Dyn(a), Self::Dyn(b)) => {
|
|
||||||
downcast(a.as_ref()).spanless_eq(downcast(b.as_ref()))
|
|
||||||
}
|
|
||||||
(Self::Par(a), Self::Par(b)) => a.spanless_eq(b),
|
|
||||||
(a, b) => a == b,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpanlessEq for DebugNode {
|
|
||||||
fn spanless_eq(&self, other: &Self) -> bool {
|
|
||||||
self.0.spanless_eq(&other.0)
|
|
||||||
&& self.1.spanless_eq(&other.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpanlessEq for FuncCall {
|
|
||||||
fn spanless_eq(&self, other: &Self) -> bool {
|
|
||||||
self.name.spanless_eq(&other.name)
|
|
||||||
&& self.args.spanless_eq(&other.args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpanlessEq for FuncArgs {
|
|
||||||
fn spanless_eq(&self, other: &Self) -> bool {
|
|
||||||
self.key.spanless_eq(&other.key)
|
|
||||||
&& self.pos.spanless_eq(&other.pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpanlessEq for FuncArg {
|
|
||||||
fn spanless_eq(&self, other: &Self) -> bool {
|
|
||||||
match (self, other) {
|
|
||||||
(FuncArg::Pos(a), FuncArg::Pos(b)) => a.spanless_eq(b),
|
|
||||||
(FuncArg::Key(a), FuncArg::Key(b)) => a.spanless_eq(b),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpanlessEq for Expr {
|
|
||||||
fn spanless_eq(&self, other: &Self) -> bool {
|
|
||||||
match (self, other) {
|
|
||||||
(Expr::Tuple(a), Expr::Tuple(b)) => a.spanless_eq(b),
|
|
||||||
(Expr::NamedTuple(a), Expr::NamedTuple(b)) => a.spanless_eq(b),
|
|
||||||
(Expr::Object(a), Expr::Object(b)) => a.spanless_eq(b),
|
|
||||||
(Expr::Neg(a), Expr::Neg(b)) => a.spanless_eq(&b),
|
|
||||||
(Expr::Add(a1, a2), Expr::Add(b1, b2)) => a1.spanless_eq(&b1) && a2.spanless_eq(&b2),
|
|
||||||
(Expr::Sub(a1, a2), Expr::Sub(b1, b2)) => a1.spanless_eq(&b1) && a2.spanless_eq(&b2),
|
|
||||||
(Expr::Mul(a1, a2), Expr::Mul(b1, b2)) => a1.spanless_eq(&b1) && a2.spanless_eq(&b2),
|
|
||||||
(Expr::Div(a1, a2), Expr::Div(b1, b2)) => a1.spanless_eq(&b1) && a2.spanless_eq(&b2),
|
|
||||||
(a, b) => a == b,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpanlessEq for Tuple {
|
|
||||||
fn spanless_eq(&self, other: &Self) -> bool {
|
|
||||||
self.0.spanless_eq(&other.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpanlessEq for NamedTuple {
|
|
||||||
fn spanless_eq(&self, other: &Self) -> bool {
|
|
||||||
self.name.v == other.name.v
|
|
||||||
&& self.tuple.v.spanless_eq(&other.tuple.v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpanlessEq for Object {
|
|
||||||
fn spanless_eq(&self, other: &Self) -> bool {
|
|
||||||
self.0.spanless_eq(&other.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpanlessEq for Pair {
|
|
||||||
fn spanless_eq(&self, other: &Self) -> bool {
|
|
||||||
self.key.spanless_eq(&other.key) && self.value.spanless_eq(&other.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: SpanlessEq> SpanlessEq for Vec<T> {
|
|
||||||
fn spanless_eq(&self, other: &Self) -> bool {
|
|
||||||
self.len() == other.len()
|
|
||||||
&& self.iter().zip(other).all(|(x, y)| x.spanless_eq(&y))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: SpanlessEq> SpanlessEq for Spanned<T> {
|
|
||||||
fn spanless_eq(&self, other: &Self) -> bool {
|
|
||||||
self.v.spanless_eq(&other.v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: SpanlessEq> SpanlessEq for Box<T> {
|
|
||||||
fn spanless_eq(&self, other: &Self) -> bool {
|
|
||||||
(&**self).spanless_eq(&**other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: SpanlessEq> SpanlessEq for Option<T> {
|
|
||||||
fn spanless_eq(&self, other: &Self) -> bool {
|
|
||||||
match (self, other) {
|
|
||||||
(Some(a), Some(b)) => a.spanless_eq(b),
|
|
||||||
(None, None) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_through_partial_eq {
|
|
||||||
($type:ty) => {
|
|
||||||
impl SpanlessEq for $type {
|
|
||||||
fn spanless_eq(&self, other: &$type) -> bool {
|
|
||||||
self == other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_through_partial_eq!(Token<'_>);
|
|
||||||
impl_through_partial_eq!(Ident);
|
|
||||||
|
|
||||||
// Implement for string and decoration to be able to compare feedback.
|
|
||||||
impl_through_partial_eq!(String);
|
|
||||||
impl_through_partial_eq!(Decoration);
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user