mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 14:54:47 +00:00
report malformed int and float patterns
This commit is contained in:
parent
a05b664be0
commit
0c7a4179aa
8 changed files with 208 additions and 27 deletions
|
@ -719,6 +719,7 @@ fn pattern_to_vars_by_symbol(
|
||||||
| FloatLiteral(_)
|
| FloatLiteral(_)
|
||||||
| StrLiteral(_)
|
| StrLiteral(_)
|
||||||
| Underscore
|
| Underscore
|
||||||
|
| MalformedPattern(_, _)
|
||||||
| UnsupportedPattern(_) => {}
|
| UnsupportedPattern(_) => {}
|
||||||
|
|
||||||
Shadowed(_, _) => {}
|
Shadowed(_, _) => {}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use roc_module::ident::{Ident, Lowercase, TagName};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_parse::ast;
|
use roc_parse::ast;
|
||||||
use roc_parse::pattern::PatternType;
|
use roc_parse::pattern::PatternType;
|
||||||
use roc_problem::can::{Problem, RuntimeError};
|
use roc_problem::can::{MalformedPatternProblem, Problem, RuntimeError};
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
|
|
||||||
|
@ -35,6 +35,8 @@ pub enum Pattern {
|
||||||
Shadowed(Region, Located<Ident>),
|
Shadowed(Region, Located<Ident>),
|
||||||
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
|
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
|
||||||
UnsupportedPattern(Region),
|
UnsupportedPattern(Region),
|
||||||
|
// parse error patterns
|
||||||
|
MalformedPattern(MalformedPatternProblem, Region),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -76,6 +78,7 @@ pub fn symbols_from_pattern_help(pattern: &Pattern, symbols: &mut Vec<Symbol>) {
|
||||||
| FloatLiteral(_)
|
| FloatLiteral(_)
|
||||||
| StrLiteral(_)
|
| StrLiteral(_)
|
||||||
| Underscore
|
| Underscore
|
||||||
|
| MalformedPattern(_, _)
|
||||||
| UnsupportedPattern(_) => {}
|
| UnsupportedPattern(_) => {}
|
||||||
|
|
||||||
Shadowed(_, _) => {}
|
Shadowed(_, _) => {}
|
||||||
|
@ -165,12 +168,13 @@ pub fn canonicalize_pattern<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
FloatLiteral(ref string) => match pattern_type {
|
FloatLiteral(ref string) => match pattern_type {
|
||||||
WhenBranch => {
|
WhenBranch => match finish_parsing_float(string) {
|
||||||
let float = finish_parsing_float(string)
|
Err(_error) => {
|
||||||
.unwrap_or_else(|_| panic!("TODO handle malformed float pattern"));
|
let problem = MalformedPatternProblem::MalformedFloat;
|
||||||
|
malformed_pattern(env, problem, region)
|
||||||
Pattern::FloatLiteral(float)
|
}
|
||||||
}
|
Ok(float) => Pattern::FloatLiteral(float),
|
||||||
|
},
|
||||||
ptype @ DefExpr | ptype @ TopLevelDef | ptype @ FunctionArg => {
|
ptype @ DefExpr | ptype @ TopLevelDef | ptype @ FunctionArg => {
|
||||||
unsupported_pattern(env, ptype, region)
|
unsupported_pattern(env, ptype, region)
|
||||||
}
|
}
|
||||||
|
@ -182,12 +186,13 @@ pub fn canonicalize_pattern<'a>(
|
||||||
},
|
},
|
||||||
|
|
||||||
NumLiteral(string) => match pattern_type {
|
NumLiteral(string) => match pattern_type {
|
||||||
WhenBranch => {
|
WhenBranch => match finish_parsing_int(string) {
|
||||||
let int = finish_parsing_int(string)
|
Err(_error) => {
|
||||||
.unwrap_or_else(|_| panic!("TODO handle malformed int pattern"));
|
let problem = MalformedPatternProblem::MalformedInt;
|
||||||
|
malformed_pattern(env, problem, region)
|
||||||
Pattern::NumLiteral(var_store.fresh(), int)
|
}
|
||||||
}
|
Ok(int) => Pattern::NumLiteral(var_store.fresh(), int),
|
||||||
|
},
|
||||||
ptype @ DefExpr | ptype @ TopLevelDef | ptype @ FunctionArg => {
|
ptype @ DefExpr | ptype @ TopLevelDef | ptype @ FunctionArg => {
|
||||||
unsupported_pattern(env, ptype, region)
|
unsupported_pattern(env, ptype, region)
|
||||||
}
|
}
|
||||||
|
@ -198,16 +203,19 @@ pub fn canonicalize_pattern<'a>(
|
||||||
base,
|
base,
|
||||||
is_negative,
|
is_negative,
|
||||||
} => match pattern_type {
|
} => match pattern_type {
|
||||||
WhenBranch => {
|
WhenBranch => match finish_parsing_base(string, *base) {
|
||||||
let int = finish_parsing_base(string, *base)
|
Err(_error) => {
|
||||||
.unwrap_or_else(|_| panic!("TODO handle malformed {:?} pattern", base));
|
let problem = MalformedPatternProblem::MalformedBase(*base);
|
||||||
|
malformed_pattern(env, problem, region)
|
||||||
if *is_negative {
|
|
||||||
Pattern::IntLiteral(-int)
|
|
||||||
} else {
|
|
||||||
Pattern::IntLiteral(int)
|
|
||||||
}
|
}
|
||||||
}
|
Ok(int) => {
|
||||||
|
if *is_negative {
|
||||||
|
Pattern::IntLiteral(-int)
|
||||||
|
} else {
|
||||||
|
Pattern::IntLiteral(int)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
ptype @ DefExpr | ptype @ TopLevelDef | ptype @ FunctionArg => {
|
ptype @ DefExpr | ptype @ TopLevelDef | ptype @ FunctionArg => {
|
||||||
unsupported_pattern(env, ptype, region)
|
unsupported_pattern(env, ptype, region)
|
||||||
}
|
}
|
||||||
|
@ -215,8 +223,8 @@ pub fn canonicalize_pattern<'a>(
|
||||||
|
|
||||||
StrLiteral(_string) => match pattern_type {
|
StrLiteral(_string) => match pattern_type {
|
||||||
WhenBranch => {
|
WhenBranch => {
|
||||||
panic!("TODO check whether string pattern is malformed.");
|
// TODO report whether string was malformed
|
||||||
// Pattern::StrLiteral((*string).into())
|
Pattern::StrLiteral((*string).into())
|
||||||
}
|
}
|
||||||
ptype @ DefExpr | ptype @ TopLevelDef | ptype @ FunctionArg => {
|
ptype @ DefExpr | ptype @ TopLevelDef | ptype @ FunctionArg => {
|
||||||
unsupported_pattern(env, ptype, region)
|
unsupported_pattern(env, ptype, region)
|
||||||
|
@ -325,6 +333,20 @@ fn unsupported_pattern<'a>(
|
||||||
Pattern::UnsupportedPattern(region)
|
Pattern::UnsupportedPattern(region)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// When we detect a malformed pattern like `3.X` or `0b5`,
|
||||||
|
/// report it to Env and return an UnsupportedPattern runtime error pattern.
|
||||||
|
fn malformed_pattern<'a>(
|
||||||
|
env: &mut Env<'a>,
|
||||||
|
problem: MalformedPatternProblem,
|
||||||
|
region: Region,
|
||||||
|
) -> Pattern {
|
||||||
|
env.problem(Problem::RuntimeError(RuntimeError::MalformedPattern(
|
||||||
|
problem, region,
|
||||||
|
)));
|
||||||
|
|
||||||
|
Pattern::MalformedPattern(problem, region)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn bindings_from_patterns<'a, I>(loc_patterns: I, scope: &Scope) -> Vec<(Symbol, Region)>
|
pub fn bindings_from_patterns<'a, I>(loc_patterns: I, scope: &Scope) -> Vec<(Symbol, Region)>
|
||||||
where
|
where
|
||||||
I: Iterator<Item = &'a Located<Pattern>>,
|
I: Iterator<Item = &'a Located<Pattern>>,
|
||||||
|
@ -374,6 +396,7 @@ fn add_bindings_from_patterns(
|
||||||
| StrLiteral(_)
|
| StrLiteral(_)
|
||||||
| Underscore
|
| Underscore
|
||||||
| Shadowed(_, _)
|
| Shadowed(_, _)
|
||||||
|
| MalformedPattern(_, _)
|
||||||
| UnsupportedPattern(_) => (),
|
| UnsupportedPattern(_) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ fn headers_from_annotation_help(
|
||||||
}
|
}
|
||||||
Underscore
|
Underscore
|
||||||
| Shadowed(_, _)
|
| Shadowed(_, _)
|
||||||
|
| MalformedPattern(_, _)
|
||||||
| UnsupportedPattern(_)
|
| UnsupportedPattern(_)
|
||||||
| NumLiteral(_, _)
|
| NumLiteral(_, _)
|
||||||
| IntLiteral(_)
|
| IntLiteral(_)
|
||||||
|
@ -117,7 +118,7 @@ pub fn constrain_pattern(
|
||||||
state: &mut PatternState,
|
state: &mut PatternState,
|
||||||
) {
|
) {
|
||||||
match pattern {
|
match pattern {
|
||||||
Underscore | UnsupportedPattern(_) | Shadowed(_, _) => {
|
Underscore | UnsupportedPattern(_) | MalformedPattern(_, _) | Shadowed(_, _) => {
|
||||||
// Neither the _ pattern nor erroneous ones add any constraints.
|
// Neither the _ pattern nor erroneous ones add any constraints.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -342,7 +342,7 @@ fn constrain_pattern(
|
||||||
state.constraints.push(tag_con);
|
state.constraints.push(tag_con);
|
||||||
}
|
}
|
||||||
|
|
||||||
Underscore | Shadowed(_, _) | UnsupportedPattern(_) => {
|
Underscore | Shadowed(_, _) | MalformedPattern(_, _) | UnsupportedPattern(_) => {
|
||||||
// no constraints
|
// no constraints
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -446,6 +446,12 @@ fn pattern_to_when<'a>(
|
||||||
(env.unique_symbol(), Located::at_zero(RuntimeError(error)))
|
(env.unique_symbol(), Located::at_zero(RuntimeError(error)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MalformedPattern(problem, region) => {
|
||||||
|
// create the runtime error here, instead of delegating to When.
|
||||||
|
let error = roc_problem::can::RuntimeError::MalformedPattern(*problem, *region);
|
||||||
|
(env.unique_symbol(), Located::at_zero(RuntimeError(error)))
|
||||||
|
}
|
||||||
|
|
||||||
AppliedTag { .. } | RecordDestructure { .. } => {
|
AppliedTag { .. } | RecordDestructure { .. } => {
|
||||||
let symbol = env.unique_symbol();
|
let symbol = env.unique_symbol();
|
||||||
|
|
||||||
|
@ -1545,7 +1551,10 @@ fn from_can_pattern<'a>(
|
||||||
StrLiteral(v) => Pattern::StrLiteral(v.clone()),
|
StrLiteral(v) => Pattern::StrLiteral(v.clone()),
|
||||||
Shadowed(region, ident) => Pattern::Shadowed(*region, ident.clone()),
|
Shadowed(region, ident) => Pattern::Shadowed(*region, ident.clone()),
|
||||||
UnsupportedPattern(region) => Pattern::UnsupportedPattern(*region),
|
UnsupportedPattern(region) => Pattern::UnsupportedPattern(*region),
|
||||||
|
MalformedPattern(_problem, region) => {
|
||||||
|
// TODO preserve malformed problem information here?
|
||||||
|
Pattern::UnsupportedPattern(*region)
|
||||||
|
}
|
||||||
NumLiteral(var, num) => match num_argument_to_int_or_float(env.subs, *var) {
|
NumLiteral(var, num) => match num_argument_to_int_or_float(env.subs, *var) {
|
||||||
IntOrFloat::IntType => Pattern::IntLiteral(*num),
|
IntOrFloat::IntType => Pattern::IntLiteral(*num),
|
||||||
IntOrFloat::FloatType => Pattern::FloatLiteral(*num as u64),
|
IntOrFloat::FloatType => Pattern::FloatLiteral(*num as u64),
|
||||||
|
|
|
@ -3,6 +3,7 @@ use roc_collections::all::MutSet;
|
||||||
use roc_module::ident::{Ident, Lowercase, TagName};
|
use roc_module::ident::{Ident, Lowercase, TagName};
|
||||||
use roc_module::operator::BinOp;
|
use roc_module::operator::BinOp;
|
||||||
use roc_module::symbol::{ModuleId, Symbol};
|
use roc_module::symbol::{ModuleId, Symbol};
|
||||||
|
use roc_parse::ast::Base;
|
||||||
use roc_parse::pattern::PatternType;
|
use roc_parse::pattern::PatternType;
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
|
|
||||||
|
@ -69,6 +70,8 @@ pub enum RuntimeError {
|
||||||
},
|
},
|
||||||
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
|
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
|
||||||
UnsupportedPattern(Region),
|
UnsupportedPattern(Region),
|
||||||
|
// Example: when 1 is 1.X -> 32
|
||||||
|
MalformedPattern(MalformedPatternProblem, Region),
|
||||||
UnrecognizedFunctionName(Located<InlinableString>),
|
UnrecognizedFunctionName(Located<InlinableString>),
|
||||||
LookupNotInScope(Located<InlinableString>, MutSet<Box<str>>),
|
LookupNotInScope(Located<InlinableString>, MutSet<Box<str>>),
|
||||||
ValueNotExposed {
|
ValueNotExposed {
|
||||||
|
@ -95,3 +98,10 @@ pub enum RuntimeError {
|
||||||
/// When the author specifies a type annotation but no implementation
|
/// When the author specifies a type annotation but no implementation
|
||||||
NoImplementation,
|
NoImplementation,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum MalformedPatternProblem {
|
||||||
|
MalformedInt,
|
||||||
|
MalformedFloat,
|
||||||
|
MalformedBase(Base),
|
||||||
|
}
|
||||||
|
|
|
@ -338,6 +338,28 @@ fn pretty_runtime_error<'b>(
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RuntimeError::MalformedPattern(problem, region) => {
|
||||||
|
use roc_parse::ast::Base;
|
||||||
|
use roc_problem::can::MalformedPatternProblem::*;
|
||||||
|
|
||||||
|
let name = match problem {
|
||||||
|
MalformedInt => "integer",
|
||||||
|
MalformedFloat => "float",
|
||||||
|
MalformedBase(Base::Hex) => "hex integer",
|
||||||
|
MalformedBase(Base::Binary) => "binary integer",
|
||||||
|
MalformedBase(Base::Octal) => "octal integer",
|
||||||
|
};
|
||||||
|
|
||||||
|
alloc.stack(vec![
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow("This "),
|
||||||
|
alloc.text(name),
|
||||||
|
alloc.reflow(" pattern is malformed:"),
|
||||||
|
]),
|
||||||
|
alloc.region(region),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
other => {
|
other => {
|
||||||
// // Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
|
// // Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
|
||||||
// UnsupportedPattern(Region),
|
// UnsupportedPattern(Region),
|
||||||
|
|
|
@ -1468,6 +1468,121 @@ mod test_reporting {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn malformed_int_pattern() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when 1 is
|
||||||
|
100A -> 3
|
||||||
|
_ -> 4
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
-- SYNTAX PROBLEM --------------------------------------------------------------
|
||||||
|
|
||||||
|
This integer pattern is malformed:
|
||||||
|
|
||||||
|
2 ┆ 100A -> 3
|
||||||
|
┆ ^^^^
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn malformed_float_pattern() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when 1 is
|
||||||
|
2.X -> 3
|
||||||
|
_ -> 4
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
-- SYNTAX PROBLEM --------------------------------------------------------------
|
||||||
|
|
||||||
|
This float pattern is malformed:
|
||||||
|
|
||||||
|
2 ┆ 2.X -> 3
|
||||||
|
┆ ^^^
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn malformed_hex_pattern() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when 1 is
|
||||||
|
0xZ -> 3
|
||||||
|
_ -> 4
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
-- SYNTAX PROBLEM --------------------------------------------------------------
|
||||||
|
|
||||||
|
This hex integer pattern is malformed:
|
||||||
|
|
||||||
|
2 ┆ 0xZ -> 3
|
||||||
|
┆ ^^^
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn malformed_oct_pattern() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when 1 is
|
||||||
|
0o9 -> 3
|
||||||
|
_ -> 4
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
-- SYNTAX PROBLEM --------------------------------------------------------------
|
||||||
|
|
||||||
|
This octal integer pattern is malformed:
|
||||||
|
|
||||||
|
2 ┆ 0o9 -> 3
|
||||||
|
┆ ^^^
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn malformed_bin_pattern() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when 1 is
|
||||||
|
0b4 -> 3
|
||||||
|
_ -> 4
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
-- SYNTAX PROBLEM --------------------------------------------------------------
|
||||||
|
|
||||||
|
This binary integer pattern is malformed:
|
||||||
|
|
||||||
|
2 ┆ 0b4 -> 3
|
||||||
|
┆ ^^^
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn missing_fields() {
|
fn missing_fields() {
|
||||||
report_problem_as(
|
report_problem_as(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue