This commit is contained in:
Sunli 2022-06-25 10:51:59 +08:00
parent 79e10a221d
commit c129cf57d6
1 changed files with 88 additions and 9 deletions

View File

@ -20,18 +20,18 @@ impl<'a> Visitor<'a> for OverlappingFieldsCanBeMerged {
visited: Default::default(),
ctx,
};
find_conflicts.find(selection_set);
find_conflicts.find(None, selection_set);
}
}
struct FindConflicts<'a, 'ctx> {
outputs: HashMap<&'a str, &'a Positioned<Field>>,
outputs: HashMap<(Option<&'a str>, &'a str), &'a Positioned<Field>>,
visited: HashSet<&'a str>,
ctx: &'a mut VisitorContext<'ctx>,
}
impl<'a, 'ctx> FindConflicts<'a, 'ctx> {
pub fn find(&mut self, selection_set: &'a Positioned<SelectionSet>) {
pub fn find(&mut self, on_type: Option<&'a str>, selection_set: &'a Positioned<SelectionSet>) {
for selection in &selection_set.node.items {
match &selection.node {
Selection::Field(field) => {
@ -41,15 +41,22 @@ impl<'a, 'ctx> FindConflicts<'a, 'ctx> {
.as_ref()
.map(|name| &name.node)
.unwrap_or_else(|| &field.node.name.node);
self.add_output(&output_name, field);
self.add_output(on_type, &output_name, field);
}
Selection::InlineFragment(inline_fragment) => {
self.find(&inline_fragment.node.selection_set);
let on_type = inline_fragment
.node
.type_condition
.as_ref()
.map(|cond| cond.node.on.node.as_str());
self.find(on_type, &inline_fragment.node.selection_set);
}
Selection::FragmentSpread(fragment_spread) => {
if let Some(fragment) =
self.ctx.fragment(&fragment_spread.node.fragment_name.node)
{
let on_type = Some(fragment.node.type_condition.node.on.node.as_str());
if !self
.visited
.insert(fragment_spread.node.fragment_name.node.as_str())
@ -58,15 +65,21 @@ impl<'a, 'ctx> FindConflicts<'a, 'ctx> {
// `NoFragmentCycles` validator.
continue;
}
self.find(&fragment.node.selection_set);
self.find(on_type, &fragment.node.selection_set);
}
}
}
}
}
fn add_output(&mut self, name: &'a str, field: &'a Positioned<Field>) {
if let Some(prev_field) = self.outputs.get(name) {
fn add_output(
&mut self,
on_type: Option<&'a str>,
name: &'a str,
field: &'a Positioned<Field>,
) {
if let Some(prev_field) = self.outputs.get(&(on_type, name)) {
if prev_field.node.name.node != field.node.name.node {
self.ctx.report_error(
vec![prev_field.pos, field.pos],
@ -90,7 +103,73 @@ impl<'a, 'ctx> FindConflicts<'a, 'ctx> {
}
}
} else {
self.outputs.insert(name, field);
self.outputs.insert((on_type, name), field);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
pub fn factory() -> OverlappingFieldsCanBeMerged {
OverlappingFieldsCanBeMerged
}
#[test]
fn same_field_on_different_type() {
expect_passes_rule!(
factory,
r#"
{
pet {
... on Dog {
doesKnowCommand(dogCommand: SIT)
}
... on Cat {
doesKnowCommand(catCommand: JUMP)
}
}
}
"#,
);
}
#[test]
fn same_field_on_same_type() {
expect_fails_rule!(
factory,
r#"
{
pet {
... on Dog {
doesKnowCommand(dogCommand: SIT)
}
... on Dog {
doesKnowCommand(dogCommand: Heel)
}
}
}
"#,
);
}
#[test]
fn same_alias_on_different_type() {
expect_passes_rule!(
factory,
r#"
{
pet {
... on Dog {
volume: barkVolume
}
... on Cat {
volume: meowVolume
}
}
}
"#,
);
}
}