mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 08:11:12 +00:00
add cycle helper
This commit is contained in:
parent
931567ac4d
commit
06cd298126
2 changed files with 162 additions and 29 deletions
|
@ -8,6 +8,46 @@ use std::fmt;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder, Render, RenderAnnotated};
|
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder, Render, RenderAnnotated};
|
||||||
|
|
||||||
|
// const IS_WINDOWS: bool = std::env::consts::OS == "windows";
|
||||||
|
const IS_WINDOWS: bool = false;
|
||||||
|
|
||||||
|
// trick to branch in a const. Can be replaced by an if when that is merged into rustc
|
||||||
|
const CYCLE_TOP: &str = ["+-----+", "┌─────┐"][(!IS_WINDOWS) as usize];
|
||||||
|
const CYCLE_LN: &str = ["| ", "│ "][!IS_WINDOWS as usize];
|
||||||
|
const CYCLE_MID: &str = ["| |", "│ ↓"][!IS_WINDOWS as usize];
|
||||||
|
const CYCLE_END: &str = ["+-<---+", "└─────┘"][!IS_WINDOWS as usize];
|
||||||
|
|
||||||
|
fn cycle<'b>(
|
||||||
|
alloc: &'b RocDocAllocator<'b>,
|
||||||
|
indent: usize,
|
||||||
|
name: RocDocBuilder<'b>,
|
||||||
|
names: Vec<RocDocBuilder<'b>>,
|
||||||
|
) -> RocDocBuilder<'b> {
|
||||||
|
let mut lines = Vec::with_capacity(4 + (2 * names.len() - 1));
|
||||||
|
|
||||||
|
lines.push(alloc.text(CYCLE_TOP));
|
||||||
|
|
||||||
|
lines.push(alloc.text(CYCLE_LN).append(name));
|
||||||
|
lines.push(alloc.text(CYCLE_MID));
|
||||||
|
|
||||||
|
let mut it = names.into_iter().peekable();
|
||||||
|
|
||||||
|
while let Some(other_name) = it.next() {
|
||||||
|
lines.push(alloc.text(CYCLE_LN).append(other_name));
|
||||||
|
|
||||||
|
if it.peek().is_some() {
|
||||||
|
lines.push(alloc.text(CYCLE_MID));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push(alloc.text(CYCLE_END));
|
||||||
|
|
||||||
|
alloc
|
||||||
|
.vcat(lines)
|
||||||
|
.indent(indent)
|
||||||
|
.annotate(Annotation::TypeBlock)
|
||||||
|
}
|
||||||
|
|
||||||
/// A textual report.
|
/// A textual report.
|
||||||
pub struct Report<'b> {
|
pub struct Report<'b> {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
@ -155,24 +195,55 @@ pub fn can_problem<'b>(
|
||||||
Problem::PrecedenceProblem(BothNonAssociative(region, left_bin_op, right_bin_op)) => alloc
|
Problem::PrecedenceProblem(BothNonAssociative(region, left_bin_op, right_bin_op)) => alloc
|
||||||
.stack(vec![
|
.stack(vec![
|
||||||
if left_bin_op.value == right_bin_op.value {
|
if left_bin_op.value == right_bin_op.value {
|
||||||
alloc.reflow("Using more than one ")
|
alloc.concat(vec![
|
||||||
.append(alloc.binop(left_bin_op.value))
|
alloc.reflow("Using more than one "),
|
||||||
.append(alloc.reflow(
|
alloc.binop(left_bin_op.value),
|
||||||
" like this requires parentheses, to clarify how things should be grouped.",
|
alloc.reflow(concat!(
|
||||||
))
|
" like this requires parentheses,",
|
||||||
|
" to clarify how things should be grouped.",
|
||||||
|
)),
|
||||||
|
])
|
||||||
} else {
|
} else {
|
||||||
(alloc.reflow("Using "))
|
alloc.concat(vec![
|
||||||
.append(alloc.binop(left_bin_op.value))
|
alloc.reflow("Using "),
|
||||||
.append(alloc.reflow(" and "))
|
alloc.binop(left_bin_op.value),
|
||||||
.append(alloc.binop(right_bin_op.value))
|
alloc.reflow(" and "),
|
||||||
.append(alloc.reflow(
|
alloc.binop(right_bin_op.value),
|
||||||
" together requires parentheses, to clarify how they should be grouped.",
|
alloc.reflow(concat!(
|
||||||
))
|
" together requires parentheses, ",
|
||||||
|
"to clarify how they should be grouped."
|
||||||
|
)),
|
||||||
|
])
|
||||||
},
|
},
|
||||||
alloc.region(region),
|
alloc.region(region),
|
||||||
]),
|
]),
|
||||||
Problem::UnsupportedPattern(_pattern_type, _region) => {
|
Problem::UnsupportedPattern(pattern_type, region) => {
|
||||||
panic!("TODO implement unsupported pattern report")
|
use roc_parse::pattern::PatternType::*;
|
||||||
|
|
||||||
|
let this_thing = match pattern_type {
|
||||||
|
TopLevelDef => "a top-level definition:",
|
||||||
|
DefExpr => "a value definition:",
|
||||||
|
FunctionArg => "function arguments:",
|
||||||
|
WhenBranch => unreachable!("all patterns are allowed in a When"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let suggestion = vec![
|
||||||
|
alloc.reflow(
|
||||||
|
"Patterns like this don't cover all possible shapes of the input type. Use a ",
|
||||||
|
),
|
||||||
|
alloc.keyword("when"),
|
||||||
|
alloc.reflow(" ... "),
|
||||||
|
alloc.keyword("is"),
|
||||||
|
alloc.reflow(" instead."),
|
||||||
|
];
|
||||||
|
|
||||||
|
alloc.stack(vec![
|
||||||
|
alloc
|
||||||
|
.reflow("This pattern is not allowed in ")
|
||||||
|
.append(alloc.reflow(this_thing)),
|
||||||
|
alloc.region(region),
|
||||||
|
alloc.concat(suggestion),
|
||||||
|
])
|
||||||
}
|
}
|
||||||
Problem::ShadowingInAnnotation {
|
Problem::ShadowingInAnnotation {
|
||||||
original_region,
|
original_region,
|
||||||
|
@ -261,20 +332,51 @@ fn pretty_runtime_error<'b>(
|
||||||
RuntimeError::LookupNotInScope(loc_name, options) => {
|
RuntimeError::LookupNotInScope(loc_name, options) => {
|
||||||
not_found(alloc, loc_name.region, &loc_name.value, "value", options)
|
not_found(alloc, loc_name.region, &loc_name.value, "value", options)
|
||||||
}
|
}
|
||||||
RuntimeError::CircularDef(idents, _regions) => match idents.first() {
|
RuntimeError::CircularDef(mut idents, regions) => {
|
||||||
Some(ident) => alloc.stack(vec![alloc
|
let first = idents.remove(0);
|
||||||
|
|
||||||
|
if idents.is_empty() {
|
||||||
|
alloc
|
||||||
.reflow("The ")
|
.reflow("The ")
|
||||||
.append(alloc.ident(ident.value.clone()))
|
.append(alloc.ident(first.value.clone()))
|
||||||
.append(alloc.reflow(
|
.append(alloc.reflow(
|
||||||
" value is defined directly in terms of itself, causing an infinite loop.",
|
" value is defined directly in terms of itself, causing an infinite loop.",
|
||||||
))]),
|
))
|
||||||
None => alloc.nil(),
|
// TODO "are you trying to mutate a variable?
|
||||||
},
|
// TODO hint?
|
||||||
|
} else {
|
||||||
|
alloc.stack(vec![
|
||||||
|
alloc
|
||||||
|
.reflow("The ")
|
||||||
|
.append(alloc.ident(first.value.clone()))
|
||||||
|
.append(
|
||||||
|
alloc.reflow(" definition is causing a very tricky infinite loop:"),
|
||||||
|
),
|
||||||
|
alloc.region(regions[0].0),
|
||||||
|
alloc
|
||||||
|
.reflow("The ")
|
||||||
|
.append(alloc.ident(first.value.clone()))
|
||||||
|
.append(alloc.reflow(
|
||||||
|
" value depends on itself through the following chain of definitions:",
|
||||||
|
)),
|
||||||
|
cycle(
|
||||||
|
alloc,
|
||||||
|
4,
|
||||||
|
alloc.ident(first.value),
|
||||||
|
idents
|
||||||
|
.into_iter()
|
||||||
|
.map(|ident| alloc.ident(ident.value))
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
),
|
||||||
|
// TODO hint?
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
other => {
|
other => {
|
||||||
// // Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
|
// // Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
|
||||||
// UnsupportedPattern(Region),
|
// UnsupportedPattern(Region),
|
||||||
// UnrecognizedFunctionName(Located<InlinableString>),
|
// UnrecognizedFunctionName(Located<InlinableString>),
|
||||||
// alloc.symbol_unqualified(NotExposed {
|
// SymbolNotExposed {
|
||||||
// module_name: InlinableString,
|
// module_name: InlinableString,
|
||||||
// ident: InlinableString,
|
// ident: InlinableString,
|
||||||
// region: Region,
|
// region: Region,
|
||||||
|
|
|
@ -1690,14 +1690,11 @@ mod test_reporting {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn circular_definition() {
|
fn circular_definition_self() {
|
||||||
// these error messages seem pretty helpful
|
|
||||||
report_problem_as(
|
report_problem_as(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
f = g
|
f = f
|
||||||
|
|
||||||
g = f
|
|
||||||
|
|
||||||
f
|
f
|
||||||
"#
|
"#
|
||||||
|
@ -1712,4 +1709,38 @@ mod test_reporting {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn circular_definition() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
foo = bar
|
||||||
|
|
||||||
|
bar = foo
|
||||||
|
|
||||||
|
foo
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
-- SYNTAX PROBLEM --------------------------------------------------------------
|
||||||
|
|
||||||
|
The `foo` definition is causing a very tricky infinite loop:
|
||||||
|
|
||||||
|
1 ┆ foo = bar
|
||||||
|
┆ ^^^
|
||||||
|
|
||||||
|
The `foo` value depends on itself through the following chain of
|
||||||
|
definitions:
|
||||||
|
|
||||||
|
┌─────┐
|
||||||
|
│ foo
|
||||||
|
│ ↓
|
||||||
|
│ bar
|
||||||
|
└─────┘
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue