Introduce LineColumnRegion and force conversion

This commit is contained in:
Joshua Warner 2021-12-23 19:15:54 -08:00
parent 721233f9c8
commit 82d2be0635
11 changed files with 507 additions and 287 deletions

View file

@ -12,6 +12,7 @@ use roc_gen_llvm::llvm::externs::add_default_roc_externs;
use roc_load::file::LoadingProblem; use roc_load::file::LoadingProblem;
use roc_mono::ir::OptLevel; use roc_mono::ir::OptLevel;
use roc_parse::parser::SyntaxError; use roc_parse::parser::SyntaxError;
use roc_region::all::LineInfo;
use roc_types::pretty_print::{content_to_string, name_all_type_vars}; use roc_types::pretty_print::{content_to_string, name_all_type_vars};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str::from_utf8_unchecked; use std::str::from_utf8_unchecked;
@ -91,6 +92,7 @@ pub fn gen_and_eval<'a>(
continue; continue;
} }
let line_info = LineInfo::new(&src);
let src_lines: Vec<&str> = src.split('\n').collect(); let src_lines: Vec<&str> = src.split('\n').collect();
let palette = DEFAULT_PALETTE; let palette = DEFAULT_PALETTE;
@ -98,7 +100,7 @@ pub fn gen_and_eval<'a>(
let alloc = RocDocAllocator::new(&src_lines, home, &interns); let alloc = RocDocAllocator::new(&src_lines, home, &interns);
for problem in can_problems.into_iter() { for problem in can_problems.into_iter() {
let report = can_problem(&alloc, module_path.clone(), problem); let report = can_problem(&alloc, &line_info, module_path.clone(), problem);
let mut buf = String::new(); let mut buf = String::new();
report.render_color_terminal(&mut buf, &alloc, &palette); report.render_color_terminal(&mut buf, &alloc, &palette);
@ -107,7 +109,7 @@ pub fn gen_and_eval<'a>(
} }
for problem in type_problems { for problem in type_problems {
if let Some(report) = type_problem(&alloc, module_path.clone(), problem) { if let Some(report) = type_problem(&alloc, &line_info, module_path.clone(), problem) {
let mut buf = String::new(); let mut buf = String::new();
report.render_color_terminal(&mut buf, &alloc, &palette); report.render_color_terminal(&mut buf, &alloc, &palette);
@ -117,7 +119,7 @@ pub fn gen_and_eval<'a>(
} }
for problem in mono_problems { for problem in mono_problems {
let report = mono_problem(&alloc, module_path.clone(), problem); let report = mono_problem(&alloc, &line_info, module_path.clone(), problem);
let mut buf = String::new(); let mut buf = String::new();
report.render_color_terminal(&mut buf, &alloc, &palette); report.render_color_terminal(&mut buf, &alloc, &palette);

View file

@ -5,6 +5,7 @@ pub use roc_gen_llvm::llvm::build::FunctionIterator;
use roc_load::file::{LoadedModule, MonomorphizedModule}; use roc_load::file::{LoadedModule, MonomorphizedModule};
use roc_module::symbol::{Interns, ModuleId}; use roc_module::symbol::{Interns, ModuleId};
use roc_mono::ir::OptLevel; use roc_mono::ir::OptLevel;
use roc_region::all::LineInfo;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::time::Duration; use std::time::Duration;
@ -81,13 +82,15 @@ fn report_problems_help(
src_lines.extend(src.split('\n')); src_lines.extend(src.split('\n'));
} }
let lines = LineInfo::new(src);
// Report parsing and canonicalization problems // Report parsing and canonicalization problems
let alloc = RocDocAllocator::new(&src_lines, *home, interns); let alloc = RocDocAllocator::new(&src_lines, *home, interns);
let problems = can_problems.remove(home).unwrap_or_default(); let problems = can_problems.remove(home).unwrap_or_default();
for problem in problems.into_iter() { for problem in problems.into_iter() {
let report = can_problem(&alloc, module_path.clone(), problem); let report = can_problem(&alloc, &lines, module_path.clone(), problem);
let severity = report.severity; let severity = report.severity;
let mut buf = String::new(); let mut buf = String::new();
@ -106,7 +109,7 @@ fn report_problems_help(
let problems = type_problems.remove(home).unwrap_or_default(); let problems = type_problems.remove(home).unwrap_or_default();
for problem in problems { for problem in problems {
if let Some(report) = type_problem(&alloc, module_path.clone(), problem) { if let Some(report) = type_problem(&alloc, &lines, module_path.clone(), problem) {
let severity = report.severity; let severity = report.severity;
let mut buf = String::new(); let mut buf = String::new();
@ -126,7 +129,7 @@ fn report_problems_help(
let problems = mono_problems.remove(home).unwrap_or_default(); let problems = mono_problems.remove(home).unwrap_or_default();
for problem in problems { for problem in problems {
let report = mono_problem(&alloc, module_path.clone(), problem); let report = mono_problem(&alloc, &lines, module_path.clone(), problem);
let severity = report.severity; let severity = report.severity;
let mut buf = String::new(); let mut buf = String::new();

View file

@ -28,7 +28,7 @@ use roc_parse::header::PackageName;
use roc_parse::header::{ExposedName, ImportsEntry, PackageEntry, PlatformHeader, To, TypedIdent}; use roc_parse::header::{ExposedName, ImportsEntry, PackageEntry, PlatformHeader, To, TypedIdent};
use roc_parse::module::module_defs; use roc_parse::module::module_defs;
use roc_parse::parser::{ParseProblem, Parser, SyntaxError}; use roc_parse::parser::{ParseProblem, Parser, SyntaxError};
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region, LineInfo};
use roc_solve::module::SolvedModule; use roc_solve::module::SolvedModule;
use roc_solve::solve; use roc_solve::solve;
use roc_types::solved_types::Solved; use roc_types::solved_types::Solved;
@ -4331,7 +4331,10 @@ fn to_parse_problem_report<'a>(
let alloc = RocDocAllocator::new(&src_lines, module_id, &interns); let alloc = RocDocAllocator::new(&src_lines, module_id, &interns);
let starting_line = 0; let starting_line = 0;
let report = parse_problem(&alloc, problem.filename.clone(), starting_line, problem);
let lines = LineInfo::new(src);
let report = parse_problem(&alloc, &lines, problem.filename.clone(), starting_line, problem);
let mut buf = String::new(); let mut buf = String::new();
let palette = DEFAULT_PALETTE; let palette = DEFAULT_PALETTE;

View file

@ -205,6 +205,96 @@ pub struct LineColumn {
pub column: u16, pub column: u16,
} }
impl LineColumn {
pub const fn zero() -> Self {
LineColumn {
line: 0,
column: 0,
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Default)]
pub struct LineColumnRegion {
pub start: LineColumn,
pub end: LineColumn,
}
impl LineColumnRegion {
pub const fn zero() -> Self {
LineColumnRegion {
start: LineColumn::zero(),
end: LineColumn::zero(),
}
}
pub fn contains(&self, other: &Self) -> bool {
use std::cmp::Ordering::*;
match self.start.line.cmp(&other.start.line) {
Greater => false,
Equal => match self.end.line.cmp(&other.end.line) {
Less => false,
Equal => self.start.column <= other.start.column && self.end.column >= other.end.column,
Greater => self.start.column >= other.start.column,
},
Less => match self.end.line.cmp(&other.end.line) {
Less => false,
Equal => self.end.column >= other.end.column,
Greater => true,
},
}
}
pub fn is_empty(&self) -> bool {
self.end.line == self.start.line && self.start.column == self.end.column
}
pub fn span_across(start: &LineColumnRegion, end: &LineColumnRegion) -> Self {
LineColumnRegion {
start: start.start,
end: end.end,
}
}
pub fn across_all<'a, I>(regions: I) -> Self
where
I: IntoIterator<Item = &'a LineColumnRegion>,
{
let mut it = regions.into_iter();
if let Some(first) = it.next() {
let mut result = *first;
for r in it {
result = Self::span_across(&result, r);
}
result
} else {
Self::zero()
}
}
pub fn lines_between(&self, other: &LineColumnRegion) -> u32 {
if self.end.line <= other.start.line {
other.start.line - self.end.line
} else if self.start.line >= other.end.line {
self.start.line - other.end.line
} else {
// intersection
0
}
}
pub const fn start(&self) -> LineColumn {
self.start
}
pub const fn end(&self) -> LineColumn {
self.end
}
}
#[derive(Clone, Eq, Copy, PartialEq, PartialOrd, Ord, Hash)] #[derive(Clone, Eq, Copy, PartialEq, PartialOrd, Ord, Hash)]
pub struct Loc<T> { pub struct Loc<T> {
pub region: Region, pub region: Region,
@ -269,3 +359,29 @@ where
} }
} }
} }
pub struct LineInfo {
// TODO
}
impl LineInfo {
pub fn new(_text: &str) -> LineInfo {
// TODO
LineInfo {}
}
pub fn convert_pos(&self, pos: Position) -> LineColumn {
// TODO
LineColumn {
line: pos.line,
column: pos.column,
}
}
pub fn convert_region(&self, region: Region) -> LineColumnRegion {
LineColumnRegion {
start: self.convert_pos(region.start()),
end: self.convert_pos(region.end()),
}
}
}

View file

@ -9,6 +9,7 @@ use roc_collections::all::{MutMap, MutSet};
use roc_gen_llvm::llvm::externs::add_default_roc_externs; use roc_gen_llvm::llvm::externs::add_default_roc_externs;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_mono::ir::OptLevel; use roc_mono::ir::OptLevel;
use roc_region::all::LineInfo;
use roc_types::subs::VarStore; use roc_types::subs::VarStore;
use target_lexicon::Triple; use target_lexicon::Triple;
@ -105,6 +106,7 @@ fn create_llvm_module<'a>(
continue; continue;
} }
let line_info = LineInfo::new(&src);
let src_lines: Vec<&str> = src.split('\n').collect(); let src_lines: Vec<&str> = src.split('\n').collect();
let palette = DEFAULT_PALETTE; let palette = DEFAULT_PALETTE;
@ -121,7 +123,7 @@ fn create_llvm_module<'a>(
| RuntimeError(_) | RuntimeError(_)
| UnsupportedPattern(_, _) | UnsupportedPattern(_, _)
| ExposedButNotDefined(_) => { | ExposedButNotDefined(_) => {
let report = can_problem(&alloc, module_path.clone(), problem); let report = can_problem(&alloc, &line_info, module_path.clone(), problem);
let mut buf = String::new(); let mut buf = String::new();
report.render_color_terminal(&mut buf, &alloc, &palette); report.render_color_terminal(&mut buf, &alloc, &palette);
@ -130,7 +132,7 @@ fn create_llvm_module<'a>(
lines.push(buf); lines.push(buf);
} }
_ => { _ => {
let report = can_problem(&alloc, module_path.clone(), problem); let report = can_problem(&alloc, &line_info, module_path.clone(), problem);
let mut buf = String::new(); let mut buf = String::new();
report.render_color_terminal(&mut buf, &alloc, &palette); report.render_color_terminal(&mut buf, &alloc, &palette);
@ -141,7 +143,7 @@ fn create_llvm_module<'a>(
} }
for problem in type_problems { for problem in type_problems {
if let Some(report) = type_problem(&alloc, module_path.clone(), problem) { if let Some(report) = type_problem(&alloc, &line_info, module_path.clone(), problem) {
let mut buf = String::new(); let mut buf = String::new();
report.render_color_terminal(&mut buf, &alloc, &palette); report.render_color_terminal(&mut buf, &alloc, &palette);
@ -151,7 +153,7 @@ fn create_llvm_module<'a>(
} }
for problem in mono_problems { for problem in mono_problems {
let report = mono_problem(&alloc, module_path.clone(), problem); let report = mono_problem(&alloc, &line_info, module_path.clone(), problem);
let mut buf = String::new(); let mut buf = String::new();
report.render_color_terminal(&mut buf, &alloc, &palette); report.render_color_terminal(&mut buf, &alloc, &palette);

View file

@ -2,7 +2,7 @@ use roc_collections::all::MutSet;
use roc_module::ident::{Ident, Lowercase, ModuleName}; use roc_module::ident::{Ident, Lowercase, ModuleName};
use roc_problem::can::PrecedenceProblem::BothNonAssociative; use roc_problem::can::PrecedenceProblem::BothNonAssociative;
use roc_problem::can::{BadPattern, FloatErrorKind, IntErrorKind, Problem, RuntimeError}; use roc_problem::can::{BadPattern, FloatErrorKind, IntErrorKind, Problem, RuntimeError};
use roc_region::all::{Loc, Position, Region}; use roc_region::all::{Loc, Position, Region, LineInfo};
use std::path::PathBuf; use std::path::PathBuf;
use crate::error::r#type::suggest; use crate::error::r#type::suggest;
@ -27,6 +27,7 @@ const MODULE_NOT_IMPORTED: &str = "MODULE NOT IMPORTED";
pub fn can_problem<'b>( pub fn can_problem<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
lines: &LineInfo,
filename: PathBuf, filename: PathBuf,
problem: Problem, problem: Problem,
) -> Report<'b> { ) -> Report<'b> {
@ -43,7 +44,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(region), alloc.region(lines.convert_region(region)),
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))
@ -60,7 +61,7 @@ pub fn can_problem<'b>(
alloc.module(module_id), alloc.module(module_id),
alloc.reflow(" is used in this module."), alloc.reflow(" is used in this module."),
]), ]),
alloc.region(region), alloc.region(lines.convert_region(region)),
alloc.concat(vec![ alloc.concat(vec![
alloc.reflow("Since "), alloc.reflow("Since "),
alloc.module(module_id), alloc.module(module_id),
@ -97,7 +98,7 @@ pub fn can_problem<'b>(
alloc.symbol_unqualified(argument_symbol), alloc.symbol_unqualified(argument_symbol),
alloc.text("."), alloc.text("."),
]), ]),
alloc.region(region), alloc.region(lines.convert_region(region)),
alloc.concat(vec![ alloc.concat(vec![
alloc.reflow("If you don't need "), alloc.reflow("If you don't need "),
alloc.symbol_unqualified(argument_symbol), alloc.symbol_unqualified(argument_symbol),
@ -137,7 +138,7 @@ pub fn can_problem<'b>(
)), )),
]) ])
}, },
alloc.region(region), alloc.region(lines.convert_region(region)),
]); ]);
title = SYNTAX_PROBLEM.to_string(); title = SYNTAX_PROBLEM.to_string();
@ -146,7 +147,7 @@ pub fn can_problem<'b>(
Problem::UnsupportedPattern(BadPattern::UnderscoreInDef, region) => { Problem::UnsupportedPattern(BadPattern::UnderscoreInDef, region) => {
doc = alloc.stack(vec![ doc = alloc.stack(vec![
alloc.reflow("Underscore patterns are not allowed in definitions"), alloc.reflow("Underscore patterns are not allowed in definitions"),
alloc.region(region), alloc.region(lines.convert_region(region)),
]); ]);
title = SYNTAX_PROBLEM.to_string(); title = SYNTAX_PROBLEM.to_string();
@ -176,7 +177,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(region), alloc.region(lines.convert_region(region)),
alloc.concat(suggestion), alloc.concat(suggestion),
]); ]);
@ -187,13 +188,13 @@ pub fn can_problem<'b>(
original_region, original_region,
shadow, shadow,
} => { } => {
doc = report_shadowing(alloc, original_region, shadow); doc = report_shadowing(alloc, lines, original_region, shadow);
title = DUPLICATE_NAME.to_string(); title = DUPLICATE_NAME.to_string();
severity = Severity::RuntimeError; severity = Severity::RuntimeError;
} }
Problem::CyclicAlias(symbol, region, others) => { Problem::CyclicAlias(symbol, region, others) => {
let answer = crate::error::r#type::cyclic_alias(alloc, symbol, region, others); let answer = crate::error::r#type::cyclic_alias(alloc, lines, symbol, region, others);
doc = answer.0; doc = answer.0;
title = answer.1; title = answer.1;
@ -212,7 +213,7 @@ pub fn can_problem<'b>(
alloc.symbol_unqualified(alias), alloc.symbol_unqualified(alias),
alloc.reflow(" alias definition:"), alloc.reflow(" alias definition:"),
]), ]),
alloc.region(variable_region), alloc.region(lines.convert_region(variable_region)),
alloc.reflow("Roc does not allow unused type alias parameters!"), alloc.reflow("Roc does not allow unused type alias 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(
@ -225,7 +226,7 @@ pub fn can_problem<'b>(
severity = Severity::RuntimeError; severity = Severity::RuntimeError;
} }
Problem::BadRecursion(entries) => { Problem::BadRecursion(entries) => {
doc = to_circular_def_doc(alloc, &entries); doc = to_circular_def_doc(alloc, lines, &entries);
title = CIRCULAR_DEF.to_string(); title = CIRCULAR_DEF.to_string();
severity = Severity::RuntimeError; severity = Severity::RuntimeError;
} }
@ -242,16 +243,16 @@ pub fn can_problem<'b>(
alloc.reflow(" field twice!"), alloc.reflow(" field twice!"),
]), ]),
alloc.region_all_the_things( alloc.region_all_the_things(
record_region, lines.convert_region(record_region),
replaced_region, lines.convert_region(replaced_region),
field_region, lines.convert_region(field_region),
Annotation::Error, Annotation::Error,
), ),
alloc.reflow(r"In the rest of the program, I will only use the latter definition:"), alloc.reflow(r"In the rest of the program, I will only use the latter definition:"),
alloc.region_all_the_things( alloc.region_all_the_things(
record_region, lines.convert_region(record_region),
field_region, lines.convert_region(field_region),
field_region, lines.convert_region(field_region),
Annotation::TypoSuggestion, Annotation::TypoSuggestion,
), ),
alloc.concat(vec![ alloc.concat(vec![
@ -271,6 +272,7 @@ pub fn can_problem<'b>(
} => { } => {
return to_invalid_optional_value_report( return to_invalid_optional_value_report(
alloc, alloc,
lines,
filename, filename,
field_name, field_name,
field_region, field_region,
@ -290,16 +292,16 @@ pub fn can_problem<'b>(
alloc.reflow(" field twice!"), alloc.reflow(" field twice!"),
]), ]),
alloc.region_all_the_things( alloc.region_all_the_things(
record_region, lines.convert_region(record_region),
replaced_region, lines.convert_region(replaced_region),
field_region, lines.convert_region(field_region),
Annotation::Error, Annotation::Error,
), ),
alloc.reflow("In the rest of the program, I will only use the latter definition:"), alloc.reflow("In the rest of the program, I will only use the latter definition:"),
alloc.region_all_the_things( alloc.region_all_the_things(
record_region, lines.convert_region(record_region),
field_region, lines.convert_region(field_region),
field_region, lines.convert_region(field_region),
Annotation::TypoSuggestion, Annotation::TypoSuggestion,
), ),
alloc.concat(vec![ alloc.concat(vec![
@ -325,16 +327,16 @@ pub fn can_problem<'b>(
alloc.reflow(" tag twice!"), alloc.reflow(" tag twice!"),
]), ]),
alloc.region_all_the_things( alloc.region_all_the_things(
tag_union_region, lines.convert_region(tag_union_region),
replaced_region, lines.convert_region(replaced_region),
tag_region, lines.convert_region(tag_region),
Annotation::Error, Annotation::Error,
), ),
alloc.reflow("In the rest of the program, I will only use the latter definition:"), alloc.reflow("In the rest of the program, I will only use the latter definition:"),
alloc.region_all_the_things( alloc.region_all_the_things(
tag_union_region, lines.convert_region(tag_union_region),
tag_region, lines.convert_region(tag_region),
tag_region, lines.convert_region(tag_region),
Annotation::TypoSuggestion, Annotation::TypoSuggestion,
), ),
alloc.concat(vec![ alloc.concat(vec![
@ -355,7 +357,8 @@ pub fn can_problem<'b>(
alloc.reflow( alloc.reflow(
"This annotation does not match the definition immediately following it:", "This annotation does not match the definition immediately following it:",
), ),
alloc.region(Region::span_across(annotation_pattern, def_pattern)), alloc.region(lines.convert_region(
Region::span_across(annotation_pattern, def_pattern))),
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."),
]); ]);
@ -369,7 +372,7 @@ pub fn can_problem<'b>(
alloc.symbol_unqualified(alias_name), alloc.symbol_unqualified(alias_name),
alloc.reflow(" is not what I expect:"), alloc.reflow(" is not what I expect:"),
]), ]),
alloc.region(region), alloc.region(lines.convert_region(region)),
alloc.concat(vec![ alloc.concat(vec![
alloc.reflow("Only type variables like "), alloc.reflow("Only type variables like "),
alloc.type_variable("a".into()), alloc.type_variable("a".into()),
@ -385,7 +388,7 @@ pub fn can_problem<'b>(
Problem::InvalidHexadecimal(region) => { Problem::InvalidHexadecimal(region) => {
doc = alloc.stack(vec![ doc = alloc.stack(vec![
alloc.reflow("This unicode code point is invalid:"), alloc.reflow("This unicode code point is invalid:"),
alloc.region(region), alloc.region(lines.convert_region(region)),
alloc.concat(vec![ alloc.concat(vec![
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)"),
@ -402,7 +405,7 @@ pub fn can_problem<'b>(
Problem::InvalidUnicodeCodePt(region) => { Problem::InvalidUnicodeCodePt(region) => {
doc = alloc.stack(vec![ doc = alloc.stack(vec![
alloc.reflow("This unicode code point is invalid:"), alloc.reflow("This unicode code point is invalid:"),
alloc.region(region), alloc.region(lines.convert_region(region)),
alloc.reflow("Learn more about working with unicode in roc at TODO"), alloc.reflow("Learn more about working with unicode in roc at TODO"),
]); ]);
@ -412,7 +415,7 @@ pub fn can_problem<'b>(
Problem::InvalidInterpolation(region) => { Problem::InvalidInterpolation(region) => {
doc = alloc.stack(vec![ doc = alloc.stack(vec![
alloc.reflow("This string interpolation is invalid:"), alloc.reflow("This string interpolation is invalid:"),
alloc.region(region), alloc.region(lines.convert_region(region)),
alloc.concat(vec![ alloc.concat(vec![
alloc.reflow(r"I was expecting an identifier, like "), alloc.reflow(r"I was expecting an identifier, like "),
alloc.parser_suggestion("\\u(message)"), alloc.parser_suggestion("\\u(message)"),
@ -427,7 +430,7 @@ pub fn can_problem<'b>(
severity = Severity::RuntimeError; severity = Severity::RuntimeError;
} }
Problem::RuntimeError(runtime_error) => { Problem::RuntimeError(runtime_error) => {
let answer = pretty_runtime_error(alloc, runtime_error); let answer = pretty_runtime_error(alloc, lines, runtime_error);
doc = answer.0; doc = answer.0;
title = answer.1.to_string(); title = answer.1.to_string();
@ -445,12 +448,13 @@ pub fn can_problem<'b>(
fn to_invalid_optional_value_report<'b>( fn to_invalid_optional_value_report<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
lines: &LineInfo,
filename: PathBuf, filename: PathBuf,
field_name: Lowercase, field_name: Lowercase,
field_region: Region, field_region: Region,
record_region: Region, record_region: Region,
) -> Report { ) -> Report<'b> {
let doc = to_invalid_optional_value_report_help(alloc, field_name, field_region, record_region); let doc = to_invalid_optional_value_report_help(alloc, lines, field_name, field_region, record_region);
Report { Report {
title: "BAD OPTIONAL VALUE".to_string(), title: "BAD OPTIONAL VALUE".to_string(),
@ -462,6 +466,7 @@ fn to_invalid_optional_value_report<'b>(
fn to_invalid_optional_value_report_help<'b>( fn to_invalid_optional_value_report_help<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
lines: &LineInfo,
field_name: Lowercase, field_name: Lowercase,
field_region: Region, field_region: Region,
record_region: Region, record_region: Region,
@ -472,7 +477,7 @@ fn to_invalid_optional_value_report_help<'b>(
alloc.record_field(field_name), alloc.record_field(field_name),
alloc.reflow(" field in an incorrect context!"), alloc.reflow(" field in an incorrect context!"),
]), ]),
alloc.region_all_the_things(record_region, field_region, field_region, Annotation::Error), alloc.region_all_the_things(lines.convert_region(record_region), lines.convert_region(field_region), lines.convert_region(field_region), Annotation::Error),
alloc.reflow(r"You can only use optional values in record destructuring, like:"), alloc.reflow(r"You can only use optional values in record destructuring, like:"),
alloc alloc
.reflow(r"{ answer ? 42, otherField } = myRecord") .reflow(r"{ answer ? 42, otherField } = myRecord")
@ -482,6 +487,7 @@ fn to_invalid_optional_value_report_help<'b>(
fn to_bad_ident_expr_report<'b>( fn to_bad_ident_expr_report<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
lines: &LineInfo,
bad_ident: roc_parse::ident::BadIdent, bad_ident: roc_parse::ident::BadIdent,
surroundings: Region, surroundings: Region,
) -> RocDocBuilder<'b> { ) -> RocDocBuilder<'b> {
@ -494,7 +500,7 @@ fn to_bad_ident_expr_report<'b>(
alloc.stack(vec![ alloc.stack(vec![
alloc.reflow(r"I trying to parse a record field access here:"), alloc.reflow(r"I trying to parse a record field access here:"),
alloc.region_with_subregion(surroundings, region), alloc.region_with_subregion(lines.convert_region(surroundings), lines.convert_region(region)),
alloc.concat(vec![ alloc.concat(vec![
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"),
@ -507,7 +513,7 @@ fn to_bad_ident_expr_report<'b>(
WeirdAccessor(_pos) => alloc.stack(vec![ WeirdAccessor(_pos) => alloc.stack(vec![
alloc.reflow("I am very confused by this field access"), alloc.reflow("I am very confused by this field access"),
alloc.region(surroundings), alloc.region(lines.convert_region(surroundings)),
alloc.concat(vec![ alloc.concat(vec![
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"),
@ -525,7 +531,7 @@ fn to_bad_ident_expr_report<'b>(
alloc.stack(vec![ alloc.stack(vec![
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(surroundings, region), alloc.region_with_subregion(lines.convert_region(surroundings), lines.convert_region(region)),
alloc.concat(vec![ alloc.concat(vec![
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"),
@ -540,7 +546,7 @@ fn to_bad_ident_expr_report<'b>(
alloc.stack(vec![ alloc.stack(vec![
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(surroundings, region), alloc.region_with_subregion(lines.convert_region(surroundings), lines.convert_region(region)),
alloc.concat(vec![ alloc.concat(vec![
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! "),
@ -555,7 +561,7 @@ fn to_bad_ident_expr_report<'b>(
let region = Region::new(surroundings.start(), pos); let region = Region::new(surroundings.start(), pos);
alloc.stack(vec![ alloc.stack(vec![
alloc.reflow("Underscores are not allowed in identifier names:"), alloc.reflow("Underscores are not allowed in identifier names:"),
alloc.region_with_subregion(surroundings, region), alloc.region_with_subregion(lines.convert_region(surroundings), lines.convert_region(region)),
alloc.concat(vec![alloc.reflow( alloc.concat(vec![alloc.reflow(
r"I recommend using camelCase, it is the standard in the Roc ecosystem.", r"I recommend using camelCase, it is the standard in the Roc ecosystem.",
)]), )]),
@ -569,7 +575,7 @@ fn to_bad_ident_expr_report<'b>(
let region = Region::new(pos, pos.bump_column(width)); let region = Region::new(pos, pos.bump_column(width));
alloc.stack(vec![ alloc.stack(vec![
alloc.reflow("I am very confused by this field access:"), alloc.reflow("I am very confused by this field access:"),
alloc.region_with_subregion(surroundings, region), alloc.region_with_subregion(lines.convert_region(surroundings), lines.convert_region(region)),
alloc.concat(vec![ alloc.concat(vec![
alloc.reflow(r"It looks like a record field access on a private tag.") alloc.reflow(r"It looks like a record field access on a private tag.")
]), ]),
@ -579,7 +585,7 @@ fn to_bad_ident_expr_report<'b>(
let region = Region::new(pos, pos.bump_column(width)); let region = Region::new(pos, pos.bump_column(width));
alloc.stack(vec![ alloc.stack(vec![
alloc.reflow("I am very confused by this expression:"), alloc.reflow("I am very confused by this expression:"),
alloc.region_with_subregion(surroundings, region), alloc.region_with_subregion(lines.convert_region(surroundings), lines.convert_region(region)),
alloc.concat(vec![ alloc.concat(vec![
alloc.reflow( alloc.reflow(
r"Looks like a private tag is treated like a module name. ", r"Looks like a private tag is treated like a module name. ",
@ -595,7 +601,7 @@ fn to_bad_ident_expr_report<'b>(
Region::new(surroundings.start().bump_column(1), pos.bump_column(1)); Region::new(surroundings.start().bump_column(1), pos.bump_column(1));
alloc.stack(vec![ alloc.stack(vec![
alloc.reflow("I am trying to parse a private tag here:"), alloc.reflow("I am trying to parse a private tag here:"),
alloc.region_with_subregion(surroundings, region), alloc.region_with_subregion(lines.convert_region(surroundings), lines.convert_region(region)),
alloc.concat(vec![ alloc.concat(vec![
alloc.reflow(r"But after the "), alloc.reflow(r"But after the "),
alloc.keyword("@"), alloc.keyword("@"),
@ -617,6 +623,7 @@ fn to_bad_ident_expr_report<'b>(
fn to_bad_ident_pattern_report<'b>( fn to_bad_ident_pattern_report<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
lines: &LineInfo,
bad_ident: roc_parse::ident::BadIdent, bad_ident: roc_parse::ident::BadIdent,
surroundings: Region, surroundings: Region,
) -> RocDocBuilder<'b> { ) -> RocDocBuilder<'b> {
@ -629,7 +636,7 @@ fn to_bad_ident_pattern_report<'b>(
alloc.stack(vec![ alloc.stack(vec![
alloc.reflow(r"I trying to parse a record field accessor here:"), alloc.reflow(r"I trying to parse a record field accessor here:"),
alloc.region_with_subregion(surroundings, region), alloc.region_with_subregion(lines.convert_region(surroundings), lines.convert_region(region)),
alloc.concat(vec![ alloc.concat(vec![
alloc.reflow("Something like "), alloc.reflow("Something like "),
alloc.parser_suggestion(".name"), alloc.parser_suggestion(".name"),
@ -642,7 +649,7 @@ fn to_bad_ident_pattern_report<'b>(
WeirdAccessor(_pos) => alloc.stack(vec![ WeirdAccessor(_pos) => alloc.stack(vec![
alloc.reflow("I am very confused by this field access"), alloc.reflow("I am very confused by this field access"),
alloc.region(surroundings), alloc.region(lines.convert_region(surroundings)),
alloc.concat(vec![ alloc.concat(vec![
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"),
@ -660,7 +667,7 @@ fn to_bad_ident_pattern_report<'b>(
alloc.stack(vec![ alloc.stack(vec![
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(surroundings, region), alloc.region_with_subregion(lines.convert_region(surroundings), lines.convert_region(region)),
alloc.concat(vec![ alloc.concat(vec![
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"),
@ -675,7 +682,7 @@ fn to_bad_ident_pattern_report<'b>(
alloc.stack(vec![ alloc.stack(vec![
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(surroundings, region), alloc.region_with_subregion(lines.convert_region(surroundings), lines.convert_region(region)),
alloc.concat(vec![ alloc.concat(vec![
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! "),
@ -694,7 +701,7 @@ fn to_bad_ident_pattern_report<'b>(
alloc.stack(vec![ alloc.stack(vec![
alloc.reflow("I am trying to parse an identifier here:"), alloc.reflow("I am trying to parse an identifier here:"),
alloc.region_with_subregion(surroundings, region), alloc.region_with_subregion(lines.convert_region(surroundings), lines.convert_region(region)),
alloc.concat(vec![alloc.reflow( alloc.concat(vec![alloc.reflow(
r"Underscores are not allowed in identifiers. Use camelCase instead!", r"Underscores are not allowed in identifiers. Use camelCase instead!",
)]), )]),
@ -770,6 +777,7 @@ where
fn report_shadowing<'b>( fn report_shadowing<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
lines: &LineInfo,
original_region: Region, original_region: Region,
shadow: Loc<Ident>, shadow: Loc<Ident>,
) -> RocDocBuilder<'b> { ) -> RocDocBuilder<'b> {
@ -780,15 +788,16 @@ 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(original_region), alloc.region(lines.convert_region(original_region)),
alloc.reflow("But then it's defined a second time here:"), alloc.reflow("But then it's defined a second time here:"),
alloc.region(shadow.region), alloc.region(lines.convert_region(shadow.region)),
alloc.reflow(line), alloc.reflow(line),
]) ])
} }
fn pretty_runtime_error<'b>( fn pretty_runtime_error<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
lines: &LineInfo,
runtime_error: RuntimeError, runtime_error: RuntimeError,
) -> (RocDocBuilder<'b>, &'static str) { ) -> (RocDocBuilder<'b>, &'static str) {
let doc; let doc;
@ -810,16 +819,16 @@ fn pretty_runtime_error<'b>(
original_region, original_region,
shadow, shadow,
} => { } => {
doc = report_shadowing(alloc, original_region, shadow); doc = report_shadowing(alloc, lines, original_region, shadow);
title = DUPLICATE_NAME; title = DUPLICATE_NAME;
} }
RuntimeError::LookupNotInScope(loc_name, options) => { RuntimeError::LookupNotInScope(loc_name, options) => {
doc = not_found(alloc, loc_name.region, &loc_name.value, "value", options); doc = not_found(alloc, lines, loc_name.region, &loc_name.value, "value", options);
title = UNRECOGNIZED_NAME; title = UNRECOGNIZED_NAME;
} }
RuntimeError::CircularDef(entries) => { RuntimeError::CircularDef(entries) => {
doc = to_circular_def_doc(alloc, &entries); doc = to_circular_def_doc(alloc, lines, &entries);
title = CIRCULAR_DEF; title = CIRCULAR_DEF;
} }
RuntimeError::MalformedPattern(problem, region) => { RuntimeError::MalformedPattern(problem, region) => {
@ -835,7 +844,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, bad_ident, region); doc = to_bad_ident_pattern_report(alloc, lines, bad_ident, region);
return (doc, title); return (doc, title);
} }
@ -859,7 +868,7 @@ fn pretty_runtime_error<'b>(
alloc.text(name), alloc.text(name),
alloc.reflow("pattern is malformed:"), alloc.reflow("pattern is malformed:"),
]), ]),
alloc.region(region), alloc.region(lines.convert_region(region)),
tip, tip,
]); ]);
@ -900,7 +909,7 @@ fn pretty_runtime_error<'b>(
alloc.string(ident.to_string()), alloc.string(ident.to_string()),
alloc.reflow("`:"), alloc.reflow("`:"),
]), ]),
alloc.region(region), alloc.region(lines.convert_region(region)),
did_you_mean, did_you_mean,
]); ]);
@ -912,7 +921,7 @@ fn pretty_runtime_error<'b>(
imported_modules, imported_modules,
region, region,
} => { } => {
doc = module_not_found(alloc, region, &module_name, imported_modules); doc = module_not_found(alloc, lines, region, &module_name, imported_modules);
title = MODULE_NOT_IMPORTED; title = MODULE_NOT_IMPORTED;
} }
@ -921,14 +930,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, bad_ident, surroundings); doc = to_bad_ident_expr_report(alloc, lines, bad_ident, surroundings);
title = SYNTAX_PROBLEM; title = SYNTAX_PROBLEM;
} }
RuntimeError::MalformedTypeName(_box_str, surroundings) => { RuntimeError::MalformedTypeName(_box_str, surroundings) => {
doc = alloc.stack(vec![ doc = alloc.stack(vec![
alloc.reflow(r"I am confused by this type name:"), alloc.reflow(r"I am confused by this type name:"),
alloc.region(surroundings), alloc.region(lines.convert_region(surroundings)),
alloc.concat(vec![ alloc.concat(vec![
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 "),
@ -962,7 +971,7 @@ fn pretty_runtime_error<'b>(
alloc.text(big_or_small), alloc.text(big_or_small),
alloc.reflow(":"), alloc.reflow(":"),
]), ]),
alloc.region(region), alloc.region(lines.convert_region(region)),
alloc.concat(vec![ alloc.concat(vec![
alloc alloc
.reflow("Roc uses signed 64-bit floating points, allowing values between "), .reflow("Roc uses signed 64-bit floating points, allowing values between "),
@ -984,7 +993,7 @@ fn pretty_runtime_error<'b>(
alloc.concat(vec![ alloc.concat(vec![
alloc.reflow("This float literal contains an invalid digit:"), alloc.reflow("This float literal contains an invalid digit:"),
]), ]),
alloc.region(region), alloc.region(lines.convert_region(region)),
alloc.concat(vec![ alloc.concat(vec![
alloc.reflow("Floating point literals can only contain the digits 0-9, or use scientific notation 10e4"), alloc.reflow("Floating point literals can only contain the digits 0-9, or use scientific notation 10e4"),
]), ]),
@ -1042,7 +1051,7 @@ fn pretty_runtime_error<'b>(
alloc.text(problem), alloc.text(problem),
alloc.text(":"), alloc.text(":"),
]), ]),
alloc.region(region), alloc.region(lines.convert_region(region)),
alloc.concat(vec![ alloc.concat(vec![
alloc.text(plurals), alloc.text(plurals),
contains, contains,
@ -1072,7 +1081,7 @@ fn pretty_runtime_error<'b>(
alloc.text(big_or_small), alloc.text(big_or_small),
alloc.reflow(":"), alloc.reflow(":"),
]), ]),
alloc.region(region), alloc.region(lines.convert_region(region)),
alloc.reflow("Roc uses signed 64-bit integers, allowing values between 9_223_372_036_854_775_808 and 9_223_372_036_854_775_807."), alloc.reflow("Roc uses signed 64-bit integers, allowing values between 9_223_372_036_854_775_808 and 9_223_372_036_854_775_807."),
tip, tip,
]); ]);
@ -1086,6 +1095,7 @@ fn pretty_runtime_error<'b>(
} => { } => {
doc = to_invalid_optional_value_report_help( doc = to_invalid_optional_value_report_help(
alloc, alloc,
lines,
field_name, field_name,
field_region, field_region,
record_region, record_region,
@ -1099,7 +1109,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(region), alloc.region(lines.convert_region(region)),
alloc.reflow("Only variables can be updated with record update syntax."), alloc.reflow("Only variables can be updated with record update syntax."),
]); ]);
@ -1147,6 +1157,7 @@ fn pretty_runtime_error<'b>(
fn to_circular_def_doc<'b>( fn to_circular_def_doc<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
lines: &LineInfo,
entries: &[roc_problem::can::CycleEntry], entries: &[roc_problem::can::CycleEntry],
) -> RocDocBuilder<'b> { ) -> RocDocBuilder<'b> {
// TODO "are you trying to mutate a variable? // TODO "are you trying to mutate a variable?
@ -1165,7 +1176,7 @@ 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(first.symbol_region), alloc.region(lines.convert_region(first.symbol_region)),
alloc alloc
.reflow("The ") .reflow("The ")
.append(alloc.symbol_unqualified(first.symbol)) .append(alloc.symbol_unqualified(first.symbol))
@ -1189,6 +1200,7 @@ fn to_circular_def_doc<'b>(
fn not_found<'b>( fn not_found<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
lines: &LineInfo,
region: roc_region::all::Region, region: roc_region::all::Region,
name: &Ident, name: &Ident,
thing: &'b str, thing: &'b str,
@ -1230,13 +1242,14 @@ fn not_found<'b>(
alloc.reflow("` "), alloc.reflow("` "),
alloc.reflow(thing), alloc.reflow(thing),
]), ]),
alloc.region(region), alloc.region(lines.convert_region(region)),
to_details(default_no, default_yes), to_details(default_no, default_yes),
]) ])
} }
fn module_not_found<'b>( fn module_not_found<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
lines: &LineInfo,
region: roc_region::all::Region, region: roc_region::all::Region,
name: &ModuleName, name: &ModuleName,
options: MutSet<Box<str>>, options: MutSet<Box<str>>,
@ -1275,7 +1288,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(region), alloc.region(lines.convert_region(region)),
to_details(default_no, default_yes), to_details(default_no, default_yes),
]) ])
} }

View file

@ -1,9 +1,11 @@
use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder, Severity}; use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder, Severity};
use std::path::PathBuf; use std::path::PathBuf;
use roc_region::all::LineInfo;
use ven_pretty::DocAllocator; use ven_pretty::DocAllocator;
pub fn mono_problem<'b>( pub fn mono_problem<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
lines: &LineInfo,
filename: PathBuf, filename: PathBuf,
problem: roc_mono::ir::MonoProblem, problem: roc_mono::ir::MonoProblem,
) -> Report<'b> { ) -> Report<'b> {
@ -16,7 +18,7 @@ pub fn mono_problem<'b>(
BadArg => { BadArg => {
let doc = alloc.stack(vec![ let doc = alloc.stack(vec![
alloc.reflow("This pattern does not cover all the possibilities:"), alloc.reflow("This pattern does not cover all the possibilities:"),
alloc.region(region), alloc.region(lines.convert_region(region)),
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(vec![ alloc.concat(vec![
@ -39,7 +41,7 @@ pub fn mono_problem<'b>(
BadDestruct => { BadDestruct => {
let doc = alloc.stack(vec![ let doc = alloc.stack(vec![
alloc.reflow("This pattern does not cover all the possibilities:"), alloc.reflow("This pattern does not cover all the possibilities:"),
alloc.region(region), alloc.region(lines.convert_region(region)),
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(vec![ alloc.concat(vec![
@ -67,7 +69,7 @@ pub fn mono_problem<'b>(
alloc.keyword("when"), alloc.keyword("when"),
alloc.reflow(" does not cover all the possibilities:"), alloc.reflow(" does not cover all the possibilities:"),
]), ]),
alloc.region(region), alloc.region(lines.convert_region(region)),
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(
@ -96,7 +98,7 @@ pub fn mono_problem<'b>(
alloc.string(index.ordinal()), alloc.string(index.ordinal()),
alloc.reflow(" pattern is redundant:"), alloc.reflow(" pattern is redundant:"),
]), ]),
alloc.region_with_subregion(overall_region, branch_region), alloc.region_with_subregion(lines.convert_region(overall_region), lines.convert_region(branch_region)),
alloc.reflow( alloc.reflow(
"Any value of this shape will be handled by \ "Any value of this shape will be handled by \
a previous pattern, so this one should be removed.", a previous pattern, so this one should be removed.",

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,7 @@ use roc_collections::all::{Index, MutSet, SendMap};
use roc_module::called_via::{BinOp, CalledVia}; use roc_module::called_via::{BinOp, CalledVia};
use roc_module::ident::{Ident, IdentStr, Lowercase, TagName}; use roc_module::ident::{Ident, IdentStr, Lowercase, TagName};
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region, LineInfo};
use roc_solve::solve; use roc_solve::solve;
use roc_types::pretty_print::{Parens, WILDCARD}; use roc_types::pretty_print::{Parens, WILDCARD};
use roc_types::types::{Category, ErrorType, PatternCategory, Reason, RecordField, TypeExt}; use roc_types::types::{Category, ErrorType, PatternCategory, Reason, RecordField, TypeExt};
@ -18,6 +18,7 @@ const ADD_ANNOTATIONS: &str = r#"Can more type annotations be added? Type annota
pub fn type_problem<'b>( pub fn type_problem<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
lines: &LineInfo,
filename: PathBuf, filename: PathBuf,
problem: solve::TypeError, problem: solve::TypeError,
) -> Option<Report<'b>> { ) -> Option<Report<'b>> {
@ -34,13 +35,14 @@ pub fn type_problem<'b>(
match problem { match problem {
BadExpr(region, category, found, expected) => Some(to_expr_report( BadExpr(region, category, found, expected) => Some(to_expr_report(
alloc, filename, region, category, found, expected, alloc, lines, filename, region, category, found, expected,
)), )),
BadPattern(region, category, found, expected) => Some(to_pattern_report( BadPattern(region, category, found, expected) => Some(to_pattern_report(
alloc, filename, region, category, found, expected, alloc, lines, filename, region, category, found, expected,
)), )),
CircularType(region, symbol, overall_type) => Some(to_circular_report( CircularType(region, symbol, overall_type) => Some(to_circular_report(
alloc, alloc,
lines,
filename, filename,
region, region,
symbol, symbol,
@ -87,7 +89,7 @@ pub fn type_problem<'b>(
found_arguments, found_arguments,
alloc.reflow(" instead:"), alloc.reflow(" instead:"),
]), ]),
alloc.region(region), alloc.region(lines.convert_region(region)),
alloc.reflow("Are there missing parentheses?"), alloc.reflow("Are there missing parentheses?"),
]); ]);
@ -100,7 +102,7 @@ pub fn type_problem<'b>(
report(title, doc, filename) report(title, doc, filename)
} }
CyclicAlias(symbol, region, others) => { CyclicAlias(symbol, region, others) => {
let (doc, title) = cyclic_alias(alloc, symbol, region, others); let (doc, title) = cyclic_alias(alloc, lines, symbol, region, others);
report(title, doc, filename) report(title, doc, filename)
} }
@ -108,7 +110,7 @@ pub fn type_problem<'b>(
SolvedTypeError => None, // Don't re-report cascading errors - see https://github.com/rtfeldman/roc/pull/1711 SolvedTypeError => None, // Don't re-report cascading errors - see https://github.com/rtfeldman/roc/pull/1711
Shadowed(original_region, shadow) => { Shadowed(original_region, shadow) => {
let doc = report_shadowing(alloc, original_region, shadow); let doc = report_shadowing(alloc, lines, original_region, shadow);
let title = DUPLICATE_NAME.to_string(); let title = DUPLICATE_NAME.to_string();
report(title, doc, filename) report(title, doc, filename)
@ -122,6 +124,7 @@ pub fn type_problem<'b>(
fn report_shadowing<'b>( fn report_shadowing<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
lines: &LineInfo,
original_region: Region, original_region: Region,
shadow: Loc<Ident>, shadow: Loc<Ident>,
) -> RocDocBuilder<'b> { ) -> RocDocBuilder<'b> {
@ -132,15 +135,16 @@ 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(original_region), alloc.region(lines.convert_region(original_region)),
alloc.reflow("But then it's defined a second time here:"), alloc.reflow("But then it's defined a second time here:"),
alloc.region(shadow.region), alloc.region(lines.convert_region(shadow.region)),
alloc.reflow(line), alloc.reflow(line),
]) ])
} }
pub fn cyclic_alias<'b>( pub fn cyclic_alias<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
lines: &LineInfo,
symbol: Symbol, symbol: Symbol,
region: roc_region::all::Region, region: roc_region::all::Region,
others: Vec<Symbol>, others: Vec<Symbol>,
@ -151,7 +155,7 @@ pub fn cyclic_alias<'b>(
.reflow("The ") .reflow("The ")
.append(alloc.symbol_unqualified(symbol)) .append(alloc.symbol_unqualified(symbol))
.append(alloc.reflow(" alias is self-recursive in an invalid way:")), .append(alloc.reflow(" alias is self-recursive in an invalid way:")),
alloc.region(region), alloc.region(lines.convert_region(region)),
alloc.reflow("Recursion in aliases is only allowed if recursion happens behind a tag."), alloc.reflow("Recursion in aliases is only allowed if recursion happens behind a tag."),
]) ])
} else { } else {
@ -160,7 +164,7 @@ pub fn cyclic_alias<'b>(
.reflow("The ") .reflow("The ")
.append(alloc.symbol_unqualified(symbol)) .append(alloc.symbol_unqualified(symbol))
.append(alloc.reflow(" alias is recursive in an invalid way:")), .append(alloc.reflow(" alias is recursive in an invalid way:")),
alloc.region(region), alloc.region(lines.convert_region(region)),
alloc alloc
.reflow("The ") .reflow("The ")
.append(alloc.symbol_unqualified(symbol)) .append(alloc.symbol_unqualified(symbol))
@ -186,6 +190,7 @@ pub fn cyclic_alias<'b>(
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn report_mismatch<'b>( fn report_mismatch<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
lines: &LineInfo,
filename: PathBuf, filename: PathBuf,
category: &Category, category: &Category,
found: ErrorType, found: ErrorType,
@ -198,9 +203,9 @@ fn report_mismatch<'b>(
further_details: Option<RocDocBuilder<'b>>, further_details: Option<RocDocBuilder<'b>>,
) -> Report<'b> { ) -> Report<'b> {
let snippet = if let Some(highlight) = opt_highlight { let snippet = if let Some(highlight) = opt_highlight {
alloc.region_with_subregion(highlight, region) alloc.region_with_subregion(lines.convert_region(highlight), lines.convert_region(region))
} else { } else {
alloc.region(region) alloc.region(lines.convert_region(region))
}; };
let lines = vec![ let lines = vec![
problem, problem,
@ -227,6 +232,7 @@ fn report_mismatch<'b>(
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn report_bad_type<'b>( fn report_bad_type<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
lines: &LineInfo,
filename: PathBuf, filename: PathBuf,
category: &Category, category: &Category,
found: ErrorType, found: ErrorType,
@ -238,9 +244,9 @@ fn report_bad_type<'b>(
further_details: RocDocBuilder<'b>, further_details: RocDocBuilder<'b>,
) -> Report<'b> { ) -> Report<'b> {
let snippet = if let Some(highlight) = opt_highlight { let snippet = if let Some(highlight) = opt_highlight {
alloc.region_with_subregion(highlight, region) alloc.region_with_subregion(lines.convert_region(highlight), lines.convert_region(region))
} else { } else {
alloc.region(region) alloc.region(lines.convert_region(region))
}; };
let lines = vec![ let lines = vec![
problem, problem,
@ -285,6 +291,7 @@ fn lowercase_first(s: &str) -> String {
fn to_expr_report<'b>( fn to_expr_report<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
lines: &LineInfo,
filename: PathBuf, filename: PathBuf,
expr_region: roc_region::all::Region, expr_region: roc_region::all::Region,
category: Category, category: Category,
@ -308,7 +315,7 @@ fn to_expr_report<'b>(
title: "TYPE MISMATCH".to_string(), title: "TYPE MISMATCH".to_string(),
doc: alloc.stack(vec![ doc: alloc.stack(vec![
alloc.text("This expression is used in an unexpected way:"), alloc.text("This expression is used in an unexpected way:"),
alloc.region(expr_region), alloc.region(lines.convert_region(expr_region)),
comparison, comparison,
]), ]),
severity: Severity::RuntimeError, severity: Severity::RuntimeError,
@ -421,7 +428,7 @@ fn to_expr_report<'b>(
// for typed bodies, include the line(s) with the signature // for typed bodies, include the line(s) with the signature
let joined = let joined =
roc_region::all::Region::span_across(&ann_region, &expr_region); roc_region::all::Region::span_across(&ann_region, &expr_region);
alloc.region_with_subregion(joined, expr_region) alloc.region_with_subregion(lines.convert_region(joined), lines.convert_region(expr_region))
}, },
comparison, comparison,
]), ]),
@ -440,6 +447,7 @@ fn to_expr_report<'b>(
report_bad_type( report_bad_type(
alloc, alloc,
lines,
filename, filename,
&category, &category,
found, found,
@ -478,6 +486,7 @@ fn to_expr_report<'b>(
report_bad_type( report_bad_type(
alloc, alloc,
lines,
filename, filename,
&category, &category,
found, found,
@ -515,6 +524,7 @@ fn to_expr_report<'b>(
]); ]);
report_bad_type( report_bad_type(
alloc, alloc,
lines,
filename, filename,
&category, &category,
found, found,
@ -542,6 +552,7 @@ fn to_expr_report<'b>(
} => match total_branches { } => match total_branches {
2 => report_mismatch( 2 => report_mismatch(
alloc, alloc,
lines,
filename, filename,
&category, &category,
found, found,
@ -575,6 +586,7 @@ fn to_expr_report<'b>(
), ),
_ => report_mismatch( _ => report_mismatch(
alloc, alloc,
lines,
filename, filename,
&category, &category,
found, found,
@ -599,6 +611,7 @@ fn to_expr_report<'b>(
}, },
Reason::WhenBranch { index } => report_mismatch( Reason::WhenBranch { index } => report_mismatch(
alloc, alloc,
lines,
filename, filename,
&category, &category,
found, found,
@ -637,6 +650,7 @@ fn to_expr_report<'b>(
report_mismatch( report_mismatch(
alloc, alloc,
lines,
filename, filename,
&category, &category,
found, found,
@ -651,6 +665,7 @@ fn to_expr_report<'b>(
} }
Reason::RecordUpdateValue(field) => report_mismatch( Reason::RecordUpdateValue(field) => report_mismatch(
alloc, alloc,
lines,
filename, filename,
&category, &category,
found, found,
@ -685,6 +700,7 @@ fn to_expr_report<'b>(
match diff.next().and_then(|k| Some((k, expected_fields.get(k)?))) { match diff.next().and_then(|k| Some((k, expected_fields.get(k)?))) {
None => report_mismatch( None => report_mismatch(
alloc, alloc,
lines,
filename, filename,
&category, &category,
found, found,
@ -719,7 +735,7 @@ fn to_expr_report<'b>(
let doc = alloc.stack(vec![ let doc = alloc.stack(vec![
header, header,
alloc.region(*field_region), alloc.region(lines.convert_region(*field_region)),
if suggestions.is_empty() { if suggestions.is_empty() {
alloc.concat(vec![ alloc.concat(vec![
alloc.reflow("In fact, "), alloc.reflow("In fact, "),
@ -764,6 +780,7 @@ fn to_expr_report<'b>(
} }
_ => report_bad_type( _ => report_bad_type(
alloc, alloc,
lines,
filename, filename,
&category, &category,
found, found,
@ -798,7 +815,7 @@ fn to_expr_report<'b>(
} }
)), )),
]), ]),
alloc.region(expr_region), alloc.region(lines.convert_region(expr_region)),
alloc.reflow("Are there any missing commas? Or missing parentheses?"), alloc.reflow("Are there any missing commas? Or missing parentheses?"),
]; ];
@ -833,7 +850,7 @@ fn to_expr_report<'b>(
arity arity
)), )),
]), ]),
alloc.region(expr_region), alloc.region(lines.convert_region(expr_region)),
alloc.reflow("Are there any missing commas? Or missing parentheses?"), alloc.reflow("Are there any missing commas? Or missing parentheses?"),
]; ];
@ -857,7 +874,7 @@ fn to_expr_report<'b>(
arity arity
)), )),
]), ]),
alloc.region(expr_region), alloc.region(lines.convert_region(expr_region)),
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.",
@ -883,6 +900,7 @@ fn to_expr_report<'b>(
report_mismatch( report_mismatch(
alloc, alloc,
lines,
filename, filename,
&category, &category,
found, found,
@ -1230,6 +1248,7 @@ fn add_category<'b>(
fn to_pattern_report<'b>( fn to_pattern_report<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
lines: &LineInfo,
filename: PathBuf, filename: PathBuf,
expr_region: roc_region::all::Region, expr_region: roc_region::all::Region,
category: PatternCategory, category: PatternCategory,
@ -1242,7 +1261,7 @@ fn to_pattern_report<'b>(
PExpected::NoExpectation(expected_type) => { PExpected::NoExpectation(expected_type) => {
let doc = alloc.stack(vec![ let doc = alloc.stack(vec![
alloc.text("This pattern is being used in an unexpected way:"), alloc.text("This pattern is being used in an unexpected way:"),
alloc.region(expr_region), alloc.region(lines.convert_region(expr_region)),
pattern_type_comparison( pattern_type_comparison(
alloc, alloc,
found, found,
@ -1275,7 +1294,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(region), alloc.region(lines.convert_region(region)),
pattern_type_comparison( pattern_type_comparison(
alloc, alloc,
found, found,
@ -1310,7 +1329,7 @@ fn to_pattern_report<'b>(
.text("The 1st pattern in this ") .text("The 1st pattern in this ")
.append(alloc.keyword("when")) .append(alloc.keyword("when"))
.append(alloc.text(" is causing a mismatch:")), .append(alloc.text(" is causing a mismatch:")),
alloc.region(region), alloc.region(lines.convert_region(region)),
pattern_type_comparison( pattern_type_comparison(
alloc, alloc,
found, found,
@ -1343,7 +1362,7 @@ fn to_pattern_report<'b>(
.string(format!("The {} pattern in this ", index.ordinal())) .string(format!("The {} pattern in this ", index.ordinal()))
.append(alloc.keyword("when")) .append(alloc.keyword("when"))
.append(alloc.text(" does not match the previous ones:")), .append(alloc.text(" does not match the previous ones:")),
alloc.region(region), alloc.region(lines.convert_region(region)),
pattern_type_comparison( pattern_type_comparison(
alloc, alloc,
found, found,
@ -1432,6 +1451,7 @@ fn add_pattern_category<'b>(
fn to_circular_report<'b>( fn to_circular_report<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
lines: &LineInfo,
filename: PathBuf, filename: PathBuf,
region: roc_region::all::Region, region: roc_region::all::Region,
symbol: Symbol, symbol: Symbol,
@ -1446,7 +1466,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(region), alloc.region(lines.convert_region(region)),
alloc.stack(vec![ alloc.stack(vec![
alloc.reflow( alloc.reflow(
"Here is my best effort at writing down the type. \ "Here is my best effort at writing down the type. \

View file

@ -1,6 +1,7 @@
use roc_module::ident::Ident; use roc_module::ident::Ident;
use roc_module::ident::{Lowercase, ModuleName, TagName, Uppercase}; use roc_module::ident::{Lowercase, ModuleName, TagName, Uppercase};
use roc_module::symbol::{Interns, ModuleId, Symbol}; use roc_module::symbol::{Interns, ModuleId, Symbol};
use roc_region::all::LineColumnRegion;
use std::fmt; use std::fmt;
use std::path::PathBuf; use std::path::PathBuf;
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder, Render, RenderAnnotated}; use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder, Render, RenderAnnotated};
@ -391,9 +392,9 @@ impl<'a> RocDocAllocator<'a> {
pub fn region_all_the_things( pub fn region_all_the_things(
&'a self, &'a self,
region: roc_region::all::Region, region: LineColumnRegion,
sub_region1: roc_region::all::Region, sub_region1: LineColumnRegion,
sub_region2: roc_region::all::Region, sub_region2: LineColumnRegion,
error_annotation: Annotation, error_annotation: Annotation,
) -> DocBuilder<'a, Self, Annotation> { ) -> DocBuilder<'a, Self, Annotation> {
debug_assert!(region.contains(&sub_region1)); debug_assert!(region.contains(&sub_region1));
@ -502,8 +503,8 @@ impl<'a> RocDocAllocator<'a> {
pub fn region_with_subregion( pub fn region_with_subregion(
&'a self, &'a self,
region: roc_region::all::Region, region: LineColumnRegion,
sub_region: roc_region::all::Region, sub_region: LineColumnRegion,
) -> DocBuilder<'a, Self, Annotation> { ) -> DocBuilder<'a, Self, Annotation> {
// debug_assert!(region.contains(&sub_region)); // debug_assert!(region.contains(&sub_region));
@ -588,13 +589,13 @@ impl<'a> RocDocAllocator<'a> {
result result
} }
pub fn region(&'a self, region: roc_region::all::Region) -> DocBuilder<'a, Self, Annotation> { pub fn region(&'a self, region: LineColumnRegion) -> DocBuilder<'a, Self, Annotation> {
self.region_with_subregion(region, region) self.region_with_subregion(region, region)
} }
pub fn region_without_error( pub fn region_without_error(
&'a self, &'a self,
region: roc_region::all::Region, region: LineColumnRegion,
) -> DocBuilder<'a, Self, Annotation> { ) -> DocBuilder<'a, Self, Annotation> {
let mut result = self.nil(); let mut result = self.nil();
for i in region.start().line..=region.end().line { for i in region.start().line..=region.end().line {

View file

@ -15,6 +15,7 @@ mod test_reporting {
use roc_module::symbol::{Interns, ModuleId}; use roc_module::symbol::{Interns, ModuleId};
use roc_mono::ir::{Procs, Stmt, UpdateModeIds}; use roc_mono::ir::{Procs, Stmt, UpdateModeIds};
use roc_mono::layout::LayoutCache; use roc_mono::layout::LayoutCache;
use roc_region::all::LineInfo;
use roc_reporting::report::{ use roc_reporting::report::{
can_problem, mono_problem, parse_problem, type_problem, Report, Severity, BLUE_CODE, can_problem, mono_problem, parse_problem, type_problem, Report, Severity, BLUE_CODE,
BOLD_CODE, CYAN_CODE, DEFAULT_PALETTE, GREEN_CODE, MAGENTA_CODE, RED_CODE, RESET_CODE, BOLD_CODE, CYAN_CODE, DEFAULT_PALETTE, GREEN_CODE, MAGENTA_CODE, RED_CODE, RESET_CODE,
@ -126,6 +127,7 @@ mod test_reporting {
use ven_pretty::DocAllocator; use ven_pretty::DocAllocator;
let src_lines: Vec<&str> = src.split('\n').collect(); let src_lines: Vec<&str> = src.split('\n').collect();
let lines = LineInfo::new(src);
let filename = filename_from_string(r"\code\proj\Main.roc"); let filename = filename_from_string(r"\code\proj\Main.roc");
@ -140,7 +142,7 @@ mod test_reporting {
let alloc = RocDocAllocator::new(&src_lines, home, &interns); let alloc = RocDocAllocator::new(&src_lines, home, &interns);
let problem = fail.into_parse_problem(filename.clone(), "", src.as_bytes()); let problem = fail.into_parse_problem(filename.clone(), "", src.as_bytes());
let doc = parse_problem(&alloc, filename, 0, problem); let doc = parse_problem(&alloc, &lines, filename, 0, problem);
callback(doc.pretty(&alloc).append(alloc.line()), buf) callback(doc.pretty(&alloc).append(alloc.line()), buf)
} }
@ -150,18 +152,18 @@ mod test_reporting {
let alloc = RocDocAllocator::new(&src_lines, home, &interns); let alloc = RocDocAllocator::new(&src_lines, home, &interns);
for problem in can_problems { for problem in can_problems {
let report = can_problem(&alloc, filename.clone(), problem.clone()); let report = can_problem(&alloc, &lines, filename.clone(), problem.clone());
reports.push(report); reports.push(report);
} }
for problem in type_problems { for problem in type_problems {
if let Some(report) = type_problem(&alloc, filename.clone(), problem.clone()) { if let Some(report) = type_problem(&alloc, &lines, filename.clone(), problem.clone()) {
reports.push(report); reports.push(report);
} }
} }
for problem in mono_problems { for problem in mono_problems {
let report = mono_problem(&alloc, filename.clone(), problem.clone()); let report = mono_problem(&alloc, &lines, filename.clone(), problem.clone());
reports.push(report); reports.push(report);
} }
@ -192,6 +194,7 @@ mod test_reporting {
let filename = filename_from_string(r"\code\proj\Main.roc"); let filename = filename_from_string(r"\code\proj\Main.roc");
let src_lines: Vec<&str> = src.split('\n').collect(); let src_lines: Vec<&str> = src.split('\n').collect();
let lines = LineInfo::new(src);
match roc_parse::module::parse_header(arena, state) { match roc_parse::module::parse_header(arena, state) {
Err(fail) => { Err(fail) => {
@ -206,7 +209,7 @@ mod test_reporting {
"", "",
src.as_bytes(), src.as_bytes(),
); );
let doc = parse_problem(&alloc, filename, 0, problem); let doc = parse_problem(&alloc, &lines, filename, 0, problem);
callback(doc.pretty(&alloc).append(alloc.line()), buf) callback(doc.pretty(&alloc).append(alloc.line()), buf)
} }