Skip to content

Commit ac2db18

Browse files
fix: Removes many force-unwraps/trys
1 parent 13bb77c commit ac2db18

22 files changed

+314
-174
lines changed

Sources/GraphQL/Error/SyntaxError.swift

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,19 @@ func highlightSourceAtLocation(source: Source, location: SourceLocation) -> Stri
5050

5151
func splitLines(string: String) -> [String] {
5252

53-
let nsstring = NSString(string: string)
54-
let regex = try! NSRegularExpression(pattern: "\r\n|[\n\r]", options: [])
55-
5653
var lines: [String] = []
5754
var location = 0
58-
59-
for match in regex.matches(in: string, options: [], range: NSRange(0..<nsstring.length)) {
60-
let range = NSRange(location..<match.range.location)
61-
lines.append(nsstring.substring(with: range))
62-
location = match.range.location + match.range.length
55+
56+
let nsstring = NSString(string: string)
57+
do {
58+
let regex = try NSRegularExpression(pattern: "\r\n|[\n\r]", options: [])
59+
for match in regex.matches(in: string, options: [], range: NSRange(0..<nsstring.length)) {
60+
let range = NSRange(location..<match.range.location)
61+
lines.append(nsstring.substring(with: range))
62+
location = match.range.location + match.range.length
63+
}
64+
} catch {
65+
// Let lines and location remain unchanged
6366
}
6467

6568
if lines.isEmpty {

Sources/GraphQL/Execution/Execute.swift

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,7 @@ public func resolveField(
670670
let fieldAST = fieldASTs[0]
671671
let fieldName = fieldAST.name.value
672672

673-
let fieldDef = getFieldDef(
673+
let fieldDef = try getFieldDef(
674674
schema: exeContext.schema,
675675
parentType: parentType,
676676
fieldName: fieldName
@@ -1195,15 +1195,18 @@ func getFieldDef(
11951195
schema: GraphQLSchema,
11961196
parentType: GraphQLObjectType,
11971197
fieldName: String
1198-
) -> GraphQLFieldDefinition {
1198+
) throws -> GraphQLFieldDefinition {
11991199
if fieldName == SchemaMetaFieldDef.name && schema.queryType.name == parentType.name {
12001200
return SchemaMetaFieldDef
12011201
} else if fieldName == TypeMetaFieldDef.name && schema.queryType.name == parentType.name {
12021202
return TypeMetaFieldDef
12031203
} else if fieldName == TypeNameMetaFieldDef.name {
12041204
return TypeNameMetaFieldDef
12051205
}
1206-
1207-
// we know this field exists because we passed validation before execution
1208-
return parentType.fields[fieldName]!
1206+
1207+
// This field should exist because we passed validation before execution
1208+
guard let fieldDefinition = parentType.fields[fieldName] else {
1209+
throw GraphQLError(message: "Expected field definition not found: '\(fieldName)' on '\(parentType.name)'")
1210+
}
1211+
return fieldDefinition
12091212
}

Sources/GraphQL/GraphQL.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,11 @@ public struct GraphQLResult : Equatable, Codable, CustomStringConvertible {
3333
}
3434

3535
public var description: String {
36-
let data = try! GraphQLJSONEncoder().encode(self)
37-
return String(data: data, encoding: .utf8)!
36+
guard let data = try? GraphQLJSONEncoder().encode(self),
37+
let dataString = String(data: data, encoding: .utf8) else {
38+
return "Unable to encode GraphQLResult"
39+
}
40+
return dataString
3841
}
3942
}
4043

Sources/GraphQL/Language/Lexer.swift

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ func advanceLexer(lexer: Lexer) throws -> Token {
3535

3636
if token.kind != .eof {
3737
repeat {
38-
token.next = try readToken(lexer: lexer, prev: token)
39-
token = token.next!
38+
let nextToken = try readToken(lexer: lexer, prev: token)
39+
token.next = nextToken
40+
token = nextToken
4041
} while token.kind == .comment
4142

4243
lexer.token = token
@@ -608,7 +609,14 @@ func readString(source: Source, start: Int, line: Int, col: Int, prev: Token) th
608609
positionIndex = body.utf8.index(after: positionIndex)
609610

610611
if code == 92 { // \
611-
value += String(body.utf8[chunkStartIndex..<startIterationIndex])!
612+
guard let chunk = String(body.utf8[chunkStartIndex..<startIterationIndex]) else {
613+
throw syntaxError(
614+
source: source,
615+
position: body.offset(of: positionIndex),
616+
description: "Unable to initialize String from chunk: \(body.utf8[chunkStartIndex..<startIterationIndex])."
617+
)
618+
}
619+
value += chunk
612620
currentCode = body.charCode(at: positionIndex)
613621

614622
if let code = currentCode {
@@ -643,8 +651,16 @@ func readString(source: Source, start: Int, line: Int, col: Int, prev: Token) th
643651
"\\u\(body.utf8[aIndex...dIndex])."
644652
)
645653
}
646-
647-
value += String(Character(UnicodeScalar(UInt32(charCode))!))
654+
655+
guard let unicodeScalar = UnicodeScalar(UInt32(charCode)) else {
656+
throw syntaxError(
657+
source: source,
658+
position: body.offset(of: positionIndex),
659+
description:
660+
"Invalid unicode character code: \\u\(charCode)."
661+
)
662+
}
663+
value += String(Character(unicodeScalar))
648664

649665
positionIndex = dIndex
650666
default:
@@ -668,8 +684,15 @@ func readString(source: Source, start: Int, line: Int, col: Int, prev: Token) th
668684
description: "Unterminated string."
669685
)
670686
}
671-
672-
value += String(body.utf8[chunkStartIndex..<positionIndex])!
687+
688+
guard let chunk = String(body.utf8[chunkStartIndex..<positionIndex]) else {
689+
throw syntaxError(
690+
source: source,
691+
position: body.offset(of: positionIndex),
692+
description: "Unable to initialize String from chunk: \(body.utf8[chunkStartIndex..<positionIndex])."
693+
)
694+
}
695+
value += chunk
673696

674697
return Token(
675698
kind: .string,
@@ -702,7 +725,15 @@ func readBlockString(lexer: Lexer, source: Source, start: Int, line: Int, col: I
702725
body.utf8[body.utf8.index(positionIndex, offsetBy: 1)] == 34,
703726
body.utf8[body.utf8.index(positionIndex, offsetBy: 2)] == 34 {
704727

705-
rawValue += String(body.utf8[chunkStartIndex..<positionIndex])!
728+
guard let chunk = String(body.utf8[chunkStartIndex..<positionIndex]) else {
729+
throw syntaxError(
730+
source: source,
731+
position: body.offset(of: positionIndex),
732+
description: "Unable to initialize String from chunk: \(body.utf8[chunkStartIndex..<positionIndex])."
733+
)
734+
}
735+
rawValue += chunk
736+
706737
return Token(
707738
kind: .blockstring,
708739
start: start,
@@ -747,7 +778,15 @@ func readBlockString(lexer: Lexer, source: Source, start: Int, line: Int, col: I
747778
body.utf8[body.utf8.index(positionIndex, offsetBy: 2)] == 34,
748779
body.utf8[body.utf8.index(positionIndex, offsetBy: 3)] == 34 {
749780
// escaped triple quote (\""")
750-
rawValue += String(body.utf8[chunkStartIndex..<positionIndex])! + "\"\"\""
781+
782+
guard let chunk = String(body.utf8[chunkStartIndex..<positionIndex]) else {
783+
throw syntaxError(
784+
source: source,
785+
position: body.offset(of: positionIndex),
786+
description: "Unable to initialize String from chunk: \(body.utf8[chunkStartIndex..<positionIndex])."
787+
)
788+
}
789+
rawValue += chunk + "\"\"\""
751790
positionIndex = body.utf8.index(positionIndex, offsetBy: 4)
752791
chunkStartIndex = positionIndex
753792
} else {

Sources/GraphQL/Language/Location.swift

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ func getLocation(source: Source, position: Int) -> SourceLocation {
1818
var line = 1
1919
var column = position + 1
2020

21-
let regex = try! NSRegularExpression(pattern: "\r\n|[\n\r]", options: [])
22-
23-
let matches = regex.matches(in: source.body, options: [], range: NSRange(0..<source.body.utf16.count))
24-
25-
for match in matches where match.range.location < position {
26-
line += 1
27-
column = position + 1 - (match.range.location + match.range.length)
21+
do {
22+
let regex = try NSRegularExpression(pattern: "\r\n|[\n\r]", options: [])
23+
let matches = regex.matches(in: source.body, options: [], range: NSRange(0..<source.body.utf16.count))
24+
for match in matches where match.range.location < position {
25+
line += 1
26+
column = position + 1 - (match.range.location + match.range.length)
27+
}
28+
} catch {
29+
// Leave line and position unset if regex fails
2830
}
2931

3032
return SourceLocation(line: line, column: column)

Sources/GraphQL/Language/Parser.swift

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,12 @@ func parseType(source: Source, noLocation: Bool = false) throws -> Type {
118118
*/
119119
func parseName(lexer: Lexer) throws -> Name {
120120
let token = try expect(lexer: lexer, kind: .name)
121+
guard let value = token.value else {
122+
throw GraphQLError(message: "Expected name token to have value: \(token)")
123+
}
121124
return Name(
122125
loc: loc(lexer: lexer, startToken: token),
123-
value: token.value!
126+
value: value
124127
)
125128
}
126129

@@ -172,8 +175,10 @@ func parseDefinition(lexer: Lexer) throws -> Definition {
172175
}
173176

174177
if peek(lexer: lexer, kind: .name) {
175-
switch lexer.token.value! {
176-
// Note: subscription is an experimental non-spec addition.
178+
guard let value = lexer.token.value else {
179+
throw GraphQLError(message: "Expected name token to have value: \(lexer.token)")
180+
}
181+
switch value {
177182
case "query", "mutation", "subscription":
178183
return try parseOperationDefinition(lexer: lexer);
179184
case "fragment":
@@ -236,11 +241,13 @@ func parseOperationDefinition(lexer: Lexer) throws -> OperationDefinition {
236241
*/
237242
func parseOperationType(lexer: Lexer) throws -> OperationType {
238243
let operationToken = try expect(lexer: lexer, kind: .name)
244+
guard let value = operationToken.value else {
245+
throw GraphQLError(message: "Expected name token to have value: \(operationToken)")
246+
}
239247

240-
switch operationToken.value! {
248+
switch value {
241249
case "query": return .query
242250
case "mutation": return .mutation
243-
// Note: subscription is an experimental non-spec addition.
244251
case "subscription": return .subscription
245252
default: throw unexpected(lexer: lexer, atToken: operationToken)
246253
}
@@ -458,26 +465,35 @@ func parseValueLiteral(lexer: Lexer, isConst: Bool) throws -> Value {
458465
return try parseObject(lexer: lexer, isConst: isConst)
459466
case .int:
460467
try lexer.advance()
468+
guard let value = token.value else {
469+
throw GraphQLError(message: "Expected int token to have value: \(token)")
470+
}
461471
return IntValue(
462472
loc: loc(lexer: lexer, startToken: token),
463-
value: token.value!
473+
value: value
464474
)
465475
case .float:
466476
try lexer.advance()
477+
guard let value = token.value else {
478+
throw GraphQLError(message: "Expected float token to have value: \(token)")
479+
}
467480
return FloatValue(
468481
loc: loc(lexer: lexer, startToken: token),
469-
value: token.value!
482+
value: value
470483
)
471484
case .string, .blockstring:
472485
return try parseStringLiteral(lexer: lexer, startToken: token)
473486
case .name:
474-
if (token.value == "true" || token.value == "false") {
487+
guard let value = token.value else {
488+
throw GraphQLError(message: "Expected name token to have value: \(token)")
489+
}
490+
if (value == "true" || value == "false") {
475491
try lexer.advance()
476492
return BooleanValue(
477493
loc: loc(lexer: lexer, startToken: token),
478-
value: token.value == "true"
494+
value: value == "true"
479495
)
480-
} else if token.value == "null" {
496+
} else if value == "null" {
481497
try lexer.advance()
482498
return NullValue(
483499
loc: loc(lexer: lexer, startToken: token)
@@ -486,7 +502,7 @@ func parseValueLiteral(lexer: Lexer, isConst: Bool) throws -> Value {
486502
try lexer.advance()
487503
return EnumValue(
488504
loc: loc(lexer: lexer, startToken: token),
489-
value: token.value!
505+
value: value
490506
)
491507
}
492508
case .dollar:
@@ -564,10 +580,13 @@ func parseObjectField(lexer: Lexer, isConst: Bool) throws -> ObjectField {
564580
*/
565581

566582
func parseStringLiteral(lexer: Lexer, startToken: Token) throws -> StringValue {
567-
try lexer.advance();
583+
try lexer.advance()
584+
guard let value = startToken.value else {
585+
throw GraphQLError(message: "Expected string literal token to have value: \(startToken)")
586+
}
568587
return StringValue(
569588
loc: loc(lexer: lexer, startToken: startToken),
570-
value: startToken.value!,
589+
value: value,
571590
block: startToken.kind == .blockstring
572591
)
573592
}
@@ -668,7 +687,10 @@ func parseTypeSystemDefinition(lexer: Lexer) throws -> TypeSystemDefinition {
668687
: lexer.token
669688

670689
if keywordToken.kind == .name {
671-
switch keywordToken.value! {
690+
guard let value = keywordToken.value else {
691+
throw GraphQLError(message: "Expected keyword token to have value: \(keywordToken)")
692+
}
693+
switch value {
672694
case "schema": return try parseSchemaDefinition(lexer: lexer);
673695
case "scalar": return try parseScalarTypeDefinition(lexer: lexer);
674696
case "type": return try parseObjectTypeDefinition(lexer: lexer);

Sources/GraphQL/Map/AnySerialization.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ public struct AnySerialization {
66
}
77

88
static func object(with map: Any) throws -> NSObject {
9-
return map as! NSObject
9+
guard let result = map as? NSObject else {
10+
throw EncodingError.invalidValue(
11+
map,
12+
EncodingError.Context(
13+
codingPath: [],
14+
debugDescription: "Expected object input to be castable to NSObject: \(type(of: map))"
15+
)
16+
)
17+
}
18+
return result
1019
}
1120
}

Sources/GraphQL/Map/Map.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -687,7 +687,15 @@ extension Map : Codable {
687687
var container = encoder.container(keyedBy: _DictionaryCodingKey.self)
688688
for (key, value) in dictionary {
689689
if !value.isUndefined {
690-
let codingKey = _DictionaryCodingKey(stringValue: key)!
690+
guard let codingKey = _DictionaryCodingKey(stringValue: key) else {
691+
throw EncodingError.invalidValue(
692+
self,
693+
EncodingError.Context(
694+
codingPath: [],
695+
debugDescription: "codingKey not found for dictionary key: \(key)"
696+
)
697+
)
698+
}
691699
try container.encode(value, forKey: codingKey)
692700
}
693701
}

Sources/GraphQL/Map/MapSerialization.swift

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,40 @@ public struct MapSerialization {
1212
return .string(string as String)
1313
case let array as NSArray:
1414
let array: [Map] = try array.map { value in
15-
try self.map(with: value as! NSObject)
15+
guard let value = value as? NSObject else {
16+
throw EncodingError.invalidValue(
17+
array,
18+
EncodingError.Context(
19+
codingPath: [],
20+
debugDescription: "Array value was not an object: \(value) in \(array)"
21+
)
22+
)
23+
}
24+
return try self.map(with: value)
1625
}
1726
return .array(array)
1827
case let dictionary as NSDictionary:
1928
// Extract from an unordered dictionary, using NSDictionary extraction order
2029
let orderedDictionary: OrderedDictionary<String, Map> = try dictionary.reduce(into: [:]) { (dictionary, pair) in
21-
dictionary[pair.key as! String] = try self.map(with: pair.value as! NSObject)
30+
guard let key = pair.key as? String else {
31+
throw EncodingError.invalidValue(
32+
dictionary,
33+
EncodingError.Context(
34+
codingPath: [],
35+
debugDescription: "Dictionary key was not string: \(pair.key) in \(dictionary)"
36+
)
37+
)
38+
}
39+
guard let value = pair.value as? NSObject else{
40+
throw EncodingError.invalidValue(
41+
dictionary,
42+
EncodingError.Context(
43+
codingPath: [],
44+
debugDescription: "Dictionary value was not an object: \(key) in \(dictionary)"
45+
)
46+
)
47+
}
48+
dictionary[key] = try self.map(with: value)
2249
}
2350
return .dictionary(orderedDictionary)
2451
default:

0 commit comments

Comments
 (0)