mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-20 15:27:45 +03:00
refactor and clean up
This commit is contained in:
parent
604dbf7215
commit
1981a7e467
@ -16,7 +16,7 @@ use roc_region::all::{Located, Region};
|
||||
use roc_types::subs::Variable;
|
||||
use roc_types::types::AnnotationSource::{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
|
||||
#[derive(Default, Debug)]
|
||||
|
@ -14,7 +14,7 @@ use roc_types::boolean_algebra::{Atom, Bool};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::types::AnnotationSource::{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::sharing::{self, Container, FieldAccess, Mark, Usage, VarUsage};
|
||||
|
||||
|
@ -1,12 +1,10 @@
|
||||
use crate::report::ReportText::{Batch, BinOp, Module, Region, Value};
|
||||
use roc_can::expected::{Expected, PExpected};
|
||||
use crate::report::ReportText::{BinOp, Concat, Module, Region, Value};
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
use roc_problem::can::PrecedenceProblem::BothNonAssociative;
|
||||
use roc_problem::can::Problem;
|
||||
use roc_solve::solve;
|
||||
use roc_types::pretty_print::content_to_string;
|
||||
use roc_types::subs::{Content, Subs, Variable};
|
||||
use roc_types::types::{write_error_type, Category, ErrorType, PReason, PatternCategory, Reason};
|
||||
use roc_types::subs::{Content, Subs};
|
||||
use roc_types::types::{write_error_type, ErrorType};
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// 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 {
|
||||
let mut texts = Vec::new();
|
||||
|
||||
@ -352,7 +148,7 @@ pub fn can_problem(filename: PathBuf, problem: Problem) -> Report {
|
||||
|
||||
Report {
|
||||
filename,
|
||||
text: Batch(texts),
|
||||
text: Concat(texts),
|
||||
}
|
||||
}
|
||||
|
||||
@ -387,7 +183,12 @@ pub enum ReportText {
|
||||
BinOp(roc_parse::operator::BinOp),
|
||||
|
||||
/// 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 {
|
||||
@ -408,9 +209,8 @@ pub fn url(str: &str) -> ReportText {
|
||||
Url(Box::from(str))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn newline() -> ReportText {
|
||||
plain_text("\n")
|
||||
pub fn with_indent(n: usize, report_text: ReportText) -> ReportText {
|
||||
ReportText::Indent(n, Box::new(report_text))
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
struct CiEnv<'a> {
|
||||
home: ModuleId,
|
||||
src_lines: &'a [&'a str],
|
||||
interns: &'a Interns,
|
||||
}
|
||||
|
||||
impl ReportText {
|
||||
/// Render to CI console output, where no colors are available.
|
||||
pub fn render_ci(
|
||||
@ -483,6 +289,16 @@ impl ReportText {
|
||||
src_lines: &[&str],
|
||||
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::*;
|
||||
|
||||
match self {
|
||||
@ -499,25 +315,31 @@ impl ReportText {
|
||||
buf.push('>');
|
||||
}
|
||||
Value(symbol) => {
|
||||
if symbol.module_id() == home {
|
||||
if symbol.module_id() == env.home {
|
||||
// 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 {
|
||||
buf.push_str(symbol.module_string(interns));
|
||||
buf.push_str(symbol.module_string(env.interns));
|
||||
buf.push('.');
|
||||
buf.push_str(symbol.ident_string(interns));
|
||||
buf.push_str(symbol.ident_string(env.interns));
|
||||
}
|
||||
}
|
||||
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) => {
|
||||
buf.push('\n');
|
||||
buf.push('\n');
|
||||
|
||||
dbg!(region);
|
||||
// widest displayed line number
|
||||
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(" ┆");
|
||||
|
||||
let line = src_lines[i as usize];
|
||||
let line = env.src_lines[i as usize];
|
||||
|
||||
if !line.trim().is_empty() {
|
||||
buf.push_str(" ");
|
||||
buf.push_str(src_lines[i as usize]);
|
||||
buf.push_str(env.src_lines[i as usize]);
|
||||
}
|
||||
|
||||
buf.push('\n');
|
||||
@ -566,11 +388,11 @@ impl ReportText {
|
||||
buf.push_str(line_number);
|
||||
buf.push_str(" ┆>");
|
||||
|
||||
let line = src_lines[i as usize];
|
||||
let line = env.src_lines[i as usize];
|
||||
|
||||
if !line.trim().is_empty() {
|
||||
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 {
|
||||
@ -582,12 +404,27 @@ impl ReportText {
|
||||
buf.push('\n');
|
||||
buf.push('\n');
|
||||
}
|
||||
Indent(n, nested) => {
|
||||
nested.render_ci_help(env, buf, subs, indent + n);
|
||||
}
|
||||
Docs(_) => {
|
||||
panic!("TODO implment docs");
|
||||
}
|
||||
Batch(report_texts) => {
|
||||
Concat(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) => {
|
||||
@ -729,7 +566,11 @@ impl ReportText {
|
||||
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 {
|
||||
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 roc_module::symbol::{Interns, ModuleId};
|
||||
use roc_reporting::report::{
|
||||
can_problem, em_text, plain_text, type_problem, url, Report, ReportText, BLUE_CODE,
|
||||
BOLD_CODE, CYAN_CODE, GREEN_CODE, MAGENTA_CODE, RED_CODE, RESET_CODE, TEST_PALETTE,
|
||||
UNDERLINE_CODE, WHITE_CODE, YELLOW_CODE,
|
||||
can_problem, em_text, plain_text, url, Report, ReportText, BLUE_CODE, BOLD_CODE, CYAN_CODE,
|
||||
GREEN_CODE, MAGENTA_CODE, RED_CODE, RESET_CODE, TEST_PALETTE, UNDERLINE_CODE, WHITE_CODE,
|
||||
YELLOW_CODE,
|
||||
};
|
||||
use roc_reporting::type_error::type_problem;
|
||||
use roc_types::pretty_print::name_all_type_vars;
|
||||
use roc_types::subs::Subs;
|
||||
use std::path::PathBuf;
|
||||
// use roc_region::all;
|
||||
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_types::subs::Content::{FlexVar, RigidVar, Structure};
|
||||
use roc_types::subs::FlatType::EmptyRecord;
|
||||
@ -276,7 +277,7 @@ mod test_reporting {
|
||||
report_texts.push(em_text("y"));
|
||||
|
||||
report_renders_as(
|
||||
to_simple_report(Batch(report_texts)),
|
||||
to_simple_report(Concat(report_texts)),
|
||||
"Wait a second. There is a problem here. -> *y*",
|
||||
);
|
||||
}
|
||||
@ -566,7 +567,7 @@ mod test_reporting {
|
||||
report_texts.push(Type(Structure(EmptyRecord)));
|
||||
|
||||
report_renders_in_color(
|
||||
to_simple_report(Batch(report_texts)),
|
||||
to_simple_report(Concat(report_texts)),
|
||||
"<yellow>List<reset><white> <reset><green>{}<reset>",
|
||||
);
|
||||
}
|
||||
@ -775,6 +776,7 @@ mod test_reporting {
|
||||
Str
|
||||
|
||||
But I need this `if` condition to be a Bool value.
|
||||
|
||||
"#
|
||||
),
|
||||
)
|
||||
@ -803,6 +805,8 @@ mod test_reporting {
|
||||
|
||||
Num a
|
||||
|
||||
|
||||
|
||||
"#
|
||||
),
|
||||
)
|
||||
@ -839,7 +843,7 @@ mod test_reporting {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
f = \x -> f [x]
|
||||
f = \x -> f [x]
|
||||
|
||||
f
|
||||
"#
|
||||
@ -848,7 +852,7 @@ mod test_reporting {
|
||||
r#"
|
||||
I'm inferring a weird self-referential type for f:
|
||||
|
||||
1 ┆ f = \x -> f [x]
|
||||
1 ┆ f = \x -> f [x]
|
||||
┆ ^
|
||||
|
||||
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.
|
||||
|
@ -217,6 +217,8 @@ fn solve(
|
||||
}
|
||||
|
||||
Failure(vars, actual_type, expected_type) => {
|
||||
introduce(subs, rank, pools, &vars);
|
||||
|
||||
let problem = TypeError::BadExpr(
|
||||
*region,
|
||||
Category::Lookup(*symbol),
|
||||
|
@ -817,14 +817,17 @@ fn write_error_type_help(
|
||||
}
|
||||
Record(fields, ext) => {
|
||||
buf.push('{');
|
||||
|
||||
for (label, content) in fields {
|
||||
buf.push_str(label.as_str());
|
||||
buf.push_str(": ");
|
||||
write_error_type_help(home, interns, content, buf, Parens::Unnecessary);
|
||||
}
|
||||
|
||||
buf.push('}');
|
||||
write_type_ext(ext, buf);
|
||||
}
|
||||
|
||||
Infinite => {
|
||||
buf.push_str("∞");
|
||||
}
|
||||
|
||||
other => todo!("cannot format {:?} yet", other),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user