diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index 374d882f18..1634ade5a0 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -1326,7 +1326,14 @@ fn to_pending_def<'a>( } } - Err(err) => panic!("TODO gracefully handle shadowing of type alias {:?}", err), + Err((original_region, shadow)) => { + env.problem(Problem::ShadowingInAnnotation { + original_region, + shadow, + }); + + panic!("TODO gracefully handle shadowing of type alias {:?}", err) + } } } diff --git a/compiler/reporting/src/report.rs b/compiler/reporting/src/report.rs index 7f38c7edf3..69156c3a1e 100644 --- a/compiler/reporting/src/report.rs +++ b/compiler/reporting/src/report.rs @@ -1,12 +1,14 @@ 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_problem::can::{Problem, RuntimeError}; use roc_types::pretty_print::content_to_string; use roc_types::subs::{Content, Subs}; use roc_types::types::{write_error_type, ErrorType}; use std::path::PathBuf; +use roc_module::ident::Ident; +use roc_region::all::Located; use std::fmt; use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder, Render, RenderAnnotated}; @@ -110,14 +112,21 @@ pub fn can_problem(filename: PathBuf, problem: Problem) -> Report { original_region, shadow, } => { - // v-- just to satisfy clippy - let _a = original_region; - let _b = shadow; - panic!("TODO implement shadow report"); - } - Problem::RuntimeError(_runtime_error) => { - panic!("TODO implement run time error report"); + shadowing_report(&mut texts, original_region, shadow); } + Problem::RuntimeError(runtime_error) => match runtime_error { + RuntimeError::Shadowing { + original_region, + shadow, + } => { + shadowing_report(&mut texts, original_region, shadow); + } + + _ => { + dbg!(runtime_error); + panic!("TODO implement run time error reporting"); + } + }, }; Report { @@ -126,11 +135,27 @@ pub fn can_problem(filename: PathBuf, problem: Problem) -> Report { } } +fn shadowing_report( + texts: &mut Vec, + original_region: roc_region::all::Region, + shadow: Located, +) { + texts.push(name(shadow.value)); + texts.push(plain_text(" is first defined here:")); + texts.push(Region(original_region)); + texts.push(plain_text("But then it's defined a second time here:")); + texts.push(Region(shadow.region)); + texts.push(plain_text("Since these variables have the same name, it's easy to use the wrong one on accident. Give one of them a new name.")); +} + #[derive(Debug, Clone)] pub enum ReportText { /// A value. Render it qualified unless it was defined in the current module. Value(Symbol), + /// An identifier, should probably be rendered the same way as a symbol. + Name(Ident), + /// A module, Module(ModuleId), @@ -181,6 +206,10 @@ pub fn plain_text(str: &str) -> ReportText { ReportText::Plain(Box::from(str)) } +pub fn name(ident: Ident) -> ReportText { + ReportText::Name(ident) +} + pub fn em_text(str: &str) -> ReportText { ReportText::EmText(Box::from(str)) } @@ -658,6 +687,9 @@ impl ReportText { .append(alloc.line()) .append(alloc.line()) } + Name(ident) => alloc + .text(format!("{}", ident.as_inline_str())) + .annotate(Annotation::Symbol), } } } diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index ba785aa362..d8147653c2 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -305,22 +305,67 @@ mod test_reporting { ) } - // #[test] - // fn report_shadow() { - // report_problem_as( - // indoc!( - // r#" - // i = 1 - // - // s = \i -> - // i + 1 - // - // s i - // "# - // ), - // indoc!(r#" "#), - // ) - // } + #[test] + fn report_shadowing() { + report_problem_as( + indoc!( + r#" + i = 1 + + s = \i -> + i + 1 + + s i + "# + ), + indoc!( + r#" + i is first defined here: + + 1 ┆ i = 1 + ┆ ^ + + But then it's defined a second time here: + + 3 ┆ s = \i -> + ┆ ^ + + Since these variables have the same name, it's easy to use the wrong one on accident. Give one of them a new name."# + ), + ) + } + + #[test] + fn report_shadowing_in_annotation() { + report_problem_as( + indoc!( + r#" + Booly : [ Yes, No ] + + Booly : [ Yes, No, Maybe ] + + x = + No + + x + "# + ), + indoc!( + r#" + Booly is first defined here: + + 1 ┆ Booly : [ Yes, No ] + ┆ ^^^^^ + + But then it's defined a second time here: + + 3 ┆ Booly : [ Yes, No, Maybe ] + ┆ ^^^^^ + + Since these variables have the same name, it's easy to use the wrong one on accident. Give one of them a new name."# + ), + ) + } // #[test] // fn report_unsupported_top_level_def() {