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 // 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 // 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 // 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. // the cause of the change in error report.
// test_report!( // test_report!(
// def_missing_final_expression, // 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 // 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#" @r#"
TYPE MISMATCH in /code/proj/Main.roc TYPE MISMATCH in /code/proj/Main.roc
@ -13530,7 +13530,7 @@ In roc, functions are always written as a lambda, like{}
4 crash "" "" 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, expected,
} = problem; } = problem;
let severity = Severity::RuntimeError;
// SAFETY: if the module was not UTF-8, that would be reported as a parsing problem, rather // 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). // than an incorrect module name problem (the latter can happen only after parsing).
let src = unsafe { from_utf8_unchecked(src) }; let src = unsafe { from_utf8_unchecked(src) };
@ -6317,7 +6319,7 @@ fn to_incorrect_module_name_report<'a>(
let doc = alloc.stack([ let doc = alloc.stack([
alloc.reflow("This module has a different name than I expected:"), 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.reflow("Based on the nesting and use of this module, I expect it to have name"),
alloc.pq_module_name(expected).indent(4), alloc.pq_module_name(expected).indent(4),
]); ]);
@ -6326,7 +6328,7 @@ fn to_incorrect_module_name_report<'a>(
filename, filename,
doc, doc,
title: "INCORRECT MODULE NAME".to_string(), title: "INCORRECT MODULE NAME".to_string(),
severity: Severity::RuntimeError, severity,
}; };
let mut buf = String::new(); let mut buf = String::new();
@ -6346,6 +6348,7 @@ fn to_no_platform_package_report(
) -> String { ) -> String {
use roc_reporting::report::{Report, RocDocAllocator, DEFAULT_PALETTE}; use roc_reporting::report::{Report, RocDocAllocator, DEFAULT_PALETTE};
use ven_pretty::DocAllocator; 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 // 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). // 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([ let doc = alloc.stack([
alloc.reflow("This app does not specify a platform:"), 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.reflow("Make sure you have exactly one package specified as `platform`:"),
alloc alloc
.parser_suggestion(" app [main] {\n pf: platform \"…path or URL to platform…\"\n ^^^^^^^^\n }"), .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, filename,
doc, doc,
title: "UNSPECIFIED PLATFORM".to_string(), title: "UNSPECIFIED PLATFORM".to_string(),
severity: Severity::RuntimeError, severity,
}; };
let mut buf = String::new(); let mut buf = String::new();
@ -6393,6 +6396,7 @@ fn to_multiple_platform_packages_report(
) -> String { ) -> String {
use roc_reporting::report::{Report, RocDocAllocator, DEFAULT_PALETTE}; use roc_reporting::report::{Report, RocDocAllocator, DEFAULT_PALETTE};
use ven_pretty::DocAllocator; 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 // 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). // 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([ let doc = alloc.stack([
alloc.reflow("This app specifies multiple packages as `platform`:"), 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."), alloc.reflow("Roc apps must specify exactly one platform."),
]); ]);
@ -6416,7 +6420,7 @@ fn to_multiple_platform_packages_report(
filename, filename,
doc, doc,
title: "MULTIPLE PLATFORMS".to_string(), title: "MULTIPLE PLATFORMS".to_string(),
severity: Severity::RuntimeError, severity,
}; };
let mut buf = String::new(); let mut buf = String::new();

View file

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

View file

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

View file

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