Report severity and custom titles in can::Problem

This commit is contained in:
Richard Feldman 2021-09-02 20:53:43 -04:00
parent 71af5c5a1a
commit c711f0bbc7
2 changed files with 447 additions and 280 deletions

View file

@ -3,23 +3,43 @@ use roc_module::ident::{Ident, Lowercase, ModuleName};
use roc_parse::parser::{Col, Row}; use roc_parse::parser::{Col, Row};
use roc_problem::can::PrecedenceProblem::BothNonAssociative; use roc_problem::can::PrecedenceProblem::BothNonAssociative;
use roc_problem::can::{BadPattern, FloatErrorKind, IntErrorKind, Problem, RuntimeError}; use roc_problem::can::{BadPattern, FloatErrorKind, IntErrorKind, Problem, RuntimeError};
use roc_region::all::Region; use roc_region::all::{Located, Region};
use std::path::PathBuf; use std::path::PathBuf;
use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder}; use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder, Severity};
use ven_pretty::DocAllocator; use ven_pretty::DocAllocator;
const SYNTAX_PROBLEM: &str = "SYNTAX PROBLEM";
const NAMING_PROBLEM: &str = "NAMING PROBLEM";
const UNRECOGNIZED_NAME: &str = "UNRECOGNIZED NAME";
const UNUSED_DEF: &str = "UNUSED DEFINITION";
const UNUSED_IMPORT: &str = "UNUSED IMPORT";
const UNUSED_ALIAS_PARAM: &str = "UNUSED TYPE ALIAS PARAMETER";
const UNUSED_ARG: &str = "UNUSED ARGUMENT";
const MISSING_DEFINITION: &str = "MISSING_DEFINITION";
const DUPLICATE_FIELD_NAME: &str = "DUPLICATE FIELD NAME";
const DUPLICATE_TAG_NAME: &str = "DUPLICATE TAG NAME";
const INVALID_UNICODE: &str = "INVALID UNICODE";
const CIRCULAR_DEF: &str = "CIRCULAR DEFINITION";
const DUPLICATE_NAME: &str = "DUPLICATE NAME";
const VALUE_NOT_EXPOSED: &str = "NOT EXPOSED";
const MODULE_NOT_IMPORTED: &str = "MODULE NOT IMPORTED";
pub fn can_problem<'b>( pub fn can_problem<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
filename: PathBuf, filename: PathBuf,
problem: Problem, problem: Problem,
) -> Report<'b> { ) -> Report<'b> {
let doc = match problem { let doc;
let title;
let severity;
match problem {
Problem::UnusedDef(symbol, region) => { Problem::UnusedDef(symbol, region) => {
let line = let line =
r#" then remove it so future readers of your code don't wonder why it is there."#; r#" then remove it so future readers of your code don't wonder why it is there."#;
alloc.stack(vec![ doc = alloc.stack(vec![
alloc alloc
.symbol_unqualified(symbol) .symbol_unqualified(symbol)
.append(alloc.reflow(" is not used anywhere in your code.")), .append(alloc.reflow(" is not used anywhere in your code.")),
@ -28,36 +48,49 @@ pub fn can_problem<'b>(
.reflow("If you didn't intend on using ") .reflow("If you didn't intend on using ")
.append(alloc.symbol_unqualified(symbol)) .append(alloc.symbol_unqualified(symbol))
.append(alloc.reflow(line)), .append(alloc.reflow(line)),
]) ]);
title = UNUSED_DEF.to_string();
severity = Severity::Warning;
}
Problem::UnusedImport(module_id, region) => {
doc = alloc.stack(vec![
alloc.concat(vec![
alloc.reflow("Nothing from "),
alloc.module(module_id),
alloc.reflow(" is used in this module."),
]),
alloc.region(region),
alloc.concat(vec![
alloc.reflow("Since "),
alloc.module(module_id),
alloc.reflow(" isn't used, you don't need to import it."),
]),
]);
title = UNUSED_IMPORT.to_string();
severity = Severity::Warning;
}
Problem::ExposedButNotDefined(symbol) => {
doc = alloc.stack(vec![
alloc.symbol_unqualified(symbol).append(
alloc.reflow(" is listed as exposed, but it isn't defined in this module."),
),
alloc
.reflow("You can fix this by adding a definition for ")
.append(alloc.symbol_unqualified(symbol))
.append(alloc.reflow(", or by removing it from "))
.append(alloc.keyword("exposes"))
.append(alloc.reflow(".")),
]);
title = MISSING_DEFINITION.to_string();
severity = Severity::RuntimeError;
} }
Problem::UnusedImport(module_id, region) => alloc.stack(vec![
alloc.concat(vec![
alloc.reflow("Nothing from "),
alloc.module(module_id),
alloc.reflow(" is used in this module."),
]),
alloc.region(region),
alloc.concat(vec![
alloc.reflow("Since "),
alloc.module(module_id),
alloc.reflow(" isn't used, you don't need to import it."),
]),
]),
Problem::ExposedButNotDefined(symbol) => alloc.stack(vec![
alloc.symbol_unqualified(symbol).append(
alloc.reflow(" is listed as exposed, but it isn't defined in this module."),
),
alloc
.reflow("You can fix this by adding a definition for ")
.append(alloc.symbol_unqualified(symbol))
.append(alloc.reflow(", or by removing it from "))
.append(alloc.keyword("exposes"))
.append(alloc.reflow(".")),
]),
Problem::UnusedArgument(closure_symbol, argument_symbol, region) => { Problem::UnusedArgument(closure_symbol, argument_symbol, region) => {
let line = "\". Adding an underscore at the start of a variable name is a way of saying that the variable is not used."; let line = "\". Adding an underscore at the start of a variable name is a way of saying that the variable is not used.";
alloc.stack(vec![ doc = alloc.stack(vec![
alloc.concat(vec![ alloc.concat(vec![
alloc.symbol_unqualified(closure_symbol), alloc.symbol_unqualified(closure_symbol),
alloc.reflow(" doesn't use "), alloc.reflow(" doesn't use "),
@ -76,10 +109,13 @@ pub fn can_problem<'b>(
alloc.symbol_unqualified(argument_symbol), alloc.symbol_unqualified(argument_symbol),
alloc.reflow(line), alloc.reflow(line),
]), ]),
]) ]);
title = UNUSED_ARG.to_string();
severity = Severity::Warning;
} }
Problem::PrecedenceProblem(BothNonAssociative(region, left_bin_op, right_bin_op)) => alloc Problem::PrecedenceProblem(BothNonAssociative(region, left_bin_op, right_bin_op)) => {
.stack(vec![ doc = alloc.stack(vec![
if left_bin_op.value == right_bin_op.value { if left_bin_op.value == right_bin_op.value {
alloc.concat(vec![ alloc.concat(vec![
alloc.reflow("Using more than one "), alloc.reflow("Using more than one "),
@ -102,11 +138,20 @@ pub fn can_problem<'b>(
]) ])
}, },
alloc.region(region), alloc.region(region),
]), ]);
Problem::UnsupportedPattern(BadPattern::UnderscoreInDef, region) => alloc.stack(vec![
alloc.reflow("Underscore patterns are not allowed in definitions"), title = SYNTAX_PROBLEM.to_string();
alloc.region(region), severity = Severity::RuntimeError;
]), }
Problem::UnsupportedPattern(BadPattern::UnderscoreInDef, region) => {
doc = alloc.stack(vec![
alloc.reflow("Underscore patterns are not allowed in definitions"),
alloc.region(region),
]);
title = SYNTAX_PROBLEM.to_string();
severity = Severity::RuntimeError;
}
Problem::UnsupportedPattern(BadPattern::Unsupported(pattern_type), region) => { Problem::UnsupportedPattern(BadPattern::Unsupported(pattern_type), region) => {
use roc_parse::pattern::PatternType::*; use roc_parse::pattern::PatternType::*;
@ -127,84 +172,98 @@ pub fn can_problem<'b>(
alloc.reflow(" instead."), alloc.reflow(" instead."),
]; ];
alloc.stack(vec![ doc = alloc.stack(vec![
alloc alloc
.reflow("This pattern is not allowed in ") .reflow("This pattern is not allowed in ")
.append(alloc.reflow(this_thing)), .append(alloc.reflow(this_thing)),
alloc.region(region), alloc.region(region),
alloc.concat(suggestion), alloc.concat(suggestion),
]) ]);
title = SYNTAX_PROBLEM.to_string();
severity = Severity::RuntimeError;
} }
Problem::ShadowingInAnnotation { Problem::ShadowingInAnnotation {
original_region, original_region,
shadow, shadow,
} => pretty_runtime_error( } => {
alloc, doc = report_shadowing(alloc, original_region, shadow);
RuntimeError::Shadowing {
original_region,
shadow,
},
),
Problem::CyclicAlias(symbol, region, others) => {
let (doc, title) = crate::error::r#type::cyclic_alias(alloc, symbol, region, others);
return Report { title = DUPLICATE_NAME.to_string();
title, severity = Severity::RuntimeError;
filename, }
doc, Problem::CyclicAlias(symbol, region, others) => {
}; let answer = crate::error::r#type::cyclic_alias(alloc, symbol, region, others);
doc = answer.0;
title = answer.1;
severity = Severity::RuntimeError;
} }
Problem::PhantomTypeArgument { Problem::PhantomTypeArgument {
alias, alias,
variable_region, variable_region,
variable_name, variable_name,
} => alloc.stack(vec![ } => {
alloc.concat(vec![ doc = alloc.stack(vec![
alloc.reflow("The "), alloc.concat(vec![
alloc.type_variable(variable_name), alloc.reflow("The "),
alloc.reflow(" type variable is not used in the "), alloc.type_variable(variable_name),
alloc.symbol_unqualified(alias), alloc.reflow(" type parameter is not used in the "),
alloc.reflow(" alias definition:"), alloc.symbol_unqualified(alias),
]), alloc.reflow(" alias definition:"),
alloc.region(variable_region), ]),
alloc.reflow("Roc does not allow unused type parameters!"), alloc.region(variable_region),
// TODO add link to this guide section alloc.reflow("Roc does not allow unused type alias parameters!"),
alloc.tip().append(alloc.reflow( // TODO add link to this guide section
"If you want an unused type parameter (a so-called \"phantom type\"), \ alloc.tip().append(alloc.reflow(
read the guide section on phantom data.", "If you want an unused type parameter (a so-called \"phantom type\"), \
)), read the guide section on phantom values.",
]), )),
Problem::BadRecursion(entries) => to_circular_def_doc(alloc, &entries), ]);
title = UNUSED_ALIAS_PARAM.to_string();
severity = Severity::RuntimeError;
}
Problem::BadRecursion(entries) => {
doc = to_circular_def_doc(alloc, &entries);
title = CIRCULAR_DEF.to_string();
severity = Severity::RuntimeError;
}
Problem::DuplicateRecordFieldValue { Problem::DuplicateRecordFieldValue {
field_name, field_name,
field_region, field_region,
record_region, record_region,
replaced_region, replaced_region,
} => alloc.stack(vec![ } => {
alloc.concat(vec![ doc = alloc.stack(vec![
alloc.reflow("This record defines the "), alloc.concat(vec![
alloc.record_field(field_name.clone()), alloc.reflow("This record defines the "),
alloc.reflow(" field twice!"), alloc.record_field(field_name.clone()),
]), alloc.reflow(" field twice!"),
alloc.region_all_the_things( ]),
record_region, alloc.region_all_the_things(
replaced_region, record_region,
field_region, replaced_region,
Annotation::Error, field_region,
), Annotation::Error,
alloc.reflow(r"In the rest of the program, I will only use the latter definition:"), ),
alloc.region_all_the_things( alloc.reflow(r"In the rest of the program, I will only use the latter definition:"),
record_region, alloc.region_all_the_things(
field_region, record_region,
field_region, field_region,
Annotation::TypoSuggestion, field_region,
), Annotation::TypoSuggestion,
alloc.concat(vec![ ),
alloc.reflow("For clarity, remove the previous "), alloc.concat(vec![
alloc.record_field(field_name), alloc.reflow("For clarity, remove the previous "),
alloc.reflow(" definitions from this record."), alloc.record_field(field_name),
]), alloc.reflow(" definitions from this record."),
]), ]),
]);
title = DUPLICATE_FIELD_NAME.to_string();
severity = Severity::Warning;
}
Problem::InvalidOptionalValue { Problem::InvalidOptionalValue {
field_name, field_name,
field_region, field_region,
@ -223,120 +282,164 @@ pub fn can_problem<'b>(
field_region, field_region,
record_region, record_region,
replaced_region, replaced_region,
} => alloc.stack(vec![ } => {
alloc.concat(vec![ doc = alloc.stack(vec![
alloc.reflow("This record type defines the "), alloc.concat(vec![
alloc.record_field(field_name.clone()), alloc.reflow("This record type defines the "),
alloc.reflow(" field twice!"), alloc.record_field(field_name.clone()),
]), alloc.reflow(" field twice!"),
alloc.region_all_the_things( ]),
record_region, alloc.region_all_the_things(
replaced_region, record_region,
field_region, replaced_region,
Annotation::Error, field_region,
), Annotation::Error,
alloc.reflow("In the rest of the program, I will only use the latter definition:"), ),
alloc.region_all_the_things( alloc.reflow("In the rest of the program, I will only use the latter definition:"),
record_region, alloc.region_all_the_things(
field_region, record_region,
field_region, field_region,
Annotation::TypoSuggestion, field_region,
), Annotation::TypoSuggestion,
alloc.concat(vec![ ),
alloc.reflow("For clarity, remove the previous "), alloc.concat(vec![
alloc.record_field(field_name), alloc.reflow("For clarity, remove the previous "),
alloc.reflow(" definitions from this record type."), alloc.record_field(field_name),
]), alloc.reflow(" definitions from this record type."),
]), ]),
]);
title = DUPLICATE_FIELD_NAME.to_string();
severity = Severity::Warning;
}
Problem::DuplicateTag { Problem::DuplicateTag {
tag_name, tag_name,
tag_union_region, tag_union_region,
tag_region, tag_region,
replaced_region, replaced_region,
} => alloc.stack(vec![ } => {
alloc.concat(vec![ doc = alloc.stack(vec![
alloc.reflow("This tag union type defines the "), alloc.concat(vec![
alloc.tag_name(tag_name.clone()), alloc.reflow("This tag union type defines the "),
alloc.reflow(" tag twice!"), alloc.tag_name(tag_name.clone()),
]), alloc.reflow(" tag twice!"),
alloc.region_all_the_things( ]),
tag_union_region, alloc.region_all_the_things(
replaced_region, tag_union_region,
tag_region, replaced_region,
Annotation::Error, tag_region,
), Annotation::Error,
alloc.reflow("In the rest of the program, I will only use the latter definition:"), ),
alloc.region_all_the_things( alloc.reflow("In the rest of the program, I will only use the latter definition:"),
tag_union_region, alloc.region_all_the_things(
tag_region, tag_union_region,
tag_region, tag_region,
Annotation::TypoSuggestion, tag_region,
), Annotation::TypoSuggestion,
alloc.concat(vec![ ),
alloc.reflow("For clarity, remove the previous "), alloc.concat(vec![
alloc.tag_name(tag_name), alloc.reflow("For clarity, remove the previous "),
alloc.reflow(" definitions from this tag union type."), alloc.tag_name(tag_name),
]), alloc.reflow(" definitions from this tag union type."),
]), ]),
]);
title = DUPLICATE_TAG_NAME.to_string();
severity = Severity::Warning;
}
Problem::SignatureDefMismatch { Problem::SignatureDefMismatch {
ref annotation_pattern, ref annotation_pattern,
ref def_pattern, ref def_pattern,
} => alloc.stack(vec![ } => {
alloc.reflow("This annotation does not match the definition immediately following it:"), doc = alloc.stack(vec![
alloc.region(Region::span_across(annotation_pattern, def_pattern)), alloc.reflow(
alloc.reflow("Is it a typo? If not, put either a newline or comment between them."), "This annotation does not match the definition immediately following it:",
]), ),
Problem::InvalidAliasRigid { alias_name, region } => alloc.stack(vec![ alloc.region(Region::span_across(annotation_pattern, def_pattern)),
alloc.concat(vec![ alloc.reflow("Is it a typo? If not, put either a newline or comment between them."),
alloc.reflow("This pattern in the definition of "), ]);
alloc.symbol_unqualified(alias_name),
alloc.reflow(" is not what I expect:"), title = NAMING_PROBLEM.to_string();
]), severity = Severity::RuntimeError;
alloc.region(region), }
alloc.concat(vec![ Problem::InvalidAliasRigid { alias_name, region } => {
alloc.reflow("Only type variables like "), doc = alloc.stack(vec![
alloc.type_variable("a".into()), alloc.concat(vec![
alloc.reflow(" or "), alloc.reflow("This pattern in the definition of "),
alloc.type_variable("value".into()), alloc.symbol_unqualified(alias_name),
alloc.reflow(" can occur in this position."), alloc.reflow(" is not what I expect:"),
]), ]),
]), alloc.region(region),
Problem::InvalidHexadecimal(region) => alloc.stack(vec![ alloc.concat(vec![
alloc.reflow("This unicode code point is invalid:"), alloc.reflow("Only type variables like "),
alloc.region(region), alloc.type_variable("a".into()),
alloc.concat(vec![ alloc.reflow(" or "),
alloc.reflow(r"I was expecting a hexadecimal number, like "), alloc.type_variable("value".into()),
alloc.parser_suggestion("\\u(1100)"), alloc.reflow(" can occur in this position."),
alloc.reflow(" or "), ]),
alloc.parser_suggestion("\\u(00FF)"), ]);
alloc.text("."),
]), title = SYNTAX_PROBLEM.to_string();
alloc.reflow(r"Learn more about working with unicode in roc at TODO"), severity = Severity::RuntimeError;
]), }
Problem::InvalidUnicodeCodePt(region) => alloc.stack(vec![ Problem::InvalidHexadecimal(region) => {
alloc.reflow("This unicode code point is invalid:"), doc = alloc.stack(vec![
alloc.region(region), alloc.reflow("This unicode code point is invalid:"),
alloc.reflow("Learn more about working with unicode in roc at TODO"), alloc.region(region),
]), alloc.concat(vec![
Problem::InvalidInterpolation(region) => alloc.stack(vec![ alloc.reflow(r"I was expecting a hexadecimal number, like "),
alloc.reflow("This string interpolation is invalid:"), alloc.parser_suggestion("\\u(1100)"),
alloc.region(region), alloc.reflow(" or "),
alloc.concat(vec![ alloc.parser_suggestion("\\u(00FF)"),
alloc.reflow(r"I was expecting an identifier, like "), alloc.text("."),
alloc.parser_suggestion("\\u(message)"), ]),
alloc.reflow(" or "), alloc.reflow(r"Learn more about working with unicode in roc at TODO"),
alloc.parser_suggestion("\\u(LoremIpsum.text)"), ]);
alloc.text("."),
]), title = INVALID_UNICODE.to_string();
alloc.reflow(r"Learn more about string interpolation at TODO"), severity = Severity::RuntimeError;
]), }
Problem::RuntimeError(runtime_error) => pretty_runtime_error(alloc, runtime_error), Problem::InvalidUnicodeCodePt(region) => {
doc = alloc.stack(vec![
alloc.reflow("This unicode code point is invalid:"),
alloc.region(region),
alloc.reflow("Learn more about working with unicode in roc at TODO"),
]);
title = INVALID_UNICODE.to_string();
severity = Severity::RuntimeError;
}
Problem::InvalidInterpolation(region) => {
doc = 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"),
]);
title = SYNTAX_PROBLEM.to_string();
severity = Severity::RuntimeError;
}
Problem::RuntimeError(runtime_error) => {
let answer = pretty_runtime_error(alloc, runtime_error);
doc = answer.0;
title = answer.1.to_string();
severity = Severity::RuntimeError;
}
}; };
Report { Report {
title: "SYNTAX PROBLEM".to_string(), title,
filename, filename,
doc, doc,
severity,
} }
} }
@ -353,6 +456,7 @@ fn to_invalid_optional_value_report<'b>(
title: "BAD OPTIONAL VALUE".to_string(), title: "BAD OPTIONAL VALUE".to_string(),
filename, filename,
doc, doc,
severity: Severity::RuntimeError,
} }
} }
@ -666,44 +770,60 @@ where
chomped chomped
} }
fn report_shadowing<'b>(
alloc: &'b RocDocAllocator<'b>,
original_region: Region,
shadow: Located<Ident>,
) -> RocDocBuilder<'b> {
let line = r#"Since these variables have the same name, it's easy to use the wrong one on accident. Give one of them a new name."#;
alloc.stack(vec![
alloc
.text("The ")
.append(alloc.ident(shadow.value))
.append(alloc.reflow(" name is first defined here:")),
alloc.region(original_region),
alloc.reflow("But then it's defined a second time here:"),
alloc.region(shadow.region),
alloc.reflow(line),
])
}
fn pretty_runtime_error<'b>( fn pretty_runtime_error<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
runtime_error: RuntimeError, runtime_error: RuntimeError,
) -> RocDocBuilder<'b> { ) -> (RocDocBuilder<'b>, &'static str) {
let doc;
let title;
match runtime_error { match runtime_error {
RuntimeError::VoidValue => { RuntimeError::VoidValue => {
// is used to communicate to the compiler that // is used to communicate to the compiler that
// a branch is unreachable; this should never reach a user // a branch is unreachable; this should never reach a user
unreachable!("") unreachable!("");
} }
RuntimeError::UnresolvedTypeVar | RuntimeError::ErroneousType => { RuntimeError::UnresolvedTypeVar | RuntimeError::ErroneousType => {
// only generated during layout generation // only generated during layout generation
unreachable!("") unreachable!("");
} }
RuntimeError::Shadowing { RuntimeError::Shadowing {
original_region, original_region,
shadow, shadow,
} => { } => {
let line = r#"Since these variables have the same name, it's easy to use the wrong one on accident. Give one of them a new name."#; doc = report_shadowing(alloc, original_region, shadow);
title = DUPLICATE_NAME;
alloc.stack(vec![
alloc
.text("The ")
.append(alloc.ident(shadow.value))
.append(alloc.reflow(" name is first defined here:")),
alloc.region(original_region),
alloc.reflow("But then it's defined a second time here:"),
alloc.region(shadow.region),
alloc.reflow(line),
])
} }
RuntimeError::LookupNotInScope(loc_name, options) => { RuntimeError::LookupNotInScope(loc_name, options) => {
not_found(alloc, loc_name.region, &loc_name.value, "value", options) doc = not_found(alloc, loc_name.region, &loc_name.value, "value", options);
title = UNRECOGNIZED_NAME;
}
RuntimeError::CircularDef(entries) => {
doc = to_circular_def_doc(alloc, &entries);
title = CIRCULAR_DEF;
} }
RuntimeError::CircularDef(entries) => to_circular_def_doc(alloc, &entries),
RuntimeError::MalformedPattern(problem, region) => { RuntimeError::MalformedPattern(problem, region) => {
use roc_parse::ast::Base; use roc_parse::ast::Base;
use roc_problem::can::MalformedPatternProblem::*; use roc_problem::can::MalformedPatternProblem::*;
@ -716,7 +836,10 @@ fn pretty_runtime_error<'b>(
MalformedBase(Base::Octal) => " octal integer ", MalformedBase(Base::Octal) => " octal integer ",
MalformedBase(Base::Decimal) => " integer ", MalformedBase(Base::Decimal) => " integer ",
BadIdent(bad_ident) => { BadIdent(bad_ident) => {
return to_bad_ident_pattern_report(alloc, bad_ident, region) title = NAMING_PROBLEM;
doc = to_bad_ident_pattern_report(alloc, bad_ident, region);
return (doc, title);
} }
Unknown => " ", Unknown => " ",
QualifiedIdentifier => " qualified ", QualifiedIdentifier => " qualified ",
@ -732,7 +855,7 @@ fn pretty_runtime_error<'b>(
), ),
}; };
alloc.stack(vec![ doc = alloc.stack(vec![
alloc.concat(vec![ alloc.concat(vec![
alloc.reflow("This"), alloc.reflow("This"),
alloc.text(name), alloc.text(name),
@ -740,7 +863,9 @@ fn pretty_runtime_error<'b>(
]), ]),
alloc.region(region), alloc.region(region),
tip, tip,
]) ]);
title = SYNTAX_PROBLEM;
} }
RuntimeError::UnsupportedPattern(_) => { RuntimeError::UnsupportedPattern(_) => {
todo!("unsupported patterns are currently not parsed!") todo!("unsupported patterns are currently not parsed!")
@ -749,42 +874,58 @@ fn pretty_runtime_error<'b>(
module_name, module_name,
ident, ident,
region, region,
} => alloc.stack(vec![ } => {
alloc.concat(vec![ doc = alloc.stack(vec![
alloc.reflow("The "), alloc.concat(vec![
alloc.module_name(module_name), alloc.reflow("The "),
alloc.reflow(" module does not expose a "), alloc.module_name(module_name),
alloc.string(ident.to_string()), alloc.reflow(" module does not expose a "),
alloc.reflow(" value:"), alloc.string(ident.to_string()),
]), alloc.reflow(" value:"),
alloc.region(region), ]),
]), alloc.region(region),
]);
title = VALUE_NOT_EXPOSED;
}
RuntimeError::ModuleNotImported { RuntimeError::ModuleNotImported {
module_name, module_name,
imported_modules, imported_modules,
region, region,
} => module_not_found(alloc, region, &module_name, imported_modules), } => {
doc = module_not_found(alloc, region, &module_name, imported_modules);
title = MODULE_NOT_IMPORTED;
}
RuntimeError::InvalidPrecedence(_, _) => { RuntimeError::InvalidPrecedence(_, _) => {
// do nothing, reported with PrecedenceProblem // do nothing, reported with PrecedenceProblem
unreachable!() unreachable!();
} }
RuntimeError::MalformedIdentifier(_box_str, bad_ident, surroundings) => { RuntimeError::MalformedIdentifier(_box_str, bad_ident, surroundings) => {
to_bad_ident_expr_report(alloc, bad_ident, surroundings) doc = to_bad_ident_expr_report(alloc, bad_ident, surroundings);
title = SYNTAX_PROBLEM;
}
RuntimeError::MalformedTypeName(_box_str, surroundings) => {
doc = alloc.stack(vec![
alloc.reflow(r"I am confused by this type name:"),
alloc.region(surroundings),
alloc.concat(vec![
alloc.reflow("Type names start with an uppercase letter, "),
alloc.reflow("and can optionally be qualified by a module name, like "),
alloc.parser_suggestion("Bool"),
alloc.reflow(" or "),
alloc.parser_suggestion("Http.Request.Request"),
alloc.reflow("."),
]),
]);
title = SYNTAX_PROBLEM;
}
RuntimeError::MalformedClosure(_) => {
todo!("");
} }
RuntimeError::MalformedTypeName(_box_str, surroundings) => alloc.stack(vec![
alloc.reflow(r"I am confused by this type name:"),
alloc.region(surroundings),
alloc.concat(vec![
alloc.reflow("Type names start with an uppercase letter, "),
alloc.reflow("and can optionally be qualified by a module name, like "),
alloc.parser_suggestion("Bool"),
alloc.reflow(" or "),
alloc.parser_suggestion("Http.Request.Request"),
alloc.reflow("."),
]),
]),
RuntimeError::MalformedClosure(_) => todo!(""),
RuntimeError::InvalidFloat(sign @ FloatErrorKind::PositiveInfinity, region, _raw_str) RuntimeError::InvalidFloat(sign @ FloatErrorKind::PositiveInfinity, region, _raw_str)
| RuntimeError::InvalidFloat(sign @ FloatErrorKind::NegativeInfinity, region, _raw_str) => { | RuntimeError::InvalidFloat(sign @ FloatErrorKind::NegativeInfinity, region, _raw_str) => {
let tip = alloc let tip = alloc
@ -797,7 +938,7 @@ fn pretty_runtime_error<'b>(
"small" "small"
}; };
alloc.stack(vec![ doc = alloc.stack(vec![
alloc.concat(vec![ alloc.concat(vec![
alloc.reflow("This float literal is too "), alloc.reflow("This float literal is too "),
alloc.text(big_or_small), alloc.text(big_or_small),
@ -812,14 +953,16 @@ fn pretty_runtime_error<'b>(
alloc.text(format!("{:e}", f64::MAX)), alloc.text(format!("{:e}", f64::MAX)),
]), ]),
tip, tip,
]) ]);
title = SYNTAX_PROBLEM;
} }
RuntimeError::InvalidFloat(FloatErrorKind::Error, region, _raw_str) => { RuntimeError::InvalidFloat(FloatErrorKind::Error, region, _raw_str) => {
let tip = alloc let tip = alloc
.tip() .tip()
.append(alloc.reflow("Learn more about number literals at TODO")); .append(alloc.reflow("Learn more about number literals at TODO"));
alloc.stack(vec![ doc = alloc.stack(vec![
alloc.concat(vec![ alloc.concat(vec![
alloc.reflow("This float literal contains an invalid digit:"), alloc.reflow("This float literal contains an invalid digit:"),
]), ]),
@ -828,7 +971,9 @@ fn pretty_runtime_error<'b>(
alloc.reflow("Floating point literals can only contain the digits 0-9, or use scientific notation 10e4"), alloc.reflow("Floating point literals can only contain the digits 0-9, or use scientific notation 10e4"),
]), ]),
tip, tip,
]) ]);
title = SYNTAX_PROBLEM;
} }
RuntimeError::InvalidInt(error @ IntErrorKind::InvalidDigit, base, region, _raw_str) RuntimeError::InvalidInt(error @ IntErrorKind::InvalidDigit, base, region, _raw_str)
| RuntimeError::InvalidInt(error @ IntErrorKind::Empty, base, region, _raw_str) => { | RuntimeError::InvalidInt(error @ IntErrorKind::Empty, base, region, _raw_str) => {
@ -871,7 +1016,7 @@ fn pretty_runtime_error<'b>(
.tip() .tip()
.append(alloc.reflow("Learn more about number literals at TODO")); .append(alloc.reflow("Learn more about number literals at TODO"));
alloc.stack(vec![ doc = alloc.stack(vec![
alloc.concat(vec![ alloc.concat(vec![
alloc.reflow("This "), alloc.reflow("This "),
alloc.text(name), alloc.text(name),
@ -887,7 +1032,9 @@ fn pretty_runtime_error<'b>(
alloc.text("."), alloc.text("."),
]), ]),
tip, tip,
]) ]);
title = SYNTAX_PROBLEM;
} }
RuntimeError::InvalidInt(error_kind @ IntErrorKind::Underflow, _base, region, _raw_str) RuntimeError::InvalidInt(error_kind @ IntErrorKind::Underflow, _base, region, _raw_str)
| RuntimeError::InvalidInt(error_kind @ IntErrorKind::Overflow, _base, region, _raw_str) => { | RuntimeError::InvalidInt(error_kind @ IntErrorKind::Overflow, _base, region, _raw_str) => {
@ -901,7 +1048,7 @@ fn pretty_runtime_error<'b>(
.tip() .tip()
.append(alloc.reflow("Learn more about number literals at TODO")); .append(alloc.reflow("Learn more about number literals at TODO"));
alloc.stack(vec![ doc = alloc.stack(vec![
alloc.concat(vec![ alloc.concat(vec![
alloc.reflow("This integer literal is too "), alloc.reflow("This integer literal is too "),
alloc.text(big_or_small), alloc.text(big_or_small),
@ -910,21 +1057,36 @@ fn pretty_runtime_error<'b>(
alloc.region(region), alloc.region(region),
alloc.reflow("Roc uses signed 64-bit integers, allowing values between 9_223_372_036_854_775_808 and 9_223_372_036_854_775_807."), alloc.reflow("Roc uses signed 64-bit integers, allowing values between 9_223_372_036_854_775_808 and 9_223_372_036_854_775_807."),
tip, tip,
]) ]);
title = SYNTAX_PROBLEM;
} }
RuntimeError::InvalidOptionalValue { RuntimeError::InvalidOptionalValue {
field_name, field_name,
field_region, field_region,
record_region, record_region,
} => to_invalid_optional_value_report_help(alloc, field_name, field_region, record_region), } => {
RuntimeError::InvalidRecordUpdate { region } => alloc.stack(vec![ doc = to_invalid_optional_value_report_help(
alloc.concat(vec![ alloc,
alloc.reflow("This expression cannot be updated"), field_name,
alloc.reflow(":"), field_region,
]), record_region,
alloc.region(region), );
alloc.reflow("Only variables can be updated with record update syntax."),
]), title = SYNTAX_PROBLEM;
}
RuntimeError::InvalidRecordUpdate { region } => {
doc = alloc.stack(vec![
alloc.concat(vec![
alloc.reflow("This expression cannot be updated"),
alloc.reflow(":"),
]),
alloc.region(region),
alloc.reflow("Only variables can be updated with record update syntax."),
]);
title = SYNTAX_PROBLEM;
}
RuntimeError::InvalidHexadecimal(region) => { RuntimeError::InvalidHexadecimal(region) => {
todo!( todo!(
"TODO runtime error for an invalid hexadecimal number in a \\u(...) code point at region {:?}", "TODO runtime error for an invalid hexadecimal number in a \\u(...) code point at region {:?}",
@ -949,12 +1111,20 @@ fn pretty_runtime_error<'b>(
RuntimeError::NonExhaustivePattern => { RuntimeError::NonExhaustivePattern => {
unreachable!("not currently reported (but can blow up at runtime)") unreachable!("not currently reported (but can blow up at runtime)")
} }
RuntimeError::ExposedButNotDefined(symbol) => alloc.stack(vec![alloc RuntimeError::ExposedButNotDefined(symbol) => {
.symbol_unqualified(symbol) doc = alloc.stack(vec![alloc
.append(alloc.reflow(" was listed as exposed in ")) .symbol_unqualified(symbol)
.append(alloc.module(symbol.module_id())) .append(alloc.reflow(" was listed as exposed in "))
.append(alloc.reflow(", but it was not defined anywhere in that module."))]), .append(alloc.module(symbol.module_id()))
.append(
alloc.reflow(", but it was not defined anywhere in that module."),
)]);
title = MISSING_DEFINITION;
}
} }
(doc, title)
} }
fn to_circular_def_doc<'b>( fn to_circular_def_doc<'b>(

View file

@ -19,6 +19,15 @@ pub fn type_problem<'b>(
) -> Report<'b> { ) -> Report<'b> {
use solve::TypeError::*; use solve::TypeError::*;
fn report<'b>(title: String, doc: RocDocBuilder<'_>, filename: PathBuf) -> Report<'_> {
Report {
title,
filename,
doc,
severity: Severity::RuntimeError,
}
}
match problem { match problem {
BadExpr(region, category, found, expected) => { BadExpr(region, category, found, expected) => {
to_expr_report(alloc, filename, region, category, found, expected) to_expr_report(alloc, filename, region, category, found, expected)
@ -39,11 +48,7 @@ pub fn type_problem<'b>(
.append(alloc.symbol_unqualified(symbol))]) .append(alloc.symbol_unqualified(symbol))])
.append(alloc.reflow(".")); .append(alloc.reflow("."));
Report { report(title, doc, filename)
title,
filename,
doc,
}
} }
BadType(type_problem) => { BadType(type_problem) => {
use roc_types::types::Problem::*; use roc_types::types::Problem::*;
@ -84,20 +89,12 @@ pub fn type_problem<'b>(
"TOO FEW TYPE ARGUMENTS".to_string() "TOO FEW TYPE ARGUMENTS".to_string()
}; };
Report { report(title, doc, filename)
title,
filename,
doc,
}
} }
CyclicAlias(symbol, region, others) => { CyclicAlias(symbol, region, others) => {
let (doc, title) = cyclic_alias(alloc, symbol, region, others); let (doc, title) = cyclic_alias(alloc, symbol, region, others);
Report { report(title, doc, filename)
title,
filename,
doc,
}
} }
other => panic!("unhandled bad type: {:?}", other), other => panic!("unhandled bad type: {:?}", other),