mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 23:31:12 +00:00
clean up annotation canonicalization
This commit is contained in:
parent
147a482f9d
commit
362ff74b82
1 changed files with 179 additions and 162 deletions
|
@ -1,6 +1,6 @@
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
use roc_collections::all::{MutMap, MutSet, SendMap};
|
use roc_collections::all::{MutSet, SendMap};
|
||||||
use roc_module::ident::{Ident, Lowercase, TagName};
|
use roc_module::ident::{Ident, Lowercase, TagName};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_parse::ast::{AssignedField, Tag, TypeAnnotation};
|
use roc_parse::ast::{AssignedField, Tag, TypeAnnotation};
|
||||||
|
@ -294,34 +294,17 @@ fn can_annotation_help(
|
||||||
},
|
},
|
||||||
|
|
||||||
Record { fields, ext } => {
|
Record { fields, ext } => {
|
||||||
let mut field_types = SendMap::default();
|
let field_types = can_assigned_fields(
|
||||||
let mut seen = MutMap::default();
|
|
||||||
|
|
||||||
for field in fields.iter() {
|
|
||||||
let opt_field_name = can_assigned_field(
|
|
||||||
env,
|
env,
|
||||||
&field.value,
|
fields,
|
||||||
region,
|
region,
|
||||||
scope,
|
scope,
|
||||||
var_store,
|
var_store,
|
||||||
introduced_variables,
|
introduced_variables,
|
||||||
local_aliases,
|
local_aliases,
|
||||||
&mut field_types,
|
|
||||||
references,
|
references,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(added) = opt_field_name {
|
|
||||||
if let Some(replaced_region) = seen.insert(added.clone(), field.region) {
|
|
||||||
env.problem(roc_problem::can::Problem::DuplicateRecordFieldType {
|
|
||||||
field_name: added.clone(),
|
|
||||||
field_region: field.region,
|
|
||||||
record_region: region,
|
|
||||||
replaced_region,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let ext_type = match ext {
|
let ext_type = match ext {
|
||||||
Some(loc_ann) => can_annotation_help(
|
Some(loc_ann) => can_annotation_help(
|
||||||
env,
|
env,
|
||||||
|
@ -339,39 +322,22 @@ fn can_annotation_help(
|
||||||
Type::Record(field_types, Box::new(ext_type))
|
Type::Record(field_types, Box::new(ext_type))
|
||||||
}
|
}
|
||||||
TagUnion { tags, ext } => {
|
TagUnion { tags, ext } => {
|
||||||
let mut tag_types = Vec::with_capacity(tags.len());
|
let tag_types = can_tags(
|
||||||
let mut seen = MutMap::default();
|
|
||||||
|
|
||||||
for tag in tags.iter() {
|
|
||||||
let opt_tag_name = can_tag(
|
|
||||||
env,
|
env,
|
||||||
&tag.value,
|
tags,
|
||||||
region,
|
region,
|
||||||
scope,
|
scope,
|
||||||
var_store,
|
var_store,
|
||||||
introduced_variables,
|
introduced_variables,
|
||||||
local_aliases,
|
local_aliases,
|
||||||
&mut tag_types,
|
|
||||||
references,
|
references,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(added) = opt_tag_name {
|
|
||||||
if let Some(replaced_region) = seen.insert(added.clone(), tag.region) {
|
|
||||||
env.problem(roc_problem::can::Problem::DuplicateTag {
|
|
||||||
tag_name: added.clone(),
|
|
||||||
tag_region: tag.region,
|
|
||||||
tag_union_region: region,
|
|
||||||
replaced_region,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let ext_type = match ext {
|
let ext_type = match ext {
|
||||||
Some(loc_ann) => can_annotation_help(
|
Some(loc_ann) => can_annotation_help(
|
||||||
env,
|
env,
|
||||||
&loc_ann.value,
|
&loc_ann.value,
|
||||||
region,
|
loc_ann.region,
|
||||||
scope,
|
scope,
|
||||||
var_store,
|
var_store,
|
||||||
introduced_variables,
|
introduced_variables,
|
||||||
|
@ -405,19 +371,32 @@ fn can_annotation_help(
|
||||||
|
|
||||||
// TODO trim down these arguments!
|
// TODO trim down these arguments!
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn can_assigned_field<'a>(
|
fn can_assigned_fields<'a>(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
field: &AssignedField<'a, TypeAnnotation<'a>>,
|
fields: &&[Located<AssignedField<'a, TypeAnnotation<'a>>>],
|
||||||
region: Region,
|
region: Region,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
introduced_variables: &mut IntroducedVariables,
|
introduced_variables: &mut IntroducedVariables,
|
||||||
local_aliases: &mut SendMap<Symbol, Alias>,
|
local_aliases: &mut SendMap<Symbol, Alias>,
|
||||||
field_types: &mut SendMap<Lowercase, Type>,
|
|
||||||
references: &mut MutSet<Symbol>,
|
references: &mut MutSet<Symbol>,
|
||||||
) -> Option<Lowercase> {
|
) -> SendMap<Lowercase, Type> {
|
||||||
use roc_parse::ast::AssignedField::*;
|
use roc_parse::ast::AssignedField::*;
|
||||||
|
|
||||||
|
// SendMap doesn't have a `with_capacity`
|
||||||
|
let mut field_types = SendMap::default();
|
||||||
|
|
||||||
|
// field names we've seen so far in this record
|
||||||
|
let mut seen = std::collections::HashMap::with_capacity(fields.len());
|
||||||
|
|
||||||
|
'outer: for loc_field in fields.iter() {
|
||||||
|
let mut field = &loc_field.value;
|
||||||
|
|
||||||
|
// use this inner loop to unwrap the SpaceAfter/SpaceBefore
|
||||||
|
// when we find the name of this field, break out of the loop
|
||||||
|
// with that value, so we can check whether the field name is
|
||||||
|
// a duplicate
|
||||||
|
let new_name = 'inner: loop {
|
||||||
match field {
|
match field {
|
||||||
LabeledValue(field_name, _, annotation) => {
|
LabeledValue(field_name, _, annotation) => {
|
||||||
let field_type = can_annotation_help(
|
let field_type = can_annotation_help(
|
||||||
|
@ -434,7 +413,7 @@ fn can_assigned_field<'a>(
|
||||||
let label = Lowercase::from(field_name.value);
|
let label = Lowercase::from(field_name.value);
|
||||||
field_types.insert(label.clone(), field_type);
|
field_types.insert(label.clone(), field_type);
|
||||||
|
|
||||||
Some(label)
|
break 'inner label;
|
||||||
}
|
}
|
||||||
LabelOnly(loc_field_name) => {
|
LabelOnly(loc_field_name) => {
|
||||||
// Interpret { a, b } as { a : a, b : b }
|
// Interpret { a, b } as { a : a, b : b }
|
||||||
|
@ -451,36 +430,61 @@ fn can_assigned_field<'a>(
|
||||||
|
|
||||||
field_types.insert(field_name.clone(), field_type);
|
field_types.insert(field_name.clone(), field_type);
|
||||||
|
|
||||||
Some(field_name)
|
break 'inner field_name;
|
||||||
}
|
}
|
||||||
SpaceBefore(nested, _) | SpaceAfter(nested, _) => can_assigned_field(
|
SpaceBefore(nested, _) | SpaceAfter(nested, _) => {
|
||||||
env,
|
// check the nested field instead
|
||||||
nested,
|
field = nested;
|
||||||
region,
|
continue 'inner;
|
||||||
scope,
|
|
||||||
var_store,
|
|
||||||
introduced_variables,
|
|
||||||
local_aliases,
|
|
||||||
field_types,
|
|
||||||
references,
|
|
||||||
),
|
|
||||||
Malformed(_) => None,
|
|
||||||
}
|
}
|
||||||
|
Malformed(_) => {
|
||||||
|
// TODO report this?
|
||||||
|
// completely skip this element, advance to the next tag
|
||||||
|
continue 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ensure that the new name is not already in this record:
|
||||||
|
// note that the right-most tag wins when there are two with the same name
|
||||||
|
if let Some(replaced_region) = seen.insert(new_name.clone(), loc_field.region) {
|
||||||
|
env.problem(roc_problem::can::Problem::DuplicateRecordFieldType {
|
||||||
|
field_name: new_name,
|
||||||
|
record_region: region,
|
||||||
|
field_region: loc_field.region,
|
||||||
|
replaced_region,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
field_types
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO trim down these arguments!
|
// TODO trim down these arguments!
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn can_tag<'a>(
|
fn can_tags<'a>(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
tag: &Tag<'a>,
|
tags: &'a [Located<Tag<'a>>],
|
||||||
region: Region,
|
region: Region,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
introduced_variables: &mut IntroducedVariables,
|
introduced_variables: &mut IntroducedVariables,
|
||||||
local_aliases: &mut SendMap<Symbol, Alias>,
|
local_aliases: &mut SendMap<Symbol, Alias>,
|
||||||
tag_types: &mut Vec<(TagName, Vec<Type>)>,
|
|
||||||
references: &mut MutSet<Symbol>,
|
references: &mut MutSet<Symbol>,
|
||||||
) -> Option<TagName> {
|
) -> Vec<(TagName, Vec<Type>)> {
|
||||||
|
let mut tag_types = Vec::with_capacity(tags.len());
|
||||||
|
|
||||||
|
// tag names we've seen so far in this tag union
|
||||||
|
let mut seen = std::collections::HashMap::with_capacity(tags.len());
|
||||||
|
|
||||||
|
'outer: for loc_tag in tags.iter() {
|
||||||
|
let mut tag = &loc_tag.value;
|
||||||
|
|
||||||
|
// use this inner loop to unwrap the SpaceAfter/SpaceBefore
|
||||||
|
// when we find the name of this tag, break out of the loop
|
||||||
|
// with that value, so we can check whether the tag name is
|
||||||
|
// a duplicate
|
||||||
|
let new_name = 'inner: loop {
|
||||||
match tag {
|
match tag {
|
||||||
Tag::Global { name, args } => {
|
Tag::Global { name, args } => {
|
||||||
let name = name.value.into();
|
let name = name.value.into();
|
||||||
|
@ -490,7 +494,7 @@ fn can_tag<'a>(
|
||||||
let ann = can_annotation_help(
|
let ann = can_annotation_help(
|
||||||
env,
|
env,
|
||||||
&arg.value,
|
&arg.value,
|
||||||
region,
|
arg.region,
|
||||||
scope,
|
scope,
|
||||||
var_store,
|
var_store,
|
||||||
introduced_variables,
|
introduced_variables,
|
||||||
|
@ -504,7 +508,7 @@ fn can_tag<'a>(
|
||||||
let tag_name = TagName::Global(name);
|
let tag_name = TagName::Global(name);
|
||||||
tag_types.push((tag_name.clone(), arg_types));
|
tag_types.push((tag_name.clone(), arg_types));
|
||||||
|
|
||||||
Some(tag_name)
|
break 'inner tag_name;
|
||||||
}
|
}
|
||||||
Tag::Private { name, args } => {
|
Tag::Private { name, args } => {
|
||||||
let ident_id = env.ident_ids.get_or_insert(&name.value.into());
|
let ident_id = env.ident_ids.get_or_insert(&name.value.into());
|
||||||
|
@ -515,7 +519,7 @@ fn can_tag<'a>(
|
||||||
let ann = can_annotation_help(
|
let ann = can_annotation_help(
|
||||||
env,
|
env,
|
||||||
&arg.value,
|
&arg.value,
|
||||||
region,
|
arg.region,
|
||||||
scope,
|
scope,
|
||||||
var_store,
|
var_store,
|
||||||
introduced_variables,
|
introduced_variables,
|
||||||
|
@ -529,19 +533,32 @@ fn can_tag<'a>(
|
||||||
let tag_name = TagName::Private(symbol);
|
let tag_name = TagName::Private(symbol);
|
||||||
tag_types.push((tag_name.clone(), arg_types));
|
tag_types.push((tag_name.clone(), arg_types));
|
||||||
|
|
||||||
Some(tag_name)
|
break 'inner tag_name;
|
||||||
}
|
}
|
||||||
Tag::SpaceBefore(nested, _) | Tag::SpaceAfter(nested, _) => can_tag(
|
Tag::SpaceBefore(nested, _) | Tag::SpaceAfter(nested, _) => {
|
||||||
env,
|
// check the nested tag instead
|
||||||
nested,
|
tag = nested;
|
||||||
region,
|
continue 'inner;
|
||||||
scope,
|
|
||||||
var_store,
|
|
||||||
introduced_variables,
|
|
||||||
local_aliases,
|
|
||||||
tag_types,
|
|
||||||
references,
|
|
||||||
),
|
|
||||||
Tag::Malformed(_) => None,
|
|
||||||
}
|
}
|
||||||
|
Tag::Malformed(_) => {
|
||||||
|
// TODO report this?
|
||||||
|
// completely skip this element, advance to the next tag
|
||||||
|
continue 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ensure that the new name is not already in this tag union:
|
||||||
|
// note that the right-most tag wins when there are two with the same name
|
||||||
|
if let Some(replaced_region) = seen.insert(new_name.clone(), loc_tag.region) {
|
||||||
|
env.problem(roc_problem::can::Problem::DuplicateTag {
|
||||||
|
tag_name: new_name,
|
||||||
|
tag_region: loc_tag.region,
|
||||||
|
tag_union_region: region,
|
||||||
|
replaced_region,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tag_types
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue