From fd8160f3749135400b3d2c59bf6bfb729c081f16 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Wed, 5 Oct 2022 13:15:02 +0200 Subject: [PATCH] Remove `SpanPos` in favor of `ErrorPos` --- src/diag.rs | 52 +++++++++++++++++++++++++++++++++++++-------- src/eval/mod.rs | 6 +++--- src/main.rs | 2 +- src/parse/mod.rs | 5 +++-- src/parse/parser.rs | 11 +++++----- src/parse/tokens.rs | 27 +++++++++++------------ src/syntax/kind.rs | 4 ++-- src/syntax/mod.rs | 11 ++-------- src/syntax/span.rs | 36 ++++--------------------------- tests/typeset.rs | 5 +---- 10 files changed, 79 insertions(+), 80 deletions(-) diff --git a/src/diag.rs b/src/diag.rs index 0d016a21b..f13c41e12 100644 --- a/src/diag.rs +++ b/src/diag.rs @@ -2,6 +2,7 @@ use std::fmt::{self, Display, Formatter}; use std::io; +use std::ops::Range; use std::path::{Path, PathBuf}; use std::str::Utf8Error; use std::string::FromUtf8Error; @@ -9,6 +10,7 @@ use std::string::FromUtf8Error; use comemo::Tracked; use crate::syntax::{Span, Spanned}; +use crate::util::EcoString; use crate::World; /// Early-return with a [`SourceError`]. @@ -39,32 +41,64 @@ macro_rules! error { pub type SourceResult = Result>>; /// An error in a source file. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct SourceError { - /// The erroneous node in the source code. + /// The span of the erroneous node in the source code. pub span: Span, + /// The position in the node where the error should be annotated. + pub pos: ErrorPos, /// A diagnostic message describing the problem. - pub message: String, + pub message: EcoString, /// The trace of function calls leading to the error. pub trace: Vec>, } impl SourceError { /// Create a new, bare error. - pub fn new(span: Span, message: impl Into) -> Self { + pub fn new(span: Span, message: impl Into) -> Self { Self { span, + pos: ErrorPos::Full, trace: vec![], message: message.into(), } } + + /// Adjust the position in the node where the error should be annotated. + pub fn with_pos(mut self, pos: ErrorPos) -> Self { + self.pos = pos; + self + } + + /// The range in the source file identified by + /// [`self.span.source()`](Span::source) where the error should be + /// annotated. + pub fn range(&self, world: &dyn World) -> Range { + let full = world.source(self.span.source()).range(self.span); + match self.pos { + ErrorPos::Full => full, + ErrorPos::Start => full.start .. full.start, + ErrorPos::End => full.end .. full.end, + } + } +} + +/// Where in a node an error should be annotated, +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum ErrorPos { + /// Over the full width of the node. + Full, + /// At the start of the node. + Start, + /// At the end of the node. + End, } /// A part of an error's [trace](SourceError::trace). -#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] +#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum Tracepoint { /// A function call. - Call(Option), + Call(Option), /// A module import. Import, } @@ -135,7 +169,7 @@ pub trait At { impl At for Result where - S: Into, + S: Into, { fn at(self, span: Span) -> SourceResult { self.map_err(|message| Box::new(vec![error!(span, message)])) @@ -204,9 +238,9 @@ impl From for FileError { } } -impl From for String { +impl From for EcoString { fn from(error: FileError) -> Self { - error.to_string() + format_eco!("{error}") } } diff --git a/src/eval/mod.rs b/src/eval/mod.rs index cc9d3422e..26b4130bc 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -695,7 +695,7 @@ impl Eval for FuncCall { Value::Array(array) => array.get(args.into_index()?).at(self.span())?.clone(), Value::Dict(dict) => dict.get(&args.into_key()?).at(self.span())?.clone(), Value::Func(func) => { - let point = || Tracepoint::Call(func.name().map(ToString::to_string)); + let point = || Tracepoint::Call(func.name().map(Into::into)); func.call(vm, args).trace(vm.world, point, self.span())? } @@ -713,8 +713,8 @@ impl Eval for MethodCall { fn eval(&self, vm: &mut Vm) -> SourceResult { let span = self.span(); - let method = self.method(); - let point = || Tracepoint::Call(Some(method.to_string())); + let method = self.method().take(); + let point = || Tracepoint::Call(Some(method.clone())); Ok(if methods::is_mutating(&method) { let args = self.args().eval(vm)?; diff --git a/src/main.rs b/src/main.rs index 1c5790c21..ef4410913 100644 --- a/src/main.rs +++ b/src/main.rs @@ -323,7 +323,7 @@ fn print_diagnostics( for error in errors { // The main diagnostic. - let range = world.source(error.span.source()).range(error.span); + let range = error.range(world); let diag = Diagnostic::error() .with_message(error.message) .with_labels(vec![Label::primary(error.span.source(), range)]); diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 832c297e5..4f42442f1 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -11,8 +11,9 @@ pub use tokens::*; use std::collections::HashSet; +use crate::diag::ErrorPos; use crate::syntax::ast::{Assoc, BinOp, UnOp}; -use crate::syntax::{NodeKind, SpanPos, SyntaxNode}; +use crate::syntax::{NodeKind, SyntaxNode}; use crate::util::EcoString; /// Parse a source file. @@ -811,7 +812,7 @@ fn item(p: &mut Parser, keyed: bool) -> ParseResult { msg.push_str(", found "); msg.push_str(kind.name()); } - let error = NodeKind::Error(SpanPos::Full, msg); + let error = NodeKind::Error(ErrorPos::Full, msg); marker.end(p, error); p.eat(); marker.perform(p, NodeKind::Named, expr).ok(); diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 4b73c2b98..fe04f29ea 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -3,7 +3,8 @@ use std::mem; use std::ops::Range; use super::{TokenMode, Tokens}; -use crate::syntax::{InnerNode, NodeData, NodeKind, SpanPos, SyntaxNode}; +use crate::diag::ErrorPos; +use crate::syntax::{InnerNode, NodeData, NodeKind, SyntaxNode}; use crate::util::EcoString; /// A convenient token-based parser. @@ -398,7 +399,7 @@ impl Parser<'_> { pub fn unexpected(&mut self) { if let Some(found) = self.peek() { let msg = format_eco!("unexpected {}", found.name()); - let error = NodeKind::Error(SpanPos::Full, msg); + let error = NodeKind::Error(ErrorPos::Full, msg); self.perform(error, Self::eat); } } @@ -412,7 +413,7 @@ impl Parser<'_> { /// Insert an error message that `what` was expected at the marker position. pub fn expected_at(&mut self, marker: Marker, what: &str) { let msg = format_eco!("expected {}", what); - let error = NodeKind::Error(SpanPos::Full, msg); + let error = NodeKind::Error(ErrorPos::Full, msg); self.children.insert(marker.0, NodeData::new(error, 0).into()); } @@ -422,7 +423,7 @@ impl Parser<'_> { match self.peek() { Some(found) => { let msg = format_eco!("expected {}, found {}", thing, found.name()); - let error = NodeKind::Error(SpanPos::Full, msg); + let error = NodeKind::Error(ErrorPos::Full, msg); self.perform(error, Self::eat); } None => self.expected(thing), @@ -494,7 +495,7 @@ impl Marker { msg.push_str(", found "); msg.push_str(child.kind().name()); } - let error = NodeKind::Error(SpanPos::Full, msg); + let error = NodeKind::Error(ErrorPos::Full, msg); let inner = mem::take(child); *child = InnerNode::with_child(error, inner).into(); } diff --git a/src/parse/tokens.rs b/src/parse/tokens.rs index d3c497f34..7cba18237 100644 --- a/src/parse/tokens.rs +++ b/src/parse/tokens.rs @@ -4,9 +4,10 @@ use unicode_xid::UnicodeXID; use unscanny::Scanner; use super::resolve::{resolve_hex, resolve_raw, resolve_string}; +use crate::diag::ErrorPos; use crate::geom::{AngleUnit, LengthUnit}; use crate::syntax::ast::{RawNode, Unit}; -use crate::syntax::{NodeKind, SpanPos}; +use crate::syntax::NodeKind; use crate::util::EcoString; /// An iterator over the tokens of a string of source code. @@ -109,7 +110,7 @@ impl<'s> Iterator for Tokens<'s> { '/' if self.s.eat_if('/') => self.line_comment(), '/' if self.s.eat_if('*') => self.block_comment(), '*' if self.s.eat_if('/') => { - NodeKind::Error(SpanPos::Full, "unexpected end of block comment".into()) + NodeKind::Error(ErrorPos::Full, "unexpected end of block comment".into()) } c if c.is_whitespace() => self.whitespace(c), @@ -279,13 +280,13 @@ impl<'s> Tokens<'s> { NodeKind::Escape(c) } else { NodeKind::Error( - SpanPos::Full, + ErrorPos::Full, "invalid unicode escape sequence".into(), ) } } else { self.terminated = false; - NodeKind::Error(SpanPos::End, "expected closing brace".into()) + NodeKind::Error(ErrorPos::End, "expected closing brace".into()) } } @@ -388,7 +389,7 @@ impl<'s> Tokens<'s> { let remaining = backticks - found; let noun = if remaining == 1 { "backtick" } else { "backticks" }; NodeKind::Error( - SpanPos::End, + ErrorPos::End, if found == 0 { format_eco!("expected {} {}", remaining, noun) } else { @@ -416,11 +417,11 @@ impl<'s> Tokens<'s> { if !label.is_empty() { NodeKind::Label(label.into()) } else { - NodeKind::Error(SpanPos::Full, "label cannot be empty".into()) + NodeKind::Error(ErrorPos::Full, "label cannot be empty".into()) } } else { self.terminated = false; - NodeKind::Error(SpanPos::End, "expected closing angle bracket".into()) + NodeKind::Error(ErrorPos::End, "expected closing angle bracket".into()) } } @@ -519,7 +520,7 @@ impl<'s> Tokens<'s> { '"' => self.string(), // Invalid token. - _ => NodeKind::Error(SpanPos::Full, "not valid here".into()), + _ => NodeKind::Error(ErrorPos::Full, "not valid here".into()), } } @@ -578,10 +579,10 @@ impl<'s> Tokens<'s> { "em" => NodeKind::Numeric(f, Unit::Em), "fr" => NodeKind::Numeric(f, Unit::Fr), "%" => NodeKind::Numeric(f, Unit::Percent), - _ => NodeKind::Error(SpanPos::Full, "invalid number suffix".into()), + _ => NodeKind::Error(ErrorPos::Full, "invalid number suffix".into()), } } else { - NodeKind::Error(SpanPos::Full, "invalid number".into()) + NodeKind::Error(ErrorPos::Full, "invalid number".into()) } } @@ -601,7 +602,7 @@ impl<'s> Tokens<'s> { NodeKind::Str(string) } else { self.terminated = false; - NodeKind::Error(SpanPos::End, "expected quote".into()) + NodeKind::Error(ErrorPos::End, "expected quote".into()) } } } @@ -713,9 +714,9 @@ mod tests { use super::*; use crate::parse::tests::check; + use ErrorPos::*; use NodeKind::*; use Option::None; - use SpanPos::*; use TokenMode::{Code, Markup}; fn Space(newlines: usize) -> NodeKind { @@ -742,7 +743,7 @@ mod tests { NodeKind::Ident(ident.into()) } - fn Error(pos: SpanPos, message: &str) -> NodeKind { + fn Error(pos: ErrorPos, message: &str) -> NodeKind { NodeKind::Error(pos, message.into()) } diff --git a/src/syntax/kind.rs b/src/syntax/kind.rs index e76c93aaa..77d4cd994 100644 --- a/src/syntax/kind.rs +++ b/src/syntax/kind.rs @@ -2,7 +2,7 @@ use std::hash::{Hash, Hasher}; use std::sync::Arc; use super::ast::{RawNode, Unit}; -use super::SpanPos; +use crate::diag::ErrorPos; use crate::util::EcoString; /// All syntactical building blocks that can be part of a Typst document. @@ -271,7 +271,7 @@ pub enum NodeKind { ReturnExpr, /// An invalid sequence of characters. - Error(SpanPos, EcoString), + Error(ErrorPos, EcoString), } impl NodeKind { diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 5ff99d037..8b172defd 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -73,7 +73,7 @@ impl SyntaxNode { match self.kind() { NodeKind::Error(pos, message) => { - vec![SourceError::new(self.span().with_pos(*pos), message)] + vec![SourceError::new(self.span(), message.clone()).with_pos(*pos)] } _ => self .children() @@ -535,14 +535,7 @@ impl NodeData { /// If the span points into this node, convert it to a byte range. pub fn range(&self, span: Span, offset: usize) -> Option> { - (span.with_pos(SpanPos::Full) == self.span).then(|| { - let end = offset + self.len(); - match span.pos() { - SpanPos::Full => offset .. end, - SpanPos::Start => offset .. offset, - SpanPos::End => end .. end, - } - }) + (self.span == span).then(|| offset .. offset + self.len()) } } diff --git a/src/syntax/span.rs b/src/syntax/span.rs index 59c4cc5c9..744aa123f 100644 --- a/src/syntax/span.rs +++ b/src/syntax/span.rs @@ -63,10 +63,10 @@ pub struct Span(NonZeroU64); impl Span { // Data layout: - // | 2 bits span pos | 16 bits source id | 46 bits number | + // | 16 bits source id | 48 bits number | // Number of bits for and minimum and maximum numbers assignable to spans. - const BITS: usize = 46; + const BITS: usize = 48; const DETACHED: u64 = 1; /// The full range of numbers available to spans. @@ -90,12 +90,6 @@ impl Span { Self(to_non_zero(Self::DETACHED)) } - /// Return this span, but with updated position. - pub const fn with_pos(self, pos: SpanPos) -> Self { - let bits = (self.0.get() & ((1 << 62) - 1)) | ((pos as u64) << 62); - Self(to_non_zero(bits)) - } - /// The id of the source file the span points into. pub const fn source(self) -> SourceId { SourceId::from_u16((self.0.get() >> Self::BITS) as u16) @@ -105,16 +99,6 @@ impl Span { pub const fn number(self) -> u64 { self.0.get() & ((1 << Self::BITS) - 1) } - - /// Where in the node the span points to. - pub const fn pos(self) -> SpanPos { - match self.0.get() >> 62 { - 0 => SpanPos::Full, - 1 => SpanPos::Start, - 2 => SpanPos::End, - _ => panic!("span pos encoding is invalid"), - } - } } /// Convert to a non zero u64. @@ -125,17 +109,6 @@ const fn to_non_zero(v: u64) -> NonZeroU64 { } } -/// Where in a node a span points. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum SpanPos { - /// Over the full width of the node. - Full = 0, - /// At the start of the node. - Start = 1, - /// At the end of the node. - End = 2, -} - /// Result of numbering a node within an interval. pub type NumberingResult = Result<(), Unnumberable>; @@ -153,14 +126,13 @@ impl std::error::Error for Unnumberable {} #[cfg(test)] mod tests { - use super::{SourceId, Span, SpanPos}; + use super::{SourceId, Span}; #[test] fn test_span_encoding() { let id = SourceId::from_u16(5); - let span = Span::new(id, 10).with_pos(SpanPos::End); + let span = Span::new(id, 10); assert_eq!(span.source(), id); assert_eq!(span.number(), 10); - assert_eq!(span.pos(), SpanPos::End); } } diff --git a/tests/typeset.rs b/tests/typeset.rs index 0eb3fc456..9eab55c86 100644 --- a/tests/typeset.rs +++ b/tests/typeset.rs @@ -433,10 +433,7 @@ fn test_part( let mut errors: Vec<_> = errors .into_iter() .filter(|error| error.span.source() == id) - .map(|error| { - let range = world.source(error.span.source()).range(error.span); - (range, error.message.to_string()) - }) + .map(|error| (error.range(world), error.message.to_string())) .collect(); errors.sort_by_key(|error| error.0.start);