mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
refactor and clean up
This commit is contained in:
parent
604dbf7215
commit
1981a7e467
7 changed files with 355 additions and 241 deletions
|
@ -16,7 +16,7 @@ use roc_region::all::{Located, Region};
|
||||||
use roc_types::subs::Variable;
|
use roc_types::subs::Variable;
|
||||||
use roc_types::types::AnnotationSource::{self, *};
|
use roc_types::types::AnnotationSource::{self, *};
|
||||||
use roc_types::types::Type::{self, *};
|
use roc_types::types::Type::{self, *};
|
||||||
use roc_types::types::{Alias, Category, PReason, PatternCategory, Reason};
|
use roc_types::types::{Alias, Category, PReason, Reason};
|
||||||
|
|
||||||
/// This is for constraining Defs
|
/// This is for constraining Defs
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
|
|
|
@ -14,7 +14,7 @@ use roc_types::boolean_algebra::{Atom, Bool};
|
||||||
use roc_types::subs::{VarStore, Variable};
|
use roc_types::subs::{VarStore, Variable};
|
||||||
use roc_types::types::AnnotationSource::{self, *};
|
use roc_types::types::AnnotationSource::{self, *};
|
||||||
use roc_types::types::Type::{self, *};
|
use roc_types::types::Type::{self, *};
|
||||||
use roc_types::types::{Alias, Category, PReason, PatternCategory, Reason};
|
use roc_types::types::{Alias, Category, PReason, Reason};
|
||||||
use roc_uniq::builtins::{attr_type, empty_list_type, list_type, str_type};
|
use roc_uniq::builtins::{attr_type, empty_list_type, list_type, str_type};
|
||||||
use roc_uniq::sharing::{self, Container, FieldAccess, Mark, Usage, VarUsage};
|
use roc_uniq::sharing::{self, Container, FieldAccess, Mark, Usage, VarUsage};
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
use crate::report::ReportText::{Batch, BinOp, Module, Region, Value};
|
use crate::report::ReportText::{BinOp, Concat, Module, Region, Value};
|
||||||
use roc_can::expected::{Expected, PExpected};
|
|
||||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||||
use roc_problem::can::PrecedenceProblem::BothNonAssociative;
|
use roc_problem::can::PrecedenceProblem::BothNonAssociative;
|
||||||
use roc_problem::can::Problem;
|
use roc_problem::can::Problem;
|
||||||
use roc_solve::solve;
|
|
||||||
use roc_types::pretty_print::content_to_string;
|
use roc_types::pretty_print::content_to_string;
|
||||||
use roc_types::subs::{Content, Subs, Variable};
|
use roc_types::subs::{Content, Subs};
|
||||||
use roc_types::types::{write_error_type, Category, ErrorType, PReason, PatternCategory, Reason};
|
use roc_types::types::{write_error_type, ErrorType};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
/// A textual report.
|
/// A textual report.
|
||||||
|
@ -72,208 +70,6 @@ impl Color {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_problem(filename: PathBuf, problem: solve::TypeError) -> Report {
|
|
||||||
use solve::TypeError::*;
|
|
||||||
|
|
||||||
match problem {
|
|
||||||
BadExpr(region, category, found, expected) => {
|
|
||||||
to_expr_report(filename, region, category, found, expected)
|
|
||||||
}
|
|
||||||
BadPattern(region, category, found, expected) => {
|
|
||||||
to_pattern_report(filename, region, category, found, expected)
|
|
||||||
}
|
|
||||||
CircularType(region, symbol, overall_type) => {
|
|
||||||
to_circular_report(filename, region, symbol, overall_type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn type_in_focus(typ: ErrorType) -> ReportText {
|
|
||||||
ReportText::Batch(vec![
|
|
||||||
newline(),
|
|
||||||
newline(),
|
|
||||||
plain_text(" "),
|
|
||||||
ReportText::ErrorType(typ),
|
|
||||||
newline(),
|
|
||||||
newline(),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn int_to_ordinal(number: usize) -> String {
|
|
||||||
// NOTE: one-based
|
|
||||||
let remainder10 = number % 10;
|
|
||||||
let remainder100 = number % 100;
|
|
||||||
|
|
||||||
let ending = match remainder100 {
|
|
||||||
11..=13 => "th",
|
|
||||||
_ => match remainder10 {
|
|
||||||
1 => "st",
|
|
||||||
2 => "nd",
|
|
||||||
3 => "rd",
|
|
||||||
_ => "th",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
format!("{}{}", number, ending)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(too_many_arguments)]
|
|
||||||
fn report_mismatch(
|
|
||||||
filename: PathBuf,
|
|
||||||
category: &Category,
|
|
||||||
found: ErrorType,
|
|
||||||
expected_type: ErrorType,
|
|
||||||
region: roc_region::all::Region,
|
|
||||||
opt_highlight: Option<roc_region::all::Region>,
|
|
||||||
problem: &str,
|
|
||||||
this_is: &str,
|
|
||||||
instead_of: &str,
|
|
||||||
further_details: ReportText,
|
|
||||||
) -> Report {
|
|
||||||
use ReportText::*;
|
|
||||||
let lines = vec![
|
|
||||||
plain_text(problem),
|
|
||||||
Region(region),
|
|
||||||
add_category(this_is, category),
|
|
||||||
type_in_focus(found),
|
|
||||||
plain_text(instead_of),
|
|
||||||
type_in_focus(expected_type),
|
|
||||||
further_details,
|
|
||||||
];
|
|
||||||
|
|
||||||
Report {
|
|
||||||
filename,
|
|
||||||
text: Batch(lines),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(too_many_arguments)]
|
|
||||||
fn report_bad_type(
|
|
||||||
filename: PathBuf,
|
|
||||||
category: &Category,
|
|
||||||
found: ErrorType,
|
|
||||||
expected_type: ErrorType,
|
|
||||||
region: roc_region::all::Region,
|
|
||||||
opt_highlight: Option<roc_region::all::Region>,
|
|
||||||
problem: &str,
|
|
||||||
this_is: &str,
|
|
||||||
further_details: ReportText,
|
|
||||||
) -> Report {
|
|
||||||
use ReportText::*;
|
|
||||||
let lines = vec![
|
|
||||||
plain_text(problem),
|
|
||||||
Region(region),
|
|
||||||
add_category(this_is, &category),
|
|
||||||
type_in_focus(found),
|
|
||||||
further_details,
|
|
||||||
];
|
|
||||||
|
|
||||||
Report {
|
|
||||||
filename,
|
|
||||||
text: Batch(lines),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_expr_report(
|
|
||||||
filename: PathBuf,
|
|
||||||
expr_region: roc_region::all::Region,
|
|
||||||
category: Category,
|
|
||||||
found: ErrorType,
|
|
||||||
expected: Expected<ErrorType>,
|
|
||||||
) -> Report {
|
|
||||||
use ReportText::*;
|
|
||||||
|
|
||||||
match expected {
|
|
||||||
Expected::NoExpectation(expected_type) => todo!(),
|
|
||||||
Expected::FromAnnotation(name, arity, sub_context, expected_type) => todo!(),
|
|
||||||
Expected::ForReason(reason, expected_type, region) => {
|
|
||||||
match reason {
|
|
||||||
Reason::IfCondition => report_bad_type(
|
|
||||||
filename,
|
|
||||||
&category,
|
|
||||||
found,
|
|
||||||
expected_type,
|
|
||||||
region,
|
|
||||||
Some(expr_region),
|
|
||||||
"This `if` condition does not evaluate to a boolean value, True or False.",
|
|
||||||
"It is",
|
|
||||||
Batch(vec![
|
|
||||||
plain_text("But I need this `if` condition to be a "),
|
|
||||||
ReportText::Type(Content::Alias(Symbol::BOOL_BOOL, vec![], Variable::BOOL)),
|
|
||||||
plain_text(" value."),
|
|
||||||
newline(),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
Reason::IfBranch { index } => {
|
|
||||||
let ith = int_to_ordinal(index);
|
|
||||||
report_mismatch(
|
|
||||||
filename,
|
|
||||||
&category,
|
|
||||||
found,
|
|
||||||
expected_type,
|
|
||||||
region,
|
|
||||||
Some(expr_region),
|
|
||||||
&format!(
|
|
||||||
"The {} branch of this `if` does not match all the previous branches:",
|
|
||||||
ith
|
|
||||||
),
|
|
||||||
&format!("The {} branch is", ith),
|
|
||||||
"But all the previous branches result in",
|
|
||||||
Batch(vec![ /* TODO add hint */ ]),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_category(this_is: &str, category: &Category) -> ReportText {
|
|
||||||
use Category::*;
|
|
||||||
|
|
||||||
let result = match category {
|
|
||||||
Str => format!("{} a string of type:", this_is),
|
|
||||||
_ => todo!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
plain_text(&*result)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_pattern_report(
|
|
||||||
filename: PathBuf,
|
|
||||||
expr_region: roc_region::all::Region,
|
|
||||||
category: PatternCategory,
|
|
||||||
found: ErrorType,
|
|
||||||
expected: PExpected<ErrorType>,
|
|
||||||
) -> Report {
|
|
||||||
use ReportText::*;
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_circular_report(
|
|
||||||
filename: PathBuf,
|
|
||||||
region: roc_region::all::Region,
|
|
||||||
symbol: Symbol,
|
|
||||||
overall_type: ErrorType,
|
|
||||||
) -> Report {
|
|
||||||
use ReportText::*;
|
|
||||||
|
|
||||||
let lines = vec![
|
|
||||||
plain_text("I'm inferring a weird self-referential type for "),
|
|
||||||
Value(symbol),
|
|
||||||
plain_text(":"),
|
|
||||||
Region(region),
|
|
||||||
plain_text("Here is my best effort at writing down the type. You will see ∞ for parts of the type that repeat something already printed out infinitely."),
|
|
||||||
type_in_focus(overall_type),
|
|
||||||
/* TODO hint */
|
|
||||||
];
|
|
||||||
|
|
||||||
Report {
|
|
||||||
filename,
|
|
||||||
text: Batch(lines),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn can_problem(filename: PathBuf, problem: Problem) -> Report {
|
pub fn can_problem(filename: PathBuf, problem: Problem) -> Report {
|
||||||
let mut texts = Vec::new();
|
let mut texts = Vec::new();
|
||||||
|
|
||||||
|
@ -352,7 +148,7 @@ pub fn can_problem(filename: PathBuf, problem: Problem) -> Report {
|
||||||
|
|
||||||
Report {
|
Report {
|
||||||
filename,
|
filename,
|
||||||
text: Batch(texts),
|
text: Concat(texts),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,7 +183,12 @@ pub enum ReportText {
|
||||||
BinOp(roc_parse::operator::BinOp),
|
BinOp(roc_parse::operator::BinOp),
|
||||||
|
|
||||||
/// Many ReportText that should be concatenated together.
|
/// Many ReportText that should be concatenated together.
|
||||||
Batch(Vec<ReportText>),
|
Concat(Vec<ReportText>),
|
||||||
|
|
||||||
|
/// Many ReportText that each get separate lines
|
||||||
|
Stack(Vec<ReportText>),
|
||||||
|
|
||||||
|
Indent(usize, Box<ReportText>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn plain_text(str: &str) -> ReportText {
|
pub fn plain_text(str: &str) -> ReportText {
|
||||||
|
@ -408,9 +209,8 @@ pub fn url(str: &str) -> ReportText {
|
||||||
Url(Box::from(str))
|
Url(Box::from(str))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
pub fn with_indent(n: usize, report_text: ReportText) -> ReportText {
|
||||||
fn newline() -> ReportText {
|
ReportText::Indent(n, Box::new(report_text))
|
||||||
plain_text("\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const RED_CODE: &str = "\u{001b}[31m";
|
pub const RED_CODE: &str = "\u{001b}[31m";
|
||||||
|
@ -473,6 +273,12 @@ fn white(str: &str) -> String {
|
||||||
|
|
||||||
pub const RESET_CODE: &str = "\u{001b}[0m";
|
pub const RESET_CODE: &str = "\u{001b}[0m";
|
||||||
|
|
||||||
|
struct CiEnv<'a> {
|
||||||
|
home: ModuleId,
|
||||||
|
src_lines: &'a [&'a str],
|
||||||
|
interns: &'a Interns,
|
||||||
|
}
|
||||||
|
|
||||||
impl ReportText {
|
impl ReportText {
|
||||||
/// Render to CI console output, where no colors are available.
|
/// Render to CI console output, where no colors are available.
|
||||||
pub fn render_ci(
|
pub fn render_ci(
|
||||||
|
@ -483,6 +289,16 @@ impl ReportText {
|
||||||
src_lines: &[&str],
|
src_lines: &[&str],
|
||||||
interns: &Interns,
|
interns: &Interns,
|
||||||
) {
|
) {
|
||||||
|
let env = CiEnv {
|
||||||
|
home,
|
||||||
|
src_lines,
|
||||||
|
interns,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.render_ci_help(&env, buf, subs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_ci_help(self, env: &CiEnv, buf: &mut String, subs: &mut Subs, indent: usize) {
|
||||||
use ReportText::*;
|
use ReportText::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
|
@ -499,25 +315,31 @@ impl ReportText {
|
||||||
buf.push('>');
|
buf.push('>');
|
||||||
}
|
}
|
||||||
Value(symbol) => {
|
Value(symbol) => {
|
||||||
if symbol.module_id() == home {
|
if symbol.module_id() == env.home {
|
||||||
// Render it unqualified if it's in the current module.
|
// Render it unqualified if it's in the current module.
|
||||||
buf.push_str(symbol.ident_string(interns));
|
buf.push_str(symbol.ident_string(env.interns));
|
||||||
} else {
|
} else {
|
||||||
buf.push_str(symbol.module_string(interns));
|
buf.push_str(symbol.module_string(env.interns));
|
||||||
buf.push('.');
|
buf.push('.');
|
||||||
buf.push_str(symbol.ident_string(interns));
|
buf.push_str(symbol.ident_string(env.interns));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Module(module_id) => {
|
Module(module_id) => {
|
||||||
buf.push_str(&interns.module_name(module_id));
|
buf.push_str(&env.interns.module_name(module_id));
|
||||||
|
}
|
||||||
|
Type(content) => {
|
||||||
|
buf.push_str(content_to_string(content, subs, env.home, env.interns).as_str())
|
||||||
|
}
|
||||||
|
ErrorType(error_type) => {
|
||||||
|
buf.push('\n');
|
||||||
|
buf.push_str(" ".repeat(indent).as_str());
|
||||||
|
buf.push_str(&write_error_type(env.home, env.interns, error_type));
|
||||||
|
buf.push('\n');
|
||||||
}
|
}
|
||||||
Type(content) => buf.push_str(content_to_string(content, subs, home, interns).as_str()),
|
|
||||||
ErrorType(error_type) => buf.push_str(&write_error_type(home, interns, error_type)),
|
|
||||||
Region(region) => {
|
Region(region) => {
|
||||||
buf.push('\n');
|
buf.push('\n');
|
||||||
buf.push('\n');
|
buf.push('\n');
|
||||||
|
|
||||||
dbg!(region);
|
|
||||||
// widest displayed line number
|
// widest displayed line number
|
||||||
let max_line_number_length = (region.end_line + 1).to_string().len();
|
let max_line_number_length = (region.end_line + 1).to_string().len();
|
||||||
|
|
||||||
|
@ -535,11 +357,11 @@ impl ReportText {
|
||||||
buf.push_str(line_number);
|
buf.push_str(line_number);
|
||||||
buf.push_str(" ┆");
|
buf.push_str(" ┆");
|
||||||
|
|
||||||
let line = src_lines[i as usize];
|
let line = env.src_lines[i as usize];
|
||||||
|
|
||||||
if !line.trim().is_empty() {
|
if !line.trim().is_empty() {
|
||||||
buf.push_str(" ");
|
buf.push_str(" ");
|
||||||
buf.push_str(src_lines[i as usize]);
|
buf.push_str(env.src_lines[i as usize]);
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.push('\n');
|
buf.push('\n');
|
||||||
|
@ -566,11 +388,11 @@ impl ReportText {
|
||||||
buf.push_str(line_number);
|
buf.push_str(line_number);
|
||||||
buf.push_str(" ┆>");
|
buf.push_str(" ┆>");
|
||||||
|
|
||||||
let line = src_lines[i as usize];
|
let line = env.src_lines[i as usize];
|
||||||
|
|
||||||
if !line.trim().is_empty() {
|
if !line.trim().is_empty() {
|
||||||
buf.push_str(" ");
|
buf.push_str(" ");
|
||||||
buf.push_str(src_lines[i as usize]);
|
buf.push_str(env.src_lines[i as usize]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if i != region.end_line {
|
if i != region.end_line {
|
||||||
|
@ -582,12 +404,27 @@ impl ReportText {
|
||||||
buf.push('\n');
|
buf.push('\n');
|
||||||
buf.push('\n');
|
buf.push('\n');
|
||||||
}
|
}
|
||||||
|
Indent(n, nested) => {
|
||||||
|
nested.render_ci_help(env, buf, subs, indent + n);
|
||||||
|
}
|
||||||
Docs(_) => {
|
Docs(_) => {
|
||||||
panic!("TODO implment docs");
|
panic!("TODO implment docs");
|
||||||
}
|
}
|
||||||
Batch(report_texts) => {
|
Concat(report_texts) => {
|
||||||
for report_text in report_texts {
|
for report_text in report_texts {
|
||||||
report_text.render_ci(buf, subs, home, src_lines, interns);
|
report_text.render_ci_help(env, buf, subs, indent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Stack(report_texts) => {
|
||||||
|
let mut it = report_texts.into_iter().peekable();
|
||||||
|
|
||||||
|
while let Some(report_text) = it.next() {
|
||||||
|
report_text.render_ci_help(env, buf, subs, indent);
|
||||||
|
|
||||||
|
buf.push('\n');
|
||||||
|
if it.peek().is_some() {
|
||||||
|
buf.push_str(" ".repeat(indent).as_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BinOp(bin_op) => {
|
BinOp(bin_op) => {
|
||||||
|
@ -729,7 +566,11 @@ impl ReportText {
|
||||||
buf.push('\n');
|
buf.push('\n');
|
||||||
buf.push('\n');
|
buf.push('\n');
|
||||||
}
|
}
|
||||||
Batch(report_texts) => {
|
Indent(n, nested) => {
|
||||||
|
buf.push_str(" ".repeat(n).as_str());
|
||||||
|
nested.render_color_terminal(buf, subs, home, src_lines, interns, palette);
|
||||||
|
}
|
||||||
|
Concat(report_texts) => {
|
||||||
for report_text in report_texts {
|
for report_text in report_texts {
|
||||||
report_text.render_color_terminal(buf, subs, home, src_lines, interns, palette);
|
report_text.render_color_terminal(buf, subs, home, src_lines, interns, palette);
|
||||||
}
|
}
|
||||||
|
|
264
compiler/reporting/src/type_error.rs
Normal file
264
compiler/reporting/src/type_error.rs
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
use crate::report::{plain_text, with_indent, Report, ReportText};
|
||||||
|
use roc_can::expected::{Expected, PExpected};
|
||||||
|
use roc_module::symbol::Symbol;
|
||||||
|
use roc_solve::solve;
|
||||||
|
use roc_types::subs::{Content, Variable};
|
||||||
|
use roc_types::types::{Category, ErrorType, PatternCategory, Reason};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub fn type_problem(filename: PathBuf, problem: solve::TypeError) -> Report {
|
||||||
|
use solve::TypeError::*;
|
||||||
|
|
||||||
|
match problem {
|
||||||
|
BadExpr(region, category, found, expected) => {
|
||||||
|
to_expr_report(filename, region, category, found, expected)
|
||||||
|
}
|
||||||
|
BadPattern(region, category, found, expected) => {
|
||||||
|
to_pattern_report(filename, region, category, found, expected)
|
||||||
|
}
|
||||||
|
CircularType(region, symbol, overall_type) => {
|
||||||
|
to_circular_report(filename, region, symbol, overall_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_in_focus(typ: ErrorType) -> ReportText {
|
||||||
|
ReportText::ErrorType(typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int_to_ordinal(number: usize) -> String {
|
||||||
|
// NOTE: one-based
|
||||||
|
let remainder10 = number % 10;
|
||||||
|
let remainder100 = number % 100;
|
||||||
|
|
||||||
|
let ending = match remainder100 {
|
||||||
|
11..=13 => "th",
|
||||||
|
_ => match remainder10 {
|
||||||
|
1 => "st",
|
||||||
|
2 => "nd",
|
||||||
|
3 => "rd",
|
||||||
|
_ => "th",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
format!("{}{}", number, ending)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn report_mismatch(
|
||||||
|
filename: PathBuf,
|
||||||
|
category: &Category,
|
||||||
|
found: ErrorType,
|
||||||
|
expected_type: ErrorType,
|
||||||
|
region: roc_region::all::Region,
|
||||||
|
_opt_highlight: Option<roc_region::all::Region>,
|
||||||
|
problem: &str,
|
||||||
|
this_is: &str,
|
||||||
|
instead_of: &str,
|
||||||
|
further_details: ReportText,
|
||||||
|
) -> Report {
|
||||||
|
use ReportText::*;
|
||||||
|
let lines = vec![
|
||||||
|
plain_text(problem),
|
||||||
|
Region(region),
|
||||||
|
type_comparison(
|
||||||
|
found,
|
||||||
|
expected_type,
|
||||||
|
add_category(this_is, category),
|
||||||
|
instead_of,
|
||||||
|
further_details,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
Report {
|
||||||
|
filename,
|
||||||
|
text: Concat(lines),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn report_bad_type(
|
||||||
|
filename: PathBuf,
|
||||||
|
category: &Category,
|
||||||
|
found: ErrorType,
|
||||||
|
expected_type: ErrorType,
|
||||||
|
region: roc_region::all::Region,
|
||||||
|
_opt_highlight: Option<roc_region::all::Region>,
|
||||||
|
problem: &str,
|
||||||
|
this_is: &str,
|
||||||
|
further_details: ReportText,
|
||||||
|
) -> Report {
|
||||||
|
use ReportText::*;
|
||||||
|
let lines = vec![
|
||||||
|
plain_text(problem),
|
||||||
|
Region(region),
|
||||||
|
lone_type(
|
||||||
|
found,
|
||||||
|
expected_type,
|
||||||
|
add_category(this_is, &category),
|
||||||
|
further_details,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
Report {
|
||||||
|
filename,
|
||||||
|
text: Concat(lines),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_expr_report(
|
||||||
|
filename: PathBuf,
|
||||||
|
expr_region: roc_region::all::Region,
|
||||||
|
category: Category,
|
||||||
|
found: ErrorType,
|
||||||
|
expected: Expected<ErrorType>,
|
||||||
|
) -> Report {
|
||||||
|
use ReportText::*;
|
||||||
|
|
||||||
|
match expected {
|
||||||
|
Expected::NoExpectation(_expected_type) => todo!(),
|
||||||
|
Expected::FromAnnotation(_name, _arity, _sub_context, _expected_type) => todo!(),
|
||||||
|
Expected::ForReason(reason, expected_type, region) => {
|
||||||
|
match reason {
|
||||||
|
Reason::IfCondition => report_bad_type(
|
||||||
|
filename,
|
||||||
|
&category,
|
||||||
|
found,
|
||||||
|
expected_type,
|
||||||
|
region,
|
||||||
|
Some(expr_region),
|
||||||
|
"This `if` condition does not evaluate to a boolean value, True or False.",
|
||||||
|
"It is",
|
||||||
|
Concat(vec![
|
||||||
|
plain_text("But I need this `if` condition to be a "),
|
||||||
|
ReportText::Type(Content::Alias(Symbol::BOOL_BOOL, vec![], Variable::BOOL)),
|
||||||
|
plain_text(" value."),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
Reason::IfBranch { index } => {
|
||||||
|
let ith = int_to_ordinal(index);
|
||||||
|
report_mismatch(
|
||||||
|
filename,
|
||||||
|
&category,
|
||||||
|
found,
|
||||||
|
expected_type,
|
||||||
|
region,
|
||||||
|
Some(expr_region),
|
||||||
|
&format!(
|
||||||
|
"The {} branch of this `if` does not match all the previous branches:",
|
||||||
|
ith
|
||||||
|
),
|
||||||
|
&format!("The {} branch is", ith),
|
||||||
|
"But all the previous branches result in",
|
||||||
|
Concat(vec![ /* TODO add hint */ ]),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Problem {}
|
||||||
|
pub struct Comparison {
|
||||||
|
actual: ReportText,
|
||||||
|
expected: ReportText,
|
||||||
|
problems: Vec<Problem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn problems_to_hint(_problems: Vec<Problem>) -> ReportText {
|
||||||
|
// TODO
|
||||||
|
ReportText::Concat(vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_comparison(actual: ErrorType, expected: ErrorType) -> Comparison {
|
||||||
|
// TODO make this do actual comparison
|
||||||
|
|
||||||
|
Comparison {
|
||||||
|
actual: type_in_focus(actual),
|
||||||
|
expected: type_in_focus(expected),
|
||||||
|
problems: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_comparison(
|
||||||
|
actual: ErrorType,
|
||||||
|
expected: ErrorType,
|
||||||
|
i_am_seeing: ReportText,
|
||||||
|
instead_of: &str,
|
||||||
|
context_hints: ReportText,
|
||||||
|
) -> ReportText {
|
||||||
|
let comparison = to_comparison(actual, expected);
|
||||||
|
|
||||||
|
ReportText::Stack(vec![
|
||||||
|
i_am_seeing,
|
||||||
|
with_indent(4, comparison.actual),
|
||||||
|
plain_text(instead_of),
|
||||||
|
with_indent(4, comparison.expected),
|
||||||
|
context_hints,
|
||||||
|
problems_to_hint(comparison.problems),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lone_type(
|
||||||
|
actual: ErrorType,
|
||||||
|
expected: ErrorType,
|
||||||
|
i_am_seeing: ReportText,
|
||||||
|
further_details: ReportText,
|
||||||
|
) -> ReportText {
|
||||||
|
let comparison = to_comparison(actual, expected);
|
||||||
|
|
||||||
|
ReportText::Stack(vec![
|
||||||
|
i_am_seeing,
|
||||||
|
with_indent(4, comparison.actual),
|
||||||
|
further_details,
|
||||||
|
problems_to_hint(comparison.problems),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_category(this_is: &str, category: &Category) -> ReportText {
|
||||||
|
use Category::*;
|
||||||
|
|
||||||
|
let result = match category {
|
||||||
|
Str => format!("{} a string of type:", this_is),
|
||||||
|
_ => todo!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
plain_text(&*result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_pattern_report(
|
||||||
|
_filename: PathBuf,
|
||||||
|
_expr_region: roc_region::all::Region,
|
||||||
|
_category: PatternCategory,
|
||||||
|
_found: ErrorType,
|
||||||
|
_expected: PExpected<ErrorType>,
|
||||||
|
) -> Report {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_circular_report(
|
||||||
|
filename: PathBuf,
|
||||||
|
region: roc_region::all::Region,
|
||||||
|
symbol: Symbol,
|
||||||
|
overall_type: ErrorType,
|
||||||
|
) -> Report {
|
||||||
|
use ReportText::*;
|
||||||
|
|
||||||
|
let lines = vec![
|
||||||
|
plain_text("I'm inferring a weird self-referential type for "),
|
||||||
|
Value(symbol),
|
||||||
|
plain_text(":"),
|
||||||
|
Region(region),
|
||||||
|
Stack(vec![
|
||||||
|
plain_text("Here is my best effort at writing down the type. You will see ∞ for parts of the type that repeat something already printed out infinitely."),
|
||||||
|
with_indent(4, type_in_focus(overall_type)),
|
||||||
|
/* TODO hint */
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
|
||||||
|
Report {
|
||||||
|
filename,
|
||||||
|
text: Concat(lines),
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,16 +12,17 @@ mod test_reporting {
|
||||||
use crate::helpers::test_home;
|
use crate::helpers::test_home;
|
||||||
use roc_module::symbol::{Interns, ModuleId};
|
use roc_module::symbol::{Interns, ModuleId};
|
||||||
use roc_reporting::report::{
|
use roc_reporting::report::{
|
||||||
can_problem, em_text, plain_text, type_problem, url, Report, ReportText, BLUE_CODE,
|
can_problem, em_text, plain_text, url, Report, ReportText, BLUE_CODE, BOLD_CODE, CYAN_CODE,
|
||||||
BOLD_CODE, CYAN_CODE, GREEN_CODE, MAGENTA_CODE, RED_CODE, RESET_CODE, TEST_PALETTE,
|
GREEN_CODE, MAGENTA_CODE, RED_CODE, RESET_CODE, TEST_PALETTE, UNDERLINE_CODE, WHITE_CODE,
|
||||||
UNDERLINE_CODE, WHITE_CODE, YELLOW_CODE,
|
YELLOW_CODE,
|
||||||
};
|
};
|
||||||
|
use roc_reporting::type_error::type_problem;
|
||||||
use roc_types::pretty_print::name_all_type_vars;
|
use roc_types::pretty_print::name_all_type_vars;
|
||||||
use roc_types::subs::Subs;
|
use roc_types::subs::Subs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
// use roc_region::all;
|
// use roc_region::all;
|
||||||
use crate::helpers::{can_expr, infer_expr, CanExprOut};
|
use crate::helpers::{can_expr, infer_expr, CanExprOut};
|
||||||
use roc_reporting::report::ReportText::{Batch, Module, Region, Type, Value};
|
use roc_reporting::report::ReportText::{Concat, Module, Region, Type, Value};
|
||||||
use roc_solve::solve;
|
use roc_solve::solve;
|
||||||
use roc_types::subs::Content::{FlexVar, RigidVar, Structure};
|
use roc_types::subs::Content::{FlexVar, RigidVar, Structure};
|
||||||
use roc_types::subs::FlatType::EmptyRecord;
|
use roc_types::subs::FlatType::EmptyRecord;
|
||||||
|
@ -276,7 +277,7 @@ mod test_reporting {
|
||||||
report_texts.push(em_text("y"));
|
report_texts.push(em_text("y"));
|
||||||
|
|
||||||
report_renders_as(
|
report_renders_as(
|
||||||
to_simple_report(Batch(report_texts)),
|
to_simple_report(Concat(report_texts)),
|
||||||
"Wait a second. There is a problem here. -> *y*",
|
"Wait a second. There is a problem here. -> *y*",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -566,7 +567,7 @@ mod test_reporting {
|
||||||
report_texts.push(Type(Structure(EmptyRecord)));
|
report_texts.push(Type(Structure(EmptyRecord)));
|
||||||
|
|
||||||
report_renders_in_color(
|
report_renders_in_color(
|
||||||
to_simple_report(Batch(report_texts)),
|
to_simple_report(Concat(report_texts)),
|
||||||
"<yellow>List<reset><white> <reset><green>{}<reset>",
|
"<yellow>List<reset><white> <reset><green>{}<reset>",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -775,6 +776,7 @@ mod test_reporting {
|
||||||
Str
|
Str
|
||||||
|
|
||||||
But I need this `if` condition to be a Bool value.
|
But I need this `if` condition to be a Bool value.
|
||||||
|
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -803,6 +805,8 @@ mod test_reporting {
|
||||||
|
|
||||||
Num a
|
Num a
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -217,6 +217,8 @@ fn solve(
|
||||||
}
|
}
|
||||||
|
|
||||||
Failure(vars, actual_type, expected_type) => {
|
Failure(vars, actual_type, expected_type) => {
|
||||||
|
introduce(subs, rank, pools, &vars);
|
||||||
|
|
||||||
let problem = TypeError::BadExpr(
|
let problem = TypeError::BadExpr(
|
||||||
*region,
|
*region,
|
||||||
Category::Lookup(*symbol),
|
Category::Lookup(*symbol),
|
||||||
|
|
|
@ -817,12 +817,15 @@ fn write_error_type_help(
|
||||||
}
|
}
|
||||||
Record(fields, ext) => {
|
Record(fields, ext) => {
|
||||||
buf.push('{');
|
buf.push('{');
|
||||||
buf.push('}');
|
|
||||||
write_type_ext(ext, buf);
|
for (label, content) in fields {
|
||||||
|
buf.push_str(label.as_str());
|
||||||
|
buf.push_str(": ");
|
||||||
|
write_error_type_help(home, interns, content, buf, Parens::Unnecessary);
|
||||||
}
|
}
|
||||||
|
|
||||||
Infinite => {
|
buf.push('}');
|
||||||
buf.push_str("∞");
|
write_type_ext(ext, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
other => todo!("cannot format {:?} yet", other),
|
other => todo!("cannot format {:?} yet", other),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue