error messages for escaped things

This commit is contained in:
Folkert 2021-02-26 22:02:42 +01:00
parent 41720ec5db
commit 2f4099f1f0
5 changed files with 218 additions and 31 deletions

View file

@ -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> {

View file

@ -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,
)); ));
} }

View file

@ -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),
}; };

View file

@ -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,

View file

@ -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)
"#
),
)
}
} }