apollo_compiler/validation/
interface.rs1use crate::ast;
2use crate::collections::IndexSet;
3use crate::parser::SourceSpan;
4use crate::schema::validation::BuiltInScalars;
5use crate::schema::ComponentName;
6use crate::schema::InterfaceType;
7use crate::schema::Name;
8use crate::validation::diagnostics::DiagnosticData;
9use crate::validation::field::validate_field_definitions;
10use crate::validation::DiagnosticList;
11use crate::Node;
12
13pub(crate) fn validate_interface_definition(
14 diagnostics: &mut DiagnosticList,
15 schema: &crate::Schema,
16 built_in_scalars: &mut BuiltInScalars,
17 interface: &Node<InterfaceType>,
18) {
19 super::directive::validate_directives(
20 diagnostics,
21 Some(schema),
22 interface.directives.iter_ast(),
23 ast::DirectiveLocation::Interface,
24 Default::default(),
26 );
27
28 for implements_interface in &interface.implements_interfaces {
44 if *implements_interface == interface.name {
45 diagnostics.push(
46 implements_interface.location(),
47 DiagnosticData::RecursiveInterfaceDefinition {
48 name: implements_interface.name.clone(),
49 },
50 );
51 }
52 }
53
54 validate_field_definitions(diagnostics, schema, built_in_scalars, &interface.fields);
56
57 if interface.fields.is_empty() {
60 diagnostics.push(
61 interface.location(),
62 DiagnosticData::EmptyFieldSet {
63 type_name: interface.name.clone(),
64 type_location: interface.location(),
65 extensions_locations: interface
66 .extensions()
67 .iter()
68 .map(|ext| ext.location())
69 .collect(),
70 },
71 );
72 }
73
74 validate_implements_interfaces(
76 diagnostics,
77 schema,
78 &interface.name,
79 interface.location(),
80 &interface.implements_interfaces,
81 );
82
83 for implements_interface in &interface.implements_interfaces {
89 if let Some(super_interface) = schema.get_interface(implements_interface) {
90 for super_field in super_interface.fields.values() {
91 if interface.fields.contains_key(&super_field.name) {
92 continue;
93 }
94 diagnostics.push(
95 interface.location(),
96 DiagnosticData::MissingInterfaceField {
97 name: interface.name.clone(),
98 implements_location: implements_interface.location(),
99 interface: implements_interface.name.clone(),
100 field: super_field.name.clone(),
101 field_location: super_field.location(),
102 },
103 );
104 }
105 }
106 }
107}
108
109pub(crate) fn validate_implements_interfaces(
110 diagnostics: &mut DiagnosticList,
111 schema: &crate::Schema,
112 implementor_name: &Name,
113 implementor_location: Option<SourceSpan>,
114 implements_interfaces: &IndexSet<ComponentName>,
115) {
116 let interface_definitions = implements_interfaces
117 .iter()
118 .filter_map(|name| {
119 schema
120 .get_interface(name)
121 .map(|interface| (name, interface))
122 })
123 .collect::<Vec<_>>();
124
125 for interface_name in implements_interfaces {
129 if schema.get_interface(interface_name).is_some() {
130 continue;
131 }
132
133 let loc = interface_name.location();
135 diagnostics.push(
136 loc,
137 DiagnosticData::UndefinedDefinition {
138 name: interface_name.name.clone(),
139 },
140 );
141 }
142
143 let transitive_interfaces = interface_definitions.iter().flat_map(|&(name, interface)| {
148 interface
149 .implements_interfaces
150 .iter()
151 .map(|component| &component.name)
152 .zip(std::iter::repeat(name))
153 });
154 for (transitive_interface, via_interface) in transitive_interfaces {
155 if implements_interfaces.contains(transitive_interface) {
156 continue;
157 }
158
159 let transitive_loc = transitive_interface.location();
160 diagnostics.push(
161 implementor_location,
162 DiagnosticData::TransitiveImplementedInterfaces {
163 interface: implementor_name.clone(),
164 via_interface: via_interface.name.clone(),
165 missing_interface: transitive_interface.clone(),
166 transitive_interface_location: transitive_loc,
167 },
168 );
169 }
170}