mirror of
https://github.com/typst/typst
synced 2025-05-15 17:45:27 +08:00
Better error messages for #
expressions
This commit is contained in:
parent
adcc6e5506
commit
db09a5a712
@ -21,7 +21,7 @@ pub fn parse_code(text: &str) -> SyntaxNode {
|
|||||||
let m = p.marker();
|
let m = p.marker();
|
||||||
p.skip();
|
p.skip();
|
||||||
code_exprs(&mut p, |_| false);
|
code_exprs(&mut p, |_| false);
|
||||||
p.wrap_skipless(m, SyntaxKind::Code);
|
p.wrap_all(m, SyntaxKind::Code);
|
||||||
p.finish().into_iter().next().unwrap()
|
p.finish().into_iter().next().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -567,10 +567,14 @@ fn embedded_code_expr(p: &mut Parser) {
|
|||||||
let prev = p.prev_end();
|
let prev = p.prev_end();
|
||||||
code_expr_prec(p, true, 0, false);
|
code_expr_prec(p, true, 0, false);
|
||||||
|
|
||||||
// Consume error for things like `#12p` or `#"abc\"`.
|
// Consume error for things like `#12p` or `#"abc\"`.#
|
||||||
if !p.progress(prev) {
|
if !p.progress(prev) {
|
||||||
|
if p.current().is_trivia() {
|
||||||
|
// p.unskip();
|
||||||
|
} else if !p.eof() {
|
||||||
p.unexpected();
|
p.unexpected();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let semi =
|
let semi =
|
||||||
(stmt || p.directly_at(SyntaxKind::Semicolon)) && p.eat_if(SyntaxKind::Semicolon);
|
(stmt || p.directly_at(SyntaxKind::Semicolon)) && p.eat_if(SyntaxKind::Semicolon);
|
||||||
@ -1021,9 +1025,7 @@ fn set_rule(p: &mut Parser) {
|
|||||||
fn show_rule(p: &mut Parser) {
|
fn show_rule(p: &mut Parser) {
|
||||||
let m = p.marker();
|
let m = p.marker();
|
||||||
p.assert(SyntaxKind::Show);
|
p.assert(SyntaxKind::Show);
|
||||||
p.unskip();
|
let m2 = p.before_trivia();
|
||||||
let m2 = p.marker();
|
|
||||||
p.skip();
|
|
||||||
|
|
||||||
if !p.at(SyntaxKind::Colon) {
|
if !p.at(SyntaxKind::Colon) {
|
||||||
code_expr(p);
|
code_expr(p);
|
||||||
@ -1488,16 +1490,20 @@ impl<'s> Parser<'s> {
|
|||||||
.filter(|child| !child.kind().is_error() && !child.kind().is_trivia())
|
.filter(|child| !child.kind().is_error() && !child.kind().is_trivia())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrap(&mut self, m: Marker, kind: SyntaxKind) {
|
fn wrap(&mut self, from: Marker, kind: SyntaxKind) {
|
||||||
self.unskip();
|
self.wrap_within(from, self.before_trivia(), kind);
|
||||||
self.wrap_skipless(m, kind);
|
|
||||||
self.skip();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrap_skipless(&mut self, m: Marker, kind: SyntaxKind) {
|
fn wrap_all(&mut self, from: Marker, kind: SyntaxKind) {
|
||||||
let from = m.0.min(self.nodes.len());
|
self.wrap_within(from, Marker(self.nodes.len()), kind)
|
||||||
let children = self.nodes.drain(from..).collect();
|
}
|
||||||
self.nodes.push(SyntaxNode::inner(kind, children));
|
|
||||||
|
fn wrap_within(&mut self, from: Marker, to: Marker, kind: SyntaxKind) {
|
||||||
|
let len = self.nodes.len();
|
||||||
|
let to = to.0.min(len);
|
||||||
|
let from = from.0.min(to);
|
||||||
|
let children = self.nodes.drain(from..to).collect();
|
||||||
|
self.nodes.insert(from, SyntaxNode::inner(kind, children));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn progress(&self, offset: usize) -> bool {
|
fn progress(&self, offset: usize) -> bool {
|
||||||
@ -1602,24 +1608,17 @@ impl<'s> Parser<'s> {
|
|||||||
|
|
||||||
/// Produce an error that the given `thing` was expected.
|
/// Produce an error that the given `thing` was expected.
|
||||||
fn expected(&mut self, thing: &str) {
|
fn expected(&mut self, thing: &str) {
|
||||||
self.unskip();
|
|
||||||
if !self.after_error() {
|
if !self.after_error() {
|
||||||
let message = eco_format!("expected {thing}");
|
self.expected_at(self.before_trivia(), thing);
|
||||||
self.nodes.push(SyntaxNode::error(message, ""));
|
|
||||||
}
|
}
|
||||||
self.skip();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Produce an error that the given `thing` was expected but another
|
/// Produce an error that the given `thing` was expected but another
|
||||||
/// thing was `found` and consumethe next token.
|
/// thing was `found` and consume the next token.
|
||||||
fn expected_found(&mut self, thing: &str, found: &str) {
|
fn expected_found(&mut self, thing: &str, found: &str) {
|
||||||
self.unskip();
|
self.trim_errors();
|
||||||
if !self.after_error() {
|
|
||||||
self.skip();
|
|
||||||
self.convert_to_error(eco_format!("expected {thing}, found {found}"));
|
self.convert_to_error(eco_format!("expected {thing}, found {found}"));
|
||||||
}
|
}
|
||||||
self.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Produce an error that the given `thing` was expected at the position
|
/// Produce an error that the given `thing` was expected at the position
|
||||||
/// of the marker `m`.
|
/// of the marker `m`.
|
||||||
@ -1637,26 +1636,13 @@ impl<'s> Parser<'s> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consume the next token and produce an error stating that it was
|
/// Consume the next token (if any) and produce an error stating that it was
|
||||||
/// unexpected.
|
/// unexpected.
|
||||||
fn unexpected(&mut self) {
|
fn unexpected(&mut self) {
|
||||||
self.unskip();
|
self.trim_errors();
|
||||||
while self
|
|
||||||
.nodes
|
|
||||||
.last()
|
|
||||||
.map_or(false, |child| child.kind().is_error() && child.is_empty())
|
|
||||||
{
|
|
||||||
self.nodes.pop();
|
|
||||||
}
|
|
||||||
self.skip();
|
|
||||||
self.convert_to_error(eco_format!("unexpected {}", self.current.name()));
|
self.convert_to_error(eco_format!("unexpected {}", self.current.name()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the last node is an error.
|
|
||||||
fn after_error(&self) -> bool {
|
|
||||||
self.nodes.last().map_or(false, |child| child.kind().is_error())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consume the next token and turn it into an error.
|
/// Consume the next token and turn it into an error.
|
||||||
fn convert_to_error(&mut self, message: EcoString) {
|
fn convert_to_error(&mut self, message: EcoString) {
|
||||||
let kind = self.current;
|
let kind = self.current;
|
||||||
@ -1670,10 +1656,39 @@ impl<'s> Parser<'s> {
|
|||||||
|
|
||||||
/// Adds a hint to the last node, if the last node is an error.
|
/// Adds a hint to the last node, if the last node is an error.
|
||||||
fn hint(&mut self, hint: impl Into<EcoString>) {
|
fn hint(&mut self, hint: impl Into<EcoString>) {
|
||||||
self.unskip();
|
let m = self.before_trivia();
|
||||||
if let Some(last) = self.nodes.last_mut() {
|
if m.0 > 0 {
|
||||||
last.hint(hint);
|
self.nodes[m.0 - 1].hint(hint);
|
||||||
}
|
}
|
||||||
self.skip();
|
}
|
||||||
|
|
||||||
|
/// Get a marker after the last non-trivia node.
|
||||||
|
fn before_trivia(&self) -> Marker {
|
||||||
|
let mut i = self.nodes.len();
|
||||||
|
if self.lexer.mode() != LexMode::Markup && self.prev_end != self.current_start {
|
||||||
|
while i > 0 && self.nodes[i - 1].kind().is_trivia() {
|
||||||
|
i -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Marker(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the last non-trivia node is an error.
|
||||||
|
fn after_error(&mut self) -> bool {
|
||||||
|
let m = self.before_trivia();
|
||||||
|
m.0 > 0 && self.nodes[m.0 - 1].kind().is_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove trailing errors with zero length.
|
||||||
|
fn trim_errors(&mut self) {
|
||||||
|
let Marker(end) = self.before_trivia();
|
||||||
|
let mut start = end;
|
||||||
|
while start > 0
|
||||||
|
&& self.nodes[start - 1].kind().is_error()
|
||||||
|
&& self.nodes[start - 1].is_empty()
|
||||||
|
{
|
||||||
|
start -= 1;
|
||||||
|
}
|
||||||
|
self.nodes.drain(start..end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,3 +10,11 @@
|
|||||||
// Error: 7-9 expected identifier, found keyword `as`
|
// Error: 7-9 expected identifier, found keyword `as`
|
||||||
let as = 10
|
let as = 10
|
||||||
}
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 2-2 expected expression
|
||||||
|
#
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 2-2 expected expression
|
||||||
|
# hello
|
||||||
|
Loading…
x
Reference in New Issue
Block a user