Skip to content

Commit 94fc5ae

Browse files
authored
Add Double and Int Convenience Properties (#239)
* Implements SR-11580
1 parent c6bbd14 commit 94fc5ae

20 files changed

+316
-44
lines changed

Changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ Note: This is in reverse chronological order, so newer entries are added to the
44

55
## Swift 5.3
66

7+
* Introduced `integerValue` and `floatingValue` properties to `IntegerLiteralExprSyntax` and `FloatLiteralExprSyntax`, respectively. Converted their `digits` and `floatingDigits` setters, respectively, into throwing functions.
8+
79
* Introduced `FunctionCallExprSyntax.additionalTrailingClosures` property with type `MultipleTrailingClosureElementListSyntax?` for supporting [SE-0279 Multiple Trailing Closures](https://github.com/apple/swift-evolution/blob/master/proposals/0279-multiple-trailing-closures.md).
810

911
* Introduced `syntaxNodeType` property for all types conforming to `SyntaxProtocol`, which returns the underlying syntax node type. It is primarily intended as a debugging aid during development.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ swift/utils/build-script --swiftsyntax --swiftpm --llbuild -t --skip-test-cmark
181181
```
182182
This command will build SwiftSyntax and all its dependencies, tell the build script to run tests, but skip all tests but the SwiftSyntax tests.
183183

184-
Note that it is not currently supported to SwiftSyntax while building the Swift compiler using Xcode.
184+
Note that it is not currently supported to build SwiftSyntax while building the Swift compiler using Xcode.
185185

186186
### CI Testing
187187

Sources/SwiftSyntax/SyntaxBuilders.swift.gyb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,11 @@ extension ${node.name} {
8585
/// incrementally build the structure of the node.
8686
/// - Returns: A `${node.name}` with all the fields populated in the builder
8787
/// closure.
88+
% if node.must_uphold_invariant:
89+
public init?(_ build: (inout ${Builder}) -> Void) {
90+
% else:
8891
public init(_ build: (inout ${Builder}) -> Void) {
92+
% end
8993
var builder = ${Builder}()
9094
build(&builder)
9195
let data = builder.buildData()
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//===- SyntaxConvenienceMethods.swift - Convenience funcs for syntax nodes ===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
public extension FloatLiteralExprSyntax {
14+
var floatingValue: Double {
15+
return potentialFloatingValue!
16+
}
17+
18+
fileprivate var potentialFloatingValue: Double? {
19+
let floatingDigitsWithoutUnderscores = floatingDigits.text.filter {
20+
$0 != "_"
21+
}
22+
return Double(floatingDigitsWithoutUnderscores)
23+
}
24+
}
25+
26+
public extension IntegerLiteralExprSyntax {
27+
var integerValue: Int {
28+
return potentialIntegerValue!
29+
}
30+
31+
fileprivate var potentialIntegerValue: Int? {
32+
let text = digits.text
33+
let (prefixLength, radix) = IntegerLiteralExprSyntax.prefixLengthAndRadix(text: text)
34+
let digitsStartIndex = text.index(text.startIndex, offsetBy: prefixLength)
35+
let textWithoutPrefix = text.suffix(from: digitsStartIndex)
36+
37+
let textWithoutPrefixOrUnderscores = textWithoutPrefix.filter {
38+
$0 != "_"
39+
}
40+
41+
return Int(textWithoutPrefixOrUnderscores, radix: radix)
42+
}
43+
44+
private static func prefixLengthAndRadix(text: String) -> (Int, Int) {
45+
let nonDecimalPrefixLength = 2
46+
47+
let binaryPrefix = "0b"
48+
let octalPrefix = "0o"
49+
let decimalPrefix = ""
50+
let hexadecimalPrefix = "0x"
51+
52+
let binaryRadix = 2
53+
let octalRadix = 8
54+
let decimalRadix = 10
55+
let hexadecimalRadix = 16
56+
57+
switch String(text.prefix(nonDecimalPrefixLength)) {
58+
case binaryPrefix:
59+
return (binaryPrefix.count, binaryRadix)
60+
case octalPrefix:
61+
return (octalPrefix.count, octalRadix)
62+
case hexadecimalPrefix:
63+
return (hexadecimalPrefix.count, hexadecimalRadix)
64+
default:
65+
return (decimalPrefix.count, decimalRadix)
66+
}
67+
}
68+
}
69+
70+
public extension IntegerLiteralExprSyntax {
71+
var isValid: Bool {
72+
potentialIntegerValue != nil
73+
}
74+
}
75+
76+
public extension FloatLiteralExprSyntax {
77+
var isValid: Bool {
78+
potentialFloatingValue != nil
79+
}
80+
}

Sources/SwiftSyntax/SyntaxFactory.swift.gyb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,11 @@ public enum SyntaxFactory {
5757
% param_type = param_type + "?"
5858
% child_params.append("%s: %s" % (child.swift_name, param_type))
5959
% child_params = ', '.join(child_params)
60+
% if node.must_uphold_invariant:
61+
public static func make${node.syntax_kind}(${child_params}) -> ${node.name}? {
62+
% else:
6063
public static func make${node.syntax_kind}(${child_params}) -> ${node.name} {
64+
% end
6165
let layout: [RawSyntax?] = [
6266
% for child in node.children:
6367
% if child.is_optional:
@@ -82,7 +86,7 @@ public enum SyntaxFactory {
8286
}
8387
% end
8488

85-
% if not node.is_base():
89+
% if not node.is_base() and not node.must_uphold_invariant:
8690
public static func makeBlank${node.syntax_kind}() -> ${node.name} {
8791
let data = SyntaxData.forRoot(RawSyntax.create(kind: .${node.swift_syntax_kind},
8892
layout: [

Sources/SwiftSyntax/SyntaxNodes.swift.gyb.template

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,29 @@ public struct ${node.name}: ${base_type}Protocol, SyntaxHashable {
7070
public init?(_ syntax: Syntax) {
7171
guard syntax.raw.kind == .${node.swift_syntax_kind} else { return nil }
7272
self._syntaxNode = syntax
73+
% if node.must_uphold_invariant:
74+
if !isValid {
75+
fatalError("Instance of ${node.name} is invalid.")
76+
}
77+
% end
7378
}
7479

7580
/// Creates a `${node.name}` node from the given `SyntaxData`. This assumes
7681
/// that the `SyntaxData` is of the correct kind. If it is not, the behaviour
7782
/// is undefined.
83+
% if node.must_uphold_invariant:
84+
/// This initializer returns nil if the invariant is not satisfied.
85+
internal init?(_ data: SyntaxData) {
86+
% else:
7887
internal init(_ data: SyntaxData) {
88+
% end
7989
assert(data.raw.kind == .${node.swift_syntax_kind})
8090
self._syntaxNode = Syntax(data)
91+
% if node.must_uphold_invariant:
92+
if !isValid {
93+
return nil
94+
}
95+
% end
8196
}
8297

8398
public var syntaxNodeType: SyntaxProtocol.Type {
@@ -107,10 +122,33 @@ public struct ${node.name}: ${base_type}Protocol, SyntaxHashable {
107122
% end
108123
return ${child.type_name}(childData!)
109124
}
125+
% if not node.must_uphold_invariant:
110126
set(value) {
111127
self = with${child.name}(value)
112128
}
129+
% end
130+
}
131+
% if node.must_uphold_invariant:
132+
133+
public enum ${child.name}Error: Error, CustomStringConvertible {
134+
case invalid(${child.swift_name}: ${ret_type})
135+
136+
public var description: String {
137+
switch self {
138+
case .invalid(let ${child.swift_name}):
139+
return "attempted to use setter with invalid ${child.name} \"\(${child.swift_name})\""
140+
}
141+
}
113142
}
143+
144+
mutating public func set${child.name}(_ ${child.swift_name}: ${ret_type}) throws {
145+
if let childSyntax = with${child.name}(${child.swift_name}) {
146+
self = childSyntax
147+
} else {
148+
throw ${child.name}Error.invalid(${child.swift_name}: ${child.swift_name})
149+
}
150+
}
151+
% end
114152
%
115153
% # ===============
116154
% # Adding children
@@ -149,7 +187,11 @@ public struct ${node.name}: ${base_type}Protocol, SyntaxHashable {
149187
/// - param newChild: The new `${child.swift_name}` to replace the node's
150188
/// current `${child.swift_name}`, if present.
151189
public func with${child.name}(
190+
% if node.must_uphold_invariant:
191+
_ newChild: ${child.type_name}?) -> ${node.name}? {
192+
% else:
152193
_ newChild: ${child.type_name}?) -> ${node.name} {
194+
% end
153195
% if child.is_optional:
154196
let raw = newChild?.raw
155197
% else:

Sources/SwiftSyntax/SyntaxRewriter.swift.gyb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,12 @@ open class SyntaxRewriter {
9191
if let newNode = visitAny(node._syntaxNode) { return newNode }
9292
return Syntax(visit(node))
9393
% else:
94+
% if node.must_uphold_invariant:
95+
// We know that the SyntaxData is valid since we are walking a valid syntax tree and haven't modified the syntax data. Thus the initializer below will never return nil.
96+
let node = ${node.name}(data)!
97+
% else:
9498
let node = ${node.name}(data)
99+
% end
95100
// Accessing _syntaxNode directly is faster than calling Syntax(node)
96101
visitPre(node._syntaxNode)
97102
defer { visitPost(node._syntaxNode) }

Sources/SwiftSyntax/SyntaxVisitor.swift.gyb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,12 @@ open class SyntaxVisitor {
8686
}
8787
visitPost(node)
8888
% else:
89+
% if node.must_uphold_invariant:
90+
// We know that the SyntaxData is valid since we are walking a valid syntax tree and haven't modified the syntax data. Thus the initializer below will never return nil.
91+
let node = ${node.name}(data)!
92+
% else:
8993
let node = ${node.name}(data)
94+
% end
9095
let needsChildren = (visit(node) == .visitChildren)
9196
// Avoid calling into visitChildren if possible.
9297
if needsChildren && node.raw.numberOfChildren > 0 {

Sources/SwiftSyntax/gyb_generated/SyntaxBuilders.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,7 +1077,7 @@ extension FloatLiteralExprSyntax {
10771077
/// incrementally build the structure of the node.
10781078
/// - Returns: A `FloatLiteralExprSyntax` with all the fields populated in the builder
10791079
/// closure.
1080-
public init(_ build: (inout FloatLiteralExprSyntaxBuilder) -> Void) {
1080+
public init?(_ build: (inout FloatLiteralExprSyntaxBuilder) -> Void) {
10811081
var builder = FloatLiteralExprSyntaxBuilder()
10821082
build(&builder)
10831083
let data = builder.buildData()
@@ -1444,7 +1444,7 @@ extension IntegerLiteralExprSyntax {
14441444
/// incrementally build the structure of the node.
14451445
/// - Returns: A `IntegerLiteralExprSyntax` with all the fields populated in the builder
14461446
/// closure.
1447-
public init(_ build: (inout IntegerLiteralExprSyntaxBuilder) -> Void) {
1447+
public init?(_ build: (inout IntegerLiteralExprSyntaxBuilder) -> Void) {
14481448
var builder = IntegerLiteralExprSyntaxBuilder()
14491449
build(&builder)
14501450
let data = builder.buildData()

Sources/SwiftSyntax/gyb_generated/SyntaxFactory.swift

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,7 @@ public enum SyntaxFactory {
619619
], length: .zero, presence: .present))
620620
return ArrowExprSyntax(data)
621621
}
622-
public static func makeFloatLiteralExpr(floatingDigits: TokenSyntax) -> FloatLiteralExprSyntax {
622+
public static func makeFloatLiteralExpr(floatingDigits: TokenSyntax) -> FloatLiteralExprSyntax? {
623623
let layout: [RawSyntax?] = [
624624
floatingDigits.raw,
625625
]
@@ -629,13 +629,6 @@ public enum SyntaxFactory {
629629
return FloatLiteralExprSyntax(data)
630630
}
631631

632-
public static func makeBlankFloatLiteralExpr() -> FloatLiteralExprSyntax {
633-
let data = SyntaxData.forRoot(RawSyntax.create(kind: .floatLiteralExpr,
634-
layout: [
635-
RawSyntax.missingToken(TokenKind.floatingLiteral("")),
636-
], length: .zero, presence: .present))
637-
return FloatLiteralExprSyntax(data)
638-
}
639632
public static func makeTupleExpr(leftParen: TokenSyntax, elementList: TupleExprElementListSyntax, rightParen: TokenSyntax) -> TupleExprSyntax {
640633
let layout: [RawSyntax?] = [
641634
leftParen.raw,
@@ -764,7 +757,7 @@ public enum SyntaxFactory {
764757
], length: .zero, presence: .present))
765758
return DictionaryElementSyntax(data)
766759
}
767-
public static func makeIntegerLiteralExpr(digits: TokenSyntax) -> IntegerLiteralExprSyntax {
760+
public static func makeIntegerLiteralExpr(digits: TokenSyntax) -> IntegerLiteralExprSyntax? {
768761
let layout: [RawSyntax?] = [
769762
digits.raw,
770763
]
@@ -774,13 +767,6 @@ public enum SyntaxFactory {
774767
return IntegerLiteralExprSyntax(data)
775768
}
776769

777-
public static func makeBlankIntegerLiteralExpr() -> IntegerLiteralExprSyntax {
778-
let data = SyntaxData.forRoot(RawSyntax.create(kind: .integerLiteralExpr,
779-
layout: [
780-
RawSyntax.missingToken(TokenKind.integerLiteral("")),
781-
], length: .zero, presence: .present))
782-
return IntegerLiteralExprSyntax(data)
783-
}
784770
public static func makeBooleanLiteralExpr(booleanLiteral: TokenSyntax) -> BooleanLiteralExprSyntax {
785771
let layout: [RawSyntax?] = [
786772
booleanLiteral.raw,

Sources/SwiftSyntax/gyb_generated/SyntaxRewriter.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2128,7 +2128,8 @@ open class SyntaxRewriter {
21282128

21292129
/// Implementation detail of visit(_:). Do not call directly.
21302130
private func visitImplFloatLiteralExprSyntax(_ data: SyntaxData) -> Syntax {
2131-
let node = FloatLiteralExprSyntax(data)
2131+
// We know that the SyntaxData is valid since we are walking a valid syntax tree and haven't modified the syntax data. Thus the initializer below will never return nil.
2132+
let node = FloatLiteralExprSyntax(data)!
21322133
// Accessing _syntaxNode directly is faster than calling Syntax(node)
21332134
visitPre(node._syntaxNode)
21342135
defer { visitPost(node._syntaxNode) }
@@ -2198,7 +2199,8 @@ open class SyntaxRewriter {
21982199

21992200
/// Implementation detail of visit(_:). Do not call directly.
22002201
private func visitImplIntegerLiteralExprSyntax(_ data: SyntaxData) -> Syntax {
2201-
let node = IntegerLiteralExprSyntax(data)
2202+
// We know that the SyntaxData is valid since we are walking a valid syntax tree and haven't modified the syntax data. Thus the initializer below will never return nil.
2203+
let node = IntegerLiteralExprSyntax(data)!
22022204
// Accessing _syntaxNode directly is faster than calling Syntax(node)
22032205
visitPre(node._syntaxNode)
22042206
defer { visitPost(node._syntaxNode) }

Sources/SwiftSyntax/gyb_generated/SyntaxVisitor.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2868,7 +2868,8 @@ open class SyntaxVisitor {
28682868

28692869
/// Implementation detail of doVisit(_:_:). Do not call directly.
28702870
private func visitImplFloatLiteralExprSyntax(_ data: SyntaxData) {
2871-
let node = FloatLiteralExprSyntax(data)
2871+
// We know that the SyntaxData is valid since we are walking a valid syntax tree and haven't modified the syntax data. Thus the initializer below will never return nil.
2872+
let node = FloatLiteralExprSyntax(data)!
28722873
let needsChildren = (visit(node) == .visitChildren)
28732874
// Avoid calling into visitChildren if possible.
28742875
if needsChildren && node.raw.numberOfChildren > 0 {
@@ -2945,7 +2946,8 @@ open class SyntaxVisitor {
29452946

29462947
/// Implementation detail of doVisit(_:_:). Do not call directly.
29472948
private func visitImplIntegerLiteralExprSyntax(_ data: SyntaxData) {
2948-
let node = IntegerLiteralExprSyntax(data)
2949+
// We know that the SyntaxData is valid since we are walking a valid syntax tree and haven't modified the syntax data. Thus the initializer below will never return nil.
2950+
let node = IntegerLiteralExprSyntax(data)!
29492951
let needsChildren = (visit(node) == .visitChildren)
29502952
// Avoid calling into visitChildren if possible.
29512953
if needsChildren && node.raw.numberOfChildren > 0 {

0 commit comments

Comments
 (0)