From 84f30fb73518ca23cbc728b1bf414e80b344412a Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sat, 15 Aug 2020 15:01:55 +0200 Subject: [PATCH] =?UTF-8?q?Remove=20SpanlessEq=20=F0=9F=8E=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- src/syntax/span.rs | 29 ++++++++- src/syntax/test.rs | 156 +++------------------------------------------ 2 files changed, 36 insertions(+), 149 deletions(-) diff --git a/src/syntax/span.rs b/src/syntax/span.rs index c93f5c1e5..923d13c29 100644 --- a/src/syntax/span.rs +++ b/src/syntax/span.rs @@ -3,9 +3,23 @@ use std::fmt::{self, Debug, Formatter}; use std::ops::{Add, Sub}; +#[cfg(test)] +use std::cell::Cell; + #[cfg(feature = "serialize")] use serde::Serialize; +#[cfg(test)] +thread_local! { + static CMP_SPANS: Cell = 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. pub trait Offset { /// Offset all spans contained in `Self` by the given position. @@ -85,7 +99,7 @@ impl Debug for Spanned { } /// Locates a slice of source code. -#[derive(Copy, Clone, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize))] pub struct Span { /// 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 { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "<{:?}-{:?}>", self.start, self.end) diff --git a/src/syntax/test.rs b/src/syntax/test.rs index 97fecebc0..aec54ded9 100644 --- a/src/syntax/test.rs +++ b/src/syntax/test.rs @@ -1,19 +1,18 @@ use std::fmt::Debug; use crate::func::prelude::*; -use super::decoration::Decoration; -use super::expr::{Expr, Ident, NamedTuple, Object, Pair, Tuple}; -use super::parsing::{FuncArg, FuncArgs}; -use super::span::Spanned; -use super::tokens::Token; -use super::tree::{DynamicNode, SyntaxNode}; +use super::tree::SyntaxNode; +use super::span; pub fn check(src: &str, exp: T, found: T, cmp_spans: bool) where - T: Debug + PartialEq + SpanlessEq, + T: Debug + PartialEq, { - let cmp = if cmp_spans { PartialEq::eq } else { SpanlessEq::spanless_eq }; - if !cmp(&exp, &found) { + span::set_cmp(cmp_spans); + let equal = exp == found; + span::set_cmp(true); + + if !equal { println!("source: {:?}", src); println!("expected: {:#?}", exp); println!("found: {:#?}", found); @@ -72,142 +71,3 @@ impl Layout for DebugNode { unimplemented!() } } - -/// Compares elements by only looking at values and ignoring spans. -pub trait SpanlessEq { - 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::().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 SpanlessEq for Vec { - fn spanless_eq(&self, other: &Self) -> bool { - self.len() == other.len() - && self.iter().zip(other).all(|(x, y)| x.spanless_eq(&y)) - } -} - -impl SpanlessEq for Spanned { - fn spanless_eq(&self, other: &Self) -> bool { - self.v.spanless_eq(&other.v) - } -} - -impl SpanlessEq for Box { - fn spanless_eq(&self, other: &Self) -> bool { - (&**self).spanless_eq(&**other) - } -} - -impl SpanlessEq for Option { - 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);