Merge pull request #394 from rtfeldman/annotation-def-match

Report when annotation pattern does not match definition pattern
This commit is contained in:
Richard Feldman 2020-07-02 19:38:18 -04:00 committed by GitHub
commit 39b70232de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 54 additions and 7 deletions

View file

@ -168,7 +168,15 @@ pub fn canonicalize_defs<'a>(
pattern_type, pattern_type,
) )
} else { } 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), _ => to_pending_def(env, var_store, &loc_def.value, &mut scope, pattern_type),

View file

@ -46,6 +46,10 @@ pub enum Problem {
replaced_region: Region, replaced_region: Region,
}, },
RuntimeError(RuntimeError), RuntimeError(RuntimeError),
SignatureDefMismatch {
annotation_pattern: Region,
def_pattern: Region,
},
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]

View file

@ -1,6 +1,7 @@
use roc_collections::all::MutSet; use roc_collections::all::MutSet;
use roc_problem::can::PrecedenceProblem::BothNonAssociative; use roc_problem::can::PrecedenceProblem::BothNonAssociative;
use roc_problem::can::{Problem, RuntimeError}; use roc_problem::can::{Problem, RuntimeError};
use roc_region::all::Region;
use std::path::PathBuf; use std::path::PathBuf;
use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder}; use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder};
@ -238,6 +239,14 @@ pub fn can_problem<'b>(
alloc.reflow(" definitions from this tag union type."), 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), Problem::RuntimeError(runtime_error) => pretty_runtime_error(alloc, runtime_error),
}; };

View file

@ -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] #[test]
fn invalid_num() { fn invalid_num() {
report_problem_as( report_problem_as(

View file

@ -212,7 +212,7 @@ pub fn name_all_type_vars(variable: Variable, subs: &mut Subs) {
for root in roots { for root in roots {
// show the type variable number instead of `*`. useful for debugging // 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) { if let Some(Appearances::Multiple) = appearances.get(&root) {
letters_used = name_root(letters_used, root, subs, &mut taken); letters_used = name_root(letters_used, root, subs, &mut taken);
} }
@ -227,21 +227,19 @@ fn name_root(
) -> u32 { ) -> u32 {
let (generated_name, new_letters_used) = name_type_var(letters_used, taken); 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 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::*; use crate::subs::Content::*;
let mut descriptor = subs.get_without_compacting(root); let mut descriptor = subs.get_without_compacting(root);
match descriptor.content { match descriptor.content {
FlexVar(None) => { FlexVar(None) => {
descriptor.content = FlexVar(Some(name.clone())); descriptor.content = FlexVar(Some(name));
// TODO is this necessary, or was mutating descriptor in place sufficient?
subs.set(root, descriptor); subs.set(root, descriptor);
} }
FlexVar(Some(_existing)) => { FlexVar(Some(_existing)) => {