Skip to content

Commit 2e37e3d

Browse files
Merge pull request #133 from NeedleInAJayStack/fix/no-unused-variables-rule
Fixes NoUnusedVariablesRule false positive
2 parents 3bf0b96 + 8a1c8f3 commit 2e37e3d

File tree

6 files changed

+63
-35
lines changed

6 files changed

+63
-35
lines changed

Sources/GraphQL/Language/AST.swift

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,6 @@ public func == (lhs: Definition, rhs: Definition) -> Bool {
301301
public enum OperationType: String {
302302
case query
303303
case mutation
304-
// Note: subscription is an experimental non-spec addition.
305304
case subscription
306305
}
307306

@@ -949,6 +948,15 @@ public final class ObjectValue {
949948
self.loc = loc
950949
self.fields = fields
951950
}
951+
952+
public func get(key: String) -> NodeResult? {
953+
switch key {
954+
case "fields":
955+
return .array(fields)
956+
default:
957+
return nil
958+
}
959+
}
952960
}
953961

954962
extension ObjectValue: Equatable {
@@ -968,6 +976,17 @@ public final class ObjectField {
968976
self.name = name
969977
self.value = value
970978
}
979+
980+
public func get(key: String) -> NodeResult? {
981+
switch key {
982+
case "name":
983+
return .node(name)
984+
case "value":
985+
return .node(value)
986+
default:
987+
return nil
988+
}
989+
}
971990
}
972991

973992
extension ObjectField: Equatable {

Sources/GraphQL/Language/Kinds.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
public enum Kind {
1+
public enum Kind: CaseIterable {
22
case name
33
case document
44
case operationDefinition

Sources/GraphQL/Language/Visitor.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ final class Stack {
268268
* If a prior visitor edits a node, no following visitors will see that node.
269269
*/
270270
func visitInParallel(visitors: [Visitor]) -> Visitor {
271-
var skipping = [Node?](repeating: nil, count: visitors.count)
271+
var skipping = [VisitResult?](repeating: nil, count: visitors.count)
272272

273273
return Visitor(
274274
enter: { node, key, parent, path, ancestors in
@@ -283,9 +283,9 @@ func visitInParallel(visitors: [Visitor]) -> Visitor {
283283
)
284284

285285
if case .skip = result {
286-
skipping[i] = node
286+
skipping[i] = .node(node)
287287
} else if case .break = result {
288-
// skipping[i] = BREAK
288+
skipping[i] = .break
289289
} else if case .node = result {
290290
return result
291291
}
@@ -306,11 +306,11 @@ func visitInParallel(visitors: [Visitor]) -> Visitor {
306306
)
307307

308308
if case .break = result {
309-
// skipping[i] = BREAK
309+
skipping[i] = .break
310310
} else if case .node = result {
311311
return result
312312
}
313-
} // else if skipping[i] == node {
313+
} // else if case let .node(skippedNode) = skipping[i], skippedNode == node {
314314
// skipping[i] = nil
315315
// }
316316
}

Sources/GraphQL/Validation/Rules/NoUnusedVariablesRule.swift

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,38 +5,23 @@
55
* are used, either directly or within a spread fragment.
66
*/
77
func NoUnusedVariablesRule(context: ValidationContext) -> Visitor {
8-
var variableDefs: [VariableDefinition] = []
9-
108
return Visitor(
11-
enter: { node, _, _, _, _ in
12-
if node is OperationDefinition {
13-
variableDefs = []
14-
return .continue
15-
}
16-
17-
if let def = node as? VariableDefinition {
18-
variableDefs.append(def)
19-
return .continue
20-
}
21-
22-
return .continue
9+
enter: { _, _, _, _, _ in
10+
.continue
2311
},
2412
leave: { node, _, _, _, _ -> VisitResult in
2513
guard let operation = node as? OperationDefinition else {
2614
return .continue
2715
}
2816

29-
var variableNameUsed: [String: Bool] = [:]
3017
let usages = context.getRecursiveVariableUsages(operation: operation)
18+
let variableNameUsed = Set(usages.map { usage in
19+
usage.node.name.value
20+
})
3121

32-
for usage in usages {
33-
variableNameUsed[usage.node.name.value] = true
34-
}
35-
36-
for variableDef in variableDefs {
22+
for variableDef in operation.variableDefinitions {
3723
let variableName = variableDef.variable.name.value
38-
39-
if variableNameUsed[variableName] != true {
24+
if !variableNameUsed.contains(variableName) {
4025
context.report(
4126
error: GraphQLError(
4227
message: operation.name.map {
@@ -47,7 +32,6 @@ func NoUnusedVariablesRule(context: ValidationContext) -> Visitor {
4732
)
4833
}
4934
}
50-
5135
return .continue
5236
}
5337
)

Sources/GraphQL/Validation/Validate.swift

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -240,11 +240,10 @@ public final class ValidationContext {
240240
}
241241

242242
if let variable = node as? Variable {
243-
usages
244-
.append(VariableUsage(
245-
node: variable,
246-
type: typeInfo.inputType
247-
))
243+
usages.append(VariableUsage(
244+
node: variable,
245+
type: typeInfo.inputType
246+
))
248247
}
249248

250249
return .continue

Tests/GraphQLTests/ValidationTests/NoUnusedVariablesRuleTests.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,4 +256,30 @@ class NoUnusedVariablesRuleTests: ValidationTestCase {
256256
message: #"Variable "$a" is never used in operation "Bar"."#
257257
)
258258
}
259+
260+
func testVariableUsedInsideObject() throws {
261+
try assertValid(
262+
"""
263+
query Foo($a: String) {
264+
field(object: { a: $a })
265+
}
266+
"""
267+
)
268+
}
269+
270+
func testVariableUnusedInsideObject() throws {
271+
let errors = try assertInvalid(
272+
errorCount: 1,
273+
query: """
274+
query Foo($a: String, $b: String) {
275+
field(object: { a: $a })
276+
}
277+
"""
278+
)
279+
280+
try assertValidationError(
281+
error: errors[0], line: 1, column: 23,
282+
message: #"Variable "$b" is never used in operation "Foo"."#
283+
)
284+
}
259285
}

0 commit comments

Comments
 (0)