mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 14:54:47 +00:00
fix self-recursive alias case
This commit is contained in:
parent
19761a5d44
commit
18c6c37c04
3 changed files with 104 additions and 37 deletions
|
@ -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),
|
||||||
])
|
])
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
|
@ -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 ]
|
||||||
┆ ^^^^^^^
|
┆ ^^^^^^^
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue