diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index 392974852b..d8194bef68 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -168,7 +168,15 @@ pub fn canonicalize_defs<'a>( pattern_type, ) } else { - panic!("TODO gracefully handle the case where a type annotation appears immediately before a body def, but the patterns are different. This should be an error; put a newline or comment between them!"); + // the pattern of the annotation does not match the pattern of the body directly below it + env.problems.push(Problem::SignatureDefMismatch { + annotation_pattern: pattern.region, + def_pattern: body_pattern.region, + }); + + // both the annotation and definition are skipped! + iter.next(); + continue; } } _ => to_pending_def(env, var_store, &loc_def.value, &mut scope, pattern_type), diff --git a/compiler/problem/src/can.rs b/compiler/problem/src/can.rs index 5f83be28b0..3b37e40645 100644 --- a/compiler/problem/src/can.rs +++ b/compiler/problem/src/can.rs @@ -46,6 +46,10 @@ pub enum Problem { replaced_region: Region, }, RuntimeError(RuntimeError), + SignatureDefMismatch { + annotation_pattern: Region, + def_pattern: Region, + }, } #[derive(Clone, Debug, PartialEq)] diff --git a/compiler/reporting/src/error/canonicalize.rs b/compiler/reporting/src/error/canonicalize.rs index 9710ab32d5..702980fc95 100644 --- a/compiler/reporting/src/error/canonicalize.rs +++ b/compiler/reporting/src/error/canonicalize.rs @@ -1,6 +1,7 @@ use roc_collections::all::MutSet; use roc_problem::can::PrecedenceProblem::BothNonAssociative; use roc_problem::can::{Problem, RuntimeError}; +use roc_region::all::Region; use std::path::PathBuf; use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder}; @@ -238,6 +239,14 @@ pub fn can_problem<'b>( alloc.reflow(" definitions from this tag union type."), ]), ]), + Problem::SignatureDefMismatch { + ref annotation_pattern, + ref def_pattern, + } => alloc.stack(vec![ + alloc.reflow("This annotation does not match the definition immediately following it:"), + alloc.region(Region::span_across(annotation_pattern, def_pattern)), + alloc.reflow("Is it a typo? If not, put either a newline or comment between them."), + ]), Problem::RuntimeError(runtime_error) => pretty_runtime_error(alloc, runtime_error), }; diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index 90f7959672..2fb67fc285 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -2684,6 +2684,34 @@ mod test_reporting { ) } + #[test] + fn annotation_definition_mismatch() { + report_problem_as( + indoc!( + r#" + bar : Int + foo = \x -> x + + # NOTE: neither bar or foo are defined at this point + 4 + "# + ), + indoc!( + r#" + -- SYNTAX PROBLEM -------------------------------------------------------------- + + This annotation does not match the definition immediately following + it: + + 1 ┆> bar : Int + 2 ┆> foo = \x -> x + + Is it a typo? If not, put either a newline or comment between them. + "# + ), + ) + } + #[test] fn invalid_num() { report_problem_as( diff --git a/compiler/types/src/pretty_print.rs b/compiler/types/src/pretty_print.rs index 4f144cde94..71afd198eb 100644 --- a/compiler/types/src/pretty_print.rs +++ b/compiler/types/src/pretty_print.rs @@ -212,7 +212,7 @@ pub fn name_all_type_vars(variable: Variable, subs: &mut Subs) { for root in roots { // show the type variable number instead of `*`. useful for debugging - // set_root_name(root, &(format!("<{:?}>", root).into()), subs); + // set_root_name(root, (format!("<{:?}>", root).into()), subs); if let Some(Appearances::Multiple) = appearances.get(&root) { letters_used = name_root(letters_used, root, subs, &mut taken); } @@ -227,21 +227,19 @@ fn name_root( ) -> u32 { let (generated_name, new_letters_used) = name_type_var(letters_used, taken); - set_root_name(root, &generated_name, subs); + set_root_name(root, generated_name, subs); new_letters_used } -fn set_root_name(root: Variable, name: &Lowercase, subs: &mut Subs) { +fn set_root_name(root: Variable, name: Lowercase, subs: &mut Subs) { use crate::subs::Content::*; let mut descriptor = subs.get_without_compacting(root); match descriptor.content { FlexVar(None) => { - descriptor.content = FlexVar(Some(name.clone())); - - // TODO is this necessary, or was mutating descriptor in place sufficient? + descriptor.content = FlexVar(Some(name)); subs.set(root, descriptor); } FlexVar(Some(_existing)) => {