fix self-recursive alias case

This commit is contained in:
Folkert 2020-04-14 01:33:02 +02:00
parent 19761a5d44
commit 18c6c37c04
3 changed files with 104 additions and 37 deletions

View file

@ -17,7 +17,7 @@ const CYCLE_LN: &str = ["| ", "│ "][!IS_WINDOWS as usize];
const CYCLE_MID: &str = ["| |", "│ ↓"][!IS_WINDOWS as usize]; const CYCLE_MID: &str = ["| |", "│ ↓"][!IS_WINDOWS as usize];
const CYCLE_END: &str = ["+-<---+", "└─────┘"][!IS_WINDOWS as usize]; const CYCLE_END: &str = ["+-<---+", "└─────┘"][!IS_WINDOWS as usize];
fn cycle<'b>( pub fn cycle<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
indent: usize, indent: usize,
name: RocDocBuilder<'b>, name: RocDocBuilder<'b>,
@ -402,35 +402,13 @@ pub fn can_problem<'b>(
}, },
), ),
Problem::CyclicAlias(symbol, region, others) => { Problem::CyclicAlias(symbol, region, others) => {
if others.is_empty() { let (doc, title) = crate::type_error::cyclic_alias(alloc, symbol, region, others);
todo!("cyclic alias")
} else { return Report {
alloc.stack(vec![ filename,
alloc title,
.reflow("The ") doc,
.append(alloc.symbol_unqualified(symbol)) };
.append(alloc.reflow(" alias is recursive in an invalid way:")),
alloc.region(region),
alloc
.reflow("The ")
.append(alloc.symbol_unqualified(symbol))
.append(alloc.reflow(
" alias depends on itself through the following chain of definitions:",
)),
cycle(
alloc,
4,
alloc.symbol_unqualified(symbol),
others
.into_iter()
.map(|other| alloc.symbol_unqualified(other))
.collect::<Vec<_>>(),
),
alloc.reflow(
"Recursion in aliases is only allowed if recursion happens behind a tag.",
),
])
}
} }
Problem::PhantomTypeArgument { Problem::PhantomTypeArgument {
alias, alias,
@ -466,7 +444,7 @@ pub fn can_problem<'b>(
record_region, record_region,
} => alloc.stack(vec![ } => alloc.stack(vec![
alloc.concat(vec![ alloc.concat(vec![
alloc.reflow("This annotation defines the "), alloc.reflow("This record type defines the "),
alloc.record_field(field_name), alloc.record_field(field_name),
alloc.reflow(" field twice!"), alloc.reflow(" field twice!"),
]), ]),
@ -479,7 +457,7 @@ pub fn can_problem<'b>(
tag_region, tag_region,
} => alloc.stack(vec![ } => alloc.stack(vec![
alloc.concat(vec![ alloc.concat(vec![
alloc.reflow("This annotation defines the "), alloc.reflow("This tag union type defines the "),
alloc.tag_name(tag_name), alloc.tag_name(tag_name),
alloc.reflow(" tag twice!"), alloc.reflow(" tag twice!"),
]), ]),
@ -500,7 +478,7 @@ fn not_found<'b>(
alloc: &'b RocDocAllocator<'b>, alloc: &'b RocDocAllocator<'b>,
region: roc_region::all::Region, region: roc_region::all::Region,
name: &str, name: &str,
thing: &str, thing: &'b str,
options: MutSet<Box<str>>, options: MutSet<Box<str>>,
) -> RocDocBuilder<'b> { ) -> RocDocBuilder<'b> {
use crate::type_error::suggest; use crate::type_error::suggest;
@ -532,7 +510,12 @@ fn not_found<'b>(
}; };
alloc.stack(vec![ alloc.stack(vec![
alloc.string(format!("I cannot find a `{}` {}", name, thing)), alloc.concat(vec![
alloc.reflow("I cannot find a `"),
alloc.string(name.to_string()),
alloc.reflow("` "),
alloc.reflow(thing),
]),
alloc.region(region), alloc.region(region),
to_details(default_no, default_yes), to_details(default_no, default_yes),
]) ])

View file

@ -74,12 +74,66 @@ pub fn type_problem<'b>(
doc, doc,
} }
} }
CyclicAlias(symbol, region, others) => {
let (doc, title) = cyclic_alias(alloc, symbol, region, others);
Report {
filename,
title,
doc,
}
}
other => panic!("unhandled bad type: {:?}", other), other => panic!("unhandled bad type: {:?}", other),
} }
} }
} }
} }
pub fn cyclic_alias<'b>(
alloc: &'b RocDocAllocator<'b>,
symbol: Symbol,
region: roc_region::all::Region,
others: Vec<Symbol>,
) -> (RocDocBuilder<'b>, String) {
let doc = if others.is_empty() {
alloc.stack(vec![
alloc
.reflow("The ")
.append(alloc.symbol_unqualified(symbol))
.append(alloc.reflow(" alias is self-recursive in an invalid way:")),
alloc.region(region),
alloc.reflow("Recursion in aliases is only allowed if recursion happens behind a tag."),
])
} else {
alloc.stack(vec![
alloc
.reflow("The ")
.append(alloc.symbol_unqualified(symbol))
.append(alloc.reflow(" alias is recursive in an invalid way:")),
alloc.region(region),
alloc
.reflow("The ")
.append(alloc.symbol_unqualified(symbol))
.append(alloc.reflow(
" alias depends on itself through the following chain of definitions:",
)),
crate::report::cycle(
alloc,
4,
alloc.symbol_unqualified(symbol),
others
.into_iter()
.map(|other| alloc.symbol_unqualified(other))
.collect::<Vec<_>>(),
),
alloc.reflow("Recursion in aliases is only allowed if recursion happens behind a tag."),
])
};
(doc, "CYCLIC ALIAS".to_string())
}
#[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>,

View file

@ -2274,7 +2274,7 @@ mod test_reporting {
// should not report Bar as unused! // should not report Bar as unused!
indoc!( indoc!(
r#" r#"
-- SYNTAX PROBLEM -------------------------------------------------------------- -- CYCLIC ALIAS ----------------------------------------------------------------
The `Bar` alias is recursive in an invalid way: The `Bar` alias is recursive in an invalid way:
@ -2307,6 +2307,36 @@ mod test_reporting {
) )
} }
#[test]
fn self_recursive_alias() {
report_problem_as(
indoc!(
r#"
Foo : { x : Foo }
f : Foo
f = 3
f
"#
),
// should not report Bar as unused!
indoc!(
r#"
-- CYCLIC ALIAS ----------------------------------------------------------------
The `Foo` alias is self-recursive in an invalid way:
1 Foo : { x : Foo }
^^^
Recursion in aliases is only allowed if recursion happens behind a
tag.
"#
),
)
}
#[test] #[test]
fn record_duplicate_field_same_type() { fn record_duplicate_field_same_type() {
report_problem_as( report_problem_as(
@ -2368,7 +2398,7 @@ mod test_reporting {
r#" r#"
-- SYNTAX PROBLEM -------------------------------------------------------------- -- SYNTAX PROBLEM --------------------------------------------------------------
This annotation defines the `.foo` field twice! This record type defines the `.foo` field twice!
1 a : { foo : Int, bar : Float, foo : Str } 1 a : { foo : Int, bar : Float, foo : Str }
^^^^^^^^^ ^^^^^^^^^
@ -2394,7 +2424,7 @@ mod test_reporting {
r#" r#"
-- SYNTAX PROBLEM -------------------------------------------------------------- -- SYNTAX PROBLEM --------------------------------------------------------------
This annotation defines the `Foo` tag twice! This tag union type defines the `Foo` tag twice!
1 a : [ Foo Int, Bar Float, Foo Str ] 1 a : [ Foo Int, Bar Float, Foo Str ]
^^^^^^^ ^^^^^^^