mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 22:34:45 +00:00
error messages for escaped things
This commit is contained in:
parent
41720ec5db
commit
2f4099f1f0
5 changed files with 218 additions and 31 deletions
|
@ -429,17 +429,17 @@ pub enum EString<'a> {
|
||||||
Space(BadInputError, Row, Col),
|
Space(BadInputError, Row, Col),
|
||||||
EndlessSingle(Row, Col),
|
EndlessSingle(Row, Col),
|
||||||
EndlessMulti,
|
EndlessMulti,
|
||||||
StringEscape(Escape),
|
UnknownEscape(Row, Col),
|
||||||
Format(&'a SyntaxError<'a>),
|
Format(&'a SyntaxError<'a>, Row, Col),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum Escape {
|
// pub enum Escape {
|
||||||
EscapeUnknown,
|
// EscapeUnknown,
|
||||||
BadUnicodeFormat(u16),
|
// BadUnicodeFormat(u16),
|
||||||
BadUnicodeCode(u16),
|
// BadUnicodeCode(u16),
|
||||||
BadUnicodeLength(u16, i32, i32),
|
// BadUnicodeLength(u16, i32, i32),
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum ERecord<'a> {
|
pub enum ERecord<'a> {
|
||||||
|
|
|
@ -2,8 +2,8 @@ use crate::ast::{EscapedChar, StrLiteral, StrSegment};
|
||||||
use crate::expr;
|
use crate::expr;
|
||||||
use crate::parser::Progress::*;
|
use crate::parser::Progress::*;
|
||||||
use crate::parser::{
|
use crate::parser::{
|
||||||
allocated, ascii_char, loc, parse_utf8, specialize_ref, unexpected_eof, word1, BadInputError,
|
allocated, ascii_char, loc, parse_utf8, specialize_ref, word1, BadInputError, EString, Parser,
|
||||||
EString, Escape, ParseResult, Parser, State, SyntaxError,
|
State,
|
||||||
};
|
};
|
||||||
use bumpalo::collections::vec::Vec;
|
use bumpalo::collections::vec::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
@ -232,7 +232,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
||||||
// canonicalization error if that expression variant
|
// canonicalization error if that expression variant
|
||||||
// is not allowed inside a string interpolation.
|
// is not allowed inside a string interpolation.
|
||||||
let (_progress, loc_expr, new_state) = specialize_ref(
|
let (_progress, loc_expr, new_state) = specialize_ref(
|
||||||
|e, _, _| EString::Format(e),
|
EString::Format,
|
||||||
skip_second!(loc(allocated(expr::expr(0))), ascii_char(b')')),
|
skip_second!(loc(allocated(expr::expr(0))), ascii_char(b')')),
|
||||||
)
|
)
|
||||||
.parse(arena, state)?;
|
.parse(arena, state)?;
|
||||||
|
@ -296,7 +296,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
||||||
// escapable characters (\n, \t, \", \\, etc)
|
// escapable characters (\n, \t, \", \\, etc)
|
||||||
return Err((
|
return Err((
|
||||||
MadeProgress,
|
MadeProgress,
|
||||||
EString::StringEscape(Escape::EscapeUnknown),
|
EString::UnknownEscape(state.line, state.column),
|
||||||
state,
|
state,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -305,24 +305,35 @@ pub fn can_problem<'b>(
|
||||||
alloc.reflow(" can occur in this position."),
|
alloc.reflow(" can occur in this position."),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
Problem::InvalidHexadecimal(region) => {
|
Problem::InvalidHexadecimal(region) => alloc.stack(vec![
|
||||||
todo!(
|
alloc.reflow("This unicode code point is invalid:"),
|
||||||
"TODO report an invalid hexadecimal number in a \\u(...) code point at region {:?}",
|
alloc.region(region),
|
||||||
region
|
alloc.concat(vec![
|
||||||
);
|
alloc.reflow(r"I was expecting a hexadecimal number, like "),
|
||||||
}
|
alloc.parser_suggestion("\\u(1100)"),
|
||||||
Problem::InvalidUnicodeCodePoint(region) => {
|
alloc.reflow(" or "),
|
||||||
todo!(
|
alloc.parser_suggestion("\\u(00FF)"),
|
||||||
"TODO report an invalid \\u(...) code point at region {:?}",
|
alloc.text("."),
|
||||||
region
|
]),
|
||||||
);
|
alloc.reflow(r"Learn more about working with unicode in roc at TODO"),
|
||||||
}
|
]),
|
||||||
Problem::InvalidInterpolation(region) => {
|
Problem::InvalidUnicodeCodePoint(region) => alloc.stack(vec![
|
||||||
todo!(
|
alloc.reflow("This unicode code point is invalid:"),
|
||||||
"TODO report an invalid string interpolation at region {:?}",
|
alloc.region(region),
|
||||||
region
|
alloc.reflow("Learn more about working with unicode in roc at TODO"),
|
||||||
);
|
]),
|
||||||
}
|
Problem::InvalidInterpolation(region) => alloc.stack(vec![
|
||||||
|
alloc.reflow("This string interpolation is invalid:"),
|
||||||
|
alloc.region(region),
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow(r"I was expecting an identifier, like "),
|
||||||
|
alloc.parser_suggestion("\\u(message)"),
|
||||||
|
alloc.reflow(" or "),
|
||||||
|
alloc.parser_suggestion("\\u(LoremIpsum.text)"),
|
||||||
|
alloc.text("."),
|
||||||
|
]),
|
||||||
|
alloc.reflow(r"Learn more about string interpolation at TODO"),
|
||||||
|
]),
|
||||||
Problem::RuntimeError(runtime_error) => pretty_runtime_error(alloc, runtime_error),
|
Problem::RuntimeError(runtime_error) => pretty_runtime_error(alloc, runtime_error),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -181,6 +181,9 @@ fn to_expr_report<'a>(
|
||||||
to_lambda_report(alloc, filename, context, &lambda, *row, *col)
|
to_lambda_report(alloc, filename, context, &lambda, *row, *col)
|
||||||
}
|
}
|
||||||
EExpr::List(list, row, col) => to_list_report(alloc, filename, context, &list, *row, *col),
|
EExpr::List(list, row, col) => to_list_report(alloc, filename, context, &list, *row, *col),
|
||||||
|
EExpr::Str(string, row, col) => {
|
||||||
|
to_str_report(alloc, filename, context, &string, *row, *col)
|
||||||
|
}
|
||||||
_ => todo!("unhandled parse error: {:?}", parse_problem),
|
_ => todo!("unhandled parse error: {:?}", parse_problem),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -410,6 +413,88 @@ fn to_unfinished_lambda_report<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_str_report<'a>(
|
||||||
|
alloc: &'a RocDocAllocator<'a>,
|
||||||
|
filename: PathBuf,
|
||||||
|
_context: Context,
|
||||||
|
parse_problem: &roc_parse::parser::EString<'a>,
|
||||||
|
start_row: Row,
|
||||||
|
start_col: Col,
|
||||||
|
) -> Report<'a> {
|
||||||
|
use roc_parse::parser::EString;
|
||||||
|
|
||||||
|
match *parse_problem {
|
||||||
|
EString::Format(syntax, row, col) => to_syntax_report(alloc, filename, syntax, row, col),
|
||||||
|
EString::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col),
|
||||||
|
EString::UnknownEscape(row, col) => {
|
||||||
|
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||||
|
let region = Region::from_rows_cols(row, col, row, col + 2);
|
||||||
|
|
||||||
|
let suggestion = |msg, sugg| {
|
||||||
|
alloc
|
||||||
|
.text("- ")
|
||||||
|
.append(alloc.reflow(msg))
|
||||||
|
.append(alloc.parser_suggestion(sugg))
|
||||||
|
};
|
||||||
|
|
||||||
|
let doc = alloc.stack(vec![
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow(r"I was partway through parsing a "),
|
||||||
|
alloc.reflow(r" string literal, but I got stuck here:"),
|
||||||
|
]),
|
||||||
|
alloc.region_with_subregion(surroundings, region),
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow(r"This is not an escape sequence I recognize."),
|
||||||
|
alloc.reflow(r" After a backslash, I am looking for one of these:"),
|
||||||
|
]),
|
||||||
|
alloc
|
||||||
|
.vcat(vec![
|
||||||
|
suggestion("A newline: ", "\\n"),
|
||||||
|
suggestion("A caret return: ", "\\r"),
|
||||||
|
suggestion("A tab: ", "\\t"),
|
||||||
|
suggestion("An escaped quote: ", "\\\""),
|
||||||
|
suggestion("An escaped backslash: ", "\\\\"),
|
||||||
|
suggestion("A unicode code point: ", "\\u(00FF)"),
|
||||||
|
suggestion("An interpolated string: ", "\\(myVariable)"),
|
||||||
|
])
|
||||||
|
.indent(4),
|
||||||
|
]);
|
||||||
|
|
||||||
|
Report {
|
||||||
|
filename,
|
||||||
|
doc,
|
||||||
|
title: "WEIRD ESCAPE".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EString::CodePointOpen(row, col) | EString::CodePointEnd(row, col) => {
|
||||||
|
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||||
|
let region = Region::from_row_col(row, col);
|
||||||
|
|
||||||
|
let doc = alloc.stack(vec![
|
||||||
|
alloc.reflow(
|
||||||
|
r"I am partway through parsing a unicode code point, but I got stuck here:",
|
||||||
|
),
|
||||||
|
alloc.region_with_subregion(surroundings, region),
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow(r"I was expecting a hexadecimal number, like "),
|
||||||
|
alloc.parser_suggestion("\\u(1100)"),
|
||||||
|
alloc.reflow(" or "),
|
||||||
|
alloc.parser_suggestion("\\u(00FF)"),
|
||||||
|
alloc.text("."),
|
||||||
|
]),
|
||||||
|
alloc.reflow(r"Learn more about working with unicode in roc at TODO"),
|
||||||
|
]);
|
||||||
|
|
||||||
|
Report {
|
||||||
|
filename,
|
||||||
|
doc,
|
||||||
|
title: "WEIRD CODE POINT".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ref other => todo!("{:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn to_list_report<'a>(
|
fn to_list_report<'a>(
|
||||||
alloc: &'a RocDocAllocator<'a>,
|
alloc: &'a RocDocAllocator<'a>,
|
||||||
filename: PathBuf,
|
filename: PathBuf,
|
||||||
|
|
|
@ -5160,4 +5160,95 @@ mod test_reporting {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unicode_not_hex() {
|
||||||
|
report_problem_as(
|
||||||
|
r#""abc\u(zzzz)def""#,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── WEIRD CODE POINT ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
I am partway through parsing a unicode code point, but I got stuck
|
||||||
|
here:
|
||||||
|
|
||||||
|
1│ "abc\u(zzzz)def"
|
||||||
|
^
|
||||||
|
|
||||||
|
I was expecting a hexadecimal number, like \u(1100) or \u(00FF).
|
||||||
|
|
||||||
|
Learn more about working with unicode in roc at TODO
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn interpolate_not_identifier() {
|
||||||
|
report_problem_as(
|
||||||
|
r#""abc\(32)def""#,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── SYNTAX PROBLEM ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
This string interpolation is invalid:
|
||||||
|
|
||||||
|
1│ "abc\(32)def"
|
||||||
|
^^
|
||||||
|
|
||||||
|
I was expecting an identifier, like \u(message) or
|
||||||
|
\u(LoremIpsum.text).
|
||||||
|
|
||||||
|
Learn more about string interpolation at TODO
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unicode_too_large() {
|
||||||
|
report_problem_as(
|
||||||
|
r#""abc\u(110000)def""#,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── SYNTAX PROBLEM ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
This unicode code point is invalid:
|
||||||
|
|
||||||
|
1│ "abc\u(110000)def"
|
||||||
|
^^^^^^
|
||||||
|
|
||||||
|
Learn more about working with unicode in roc at TODO
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn weird_escape() {
|
||||||
|
report_problem_as(
|
||||||
|
r#""abc\qdef""#,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── WEIRD ESCAPE ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
I was partway through parsing a string literal, but I got stuck here:
|
||||||
|
|
||||||
|
1│ "abc\qdef"
|
||||||
|
^^
|
||||||
|
|
||||||
|
This is not an escape sequence I recognize. After a backslash, I am
|
||||||
|
looking for one of these:
|
||||||
|
|
||||||
|
- A newline: \n
|
||||||
|
- A caret return: \r
|
||||||
|
- A tab: \t
|
||||||
|
- An escaped quote: \"
|
||||||
|
- An escaped backslash: \\
|
||||||
|
- A unicode code point: \u(00FF)
|
||||||
|
- An interpolated string: \(myVariable)
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue