bring parse errors into the reporting tests

This commit is contained in:
Folkert 2020-04-15 19:49:05 +02:00
parent 5e500f55ae
commit e21cdfc689
4 changed files with 91 additions and 138 deletions

View file

@ -1247,7 +1247,11 @@ pub fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
(Some(_loc_args), Some((_spaces_before_equals, Either::First(_equals_indent)))) => { (Some(_loc_args), Some((_spaces_before_equals, Either::First(_equals_indent)))) => {
// We got args with an '=' after them, e.g. `foo a b = ...` // We got args with an '=' after them, e.g. `foo a b = ...`
// This is a syntax error! // This is a syntax error!
panic!("TODO gracefully handle parse error for defs like `foo a b = ...`"); let fail = Fail {
attempting: state.attempting,
reason: FailReason::ArgumentsBeforeEquals,
};
Err((fail, state))
} }
(None, Some((spaces_before_equals, Either::First(equals_indent)))) => { (None, Some((spaces_before_equals, Either::First(equals_indent)))) => {
// We got '=' with no args before it // We got '=' with no args before it

View file

@ -190,6 +190,7 @@ pub enum FailReason {
Eof(Region), Eof(Region),
InvalidPattern, InvalidPattern,
ReservedKeyword(Region), ReservedKeyword(Region),
ArgumentsBeforeEquals,
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]

View file

@ -103,96 +103,10 @@ pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn can_expr(expr_str: &str) -> CanExprOut { pub fn can_expr(expr_str: &str) -> Result<CanExprOut, Fail> {
can_expr_with(&Bump::new(), test_home(), expr_str) can_expr_with(&Bump::new(), test_home(), expr_str)
} }
#[allow(dead_code)]
pub fn uniq_expr(
expr_str: &str,
) -> (
Located<Expr>,
Output,
Vec<Problem>,
Subs,
Variable,
Constraint,
ModuleId,
Interns,
) {
let declared_idents: &ImMap<Ident, (Symbol, Region)> = &ImMap::default();
uniq_expr_with(&Bump::new(), expr_str, declared_idents)
}
#[allow(dead_code)]
pub fn uniq_expr_with(
arena: &Bump,
expr_str: &str,
declared_idents: &ImMap<Ident, (Symbol, Region)>,
) -> (
Located<Expr>,
Output,
Vec<Problem>,
Subs,
Variable,
Constraint,
ModuleId,
Interns,
) {
let home = test_home();
let CanExprOut {
loc_expr,
output,
problems,
var_store: old_var_store,
var,
interns,
..
} = can_expr_with(arena, home, expr_str);
// double check
let var_store = VarStore::new(old_var_store.fresh());
let expected2 = Expected::NoExpectation(Type::Variable(var));
let constraint = roc_constrain::uniq::constrain_declaration(
home,
&var_store,
Region::zero(),
&loc_expr,
declared_idents,
expected2,
);
let stdlib = uniq_stdlib();
let types = stdlib.types;
let imports: Vec<_> = types
.iter()
.map(|(symbol, (solved_type, region))| Import {
loc_symbol: Located::at(*region, *symbol),
solved_type: solved_type,
})
.collect();
// load builtin values
// TODO what to do with those rigids?
let (_introduced_rigids, constraint) =
constrain_imported_values(imports, constraint, &var_store);
// load builtin types
let mut constraint = load_builtin_aliases(&stdlib.aliases, constraint, &var_store);
constraint.instantiate_aliases(&var_store);
let subs2 = Subs::new(var_store.into());
(
loc_expr, output, problems, subs2, var, constraint, home, interns,
)
}
pub struct CanExprOut { pub struct CanExprOut {
pub loc_expr: Located<Expr>, pub loc_expr: Located<Expr>,
pub output: Output, pub output: Output,
@ -205,13 +119,13 @@ pub struct CanExprOut {
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut { pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> Result<CanExprOut, Fail> {
let loc_expr = parse_loc_with(&arena, expr_str).unwrap_or_else(|e| { let loc_expr = match parse_loc_with(&arena, expr_str) {
panic!( Ok(e) => e,
"can_expr_with() got a parse error when attempting to canonicalize:\n\n{:?} {:?}", Err(fail) => {
expr_str, e return Err(fail);
) }
}); };
let var_store = VarStore::default(); let var_store = VarStore::default();
let var = var_store.fresh(); let var = var_store.fresh();
@ -283,7 +197,7 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
all_ident_ids, all_ident_ids,
}; };
CanExprOut { Ok(CanExprOut {
loc_expr, loc_expr,
output, output,
problems: env.problems, problems: env.problems,
@ -292,7 +206,7 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
interns, interns,
var, var,
constraint, constraint,
} })
} }
#[allow(dead_code)] #[allow(dead_code)]

View file

@ -23,6 +23,7 @@ mod test_reporting {
use std::path::PathBuf; use std::path::PathBuf;
// use roc_region::all; // use roc_region::all;
use crate::helpers::{can_expr, infer_expr, CanExprOut}; use crate::helpers::{can_expr, infer_expr, CanExprOut};
use roc_parse::parser::Fail;
use roc_reporting::report::{RocDocAllocator, RocDocBuilder}; use roc_reporting::report::{RocDocAllocator, RocDocBuilder};
use roc_solve::solve; use roc_solve::solve;
@ -43,13 +44,16 @@ mod test_reporting {
fn infer_expr_help( fn infer_expr_help(
expr_src: &str, expr_src: &str,
) -> ( ) -> Result<
Vec<solve::TypeError>, (
Vec<roc_problem::can::Problem>, Vec<solve::TypeError>,
Vec<roc_mono::expr::MonoProblem>, Vec<roc_problem::can::Problem>,
ModuleId, Vec<roc_mono::expr::MonoProblem>,
Interns, ModuleId,
) { Interns,
),
Fail,
> {
let CanExprOut { let CanExprOut {
loc_expr, loc_expr,
output, output,
@ -60,7 +64,7 @@ mod test_reporting {
mut interns, mut interns,
problems: can_problems, problems: can_problems,
.. ..
} = can_expr(expr_src); } = can_expr(expr_src)?;
let mut subs = Subs::new(var_store.into()); let mut subs = Subs::new(var_store.into());
for (var, name) in output.introduced_variables.name_by_var { for (var, name) in output.introduced_variables.name_by_var {
@ -99,7 +103,7 @@ mod test_reporting {
); );
} }
(unify_problems, can_problems, mono_problems, home, interns) Ok((unify_problems, can_problems, mono_problems, home, interns))
} }
fn list_reports<F>(src: &str, buf: &mut String, callback: F) fn list_reports<F>(src: &str, buf: &mut String, callback: F)
@ -108,40 +112,43 @@ mod test_reporting {
{ {
use ven_pretty::DocAllocator; use ven_pretty::DocAllocator;
let (type_problems, can_problems, mono_problems, home, interns) = infer_expr_help(src); match infer_expr_help(src) {
Err(fail) => todo!(),
Ok((type_problems, can_problems, mono_problems, home, interns)) => {
let src_lines: Vec<&str> = src.split('\n').collect();
let alloc = RocDocAllocator::new(&src_lines, home, &interns);
let src_lines: Vec<&str> = src.split('\n').collect(); let filename = filename_from_string(r"\code\proj\Main.roc");
let alloc = RocDocAllocator::new(&src_lines, home, &interns); let mut reports = Vec::new();
let filename = filename_from_string(r"\code\proj\Main.roc"); for problem in can_problems {
let mut reports = Vec::new(); let report = can_problem(&alloc, filename.clone(), problem.clone());
reports.push(report);
}
for problem in can_problems { for problem in type_problems {
let report = can_problem(&alloc, filename.clone(), problem.clone()); let report = type_problem(&alloc, filename.clone(), problem.clone());
reports.push(report); reports.push(report);
}
for problem in mono_problems {
let report = mono_problem(&alloc, filename.clone(), problem.clone());
reports.push(report);
}
let has_reports = !reports.is_empty();
let doc = alloc
.stack(reports.into_iter().map(|v| v.pretty(&alloc)))
.append(if has_reports {
alloc.line()
} else {
alloc.nil()
});
callback(doc, buf)
}
} }
for problem in type_problems {
let report = type_problem(&alloc, filename.clone(), problem.clone());
reports.push(report);
}
for problem in mono_problems {
let report = mono_problem(&alloc, filename.clone(), problem.clone());
reports.push(report);
}
let has_reports = !reports.is_empty();
let doc = alloc
.stack(reports.into_iter().map(|v| v.pretty(&alloc)))
.append(if has_reports {
alloc.line()
} else {
alloc.nil()
});
callback(doc, buf)
} }
fn report_problem_as(src: &str, expected_rendering: &str) { fn report_problem_as(src: &str, expected_rendering: &str) {
@ -491,7 +498,8 @@ mod test_reporting {
"# "#
); );
let (_type_problems, _can_problems, _mono_problems, home, interns) = infer_expr_help(src); let (_type_problems, _can_problems, _mono_problems, home, interns) =
infer_expr_help(src).expect("parse error");
let mut buf = String::new(); let mut buf = String::new();
let src_lines: Vec<&str> = src.split('\n').collect(); let src_lines: Vec<&str> = src.split('\n').collect();
@ -521,7 +529,7 @@ mod test_reporting {
); );
let (_type_problems, _can_problems, _mono_problems, home, mut interns) = let (_type_problems, _can_problems, _mono_problems, home, mut interns) =
infer_expr_help(src); infer_expr_help(src).expect("parse error");
let mut buf = String::new(); let mut buf = String::new();
let src_lines: Vec<&str> = src.split('\n').collect(); let src_lines: Vec<&str> = src.split('\n').collect();
@ -2671,4 +2679,30 @@ mod test_reporting {
), ),
) )
} }
#[test]
fn elm_function_syntax() {
report_problem_as(
indoc!(
r#"
f x y = x
"#
),
indoc!(
r#"
-- SYNTAX PROBLEM --------------------------------------------------------------
The `a` type variable is not used in the `Foo` alias definition:
1 Foo a : [ Foo ]
^
Roc does not allow unused type parameters!
Hint: If you want an unused type parameter (a so-called "phantom
type"), read the guide section on phantom data.
"#
),
)
}
} }