mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 16:21:11 +00:00
bring parse errors into the reporting tests
This commit is contained in:
parent
5e500f55ae
commit
e21cdfc689
4 changed files with 91 additions and 138 deletions
|
@ -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
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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.
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue