mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-22 08:17:40 +03:00
convert ErrorType to ReportText
This commit is contained in:
parent
854ffdae5e
commit
7818e84316
@ -1,4 +1,5 @@
|
||||
use crate::report::ReportText::{BinOp, Concat, Module, Region, Value};
|
||||
use roc_module::ident::TagName;
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
use roc_problem::can::PrecedenceProblem::BothNonAssociative;
|
||||
use roc_problem::can::{Problem, RuntimeError};
|
||||
@ -162,7 +163,9 @@ pub enum ReportText {
|
||||
/// A type. Render it using roc_types::pretty_print for now, but maybe
|
||||
/// do something fancier later.
|
||||
Type(Content),
|
||||
ErrorType(ErrorType),
|
||||
|
||||
ErrorTypeInline(ErrorType),
|
||||
ErrorTypeBlock(Box<ReportText>),
|
||||
|
||||
/// Plain text
|
||||
Plain(Box<str>),
|
||||
@ -199,6 +202,11 @@ pub enum ReportText {
|
||||
/// Many ReportText that each get separate lines
|
||||
Stack(Vec<ReportText>),
|
||||
|
||||
Intersperse {
|
||||
separator: Box<ReportText>,
|
||||
items: Vec<ReportText>,
|
||||
},
|
||||
|
||||
Indent(usize, Box<ReportText>),
|
||||
}
|
||||
|
||||
@ -214,6 +222,13 @@ pub fn em_text(str: &str) -> ReportText {
|
||||
ReportText::EmText(Box::from(str))
|
||||
}
|
||||
|
||||
pub fn tag_name_text(tag_name: TagName) -> ReportText {
|
||||
match tag_name {
|
||||
TagName::Private(symbol) => ReportText::PrivateTag(symbol),
|
||||
TagName::Global(uppercase) => global_tag_text(uppercase.as_str()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn private_tag_text(symbol: Symbol) -> ReportText {
|
||||
ReportText::PrivateTag(symbol)
|
||||
}
|
||||
@ -230,10 +245,34 @@ pub fn keyword_text(str: &str) -> ReportText {
|
||||
ReportText::Keyword(Box::from(str))
|
||||
}
|
||||
|
||||
pub fn error_type_inline(err: ErrorType) -> ReportText {
|
||||
ReportText::ErrorTypeInline(err)
|
||||
}
|
||||
|
||||
pub fn error_type_block(err: ReportText) -> ReportText {
|
||||
ReportText::ErrorTypeBlock(Box::new(err))
|
||||
}
|
||||
|
||||
pub fn url(str: &str) -> ReportText {
|
||||
ReportText::Url(Box::from(str))
|
||||
}
|
||||
|
||||
pub fn concat(values: Vec<ReportText>) -> ReportText {
|
||||
ReportText::Concat(values)
|
||||
}
|
||||
|
||||
pub fn separate(values: Vec<ReportText>) -> ReportText {
|
||||
// TODO I think this should be a possibly-breaking space
|
||||
intersperse(plain_text(" "), values)
|
||||
}
|
||||
|
||||
pub fn intersperse(separator: ReportText, items: Vec<ReportText>) -> ReportText {
|
||||
ReportText::Intersperse {
|
||||
separator: Box::new(separator),
|
||||
items,
|
||||
}
|
||||
}
|
||||
|
||||
pub const RED_CODE: &str = "\u{001b}[31m";
|
||||
pub const WHITE_CODE: &str = "\u{001b}[37m";
|
||||
pub const BLUE_CODE: &str = "\u{001b}[34m";
|
||||
@ -266,12 +305,14 @@ pub enum Annotation {
|
||||
LineNumber,
|
||||
PlainText,
|
||||
CodeBlock,
|
||||
TypeBlock,
|
||||
Module,
|
||||
}
|
||||
|
||||
/// Render with minimal formatting
|
||||
pub struct CiWrite<W> {
|
||||
style_stack: Vec<Annotation>,
|
||||
in_type_block: bool,
|
||||
upstream: W,
|
||||
}
|
||||
|
||||
@ -279,6 +320,7 @@ impl<W> CiWrite<W> {
|
||||
pub fn new(upstream: W) -> CiWrite<W> {
|
||||
CiWrite {
|
||||
style_stack: vec![],
|
||||
in_type_block: false,
|
||||
upstream,
|
||||
}
|
||||
}
|
||||
@ -323,17 +365,20 @@ where
|
||||
fn push_annotation(&mut self, annotation: &Annotation) -> Result<(), Self::Error> {
|
||||
use Annotation::*;
|
||||
match annotation {
|
||||
TypeBlock => {
|
||||
self.in_type_block = true;
|
||||
}
|
||||
Emphasized => {
|
||||
self.write_str("*")?;
|
||||
}
|
||||
Url => {
|
||||
self.write_str("<")?;
|
||||
}
|
||||
GlobalTag | PrivateTag | Keyword | RecordField | Symbol => {
|
||||
GlobalTag | PrivateTag | Keyword | RecordField | Symbol if !self.in_type_block => {
|
||||
self.write_str("`")?;
|
||||
}
|
||||
CodeBlock | PlainText | LineNumber | Error | GutterBar | TypeVariable | Alias
|
||||
| Module | Structure | BinOp => {}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
self.style_stack.push(*annotation);
|
||||
Ok(())
|
||||
@ -345,17 +390,20 @@ where
|
||||
match self.style_stack.pop() {
|
||||
None => {}
|
||||
Some(annotation) => match annotation {
|
||||
TypeBlock => {
|
||||
self.in_type_block = false;
|
||||
}
|
||||
Emphasized => {
|
||||
self.write_str("*")?;
|
||||
}
|
||||
Url => {
|
||||
self.write_str(">")?;
|
||||
}
|
||||
GlobalTag | PrivateTag | Keyword | RecordField | Symbol => {
|
||||
GlobalTag | PrivateTag | Keyword | RecordField | Symbol if !self.in_type_block => {
|
||||
self.write_str("`")?;
|
||||
}
|
||||
CodeBlock | PlainText | LineNumber | Error | GutterBar | TypeVariable | Alias
|
||||
| Module | Structure | BinOp => {}
|
||||
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
@ -423,7 +471,7 @@ where
|
||||
Module => {
|
||||
self.write_str(self.palette.module_name)?;
|
||||
}
|
||||
GlobalTag | PrivateTag | RecordField | Keyword => { /* nothing yet */ }
|
||||
TypeBlock | GlobalTag | PrivateTag | RecordField | Keyword => { /* nothing yet */ }
|
||||
}
|
||||
self.style_stack.push(*annotation);
|
||||
Ok(())
|
||||
@ -440,7 +488,7 @@ where
|
||||
self.write_str(RESET_CODE)?;
|
||||
}
|
||||
|
||||
GlobalTag | PrivateTag | RecordField | Keyword => { /* nothing yet */ }
|
||||
TypeBlock | GlobalTag | PrivateTag | RecordField | Keyword => { /* nothing yet */ }
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
@ -537,8 +585,8 @@ impl ReportText {
|
||||
}
|
||||
}
|
||||
Value(symbol) => {
|
||||
if symbol.module_id() == home {
|
||||
// Render it unqualified if it's in the current module.
|
||||
if symbol.module_id() == home || symbol.module_id().is_builtin() {
|
||||
// Render it unqualified if it's in the current module or a builtin
|
||||
alloc
|
||||
.text(format!("{}", symbol.ident_string(interns)))
|
||||
.annotate(Annotation::Symbol)
|
||||
@ -571,7 +619,7 @@ impl ReportText {
|
||||
|
||||
Content::Error => alloc.text(content_to_string(content, subs, home, interns)),
|
||||
},
|
||||
ErrorType(error_type) => alloc
|
||||
ErrorTypeInline(error_type) => alloc
|
||||
.nil()
|
||||
.append(alloc.hardline())
|
||||
.append(
|
||||
@ -581,6 +629,17 @@ impl ReportText {
|
||||
)
|
||||
.append(alloc.hardline()),
|
||||
|
||||
ErrorTypeBlock(error_type) => alloc
|
||||
.nil()
|
||||
.append(alloc.hardline())
|
||||
.append(
|
||||
error_type
|
||||
.pretty(alloc, subs, home, src_lines, interns)
|
||||
.indent(4)
|
||||
.annotate(Annotation::TypeBlock),
|
||||
)
|
||||
.append(alloc.hardline()),
|
||||
|
||||
Indent(n, nested) => {
|
||||
let rest = nested.pretty(alloc, subs, home, src_lines, interns);
|
||||
alloc.nil().append(rest).indent(n)
|
||||
@ -599,6 +658,13 @@ impl ReportText {
|
||||
.map(|rep| (rep.pretty(alloc, subs, home, src_lines, interns))),
|
||||
alloc.hardline(),
|
||||
),
|
||||
Intersperse { separator, items } => alloc.intersperse(
|
||||
items
|
||||
.into_iter()
|
||||
.map(|rep| (rep.pretty(alloc, subs, home, src_lines, interns)))
|
||||
.collect::<Vec<_>>(),
|
||||
separator.pretty(alloc, subs, home, src_lines, interns),
|
||||
),
|
||||
BinOp(bin_op) => alloc.text(bin_op.to_string()).annotate(Annotation::BinOp),
|
||||
Region(region) => {
|
||||
let max_line_number_length = (region.end_line + 1).to_string().len();
|
||||
|
@ -1,12 +1,14 @@
|
||||
use crate::report::{
|
||||
global_tag_text, keyword_text, plain_text, private_tag_text, record_field_text, Report,
|
||||
ReportText,
|
||||
error_type_block, error_type_inline, global_tag_text, keyword_text, plain_text,
|
||||
private_tag_text, record_field_text, tag_name_text, Report, ReportText,
|
||||
};
|
||||
use roc_can::expected::{Expected, PExpected};
|
||||
use roc_module::ident::Lowercase;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_solve::solve;
|
||||
use roc_types::pretty_print::Parens;
|
||||
use roc_types::subs::{Content, Variable};
|
||||
use roc_types::types::{Category, ErrorType, PatternCategory, Reason};
|
||||
use roc_types::types::{Category, ErrorType, PatternCategory, Reason, TypeExt};
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn type_problem(filename: PathBuf, problem: solve::TypeError) -> Report {
|
||||
@ -25,10 +27,6 @@ pub fn type_problem(filename: PathBuf, problem: solve::TypeError) -> Report {
|
||||
}
|
||||
}
|
||||
|
||||
fn type_in_focus(typ: ErrorType) -> ReportText {
|
||||
ReportText::ErrorType(typ)
|
||||
}
|
||||
|
||||
fn int_to_ordinal(number: usize) -> String {
|
||||
// NOTE: one-based
|
||||
let remainder10 = number % 10;
|
||||
@ -337,28 +335,6 @@ fn to_expr_report(
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
@ -489,7 +465,7 @@ fn to_circular_report(
|
||||
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."),
|
||||
type_in_focus(overall_type),
|
||||
error_type_block(to_doc(Parens::Unnecessary, &overall_type)),
|
||||
/* TODO hint */
|
||||
]),
|
||||
];
|
||||
@ -500,3 +476,423 @@ fn to_circular_report(
|
||||
text: Concat(lines),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Problem {
|
||||
IntFloat,
|
||||
ArityMismatch(usize, usize),
|
||||
FieldTypo(Vec<Lowercase>),
|
||||
}
|
||||
|
||||
fn problems_to_hint(_problems: Vec<Problem>) -> ReportText {
|
||||
// TODO
|
||||
ReportText::Concat(vec![])
|
||||
}
|
||||
|
||||
pub struct Comparison {
|
||||
actual: ReportText,
|
||||
expected: ReportText,
|
||||
problems: Vec<Problem>,
|
||||
}
|
||||
|
||||
fn to_comparison(actual: ErrorType, expected: ErrorType) -> Comparison {
|
||||
let diff = to_diff(Parens::Unnecessary, &actual, &expected);
|
||||
|
||||
Comparison {
|
||||
actual: error_type_block(diff.left),
|
||||
expected: error_type_block(diff.right),
|
||||
problems: match diff.status {
|
||||
Status::Similar => vec![],
|
||||
Status::Different(problems) => problems,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Status {
|
||||
Similar, // the structure is the same or e.g. record fields are different
|
||||
Different(Vec<Problem>), // e.g. found Bool, expected Int
|
||||
}
|
||||
|
||||
impl Status {
|
||||
pub fn merge(&mut self, other: Self) {
|
||||
use Status::*;
|
||||
match self {
|
||||
Similar => {
|
||||
*self = other;
|
||||
}
|
||||
Different(problems1) => match other {
|
||||
Similar => { /* nothing */ }
|
||||
Different(problems2) => {
|
||||
// TODO pick a data structure that makes this merge cheaper
|
||||
let mut problems = Vec::with_capacity(problems1.len() + problems2.len());
|
||||
problems.extend(problems1.iter().cloned());
|
||||
problems.extend(problems2);
|
||||
*self = Different(problems);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Diff<T> {
|
||||
left: T,
|
||||
right: T,
|
||||
status: Status,
|
||||
}
|
||||
|
||||
pub fn to_doc(parens: Parens, tipe: &ErrorType) -> ReportText {
|
||||
use ErrorType::*;
|
||||
|
||||
match tipe {
|
||||
Function(args, ret) => report_text::function(
|
||||
parens,
|
||||
args.iter().map(|arg| to_doc(Parens::InFn, arg)).collect(),
|
||||
to_doc(Parens::InFn, ret),
|
||||
),
|
||||
|
||||
Infinite => plain_text("∞"),
|
||||
Error => plain_text("?"),
|
||||
|
||||
FlexVar(lowercase) => plain_text(lowercase.as_str()),
|
||||
RigidVar(lowercase) => plain_text(lowercase.as_str()),
|
||||
|
||||
Type(symbol, args) => report_text::apply(
|
||||
parens,
|
||||
ReportText::Value(*symbol),
|
||||
args.iter()
|
||||
.map(|arg| to_doc(Parens::InTypeParam, arg))
|
||||
.collect(),
|
||||
),
|
||||
|
||||
Alias(symbol, args, _) => report_text::apply(
|
||||
parens,
|
||||
ReportText::Value(*symbol),
|
||||
args.iter()
|
||||
.map(|(_, arg)| to_doc(Parens::InTypeParam, arg))
|
||||
.collect(),
|
||||
),
|
||||
|
||||
Record(fields_map, ext) => {
|
||||
let mut fields = fields_map.into_iter().collect::<Vec<_>>();
|
||||
fields.sort_by(|(a, _), (b, _)| a.cmp(&b));
|
||||
|
||||
report_text::record(
|
||||
fields
|
||||
.into_iter()
|
||||
.map(|(k, v)| (plain_text(k.as_str()), to_doc(Parens::Unnecessary, v)))
|
||||
.collect(),
|
||||
ext_to_doc(ext),
|
||||
)
|
||||
}
|
||||
|
||||
TagUnion(tags_map, ext) => {
|
||||
let mut tags = tags_map
|
||||
.into_iter()
|
||||
.map(|(name, args)| {
|
||||
(
|
||||
name,
|
||||
args.iter()
|
||||
.map(|arg| to_doc(Parens::InTypeParam, arg))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
tags.sort_by(|(a, _), (b, _)| a.cmp(&b));
|
||||
|
||||
report_text::tag_union(
|
||||
tags.into_iter()
|
||||
.map(|(k, v)| (tag_name_text(k.clone()), v))
|
||||
.collect(),
|
||||
ext_to_doc(ext),
|
||||
)
|
||||
}
|
||||
|
||||
RecursiveTagUnion(rec_var, tags_map, ext) => {
|
||||
let mut tags = tags_map
|
||||
.into_iter()
|
||||
.map(|(name, args)| {
|
||||
(
|
||||
name,
|
||||
args.iter()
|
||||
.map(|arg| to_doc(Parens::InTypeParam, arg))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
tags.sort_by(|(a, _), (b, _)| a.cmp(&b));
|
||||
|
||||
report_text::recursive_tag_union(
|
||||
to_doc(Parens::Unnecessary, rec_var),
|
||||
tags.into_iter()
|
||||
.map(|(k, v)| (tag_name_text(k.clone()), v))
|
||||
.collect(),
|
||||
ext_to_doc(ext),
|
||||
)
|
||||
}
|
||||
|
||||
Boolean(b) => plain_text(&format!("{:?}", b)),
|
||||
}
|
||||
}
|
||||
|
||||
fn ext_to_doc(ext: &TypeExt) -> Option<ReportText> {
|
||||
use TypeExt::*;
|
||||
|
||||
match ext {
|
||||
Closed => None,
|
||||
FlexOpen(lowercase) | RigidOpen(lowercase) => Some(plain_text(lowercase.as_str())),
|
||||
}
|
||||
}
|
||||
|
||||
fn same(parens: Parens, tipe: &ErrorType) -> Diff<ReportText> {
|
||||
let doc = to_doc(parens, tipe);
|
||||
|
||||
Diff {
|
||||
left: doc.clone(),
|
||||
right: doc,
|
||||
status: Status::Similar,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_diff(parens: Parens, type1: &ErrorType, type2: &ErrorType) -> Diff<ReportText> {
|
||||
use ErrorType::*;
|
||||
|
||||
match (type1, type2) {
|
||||
(Error, Error) | (Infinite, Infinite) => same(parens, type1),
|
||||
|
||||
(FlexVar(x), FlexVar(y)) if x == y => same(parens, type1),
|
||||
(RigidVar(x), RigidVar(y)) if x == y => same(parens, type1),
|
||||
|
||||
(Function(args1, ret1), Function(args2, ret2)) => {
|
||||
if args1.len() == args2.len() {
|
||||
let mut status = Status::Similar;
|
||||
let arg_diff = traverse(Parens::InFn, args1, args2);
|
||||
let ret_diff = to_diff(Parens::InFn, ret1, ret2);
|
||||
status.merge(arg_diff.status);
|
||||
status.merge(ret_diff.status);
|
||||
|
||||
let left = report_text::function(parens, arg_diff.left, ret_diff.left);
|
||||
let right = report_text::function(parens, arg_diff.right, ret_diff.right);
|
||||
|
||||
Diff {
|
||||
left,
|
||||
right,
|
||||
status,
|
||||
}
|
||||
} else {
|
||||
let left = to_doc(Parens::InFn, type1);
|
||||
let right = to_doc(Parens::InFn, type2);
|
||||
|
||||
Diff {
|
||||
left,
|
||||
right,
|
||||
status: Status::Different(vec![Problem::ArityMismatch(
|
||||
args1.len(),
|
||||
args2.len(),
|
||||
)]),
|
||||
}
|
||||
}
|
||||
}
|
||||
(Type(symbol1, args1), Type(symbol2, args2)) if symbol1 == symbol2 => {
|
||||
let args_diff = traverse(Parens::InTypeParam, args1, args2);
|
||||
let left = report_text::apply(parens, ReportText::Value(*symbol1), args_diff.left);
|
||||
let right = report_text::apply(parens, ReportText::Value(*symbol2), args_diff.right);
|
||||
|
||||
Diff {
|
||||
left,
|
||||
right,
|
||||
status: args_diff.status,
|
||||
}
|
||||
}
|
||||
|
||||
(Alias(symbol1, args1, _), Alias(symbol2, args2, _)) if symbol1 == symbol2 => {
|
||||
// TODO remove collects
|
||||
let a1 = args1.iter().map(|(_, v)| v).collect::<Vec<_>>();
|
||||
let a2 = args2.iter().map(|(_, v)| v).collect::<Vec<_>>();
|
||||
let args_diff = traverse(Parens::InTypeParam, a1, a2);
|
||||
let left = report_text::apply(parens, ReportText::Value(*symbol1), args_diff.left);
|
||||
let right = report_text::apply(parens, ReportText::Value(*symbol2), args_diff.right);
|
||||
|
||||
Diff {
|
||||
left,
|
||||
right,
|
||||
status: args_diff.status,
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
// TODO actually diff
|
||||
// (Record(fields1, ext1), Record(fields2, ext2)) => {
|
||||
|
||||
let left = to_doc(Parens::Unnecessary, type1);
|
||||
let right = to_doc(Parens::Unnecessary, type2);
|
||||
|
||||
Diff {
|
||||
left,
|
||||
right,
|
||||
status: Status::Similar,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn traverse<'a, I>(parens: Parens, args1: I, args2: I) -> Diff<Vec<ReportText>>
|
||||
where
|
||||
I: IntoIterator<Item = &'a ErrorType>,
|
||||
{
|
||||
let mut status = Status::Similar;
|
||||
|
||||
// TODO use ExactSizeIterator to pre-allocate here
|
||||
let mut left = Vec::new();
|
||||
let mut right = Vec::new();
|
||||
|
||||
for (arg1, arg2) in args1.into_iter().zip(args2.into_iter()) {
|
||||
let diff = to_diff(parens, arg1, arg2);
|
||||
|
||||
left.push(diff.left);
|
||||
right.push(diff.right);
|
||||
status.merge(diff.status);
|
||||
}
|
||||
|
||||
Diff {
|
||||
left,
|
||||
right,
|
||||
status,
|
||||
}
|
||||
}
|
||||
|
||||
mod report_text {
|
||||
|
||||
use super::ReportText;
|
||||
use crate::report::{concat, intersperse, plain_text, separate};
|
||||
use roc_types::pretty_print::Parens;
|
||||
|
||||
fn with_parens(text: ReportText) -> ReportText {
|
||||
ReportText::Concat(vec![plain_text("("), text, plain_text(")")])
|
||||
}
|
||||
|
||||
pub fn function(parens: Parens, args: Vec<ReportText>, ret: ReportText) -> ReportText {
|
||||
let function_text = concat(vec![
|
||||
intersperse(plain_text(", "), args),
|
||||
plain_text(" -> "),
|
||||
ret,
|
||||
]);
|
||||
|
||||
match parens {
|
||||
Parens::Unnecessary => function_text,
|
||||
_ => with_parens(function_text),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply(parens: Parens, name: ReportText, args: Vec<ReportText>) -> ReportText {
|
||||
if args.is_empty() {
|
||||
name
|
||||
} else {
|
||||
let apply_text = concat(vec![
|
||||
name,
|
||||
plain_text(" "),
|
||||
intersperse(plain_text(" "), args),
|
||||
]);
|
||||
|
||||
match parens {
|
||||
Parens::Unnecessary | Parens::InFn => apply_text,
|
||||
Parens::InTypeParam => with_parens(apply_text),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn record(
|
||||
entries: Vec<(ReportText, ReportText)>,
|
||||
opt_ext: Option<ReportText>,
|
||||
) -> ReportText {
|
||||
let ext_text = if let Some(t) = opt_ext {
|
||||
t
|
||||
} else {
|
||||
plain_text("")
|
||||
};
|
||||
|
||||
if entries.is_empty() {
|
||||
concat(vec![plain_text("{}"), ext_text])
|
||||
} else {
|
||||
let entry_to_text = |(field_name, field_type)| {
|
||||
separate(vec![concat(vec![field_name, plain_text(" :")]), field_type])
|
||||
};
|
||||
|
||||
let starts = std::iter::once(plain_text("{")).chain(std::iter::repeat(plain_text(",")));
|
||||
|
||||
let mut lines: Vec<_> = entries
|
||||
.into_iter()
|
||||
.zip(starts)
|
||||
.map(|(entry, start)| concat(vec![start, entry_to_text(entry)]))
|
||||
.collect();
|
||||
|
||||
lines.push(plain_text("}"));
|
||||
lines.push(ext_text);
|
||||
|
||||
concat(lines)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tag_union(
|
||||
entries: Vec<(ReportText, Vec<ReportText>)>,
|
||||
opt_ext: Option<ReportText>,
|
||||
) -> ReportText {
|
||||
let ext_text = if let Some(t) = opt_ext {
|
||||
t
|
||||
} else {
|
||||
plain_text("")
|
||||
};
|
||||
|
||||
if entries.is_empty() {
|
||||
concat(vec![plain_text("[]"), ext_text])
|
||||
} else {
|
||||
let entry_to_text = |(tag_name, arguments)| concat(vec![tag_name, separate(arguments)]);
|
||||
|
||||
let starts = std::iter::once(plain_text("[")).chain(std::iter::repeat(plain_text(",")));
|
||||
|
||||
let mut lines: Vec<_> = entries
|
||||
.into_iter()
|
||||
.zip(starts)
|
||||
.map(|(entry, start)| concat(vec![start, entry_to_text(entry)]))
|
||||
.collect();
|
||||
|
||||
lines.push(plain_text("]"));
|
||||
lines.push(ext_text);
|
||||
|
||||
concat(lines)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recursive_tag_union(
|
||||
rec_var: ReportText,
|
||||
entries: Vec<(ReportText, Vec<ReportText>)>,
|
||||
opt_ext: Option<ReportText>,
|
||||
) -> ReportText {
|
||||
let ext_text = if let Some(t) = opt_ext {
|
||||
t
|
||||
} else {
|
||||
plain_text("")
|
||||
};
|
||||
|
||||
if entries.is_empty() {
|
||||
concat(vec![plain_text("[]"), ext_text])
|
||||
} else {
|
||||
let entry_to_text = |(tag_name, arguments)| concat(vec![tag_name, separate(arguments)]);
|
||||
|
||||
let starts = std::iter::once(plain_text("[")).chain(std::iter::repeat(plain_text(",")));
|
||||
|
||||
let mut lines: Vec<_> = entries
|
||||
.into_iter()
|
||||
.zip(starts)
|
||||
.map(|(entry, start)| concat(vec![start, entry_to_text(entry)]))
|
||||
.collect();
|
||||
|
||||
lines.push(plain_text("]"));
|
||||
lines.push(ext_text);
|
||||
|
||||
lines.push(plain_text(" as "));
|
||||
lines.push(rec_var);
|
||||
|
||||
concat(lines)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1265,21 +1265,24 @@ fn flat_type_to_err_type(subs: &mut Subs, state: &mut NameState, flat_type: Flat
|
||||
err_tags.insert(tag, err_vars);
|
||||
}
|
||||
|
||||
let rec_error_type = Box::new(var_to_err_type(subs, state, rec_var));
|
||||
|
||||
match var_to_err_type(subs, state, ext_var).unwrap_alias() {
|
||||
ErrorType::RecursiveTagUnion(rec_var, sub_tags, sub_ext) => {
|
||||
ErrorType::RecursiveTagUnion(rec_var, sub_tags.union(err_tags), sub_ext)
|
||||
debug_assert!(rec_var == rec_error_type);
|
||||
ErrorType::RecursiveTagUnion(rec_error_type, sub_tags.union(err_tags), sub_ext)
|
||||
}
|
||||
|
||||
ErrorType::TagUnion(sub_tags, sub_ext) => {
|
||||
ErrorType::RecursiveTagUnion(rec_var, sub_tags.union(err_tags), sub_ext)
|
||||
ErrorType::RecursiveTagUnion(rec_error_type, sub_tags.union(err_tags), sub_ext)
|
||||
}
|
||||
|
||||
ErrorType::FlexVar(var) => {
|
||||
ErrorType::RecursiveTagUnion(rec_var, err_tags, TypeExt::FlexOpen(var))
|
||||
ErrorType::RecursiveTagUnion(rec_error_type, err_tags, TypeExt::FlexOpen(var))
|
||||
}
|
||||
|
||||
ErrorType::RigidVar(var) => {
|
||||
ErrorType::RecursiveTagUnion(rec_var, err_tags, TypeExt::RigidOpen(var))
|
||||
ErrorType::RecursiveTagUnion(rec_error_type, err_tags, TypeExt::RigidOpen(var))
|
||||
}
|
||||
|
||||
other =>
|
||||
|
@ -709,7 +709,7 @@ pub enum ErrorType {
|
||||
RigidVar(Lowercase),
|
||||
Record(SendMap<Lowercase, ErrorType>, TypeExt),
|
||||
TagUnion(SendMap<TagName, Vec<ErrorType>>, TypeExt),
|
||||
RecursiveTagUnion(Variable, SendMap<TagName, Vec<ErrorType>>, TypeExt),
|
||||
RecursiveTagUnion(Box<ErrorType>, SendMap<TagName, Vec<ErrorType>>, TypeExt),
|
||||
Function(Vec<ErrorType>, Box<ErrorType>),
|
||||
Alias(Symbol, Vec<(Lowercase, ErrorType)>, Box<ErrorType>),
|
||||
Boolean(boolean_algebra::Bool),
|
||||
|
Loading…
Reference in New Issue
Block a user