diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index af7fedce9..97c8940d9 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -29,7 +29,7 @@ pub trait AstNode: Sized { macro_rules! node { ($(#[$attr:meta])* $name:ident) => { - #[derive(Debug, Default, Clone, PartialEq, Hash)] + #[derive(Debug, Default, Clone, Hash)] #[repr(transparent)] $(#[$attr])* pub struct $name(SyntaxNode); @@ -73,7 +73,7 @@ impl Markup { } /// An expression in markup, math or code. -#[derive(Debug, Clone, PartialEq, Hash)] +#[derive(Debug, Clone, Hash)] pub enum Expr { /// Plain text without markup. Text(Text), @@ -1055,7 +1055,7 @@ impl Array { } /// An item in an array. -#[derive(Debug, Clone, PartialEq, Hash)] +#[derive(Debug, Clone, Hash)] pub enum ArrayItem { /// A bare expression: `12`. Pos(Expr), @@ -1092,7 +1092,7 @@ impl Dict { } /// An item in an dictionary expresssion. -#[derive(Debug, Clone, PartialEq, Hash)] +#[derive(Debug, Clone, Hash)] pub enum DictItem { /// A named pair: `thickness: 3pt`. Named(Named), @@ -1452,7 +1452,7 @@ impl Args { } /// An argument to a function call. -#[derive(Debug, Clone, PartialEq, Hash)] +#[derive(Debug, Clone, Hash)] pub enum Arg { /// A positional argument: `12`. Pos(Expr), @@ -1509,7 +1509,7 @@ impl Closure { } /// A parameter to a closure. -#[derive(Debug, Clone, PartialEq, Hash)] +#[derive(Debug, Clone, Hash)] pub enum Param { /// A positional parameter: `x`. Pos(Ident), @@ -1724,7 +1724,7 @@ impl ModuleImport { } /// The items that ought to be imported from a file. -#[derive(Debug, Clone, PartialEq, Hash)] +#[derive(Debug, Clone, Hash)] pub enum Imports { /// All items in the scope of the file should be imported. Wildcard, diff --git a/src/syntax/node.rs b/src/syntax/node.rs index e153b0bf2..e216d3c9e 100644 --- a/src/syntax/node.rs +++ b/src/syntax/node.rs @@ -9,11 +9,11 @@ use crate::diag::SourceError; use crate::util::EcoString; /// A node in the untyped syntax tree. -#[derive(Clone, PartialEq, Hash)] +#[derive(Clone, Eq, PartialEq, Hash)] pub struct SyntaxNode(Repr); /// The three internal representations. -#[derive(Clone, PartialEq, Hash)] +#[derive(Clone, Eq, PartialEq, Hash)] enum Repr { /// A leaf node. Leaf(LeafNode), @@ -147,6 +147,15 @@ impl SyntaxNode { .collect() } } + + /// Set a synthetic span for the node and all its descendants. + pub fn synthesize(&mut self, span: Span) { + match &mut self.0 { + Repr::Leaf(leaf) => leaf.span = span, + Repr::Inner(inner) => Arc::make_mut(inner).synthesize(span), + Repr::Error(error) => Arc::make_mut(error).span = span, + } + } } impl SyntaxNode { @@ -174,15 +183,6 @@ impl SyntaxNode { *self = SyntaxNode::error(message, text, ErrorPos::Full); } - /// Set a synthetic span for the node and all its descendants. - pub(crate) fn synthesize(&mut self, span: Span) { - match &mut self.0 { - Repr::Leaf(leaf) => leaf.span = span, - Repr::Inner(inner) => Arc::make_mut(inner).synthesize(span), - Repr::Error(error) => Arc::make_mut(error).span = span, - } - } - /// Assign spans to each node. pub(super) fn numberize( &mut self, @@ -212,7 +212,7 @@ impl SyntaxNode { } /// Whether this is a leaf node. - pub(crate) fn is_leaf(&self) -> bool { + pub(super) fn is_leaf(&self) -> bool { matches!(self.0, Repr::Leaf(_)) } @@ -291,7 +291,7 @@ impl Default for SyntaxNode { } /// A leaf node in the untyped syntax tree. -#[derive(Clone, Hash)] +#[derive(Clone, Eq, PartialEq, Hash)] struct LeafNode { /// What kind of node this is (each kind would have its own struct in a /// strongly typed AST). @@ -322,14 +322,8 @@ impl Debug for LeafNode { } } -impl PartialEq for LeafNode { - fn eq(&self, other: &Self) -> bool { - self.kind == other.kind && self.text == other.text - } -} - /// An inner node in the untyped syntax tree. -#[derive(Clone, Hash)] +#[derive(Clone, Eq, PartialEq, Hash)] struct InnerNode { /// What kind of node this is (each kind would have its own struct in a /// strongly typed AST). @@ -378,6 +372,7 @@ impl InnerNode { /// Set a synthetic span for the node and all its descendants. fn synthesize(&mut self, span: Span) { self.span = span; + self.upper = span.number(); for child in &mut self.children { child.synthesize(span); } @@ -573,18 +568,8 @@ impl Debug for InnerNode { } } -impl PartialEq for InnerNode { - fn eq(&self, other: &Self) -> bool { - self.kind == other.kind - && self.len == other.len - && self.descendants == other.descendants - && self.erroneous == other.erroneous - && self.children == other.children - } -} - /// An error node in the untyped syntax tree. -#[derive(Clone, Hash)] +#[derive(Clone, Eq, PartialEq, Hash)] struct ErrorNode { /// The error message. message: EcoString, @@ -623,12 +608,6 @@ impl Debug for ErrorNode { } } -impl PartialEq for ErrorNode { - fn eq(&self, other: &Self) -> bool { - self.message == other.message && self.text == other.text && self.pos == other.pos - } -} - /// Where in a node an error should be annotated, #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum ErrorPos { diff --git a/src/syntax/reparser.rs b/src/syntax/reparser.rs index a876e86b5..75a5cc279 100644 --- a/src/syntax/reparser.rs +++ b/src/syntax/reparser.rs @@ -213,16 +213,18 @@ fn next_nesting(node: &SyntaxNode, nesting: &mut usize) { mod tests { use std::ops::Range; - use super::super::{parse, Source}; + use super::super::{parse, Source, Span}; #[track_caller] fn test(prev: &str, range: Range, with: &str, incremental: bool) { let mut source = Source::detached(prev); let prev = source.root().clone(); let range = source.edit(range, with); - let found = source.root(); - let expected = parse(source.text()); - if found != &expected { + let mut found = source.root().clone(); + let mut expected = parse(source.text()); + found.synthesize(Span::detached()); + expected.synthesize(Span::detached()); + if found != expected { eprintln!("source: {:?}", source.text()); eprintln!("previous: {prev:#?}"); eprintln!("expected: {expected:#?}"); diff --git a/src/syntax/source.rs b/src/syntax/source.rs index 63509dc88..f00d779b9 100644 --- a/src/syntax/source.rs +++ b/src/syntax/source.rs @@ -406,6 +406,8 @@ mod tests { #[test] fn test_source_file_edit() { + // This tests only the non-parser parts. The reparsing itself is + // tested separately. #[track_caller] fn test(prev: &str, range: Range, with: &str, after: &str) { let mut source = Source::detached(prev); @@ -413,7 +415,6 @@ mod tests { source.edit(range, with); assert_eq!(source.text, result.text); assert_eq!(source.lines, result.lines); - assert_eq!(*source.root, *result.root); } // Test inserting at the begining. diff --git a/tests/src/tests.rs b/tests/src/tests.rs index b90abceeb..8895ccdd0 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -16,7 +16,7 @@ use typst::doc::{Document, Element, Frame, Meta}; use typst::font::{Font, FontBook}; use typst::geom::{Abs, RgbaColor, Sides, Smart}; use typst::model::{func, Library, Value}; -use typst::syntax::{Source, SourceId, SyntaxNode}; +use typst::syntax::{Source, SourceId, Span, SyntaxNode}; use typst::util::{Buffer, PathExt}; use typst::World; use typst_library::layout::PageNode; @@ -591,11 +591,21 @@ fn test_reparse(text: &str, i: usize, rng: &mut LinearShift) -> bool { incr_source.edit(replace.clone(), with); let edited_src = incr_source.text(); - let incr_root = incr_source.root(); let ref_source = Source::detached(edited_src); - let ref_root = ref_source.root(); - let mut ok = incr_root == ref_root; - if !ok { + let mut ref_root = ref_source.root().clone(); + let mut incr_root = incr_source.root().clone(); + + // Ensures that the span numbering invariants hold. + let spans_ok = test_spans(&ref_root) && test_spans(&incr_root); + + // Remove all spans so that the comparison works out. + let tree_ok = { + ref_root.synthesize(Span::detached()); + incr_root.synthesize(Span::detached()); + ref_root == incr_root + }; + + if !tree_ok { println!( " Subtest {i} reparse differs from clean parse when inserting '{with}' at {}-{} ❌\n", replace.start, replace.end, @@ -605,9 +615,7 @@ fn test_reparse(text: &str, i: usize, rng: &mut LinearShift) -> bool { println!(" Full source ({}):\n\"{edited_src:?}\"", edited_src.len()); } - ok &= test_spans(ref_root); - ok &= test_spans(incr_root); - ok + spans_ok && tree_ok }; let mut pick = |range: Range| {