Warnings are now yellow

This commit is contained in:
John Konecny 2024-06-09 13:22:56 -04:00
parent 0e1a9545c6
commit a189e3ab20
7 changed files with 580 additions and 426 deletions

View file

@ -4771,7 +4771,7 @@ mod test_reporting {
// TODO investigate this test. It was disabled in https://github.com/roc-lang/roc/pull/6634
// as the way Defs without final expressions are handled. The changes probably shouldn't have
// changed this error report. The exact same test_syntax test for this has not changed, so
// we know the parser is parsing thesame thing. Therefore the way the AST is desugared must be
// we know the parser is parsing the same thing. Therefore the way the AST is desugared must be
// the cause of the change in error report.
// test_report!(
// def_missing_final_expression,
@ -8162,7 +8162,7 @@ In roc, functions are always written as a lambda, like{}
"#
),
// TODO(opaques): error could be improved by saying that the opaque definition demands
// that the argument be a U8, and linking to the definitin!
// that the argument be a U8, and linking to the definition!
@r#"
TYPE MISMATCH in /code/proj/Main.roc
@ -13530,7 +13530,7 @@ In roc, functions are always written as a lambda, like{}
4 crash "" ""
^^^^^
`crash` must be given exacly one message to crash with.
`crash` must be given exactly one message to crash with.
"#
);

View file

@ -6303,6 +6303,8 @@ fn to_incorrect_module_name_report<'a>(
expected,
} = problem;
let severity = Severity::RuntimeError;
// SAFETY: if the module was not UTF-8, that would be reported as a parsing problem, rather
// than an incorrect module name problem (the latter can happen only after parsing).
let src = unsafe { from_utf8_unchecked(src) };
@ -6317,7 +6319,7 @@ fn to_incorrect_module_name_report<'a>(
let doc = alloc.stack([
alloc.reflow("This module has a different name than I expected:"),
alloc.region(lines.convert_region(found.region)),
alloc.region(lines.convert_region(found.region), severity),
alloc.reflow("Based on the nesting and use of this module, I expect it to have name"),
alloc.pq_module_name(expected).indent(4),
]);
@ -6326,7 +6328,7 @@ fn to_incorrect_module_name_report<'a>(
filename,
doc,
title: "INCORRECT MODULE NAME".to_string(),
severity: Severity::RuntimeError,
severity,
};
let mut buf = String::new();
@ -6346,6 +6348,7 @@ fn to_no_platform_package_report(
) -> String {
use roc_reporting::report::{Report, RocDocAllocator, DEFAULT_PALETTE};
use ven_pretty::DocAllocator;
let severity = Severity::RuntimeError;
// SAFETY: if the module was not UTF-8, that would be reported as a parsing problem, rather
// than an incorrect module name problem (the latter can happen only after parsing).
@ -6361,7 +6364,7 @@ fn to_no_platform_package_report(
let doc = alloc.stack([
alloc.reflow("This app does not specify a platform:"),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region),severity),
alloc.reflow("Make sure you have exactly one package specified as `platform`:"),
alloc
.parser_suggestion(" app [main] {\n pf: platform \"…path or URL to platform…\"\n ^^^^^^^^\n }"),
@ -6373,7 +6376,7 @@ fn to_no_platform_package_report(
filename,
doc,
title: "UNSPECIFIED PLATFORM".to_string(),
severity: Severity::RuntimeError,
severity,
};
let mut buf = String::new();
@ -6393,6 +6396,7 @@ fn to_multiple_platform_packages_report(
) -> String {
use roc_reporting::report::{Report, RocDocAllocator, DEFAULT_PALETTE};
use ven_pretty::DocAllocator;
let severity = Severity::RuntimeError;
// SAFETY: if the module was not UTF-8, that would be reported as a parsing problem, rather
// than an incorrect module name problem (the latter can happen only after parsing).
@ -6408,7 +6412,7 @@ fn to_multiple_platform_packages_report(
let doc = alloc.stack([
alloc.reflow("This app specifies multiple packages as `platform`:"),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow("Roc apps must specify exactly one platform."),
]);
@ -6416,7 +6420,7 @@ fn to_multiple_platform_packages_report(
filename,
doc,
title: "MULTIPLE PLATFORMS".to_string(),
severity: Severity::RuntimeError,
severity,
};
let mut buf = String::new();

View file

@ -84,7 +84,7 @@ pub fn can_problem<'b>(
alloc
.symbol_unqualified(symbol)
.append(alloc.reflow(" is not used anywhere in your code.")),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc
.reflow("If you didn't intend on using ")
.append(alloc.symbol_unqualified(symbol))
@ -99,7 +99,7 @@ pub fn can_problem<'b>(
alloc.symbol_qualified(symbol),
alloc.reflow(" is not used in this module."),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat([
alloc.reflow("Since "),
alloc.symbol_qualified(symbol),
@ -115,7 +115,7 @@ pub fn can_problem<'b>(
alloc.module(module_id),
alloc.reflow(" is imported but not used."),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat([
alloc.reflow("Since "),
alloc.module(module_id),
@ -145,17 +145,17 @@ pub fn can_problem<'b>(
alloc.reflow(" was imported here: ")
},
]),
alloc.region(lines.convert_region(new_import_region)),
alloc.region(lines.convert_region(new_import_region), severity),
match existing_import {
ScopeModuleSource::Import(exsting_import_region) => {
ScopeModuleSource::Import(existing_import_region) => {
alloc.stack([
alloc.concat([
alloc.reflow("but "),
alloc.module_name(name.clone()),
alloc.reflow(" is already used by a previous import:"),
]),
alloc.region(lines.convert_region(exsting_import_region)),
alloc.region(lines.convert_region(existing_import_region), severity),
])
}
ScopeModuleSource::Builtin => {
@ -193,7 +193,7 @@ pub fn can_problem<'b>(
alloc.module(module_id),
alloc.reflow(" was imported here:"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow("Builtins are imported automatically, so you can remove this import."),
alloc.reflow("Tip: Learn more about builtins in the tutorial:\n\n<https://www.roc-lang.org/tutorial#builtin-modules>"),
]);
@ -207,7 +207,7 @@ pub fn can_problem<'b>(
alloc.symbol_qualified(symbol),
alloc.reflow(" was imported here:"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat([
alloc.reflow("All types from builtins are automatically exposed, so you can remove "),
alloc.symbol_unqualified(symbol),
@ -230,13 +230,13 @@ pub fn can_problem<'b>(
alloc.symbol_qualified(new_symbol),
alloc.reflow(":"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat([
alloc.reflow("However, the name "),
alloc.symbol_unqualified(new_symbol),
alloc.reflow(" was already used here:"),
]),
alloc.region(lines.convert_region(existing_symbol_region)),
alloc.region(lines.convert_region(existing_symbol_region), severity),
alloc.concat([
alloc.reflow("You can rename it, or use the qualified name: "),
alloc.symbol_qualified(new_symbol),
@ -249,7 +249,7 @@ pub fn can_problem<'b>(
Problem::DefsOnlyUsedInRecursion(1, region) => {
doc = alloc.stack([
alloc.reflow("This definition is only used in recursion with itself:"),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow(
"If you don't intend to use or export this definition, it should be removed!",
),
@ -264,7 +264,7 @@ pub fn can_problem<'b>(
alloc.string(n.to_string()),
alloc.reflow(" definitions are only used in mutual recursion with themselves:"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow(
"If you don't intend to use or export any of them, they should all be removed!",
),
@ -293,7 +293,7 @@ pub fn can_problem<'b>(
.reflow("I don't know how to generate the ")
.append(alloc.ident(loc_ident.value))
.append(alloc.reflow(" function.")),
alloc.region(lines.convert_region(loc_ident.region)),
alloc.region(lines.convert_region(loc_ident.region), severity),
alloc
.reflow("Only specific functions like `after` and `map` can be generated.")
.append(alloc.reflow("Learn more about hosted modules at TODO.")),
@ -315,7 +315,7 @@ pub fn can_problem<'b>(
alloc.symbol_unqualified(argument_symbol),
alloc.text("."),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat([
alloc.reflow("If you don't need "),
alloc.symbol_unqualified(argument_symbol),
@ -343,7 +343,7 @@ pub fn can_problem<'b>(
alloc.keyword("when"),
alloc.reflow(" branch."),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat([
alloc.reflow("If you don't need to use "),
alloc.symbol_unqualified(symbol),
@ -378,7 +378,7 @@ pub fn can_problem<'b>(
)),
])
},
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
]);
title = SYNTAX_PROBLEM.to_string();
@ -407,7 +407,7 @@ pub fn can_problem<'b>(
alloc
.reflow("This pattern is not allowed in ")
.append(alloc.reflow(this_thing)),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat(suggestion),
]);
@ -419,14 +419,14 @@ pub fn can_problem<'b>(
kind,
} => {
let (res_title, res_doc) =
report_shadowing(alloc, lines, original_region, shadow, kind);
report_shadowing(alloc, lines, original_region, shadow, kind, severity);
doc = res_doc;
title = res_title.to_string();
}
Problem::CyclicAlias(symbol, region, others, alias_kind) => {
let answer = crate::error::r#type::cyclic_alias(
alloc, lines, symbol, region, others, alias_kind,
alloc, lines, symbol, region, others, alias_kind, severity,
);
doc = answer.0;
@ -448,7 +448,7 @@ pub fn can_problem<'b>(
alloc.reflow(alias_kind.as_str()),
alloc.reflow(" definition:"),
]),
alloc.region(lines.convert_region(variable_region)),
alloc.region(lines.convert_region(variable_region), severity),
alloc.reflow("Roc does not allow unused type parameters!"),
// TODO add link to this guide section
alloc.tip().append(alloc.reflow(
@ -485,7 +485,7 @@ pub fn can_problem<'b>(
alloc.reflow(") type variables. Here is one of them:"),
]));
}
stack.push(alloc.region(lines.convert_region(one_occurrence)));
stack.push(alloc.region(lines.convert_region(one_occurrence), severity));
stack.push(alloc.concat([
alloc.reflow(match kind {
AliasKind::Structural => "Type alias",
@ -526,7 +526,7 @@ pub fn can_problem<'b>(
]));
stack.push(alloc.reflow("Here is one of them:"));
}
stack.push(alloc.region(lines.convert_region(one_occurrence)));
stack.push(alloc.region(lines.convert_region(one_occurrence), severity));
stack.push(alloc.concat([
alloc.reflow(match kind {
AliasKind::Structural => "Type alias",
@ -568,7 +568,7 @@ pub fn can_problem<'b>(
]));
stack.push(alloc.reflow("Here is one of them:"));
}
stack.push(alloc.region(lines.convert_region(one_occurrence)));
stack.push(alloc.region(lines.convert_region(one_occurrence), severity));
stack.push(alloc.concat([
alloc.reflow("All type variables in "),
alloc.reflow(match kind {
@ -587,7 +587,7 @@ pub fn can_problem<'b>(
title = UNDECLARED_TYPE_VARIABLE.to_string();
}
Problem::BadRecursion(entries) => {
doc = to_circular_def_doc(alloc, lines, &entries);
doc = to_circular_def_doc(alloc, lines, &entries, severity);
title = CIRCULAR_DEF.to_string();
}
Problem::DuplicateRecordFieldValue {
@ -716,6 +716,7 @@ pub fn can_problem<'b>(
),
alloc.region(
lines.convert_region(Region::span_across(annotation_pattern, def_pattern)),
severity,
),
alloc.reflow("Is it a typo? If not, put either a newline or comment between them."),
]);
@ -732,7 +733,7 @@ pub fn can_problem<'b>(
alloc.symbol_unqualified(type_name),
alloc.reflow(" has an unexpected pattern:"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat([
alloc.reflow("Only type variables like "),
alloc.type_variable("a".into()),
@ -747,7 +748,7 @@ pub fn can_problem<'b>(
Problem::InvalidHexadecimal(region) => {
doc = alloc.stack([
alloc.reflow("This unicode code point is invalid:"),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat([
alloc.reflow(r"I was expecting a hexadecimal number, like "),
alloc.parser_suggestion("\\u(1100)"),
@ -763,7 +764,7 @@ pub fn can_problem<'b>(
Problem::InvalidUnicodeCodePt(region) => {
doc = alloc.stack([
alloc.reflow("This unicode code point is invalid:"),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow("Learn more about working with unicode in roc at TODO"),
]);
@ -772,7 +773,7 @@ pub fn can_problem<'b>(
Problem::InvalidInterpolation(region) => {
doc = alloc.stack([
alloc.reflow("This string interpolation is invalid:"),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow(r"String interpolations cannot contain newlines or other interpolations."),
alloc.reflow(r"You can learn more about string interpolation at <https://www.roc-lang.org/tutorial#string-interpolation>"),
]);
@ -795,13 +796,13 @@ pub fn can_problem<'b>(
alloc.symbol_unqualified(alias),
alloc.reflow(" is a nested datatype. Here is one recursive usage of it:"),
]),
alloc.region(lines.convert_region(differing_recursion_region)),
alloc.region(lines.convert_region(differing_recursion_region), severity),
alloc.concat([
alloc.reflow("But recursive usages of "),
alloc.symbol_unqualified(alias),
alloc.reflow(" must match its definition:"),
]),
alloc.region(lines.convert_region(def_region)),
alloc.region(lines.convert_region(def_region), severity),
alloc.reflow("Nested datatypes are not supported in Roc."),
alloc.concat([
alloc.hint("Consider rewriting the definition of "),
@ -827,7 +828,7 @@ pub fn can_problem<'b>(
alloc.text(kind_str),
alloc.reflow(" extension type is invalid:"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat([
alloc.note("A "),
alloc.reflow(kind_str),
@ -850,7 +851,7 @@ pub fn can_problem<'b>(
alloc.symbol_unqualified(name),
alloc.reflow(" ability includes type variables:"),
]),
alloc.region(lines.convert_region(variables_region)),
alloc.region(lines.convert_region(variables_region), severity),
alloc.reflow(
"Abilities cannot depend on type variables, but their member values can!",
),
@ -865,7 +866,7 @@ pub fn can_problem<'b>(
alloc.reflow(
r#"The type referenced in this "implements" clause is not an ability:"#,
),
alloc.region(lines.convert_region(clause_region)),
alloc.region(lines.convert_region(clause_region), severity),
]);
title = IMPLEMENTS_CLAUSE_IS_NOT_AN_ABILITY.to_string();
}
@ -877,7 +878,7 @@ pub fn can_problem<'b>(
alloc.keyword(roc_parse::keyword::IMPLEMENTS),
alloc.reflow(" clause is not allowed here:"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat([
alloc.keyword(roc_parse::keyword::IMPLEMENTS),
alloc.reflow(
@ -895,7 +896,7 @@ pub fn can_problem<'b>(
alloc.symbol_foreign_qualified(ability),
alloc.reflow(" ability once before:"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat([
alloc.reflow("Abilities only need to bound to a type variable once in an "),
alloc.keyword(roc_parse::keyword::IMPLEMENTS),
@ -920,7 +921,7 @@ pub fn can_problem<'b>(
alloc.symbol_unqualified(ability),
alloc.reflow(":"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat([
alloc.reflow("Ability members must include an "),
alloc.keyword(roc_parse::keyword::IMPLEMENTS),
@ -953,7 +954,7 @@ pub fn can_problem<'b>(
alloc.symbol_unqualified(ability),
alloc.keyword(" ability:"),
]),
alloc.region(lines.convert_region(span_has_clauses)),
alloc.region(lines.convert_region(span_has_clauses), severity),
alloc.reflow("Ability members can only bind one type variable to their parent ability. Otherwise, I wouldn't know what type implements an ability by looking at specializations!"),
alloc.concat([
alloc.hint("Did you mean to only bind "),
@ -971,7 +972,7 @@ pub fn can_problem<'b>(
alloc
.concat([alloc
.reflow("This ability definition is not on the top-level of a module:")]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow("Abilities can only be defined on the top-level of a Roc module."),
]);
title = ABILITY_NOT_ON_TOPLEVEL.to_string();
@ -984,7 +985,7 @@ pub fn can_problem<'b>(
alloc.symbol_unqualified(ability),
alloc.reflow(" as a type directly:"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow(
"Abilities can only be used in type annotations to constrain type variables.",
),
@ -1010,7 +1011,7 @@ pub fn can_problem<'b>(
alloc.symbol_unqualified(member),
alloc.reflow(" ability member is in a nested scope:"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow("Specializations can only be defined on the top-level of a module."),
]);
title = SPECIALIZATION_NOT_ON_TOPLEVEL.to_string();
@ -1018,7 +1019,7 @@ pub fn can_problem<'b>(
Problem::IllegalDerivedAbility(region) => {
doc = alloc.stack([
alloc.reflow("This ability cannot be derived:"),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow("Only builtin abilities can be derived."),
alloc
.note("The builtin abilities are ")
@ -1029,7 +1030,7 @@ pub fn can_problem<'b>(
Problem::NotAnAbility(region) => {
doc = alloc.stack([
alloc.reflow("This identifier is not an ability in scope:"),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow("Only abilities can be implemented."),
]);
title = NOT_AN_ABILITY.to_string();
@ -1043,7 +1044,7 @@ pub fn can_problem<'b>(
alloc.concat([
alloc.reflow("The "), alloc.symbol_unqualified(ability), alloc.reflow(" ability does not have a member "), alloc.string(name),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow("Only implementations for members an ability has can be specified in this location.")
]);
title = NOT_AN_ABILITY_MEMBER.to_string();
@ -1054,7 +1055,7 @@ pub fn can_problem<'b>(
alloc.concat([
alloc.reflow("An implementation of "), alloc.symbol_unqualified(member), alloc.reflow(" could not be found in this scope:"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.tip().append(alloc.concat([alloc.reflow("consider adding a value of name "), alloc.symbol_unqualified(member), alloc.reflow(" in this scope, or using another variable that implements this ability member, like "), alloc.type_str(&format!("{{ {member_str}: my{member_str} }}"))]))
]);
title = IMPLEMENTATION_NOT_FOUND.to_string();
@ -1071,7 +1072,7 @@ pub fn can_problem<'b>(
doc = alloc.stack([
alloc.reflow("Ability implementations cannot be optional:"),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow("Custom implementations must be supplied fully."),
hint,
]);
@ -1080,7 +1081,7 @@ pub fn can_problem<'b>(
Problem::QualifiedAbilityImpl { region } => {
doc = alloc.stack([
alloc.reflow("This ability implementation is qualified:"),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow(
"Custom implementations must be defined in the local scope, and unqualified.",
),
@ -1090,7 +1091,7 @@ pub fn can_problem<'b>(
Problem::AbilityImplNotIdent { region } => {
doc = alloc.stack([
alloc.reflow("This ability implementation is not an identifier:"),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow(
"Custom ability implementations defined in this position can only be unqualified identifiers, not arbitrary expressions.",
),
@ -1104,9 +1105,9 @@ pub fn can_problem<'b>(
} => {
doc = alloc.stack([
alloc.reflow("This ability member implementation is duplicate:"),
alloc.region(lines.convert_region(duplicate)),
alloc.region(lines.convert_region(duplicate), severity),
alloc.reflow("The first implementation was defined here:"),
alloc.region(lines.convert_region(original)),
alloc.region(lines.convert_region(original), severity),
alloc
.reflow("Only one custom implementation can be defined for an ability member."),
]);
@ -1123,7 +1124,7 @@ pub fn can_problem<'b>(
alloc.symbol_unqualified(ability),
alloc.reflow(" ability:"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow("The following implemented members should not be listed:"),
alloc.type_block(
alloc.intersperse(
@ -1147,7 +1148,7 @@ pub fn can_problem<'b>(
alloc.symbol_unqualified(ability),
alloc.reflow(" ability:"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow("The following necessary members are missing implementations:"),
alloc.type_block(
alloc.intersperse(
@ -1171,7 +1172,7 @@ pub fn can_problem<'b>(
alloc.keyword("when"),
alloc.reflow(" branch"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat([
alloc.reflow("Identifiers introduced in a "),
alloc.keyword("when"),
@ -1183,7 +1184,7 @@ pub fn can_problem<'b>(
Problem::NoIdentifiersIntroduced(region) => {
doc = alloc.stack([
alloc.reflow("This destructure assignment doesn't introduce any new variables:"),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow("If you don't need to use the value on the right-hand-side of this assignment, consider removing the assignment. Since Roc is purely functional, assignments that don't introduce variables cannot affect a program's behavior!"),
]);
title = "UNNECESSARY DEFINITION".to_string();
@ -1195,7 +1196,7 @@ pub fn can_problem<'b>(
} => {
doc = alloc.stack([
alloc.reflow("This ability member specialization is already claimed to specialize another opaque type:"),
alloc.region(lines.convert_region(overload)),
alloc.region(lines.convert_region(overload), severity),
alloc.concat([
alloc.reflow("Previously, we found it to specialize "),
alloc.symbol_unqualified(ability_member),
@ -1214,7 +1215,7 @@ pub fn can_problem<'b>(
alloc.keyword("*"),
alloc.reflow(") that isn't needed."),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat([
alloc.reflow("Annotations for tag unions which are constants, or which are returned from functions, work the same way with or without a "),
alloc.keyword("*"),
@ -1229,7 +1230,7 @@ pub fn can_problem<'b>(
Problem::MultipleListRestPattern { region } => {
doc = alloc.stack([
alloc.reflow("This list pattern match has multiple rest patterns:"),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat([
alloc.reflow("I only support compiling list patterns with one "),
alloc.parser_suggestion(".."),
@ -1267,7 +1268,7 @@ pub fn can_problem<'b>(
found_arguments,
alloc.reflow(" instead:"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow("Are there missing parentheses?"),
]);
@ -1282,7 +1283,7 @@ pub fn can_problem<'b>(
alloc.concat([
alloc.reflow("This "), alloc.keyword("crash"), alloc.reflow(" doesn't have a message given to it:")
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat([
alloc.keyword("crash"), alloc.reflow(" must be passed a message to crash with at the exact place it's used. "),
alloc.keyword("crash"), alloc.reflow(" can't be used as a value that's passed around, like functions can be - it must be applied immediately!"),
@ -1297,10 +1298,10 @@ pub fn can_problem<'b>(
alloc.keyword("crash"),
alloc.reflow(" has too many values given to it:"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat([
alloc.keyword("crash"),
alloc.reflow(" must be given exacly one message to crash with."),
alloc.reflow(" must be given exactly one message to crash with."),
]),
]);
title = "OVERAPPLIED CRASH".to_string();
@ -1384,6 +1385,7 @@ fn to_bad_ident_expr_report<'b>(
lines: &LineInfo,
bad_ident: roc_parse::ident::BadIdent,
surroundings: Region,
severity: Severity,
) -> RocDocBuilder<'b> {
use roc_parse::ident::BadIdent::*;
@ -1394,7 +1396,7 @@ fn to_bad_ident_expr_report<'b>(
alloc.stack([
alloc.reflow(r"I am trying to parse a record field access here:"),
alloc.region_with_subregion(lines.convert_region(surroundings), region),
alloc.region_with_subregion(lines.convert_region(surroundings), region, severity),
alloc.concat([
alloc.reflow("So I expect to see a lowercase letter next, like "),
alloc.parser_suggestion(".name"),
@ -1407,7 +1409,7 @@ fn to_bad_ident_expr_report<'b>(
WeirdAccessor(_pos) => alloc.stack([
alloc.reflow("I am very confused by this field access"),
alloc.region(lines.convert_region(surroundings)),
alloc.region(lines.convert_region(surroundings), severity),
alloc.concat([
alloc.reflow("It looks like a field access on an accessor. I parse"),
alloc.parser_suggestion(".client.name"),
@ -1425,7 +1427,7 @@ fn to_bad_ident_expr_report<'b>(
alloc.stack([
alloc.reflow("I am trying to parse a qualified name here:"),
alloc.region_with_subregion(lines.convert_region(surroundings), region),
alloc.region_with_subregion(lines.convert_region(surroundings), region, severity),
alloc.concat([
alloc.reflow("I was expecting to see an identifier next, like "),
alloc.parser_suggestion("height"),
@ -1440,7 +1442,7 @@ fn to_bad_ident_expr_report<'b>(
alloc.stack([
alloc.reflow("I am trying to parse a qualified name here:"),
alloc.region_with_subregion(lines.convert_region(surroundings), region),
alloc.region_with_subregion(lines.convert_region(surroundings), region, severity),
alloc.concat([
alloc.reflow("This looks like a tuple accessor on a module or tag name,"),
alloc.reflow(r"but neither modules nor tags can have tuple elements! "),
@ -1455,7 +1457,7 @@ fn to_bad_ident_expr_report<'b>(
alloc.stack([
alloc.reflow("I am trying to parse a qualified name here:"),
alloc.region_with_subregion(lines.convert_region(surroundings), region),
alloc.region_with_subregion(lines.convert_region(surroundings), region, severity),
alloc.concat([
alloc.reflow(r"This looks like a qualified tag name to me, "),
alloc.reflow(r"but tags cannot be qualified! "),
@ -1469,7 +1471,7 @@ fn to_bad_ident_expr_report<'b>(
UnderscoreAlone(_pos) => {
alloc.stack([
alloc.reflow("An underscore is being used as a variable here:"),
alloc.region(lines.convert_region(surroundings)),
alloc.region(lines.convert_region(surroundings), severity),
alloc.concat([alloc
.reflow(r"An underscore can be used to ignore a value when pattern matching, but it cannot be used as a variable.")]),
])
@ -1478,7 +1480,7 @@ fn to_bad_ident_expr_report<'b>(
UnderscoreInMiddle(_pos) => {
alloc.stack([
alloc.reflow("Underscores are not allowed in identifier names:"),
alloc.region(lines.convert_region(surroundings)),
alloc.region(lines.convert_region(surroundings), severity),
alloc.concat([alloc
.reflow(r"I recommend using camelCase. It's the standard style in Roc code!")]),
])
@ -1494,11 +1496,11 @@ fn to_bad_ident_expr_report<'b>(
None => alloc.reflow(line),
Some(declaration_region) => alloc.stack([
alloc.reflow(line),
alloc.region(lines.convert_region(declaration_region)),
alloc.region(lines.convert_region(declaration_region), severity),
alloc.reflow("But then it is used here:"),
])
},
alloc.region(lines.convert_region(surroundings)),
alloc.region(lines.convert_region(surroundings), severity),
alloc.concat([
alloc.reflow(r"A variable's name can only start with an underscore if the variable is unused. "),
match declaration_region {
@ -1521,6 +1523,7 @@ fn to_bad_ident_expr_report<'b>(
alloc.region_with_subregion(
lines.convert_region(surroundings),
lines.convert_region(region),
severity
),
alloc.concat([
alloc.reflow(r"It looks like a record field access on "),
@ -1536,6 +1539,7 @@ fn to_bad_ident_expr_report<'b>(
alloc.region_with_subregion(
lines.convert_region(surroundings),
lines.convert_region(region),
severity
),
alloc.concat([
alloc.reflow(r"Looks like "),
@ -1559,6 +1563,7 @@ fn to_bad_ident_expr_report<'b>(
alloc.region_with_subregion(
lines.convert_region(surroundings),
lines.convert_region(region),
severity,
),
alloc.concat([
alloc.reflow(r"But after the "),
@ -1584,6 +1589,7 @@ fn to_bad_ident_pattern_report<'b>(
lines: &LineInfo,
bad_ident: roc_parse::ident::BadIdent,
surroundings: Region,
severity: Severity,
) -> RocDocBuilder<'b> {
use roc_parse::ident::BadIdent::*;
@ -1594,7 +1600,7 @@ fn to_bad_ident_pattern_report<'b>(
alloc.stack([
alloc.reflow(r"I am trying to parse a record field accessor here:"),
alloc.region_with_subregion(lines.convert_region(surroundings), region),
alloc.region_with_subregion(lines.convert_region(surroundings), region, severity),
alloc.concat([
alloc.reflow("Something like "),
alloc.parser_suggestion(".name"),
@ -1607,7 +1613,7 @@ fn to_bad_ident_pattern_report<'b>(
WeirdAccessor(_pos) => alloc.stack([
alloc.reflow("I am very confused by this field access"),
alloc.region(lines.convert_region(surroundings)),
alloc.region(lines.convert_region(surroundings), severity),
alloc.concat([
alloc.reflow("It looks like a field access on an accessor. I parse"),
alloc.parser_suggestion(".client.name"),
@ -1625,7 +1631,7 @@ fn to_bad_ident_pattern_report<'b>(
alloc.stack([
alloc.reflow("I am trying to parse a qualified name here:"),
alloc.region_with_subregion(lines.convert_region(surroundings), region),
alloc.region_with_subregion(lines.convert_region(surroundings), region, severity),
alloc.concat([
alloc.reflow("I was expecting to see an identifier next, like "),
alloc.parser_suggestion("height"),
@ -1640,7 +1646,7 @@ fn to_bad_ident_pattern_report<'b>(
alloc.stack([
alloc.reflow("I am trying to parse a qualified name here:"),
alloc.region_with_subregion(lines.convert_region(surroundings), region),
alloc.region_with_subregion(lines.convert_region(surroundings), region, severity),
alloc.concat([
alloc.reflow(r"This looks like a qualified tag name to me, "),
alloc.reflow(r"but tags cannot be qualified! "),
@ -1665,6 +1671,7 @@ fn to_bad_ident_pattern_report<'b>(
alloc.region_with_subregion(
lines.convert_region(surroundings),
lines.convert_region(region),
severity,
),
alloc.concat([alloc.reflow(
r"Underscores are not allowed in identifiers. Use camelCase instead!",
@ -1677,7 +1684,7 @@ fn to_bad_ident_pattern_report<'b>(
alloc.stack([
alloc.reflow("This opaque type reference has an invalid name:"),
alloc.region_with_subregion(lines.convert_region(surroundings), region),
alloc.region_with_subregion(lines.convert_region(surroundings), region, severity),
alloc.concat([
alloc.reflow(r"Opaque type names must begin with a capital letter, "),
alloc.reflow(r"and must contain only letters and numbers."),
@ -1756,6 +1763,7 @@ fn report_shadowing<'b>(
original_region: Region,
shadow: Loc<Ident>,
kind: ShadowKind,
severity: Severity,
) -> (&'static str, RocDocBuilder<'b>) {
let (what, what_plural, is_builtin) = match kind {
ShadowKind::Variable => ("variable", "variables", false),
@ -1771,7 +1779,7 @@ fn report_shadowing<'b>(
alloc.reflow(what),
alloc.reflow(" has the same name as a builtin:"),
]),
alloc.region(lines.convert_region(shadow.region)),
alloc.region(lines.convert_region(shadow.region), severity),
alloc.concat([
alloc.reflow("All builtin "),
alloc.reflow(what_plural),
@ -1786,9 +1794,9 @@ fn report_shadowing<'b>(
.text("The ")
.append(alloc.ident(shadow.value))
.append(alloc.reflow(" name is first defined here:")),
alloc.region(lines.convert_region(original_region)),
alloc.region(lines.convert_region(original_region), severity),
alloc.reflow("But then it's defined a second time here:"),
alloc.region(lines.convert_region(shadow.region)),
alloc.region(lines.convert_region(shadow.region), severity),
alloc.concat([
alloc.reflow("Since these "),
alloc.reflow(what_plural),
@ -1808,6 +1816,8 @@ fn pretty_runtime_error<'b>(
let doc;
let title;
let severity = Severity::RuntimeError;
match runtime_error {
RuntimeError::VoidValue => {
// is used to communicate to the compiler that
@ -1825,7 +1835,7 @@ fn pretty_runtime_error<'b>(
shadow,
kind,
} => {
(title, doc) = report_shadowing(alloc, lines, original_region, shadow, kind);
(title, doc) = report_shadowing(alloc, lines, original_region, shadow, kind, severity);
}
RuntimeError::LookupNotInScope {
@ -1840,11 +1850,12 @@ fn pretty_runtime_error<'b>(
&loc_name.value,
options,
underscored_suggestion_region,
severity,
);
title = UNRECOGNIZED_NAME;
}
RuntimeError::CircularDef(entries) => {
doc = to_circular_def_doc(alloc, lines, &entries);
doc = to_circular_def_doc(alloc, lines, &entries, severity);
title = CIRCULAR_DEF;
}
RuntimeError::MalformedPattern(problem, region) => {
@ -1860,7 +1871,7 @@ fn pretty_runtime_error<'b>(
MalformedBase(Base::Decimal) => " integer ",
BadIdent(bad_ident) => {
title = NAMING_PROBLEM;
doc = to_bad_ident_pattern_report(alloc, lines, bad_ident, region);
doc = to_bad_ident_pattern_report(alloc, lines, bad_ident, region, severity);
return (doc, title);
}
@ -1892,7 +1903,7 @@ fn pretty_runtime_error<'b>(
alloc.text(name),
alloc.reflow("pattern is malformed:"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
tip,
]);
@ -1933,7 +1944,7 @@ fn pretty_runtime_error<'b>(
alloc.string(ident.to_string()),
alloc.reflow("`:"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
did_you_mean,
]);
@ -1953,6 +1964,7 @@ fn pretty_runtime_error<'b>(
&module_name,
imported_modules,
module_exists,
severity,
);
title = MODULE_NOT_IMPORTED;
@ -1972,14 +1984,14 @@ fn pretty_runtime_error<'b>(
unreachable!();
}
RuntimeError::MalformedIdentifier(_box_str, bad_ident, surroundings) => {
doc = to_bad_ident_expr_report(alloc, lines, bad_ident, surroundings);
doc = to_bad_ident_expr_report(alloc, lines, bad_ident, surroundings, severity);
title = SYNTAX_PROBLEM;
}
RuntimeError::MalformedTypeName(_box_str, surroundings) => {
doc = alloc.stack([
alloc.reflow(r"I am confused by this type name:"),
alloc.region(lines.convert_region(surroundings)),
alloc.region(lines.convert_region(surroundings), severity),
alloc.concat([
alloc.reflow("Type names start with an uppercase letter, "),
alloc.reflow("and can optionally be qualified by a module name, like "),
@ -2016,7 +2028,7 @@ fn pretty_runtime_error<'b>(
alloc.text(big_or_small),
alloc.reflow(":"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat([
alloc
.reflow("Roc uses signed 64-bit floating points, allowing values between "),
@ -2038,7 +2050,7 @@ fn pretty_runtime_error<'b>(
alloc.concat([
alloc.reflow("This float literal contains an invalid digit:"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat([
alloc.reflow("Floating point literals can only contain the digits 0-9, or use scientific notation 10e4, or have a float suffix."),
]),
@ -2052,7 +2064,7 @@ fn pretty_runtime_error<'b>(
alloc
.concat([alloc
.reflow("This number literal is a float, but it has an integer suffix:")]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
]);
title = CONFLICTING_NUMBER_SUFFIX;
@ -2106,7 +2118,7 @@ fn pretty_runtime_error<'b>(
alloc.text(problem),
alloc.text(":"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat([
alloc.text(plurals),
contains,
@ -2154,7 +2166,7 @@ fn pretty_runtime_error<'b>(
alloc.text(big_or_small),
alloc.reflow(":"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
info,
tip,
]);
@ -2166,7 +2178,7 @@ fn pretty_runtime_error<'b>(
alloc
.concat([alloc
.reflow("This number literal is an integer, but it has a float suffix:")]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
]);
title = CONFLICTING_NUMBER_SUFFIX;
@ -2183,7 +2195,7 @@ fn pretty_runtime_error<'b>(
doc = alloc.stack([
alloc.concat([alloc
.reflow("This integer literal overflows the type indicated by its suffix:")]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.tip().append(alloc.concat([
alloc.reflow("The suffix indicates this integer is a "),
alloc.type_str(suffix_type),
@ -2207,7 +2219,7 @@ fn pretty_runtime_error<'b>(
doc = alloc.stack([
alloc.concat([alloc
.reflow("This integer literal underflows the type indicated by its suffix:")]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.tip().append(alloc.concat([
alloc.reflow("The suffix indicates this integer is a "),
alloc.type_str(suffix_type),
@ -2240,7 +2252,7 @@ fn pretty_runtime_error<'b>(
alloc.reflow("This expression cannot be updated"),
alloc.reflow(":"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow("Only variables can be updated with record update syntax."),
]);
@ -2286,7 +2298,7 @@ fn pretty_runtime_error<'b>(
doc = alloc.stack([
alloc.concat([alloc.reflow("This character literal is empty.")]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
tip,
]);
@ -2301,7 +2313,7 @@ fn pretty_runtime_error<'b>(
alloc.concat([
alloc.reflow("This character literal contains more than one code point.")
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat([alloc.reflow("Character literals can only contain one code point.")]),
tip,
]);
@ -2342,13 +2354,13 @@ fn pretty_runtime_error<'b>(
alloc.type_str(opaque.as_inline_str().as_str()),
alloc.reflow(" referenced here is not defined:"),
]),
alloc.region(lines.convert_region(used_region)),
alloc.region(lines.convert_region(used_region), severity),
];
if let Some(defined_alias_region) = opt_defined_alias {
stack.push(alloc.stack([
alloc.note("There is an alias of the same name:"),
alloc.region(lines.convert_region(defined_alias_region)),
alloc.region(lines.convert_region(defined_alias_region), severity),
]));
}
@ -2369,9 +2381,9 @@ fn pretty_runtime_error<'b>(
alloc.type_str(opaque.as_inline_str().as_str()),
alloc.reflow(" referenced here:"),
]),
alloc.region(lines.convert_region(referenced_region)),
alloc.region(lines.convert_region(referenced_region), severity),
alloc.reflow("is imported from another module:"),
alloc.region(lines.convert_region(imported_region)),
alloc.region(lines.convert_region(imported_region), severity),
alloc.note(
"Opaque types can only be wrapped and unwrapped in the module they are defined in!",
),
@ -2382,7 +2394,7 @@ fn pretty_runtime_error<'b>(
RuntimeError::OpaqueNotApplied(loc_ident) => {
doc = alloc.stack([
alloc.reflow("This opaque type is not applied to an argument:"),
alloc.region(lines.convert_region(loc_ident.region)),
alloc.region(lines.convert_region(loc_ident.region), severity),
alloc.note("Opaque types always wrap exactly one argument!"),
]);
@ -2391,7 +2403,7 @@ fn pretty_runtime_error<'b>(
RuntimeError::OpaqueAppliedToMultipleArgs(region) => {
doc = alloc.stack([
alloc.reflow("This opaque type is applied to multiple arguments:"),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.note("Opaque types always wrap exactly one argument!"),
]);
@ -2400,7 +2412,7 @@ fn pretty_runtime_error<'b>(
RuntimeError::DegenerateBranch(region) => {
doc = alloc.stack([
alloc.reflow("This branch pattern does not bind all symbols its body needs:"),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
]);
title = "DEGENERATE BRANCH";
@ -2412,7 +2424,7 @@ fn pretty_runtime_error<'b>(
doc = alloc.stack([
alloc.reflow("This function is applied to multiple record builders:"),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.note("Functions can only take at most one record builder!"),
tip,
]);
@ -2422,7 +2434,7 @@ fn pretty_runtime_error<'b>(
RuntimeError::UnappliedRecordBuilder(region) => {
doc = alloc.stack([
alloc.reflow("This record builder was not applied to a function:"),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow("However, we need a function to construct the record."),
alloc.note(
"Functions must be applied directly. The pipe operator (|>) cannot be used.",
@ -2440,6 +2452,7 @@ pub fn to_circular_def_doc<'b>(
alloc: &'b RocDocAllocator<'b>,
lines: &LineInfo,
entries: &[roc_problem::can::CycleEntry],
severity: Severity,
) -> RocDocBuilder<'b> {
// TODO "are you trying to mutate a variable?
// TODO tip?
@ -2451,7 +2464,7 @@ pub fn to_circular_def_doc<'b>(
alloc.symbol_unqualified(*symbol),
alloc.reflow(" is defined directly in terms of itself:"),
]),
alloc.region(lines.convert_region(Region::span_across(symbol_region, expr_region))),
alloc.region(lines.convert_region(Region::span_across(symbol_region, expr_region)), severity),
alloc.reflow("Roc evaluates values strictly, so running this program would enter an infinite loop!"),
alloc.hint("").append(alloc.concat([
alloc.reflow("Did you mean to define "),alloc.symbol_unqualified(*symbol),alloc.reflow(" as a function?"),
@ -2463,7 +2476,7 @@ pub fn to_circular_def_doc<'b>(
.reflow("The ")
.append(alloc.symbol_unqualified(first.symbol))
.append(alloc.reflow(" definition is causing a very tricky infinite loop:")),
alloc.region(lines.convert_region(first.symbol_region)),
alloc.region(lines.convert_region(first.symbol_region), severity),
alloc
.reflow("The ")
.append(alloc.symbol_unqualified(first.symbol))
@ -2492,6 +2505,7 @@ fn not_found<'b>(
name: &Ident,
options: MutSet<Box<str>>,
underscored_suggestion_region: Option<Region>,
severity: Severity,
) -> RocDocBuilder<'b> {
let mut suggestions = suggest::sort(
name.as_inline_str().as_str(),
@ -2510,7 +2524,7 @@ fn not_found<'b>(
let default_yes = match underscored_suggestion_region {
Some(underscored_region) => alloc.stack([
alloc.reflow("There is an ignored identifier of a similar name here:"),
alloc.region(lines.convert_region(underscored_region)),
alloc.region(lines.convert_region(underscored_region), severity),
alloc.reflow("Did you mean to remove the leading underscore?"),
alloc.reflow("If not, did you mean one of these?"),
]),
@ -2536,7 +2550,7 @@ fn not_found<'b>(
alloc.string(name.to_string()),
alloc.reflow("` in this scope."),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
to_details(default_no, default_yes),
])
}
@ -2551,13 +2565,14 @@ fn module_not_found<'b>(
name: &ModuleName,
options: MutSet<Box<str>>,
module_exists: bool,
severity: Severity,
) -> RocDocBuilder<'b> {
// If the module exists, sugguest that the user import it
// If the module exists, suggest that the user import it
let details = if module_exists {
// TODO: Maybe give an example of how to do that
alloc.reflow("Did you mean to import it?")
} else {
// If the module might not exist, sugguest that it's a typo
// If the module might not exist, suggest that it's a typo
let mut suggestions =
suggest::sort(name.as_str(), options.iter().map(|v| v.as_ref()).collect());
suggestions.truncate(4);
@ -2587,7 +2602,7 @@ fn module_not_found<'b>(
alloc.string(name.to_string()),
alloc.reflow("` module is not imported:"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
details,
])
}

View file

@ -80,6 +80,7 @@ impl<'a> Renderer<'a> {
symbols: &[Symbol],
variables: &[Variable],
expressions: &[Expr<'_>],
severity: Severity,
) -> RocDocBuilder<'a> {
use ven_pretty::DocAllocator;
@ -96,7 +97,7 @@ impl<'a> Renderer<'a> {
if it.len() > 0 {
self.alloc.stack([
self.alloc.text("This expectation failed:"),
self.alloc.region(line_col_region),
self.alloc.region(line_col_region, severity),
self.alloc
.text("When it failed, these variables had these values:"),
self.alloc.stack(it),
@ -105,7 +106,7 @@ impl<'a> Renderer<'a> {
} else {
self.alloc.stack([
self.alloc.text("This expectation failed:"),
self.alloc.region(line_col_region),
self.alloc.region(line_col_region, severity),
self.alloc.text(""), // Blank line at the end
])
}
@ -147,15 +148,23 @@ impl<'a> Renderer<'a> {
W: std::io::Write,
{
use crate::report::Report;
let severity = Severity::RuntimeError;
let line_col_region = self.to_line_col_region(expect_region, failure_region);
let doc = self.render_lookups(subs, line_col_region, symbols, variables, expressions);
let doc = self.render_lookups(
subs,
line_col_region,
symbols,
variables,
expressions,
severity,
);
let report = Report {
title: "EXPECT FAILED".into(),
doc,
filename: self.filename.clone(),
severity: Severity::RuntimeError,
severity,
};
let mut buf = String::new();
@ -214,10 +223,11 @@ impl<'a> Renderer<'a> {
use ven_pretty::DocAllocator;
let line_col_region = self.line_info.convert_region(expect_region);
let severity = Severity::RuntimeError;
let doc = self.alloc.stack([
self.alloc.text("This expectation crashed while running:"),
self.alloc.region(line_col_region),
self.alloc.region(line_col_region, severity),
self.alloc.text("The crash reported this message:"),
self.alloc.text(message),
]);
@ -226,7 +236,7 @@ impl<'a> Renderer<'a> {
title: "EXPECT PANICKED".into(),
doc,
filename: self.filename.clone(),
severity: Severity::RuntimeError,
severity,
};
let mut buf = String::new();

File diff suppressed because it is too large Load diff

View file

@ -85,7 +85,7 @@ pub fn type_problem<'b>(
UnfulfilledAbility(incomplete) => {
let title = "INCOMPLETE ABILITY IMPLEMENTATION".to_string();
let doc = report_unfulfilled_ability(alloc, lines, incomplete);
let doc = report_unfulfilled_ability(alloc, lines, incomplete, severity);
report(title, doc, filename)
}
@ -96,9 +96,9 @@ pub fn type_problem<'b>(
let incomplete = incomplete
.into_iter()
.map(|unfulfilled| report_unfulfilled_ability(alloc, lines, unfulfilled));
.map(|unfulfilled| report_unfulfilled_ability(alloc, lines, unfulfilled, severity));
let note = alloc.stack(incomplete);
let snippet = alloc.region(lines.convert_region(region));
let snippet = alloc.region(lines.convert_region(region), severity);
let stack = [
alloc.text(
"This expression has a type that does not implement the abilities it's expected to:",
@ -118,9 +118,9 @@ pub fn type_problem<'b>(
BadPatternMissingAbility(region, _category, _found, incomplete) => {
let incomplete = incomplete
.into_iter()
.map(|unfulfilled| report_unfulfilled_ability(alloc, lines, unfulfilled));
.map(|unfulfilled| report_unfulfilled_ability(alloc, lines, unfulfilled, severity));
let note = alloc.stack(incomplete);
let snippet = alloc.region(lines.convert_region(region));
let snippet = alloc.region(lines.convert_region(region), severity);
let stack = [
alloc.text(
"This expression has a type does not implement the abilities it's expected to:",
@ -139,7 +139,7 @@ pub fn type_problem<'b>(
}
Exhaustive(problem) => Some(exhaustive_problem(alloc, lines, filename, problem)),
CircularDef(entries) => {
let doc = to_circular_def_doc(alloc, lines, &entries);
let doc = to_circular_def_doc(alloc, lines, &entries, severity);
let title = CIRCULAR_DEF.to_string();
Some(Report {
@ -161,7 +161,7 @@ pub fn type_problem<'b>(
alloc.symbol_unqualified(member),
alloc.reflow(" is for a non-opaque type:"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow("It is specialized for"),
alloc.type_block(error_type_to_doc(alloc, typ)),
alloc.reflow("but structural types can never specialize abilities!"),
@ -191,7 +191,7 @@ pub fn type_problem<'b>(
alloc.symbol_unqualified(ability_member),
alloc.reflow(" is not for the expected type:"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.concat([
alloc.reflow("It was previously claimed to be a specialization for "),
alloc.symbol_unqualified(expected_opaque),
@ -254,6 +254,7 @@ fn report_unfulfilled_ability<'a>(
alloc: &'a RocDocAllocator<'a>,
lines: &LineInfo,
unfulfilled: Unfulfilled,
severity: Severity,
) -> RocDocBuilder<'a> {
match unfulfilled {
Unfulfilled::OpaqueDoesNotImplement { typ, ability } => {
@ -302,7 +303,7 @@ fn report_unfulfilled_ability<'a>(
alloc.symbol_foreign_qualified(opaque),
alloc.reflow(":"),
]),
alloc.region(lines.convert_region(derive_region)),
alloc.region(lines.convert_region(derive_region), severity),
]
.into_iter()
.chain(reason)
@ -446,6 +447,7 @@ pub fn cyclic_alias<'b>(
region: roc_region::all::Region,
others: Vec<Symbol>,
alias_kind: AliasKind,
severity: Severity,
) -> (RocDocBuilder<'b>, String) {
let when_is_recursion_legal =
alloc.reflow("Recursion in ")
@ -460,7 +462,7 @@ pub fn cyclic_alias<'b>(
.append(alloc.reflow(" "))
.append(alloc.reflow(alias_kind.as_str()))
.append(alloc.reflow(" is self-recursive in an invalid way:")),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
when_is_recursion_legal,
])
} else {
@ -471,7 +473,7 @@ pub fn cyclic_alias<'b>(
.append(alloc.reflow(" "))
.append(alloc.reflow(alias_kind.as_str()))
.append(alloc.reflow(" is recursive in an invalid way:")),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc
.reflow("The ")
.append(alloc.symbol_unqualified(symbol))
@ -515,9 +517,10 @@ fn report_mismatch<'b>(
alloc.region_with_subregion(
lines.convert_region(highlight),
lines.convert_region(region),
severity,
)
} else {
alloc.region(lines.convert_region(region))
alloc.region(lines.convert_region(region), severity)
};
let lines = vec![
problem,
@ -559,9 +562,10 @@ fn report_bad_type<'b>(
alloc.region_with_subregion(
lines.convert_region(highlight),
lines.convert_region(region),
severity,
)
} else {
alloc.region(lines.convert_region(region))
alloc.region(lines.convert_region(region), severity)
};
let lines = vec![
problem,
@ -664,7 +668,7 @@ fn to_expr_report<'b>(
title: "TYPE MISMATCH".to_string(),
doc: alloc.stack([
alloc.text("This expression is used in an unexpected way:"),
alloc.region(lines.convert_region(expr_region)),
alloc.region(lines.convert_region(expr_region), severity),
comparison,
]),
severity,
@ -786,6 +790,7 @@ fn to_expr_report<'b>(
alloc.region_with_subregion(
lines.convert_region(joined),
lines.convert_region(expr_region),
severity,
)
},
comparison,
@ -1138,7 +1143,7 @@ fn to_expr_report<'b>(
" is an opaque type, so it cannot be called with an argument:",
),
]),
alloc.region(lines.convert_region(expr_region)),
alloc.region(lines.convert_region(expr_region), severity),
match called_via {
CalledVia::RecordBuilder => {
alloc.hint("Did you mean to apply it to a function first?")
@ -1160,7 +1165,7 @@ fn to_expr_report<'b>(
}
)),
]),
alloc.region(lines.convert_region(expr_region)),
alloc.region(lines.convert_region(expr_region), severity),
match called_via {
CalledVia::RecordBuilder => {
alloc.concat([
@ -1208,7 +1213,7 @@ fn to_expr_report<'b>(
arity
)),
]),
alloc.region(lines.convert_region(expr_region)),
alloc.region(lines.convert_region(expr_region), severity),
alloc.reflow("Are there any missing commas? Or missing parentheses?"),
];
@ -1232,7 +1237,7 @@ fn to_expr_report<'b>(
arity
)),
]),
alloc.region(lines.convert_region(expr_region)),
alloc.region(lines.convert_region(expr_region), severity),
alloc.reflow(
"Roc does not allow functions to be partially applied. \
Use a closure to make partial application explicit.",
@ -1407,6 +1412,7 @@ fn to_expr_report<'b>(
let snippet = alloc.region_with_subregion(
lines.convert_region(region),
lines.convert_region(expr_region),
severity,
);
let this_is = alloc.concat([
@ -1460,7 +1466,7 @@ fn to_expr_report<'b>(
.append(alloc.text(" argument to "))
.append(name.clone())
.append(alloc.text(" is weird:")),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
pattern_type_comparison(
alloc,
expected_type,
@ -1501,7 +1507,7 @@ fn to_expr_report<'b>(
.reflow("This value passed to ")
.append(alloc.keyword("crash"))
.append(alloc.reflow(" is not a string:")),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
type_comparison(
alloc,
found,
@ -1915,7 +1921,7 @@ fn to_pattern_report<'b>(
PExpected::NoExpectation(expected_type) => {
let doc = alloc.stack([
alloc.text("This pattern is being used in an unexpected way:"),
alloc.region(lines.convert_region(expr_region)),
alloc.region(lines.convert_region(expr_region), severity),
pattern_type_comparison(
alloc,
found,
@ -1948,7 +1954,7 @@ fn to_pattern_report<'b>(
.append(alloc.text(" argument to "))
.append(name.clone())
.append(alloc.text(" is weird:")),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
pattern_type_comparison(
alloc,
found,
@ -1986,6 +1992,7 @@ fn to_pattern_report<'b>(
alloc.region_with_subregion(
lines.convert_region(region),
lines.convert_region(expr_region),
severity,
),
pattern_type_comparison(
alloc,
@ -2030,6 +2037,7 @@ fn to_pattern_report<'b>(
alloc.region_with_subregion(
lines.convert_region(region),
lines.convert_region(expr_region),
severity,
),
pattern_type_comparison(
alloc,
@ -2059,7 +2067,7 @@ fn to_pattern_report<'b>(
PReason::ListElem => {
let doc = alloc.stack([
alloc.concat([alloc.reflow("This list element doesn't match the types of other elements in the pattern:")]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
pattern_type_comparison(
alloc,
found,
@ -2170,7 +2178,7 @@ fn to_circular_report<'b>(
.reflow("I'm inferring a weird self-referential type for ")
.append(alloc.symbol_unqualified(symbol))
.append(alloc.text(":")),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.stack([
alloc.reflow(
"Here is my best effort at writing down the type. \
@ -4810,7 +4818,7 @@ fn report_record_field_typo<'b>(
let doc = alloc.stack([
header,
alloc.region(lines.convert_region(field_region)),
alloc.region(lines.convert_region(field_region), severity),
if suggestions.is_empty() {
let r_doc = match opt_sym {
Some(symbol) => alloc.symbol_unqualified(symbol).append(" is"),
@ -4875,7 +4883,7 @@ fn exhaustive_problem<'a>(
BadArg => {
let doc = alloc.stack([
alloc.reflow("This pattern does not cover all the possibilities:"),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow("Other possibilities include:"),
unhandled_patterns_to_doc_block(alloc, missing),
alloc.concat([
@ -4898,7 +4906,7 @@ fn exhaustive_problem<'a>(
BadDestruct => {
let doc = alloc.stack([
alloc.reflow("This pattern does not cover all the possibilities:"),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow("Other possibilities include:"),
unhandled_patterns_to_doc_block(alloc, missing),
alloc.concat([
@ -4926,7 +4934,7 @@ fn exhaustive_problem<'a>(
alloc.keyword("when"),
alloc.reflow(" does not cover all the possibilities:"),
]),
alloc.region(lines.convert_region(region)),
alloc.region(lines.convert_region(region), severity),
alloc.reflow("Other possibilities include:"),
unhandled_patterns_to_doc_block(alloc, missing),
alloc.reflow(
@ -4958,6 +4966,7 @@ fn exhaustive_problem<'a>(
alloc.region_with_subregion(
lines.convert_region(overall_region),
lines.convert_region(branch_region),
severity,
),
alloc.reflow(
"Any value of this shape will be handled by \
@ -4986,6 +4995,7 @@ fn exhaustive_problem<'a>(
alloc.region_with_subregion(
lines.convert_region(overall_region),
lines.convert_region(branch_region),
severity,
),
alloc.reflow(
"It's impossible to create a value of this shape, \

View file

@ -223,6 +223,7 @@ pub struct Palette {
pub bold: &'static str,
pub underline: &'static str,
pub reset: &'static str,
pub warning: &'static str,
}
/// Set the default styles for various semantic elements,
@ -250,6 +251,7 @@ const fn default_palette_from_style_codes(codes: StyleCodes) -> Palette {
bold: codes.bold,
underline: codes.underline,
reset: codes.reset,
warning: codes.yellow,
}
}
@ -675,6 +677,7 @@ impl<'a> RocDocAllocator<'a> {
&'a self,
region: LineColumnRegion,
sub_region: LineColumnRegion,
severity: Severity,
) -> DocBuilder<'a, Self, Annotation> {
// debug_assert!(region.contains(&sub_region));
@ -684,10 +687,15 @@ impl<'a> RocDocAllocator<'a> {
// attempting this will recurse forever, so don't do that! Instead, give up and
// accept that this report will take up more than 1 full screen.
if !sub_region.contains(&region) {
return self.region_with_subregion(sub_region, sub_region);
return self.region_with_subregion(sub_region, sub_region, severity);
}
}
let annotation = match severity {
Severity::RuntimeError | Severity::Fatal => Annotation::Error,
Severity::Warning => Annotation::Warning,
};
// if true, the final line of the snippet will be some ^^^ that point to the region where
// the problem is. Otherwise, the snippet will have a > on the lines that are in the region
// where the problem is.
@ -727,7 +735,7 @@ impl<'a> RocDocAllocator<'a> {
self.text(" ".repeat(max_line_number_length - this_line_number_length))
.append(self.text(line_number).annotate(Annotation::LineNumber))
.append(self.text(GUTTER_BAR).annotate(Annotation::GutterBar))
.append(self.text(">").annotate(Annotation::Error))
.append(self.text(">").annotate(annotation))
.append(rest_of_line)
} else if error_highlight_line {
self.text(" ".repeat(max_line_number_length - this_line_number_length))
@ -769,7 +777,7 @@ impl<'a> RocDocAllocator<'a> {
} else {
self.text(" ".repeat(sub_region.start().column as usize))
.indent(indent)
.append(self.text(highlight_text).annotate(Annotation::Error))
.append(self.text(highlight_text).annotate(annotation))
});
result = result.append(highlight_line);
@ -778,8 +786,12 @@ impl<'a> RocDocAllocator<'a> {
result
}
pub fn region(&'a self, region: LineColumnRegion) -> DocBuilder<'a, Self, Annotation> {
self.region_with_subregion(region, region)
pub fn region(
&'a self,
region: LineColumnRegion,
severity: Severity,
) -> DocBuilder<'a, Self, Annotation> {
self.region_with_subregion(region, region, severity)
}
pub fn region_without_error(
@ -884,6 +896,7 @@ pub enum Annotation {
Tip,
Header,
ParserSuggestion,
Warning,
}
/// Render with minimal formatting
@ -1094,6 +1107,9 @@ where
ParserSuggestion => {
self.write_str(self.palette.parser_suggestion)?;
}
Warning => {
self.write_str(self.palette.warning)?;
}
TypeBlock | InlineTypeBlock | Tag | RecordField | TupleElem => { /* nothing yet */ }
}
self.style_stack.push(*annotation);
@ -1108,7 +1124,8 @@ where
Some(annotation) => match annotation {
Emphasized | Url | TypeVariable | Alias | Symbol | BinOp | UnaryOp | Error
| GutterBar | Ellipsis | Typo | TypoSuggestion | ParserSuggestion | Structure
| CodeBlock | PlainText | LineNumber | Tip | Module | Header | Keyword => {
| CodeBlock | PlainText | LineNumber | Tip | Module | Header | Keyword
| Warning => {
self.write_str(self.palette.reset)?;
}