forked from swiftlang/swift-syntax
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathExpressions.swift
2691 lines (2444 loc) · 88 KB
/
Expressions.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#if compiler(>=6)
@_spi(RawSyntax) @_spi(ExperimentalLanguageFeatures) internal import SwiftSyntax
#else
@_spi(RawSyntax) @_spi(ExperimentalLanguageFeatures) import SwiftSyntax
#endif
extension TokenConsumer {
mutating func atStartOfExpression() -> Bool {
switch self.at(anyIn: ExpressionStart.self) {
case (.awaitTryMove, let handle)?:
var lookahead = self.lookahead()
lookahead.eat(handle)
// These can be parsed as expressions with try/await.
if lookahead.at(anyIn: SingleValueStatementExpression.self) != nil {
return true
}
// Note we currently pass `preferExpr: false` to prefer diagnosing `try then`
// as needing to be `then try`, rather than parsing `then` as an expression.
if lookahead.atStartOfDeclaration() || lookahead.atStartOfStatement(preferExpr: false) {
// If after the 'try' we are at a declaration or statement, it can't be a valid expression.
// Decide how we want to consume the 'try':
// If the declaration or statement starts at a new line, the user probably just forgot to write the expression after 'try' -> parse it as a TryExpr
// If the declaration or statement starts at the same line, the user maybe tried to use 'try' as a modifier -> parse it as unexpected text in front of that decl or stmt.
return lookahead.atStartOfLine
} else {
return true
}
case (.primaryExpressionStart(.atSign), _)?:
break
case (_, _)?:
return true
case nil:
break
}
if self.at(.atSign) || self.at(.keyword(.inout)) {
var lookahead = self.lookahead()
if lookahead.canParseType() {
return true
}
}
// 'repeat' is the start of a pack expansion expression.
if (self.at(.keyword(.repeat))) {
// FIXME: 'repeat' followed by '{' could still be a pack
// expansion, but we need to do more lookahead to figure out
// whether the '{' is the start of a closure expression or a
// brace statement for 'repeat { ... } while'
let lookahead = self.lookahead()
return lookahead.peek().rawTokenKind != .leftBrace
}
return false
}
}
extension Parser {
enum ExprFlavor {
/// Parsing a normal expression, eg. inside a function calls.
case basic
/// Parsing the condition of a `if`/`guard`/`for`/... statement or something
/// like the `where` clause after a `catch` clause.
///
/// In these cases we need to disambiguate trailing closures from the
/// statement's body.
case stmtCondition
/// Parsing the condition of a `#if` directive.
///
/// We don't allow allow newlines here.
case poundIfDirective
/// Parsing an attribute's arguments, which can contain declaration
/// references like `subscript` or `deinit`.
case attributeArguments
}
enum PatternContext {
/// There is no ambient pattern context.
case none
/// We're parsing a matching pattern that is not introduced via `let` or `var`.
///
/// In this context, identifiers are references to the enclosing scopes, not a variable binding.
///
/// ```
/// case x.y <- 'x' must refer to some 'x' defined in another scope, it cannot be e.g. an enum type.
/// ```
case matching
/// We're parsing a matching pattern that is introduced via `let`, `var`, or `inout`
///
/// ```
/// case let x.y <- 'x' must refer to the base of some member access, y must refer to some pattern-compatible identifier
/// ```
case bindingIntroducer
var admitsBinding: Bool {
switch self {
case .bindingIntroducer:
return true
case .none, .matching:
return false
}
}
}
/// Parse an expression.
mutating func parseExpression(flavor: ExprFlavor, pattern: PatternContext) -> RawExprSyntax {
// If we are parsing a refutable pattern, check to see if this is the start
// of a let/var/is pattern. If so, parse it as an UnresolvedPatternExpr and
// let pattern type checking determine its final form.
//
// Only do this if we're parsing a pattern, to improve QoI on malformed
// expressions followed by (e.g.) let/var decls.
if pattern != .none {
switch self.at(anyIn: MatchingPatternStart.self) {
case (spec: .rhs(let bindingIntroducer), handle: _)?
where self.withLookahead { $0.shouldParsePatternBinding(introducer: bindingIntroducer) }:
fallthrough
case (spec: .lhs(_), handle: _)?:
let pattern = self.parseMatchingPattern(context: .matching)
return RawExprSyntax(RawPatternExprSyntax(pattern: pattern, arena: self.arena))
// If the token can't start a pattern, or contextually isn't parsed as a
// binding introducer, handle as the start of a normal expression.
case (spec: .rhs(_), handle: _)?,
nil:
// Not a pattern. Break out to parse as a normal expression below.
break
}
}
return self.parseSequenceExpression(flavor: flavor, pattern: pattern)
}
}
extension Parser {
/// Parse a sequence of expressions.
mutating func parseSequenceExpression(
flavor: ExprFlavor,
pattern: PatternContext = .none
) -> RawExprSyntax {
if flavor == .poundIfDirective && self.atStartOfLine {
return RawExprSyntax(RawMissingExprSyntax(arena: self.arena))
}
// Parsed sequence elements except 'lastElement'.
var elements = [RawExprSyntax]()
// The last element parsed. we don't eagerly append to 'elements' because we
// don't want to populate the 'Array' unless the expression is actually
// sequenced.
var lastElement: RawExprSyntax
lastElement = self.parseSequenceExpressionElement(
flavor: flavor,
pattern: pattern
)
var loopProgress = LoopProgressCondition()
while self.hasProgressed(&loopProgress) {
guard
!lastElement.is(RawMissingExprSyntax.self),
!(flavor == .poundIfDirective && self.atStartOfLine)
else {
break
}
// Parse the operator.
guard
let (operatorExpr, rhsExpr) =
self.parseSequenceExpressionOperator(flavor: flavor, pattern: pattern)
else {
// Not an operator. We're done.
break
}
elements.append(lastElement)
elements.append(operatorExpr)
if let rhsExpr {
// Operator parsing returned the RHS.
lastElement = rhsExpr
} else if flavor == .poundIfDirective && self.atStartOfLine {
// Don't allow RHS at a newline for `#if` conditions.
lastElement = RawExprSyntax(RawMissingExprSyntax(arena: self.arena))
break
} else {
lastElement = self.parseSequenceExpressionElement(
flavor: flavor,
pattern: pattern
)
}
}
// There was no operators. Return the only element we parsed.
if elements.isEmpty {
return lastElement
}
precondition(
elements.count.isMultiple(of: 2),
"elements must have an even number of elements"
)
elements.append(lastElement)
return RawExprSyntax(
RawSequenceExprSyntax(
elements: RawExprListSyntax(elements: elements, arena: self.arena),
arena: self.arena
)
)
}
/// Parse an unresolved 'as' expression.
mutating func parseUnresolvedAsExpr(
handle: TokenConsumptionHandle
) -> (operator: RawExprSyntax, rhs: RawExprSyntax) {
let asKeyword = self.eat(handle)
let failable = self.consume(if: .postfixQuestionMark, .exclamationMark)
let op = RawUnresolvedAsExprSyntax(
asKeyword: asKeyword,
questionOrExclamationMark: failable,
arena: self.arena
)
// Parse the right type expression operand as part of the 'as' production.
let type = self.parseType()
let rhs = RawTypeExprSyntax(type: type, arena: self.arena)
return (RawExprSyntax(op), RawExprSyntax(rhs))
}
/// Parse an expression sequence operators.
///
/// Returns `nil` if the current token is not at an operator.
/// Returns a tuple of an operator expression and an optional right operand
/// expression. The right operand is only returned if it is not a common
/// sequence element.
mutating func parseSequenceExpressionOperator(
flavor: ExprFlavor,
pattern: PatternContext
) -> (operator: RawExprSyntax, rhs: RawExprSyntax?)? {
enum ExpectedTokenKind: TokenSpecSet {
case binaryOperator
case infixQuestionMark
case equal
case `is`
case `as`
case async
case arrow
case `throws`
init?(lexeme: Lexer.Lexeme, experimentalFeatures: Parser.ExperimentalFeatures) {
switch PrepareForKeywordMatch(lexeme) {
case TokenSpec(.binaryOperator): self = .binaryOperator
case TokenSpec(.infixQuestionMark): self = .infixQuestionMark
case TokenSpec(.equal): self = .equal
case TokenSpec(.is): self = .is
case TokenSpec(.as): self = .as
case TokenSpec(.async): self = .async
case TokenSpec(.arrow): self = .arrow
case TokenSpec(.throws): self = .throws
default: return nil
}
}
var spec: TokenSpec {
switch self {
case .binaryOperator: return .binaryOperator
case .infixQuestionMark: return .infixQuestionMark
case .equal: return .equal
case .is: return .keyword(.is)
case .as: return .keyword(.as)
case .async: return .keyword(.async)
case .arrow: return .arrow
case .throws: return .keyword(.throws)
}
}
}
switch self.at(anyIn: ExpectedTokenKind.self) {
case (.binaryOperator, let handle)?:
// Parse the operator.
let operatorToken = self.eat(handle)
let op = RawBinaryOperatorExprSyntax(operator: operatorToken, arena: arena)
return (RawExprSyntax(op), nil)
case (.infixQuestionMark, let handle)?:
// Save the '?'.
let question = self.eat(handle)
let firstChoice = self.parseSequenceExpression(flavor: flavor, pattern: pattern)
// Make sure there's a matching ':' after the middle expr.
let (unexpectedBeforeColon, colon) = self.expect(.colon)
let op = RawUnresolvedTernaryExprSyntax(
questionMark: question,
thenExpression: firstChoice,
unexpectedBeforeColon,
colon: colon,
arena: self.arena
)
let rhs: RawExprSyntax?
if colon.isMissing, self.atStartOfLine {
rhs = RawExprSyntax(RawMissingExprSyntax(arena: self.arena))
} else {
rhs = nil
}
return (RawExprSyntax(op), rhs)
case (.equal, let handle)?:
switch pattern {
case .matching, .bindingIntroducer:
return nil
case .none:
let eq = self.eat(handle)
let op = RawAssignmentExprSyntax(
equal: eq,
arena: self.arena
)
return (RawExprSyntax(op), nil)
}
case (.is, let handle)?:
let isKeyword = self.eat(handle)
let op = RawUnresolvedIsExprSyntax(
isKeyword: isKeyword,
arena: self.arena
)
// Parse the right type expression operand as part of the 'is' production.
let type = self.parseType()
let rhs = RawTypeExprSyntax(type: type, arena: self.arena)
return (RawExprSyntax(op), RawExprSyntax(rhs))
case (.as, let handle)?:
return parseUnresolvedAsExpr(handle: handle)
case (.async, _)?:
if self.peek(isAt: .arrow, .keyword(.throws)) {
fallthrough
} else {
return nil
}
case (.arrow, _)?, (.throws, _)?:
var effectSpecifiers = self.parseTypeEffectSpecifiers()
let (unexpectedBeforeArrow, arrow) = self.expect(.arrow)
let unexpectedAfterArrow = self.parseMisplacedEffectSpecifiers(&effectSpecifiers)
let op = RawArrowExprSyntax(
effectSpecifiers: effectSpecifiers,
unexpectedBeforeArrow,
arrow: arrow,
unexpectedAfterArrow,
arena: self.arena
)
return (RawExprSyntax(op), nil)
case nil:
// Not an operator.
return nil
}
}
/// Whether the current token is a valid contextual exprssion modifier like
/// `copy`, `consume`.
///
/// `copy` etc. are only contextually a keyword if they are followed by an
/// identifier or keyword on the same line. We do this to ensure that we do
/// not break any copy functions defined by users.
private mutating func atContextualExpressionModifier() -> Bool {
return self.peek(
isAt: TokenSpec(.identifier, allowAtStartOfLine: false),
TokenSpec(.dollarIdentifier, allowAtStartOfLine: false),
TokenSpec(.self, allowAtStartOfLine: false)
)
}
/// Parse an expression sequence element.
mutating func parseSequenceExpressionElement(
flavor: ExprFlavor,
pattern: PatternContext = .none
) -> RawExprSyntax {
// Try to parse '@' sign or 'inout' as an attributed typerepr.
if self.at(.atSign, .keyword(.inout)) {
var lookahead = self.lookahead()
if lookahead.canParseType() {
let type = self.parseType()
return RawExprSyntax(
RawTypeExprSyntax(
type: type,
arena: self.arena
)
)
}
}
EXPR_PREFIX: switch self.at(anyIn: ExpressionModifierKeyword.self) {
case (.await, let handle)?:
let awaitTok = self.eat(handle)
let sub = self.parseSequenceExpressionElement(
flavor: flavor,
pattern: pattern
)
return RawExprSyntax(
RawAwaitExprSyntax(
awaitKeyword: awaitTok,
expression: sub,
arena: self.arena
)
)
case (.try, let handle)?:
let tryKeyword = self.eat(handle)
let mark = self.consume(if: .exclamationMark, .postfixQuestionMark)
let expression = self.parseSequenceExpressionElement(
flavor: flavor,
pattern: pattern
)
return RawExprSyntax(
RawTryExprSyntax(
tryKeyword: tryKeyword,
questionOrExclamationMark: mark,
expression: expression,
arena: self.arena
)
)
case (.unsafe, let handle)?:
if self.peek().isAtStartOfLine
// Closing paired syntax
|| self.peek(isAt: .rightParen, .rightSquare, .rightBrace)
// Assignment
|| self.peek(isAt: .equal)
// As an argument label or in a list context.
|| self.peek(isAt: .colon, .comma)
// Start of a closure in a context where it should be interpreted as
// being part of a statement.
|| (flavor == .stmtCondition && self.peek(isAt: .leftBrace))
// Avoid treating as an "unsafe" expression when there is no trivia
// following the "unsafe" and the following token could either be a
// postfix expression or a subexpression:
// - Member access vs. leading .
// - Call vs. tuple expression.
// - Subscript vs. array or dictionary expression
|| (self.peek(isAt: .period, .leftParen, .leftSquare) && self.peek().leadingTriviaByteLength == 0
&& self.currentToken.trailingTriviaByteLength == 0)
// End of file
|| self.peek(isAt: .endOfFile)
{
break EXPR_PREFIX
}
let unsafeTok = self.eat(handle)
let sub = self.parseSequenceExpressionElement(
flavor: flavor,
pattern: pattern
)
return RawExprSyntax(
RawUnsafeExprSyntax(
unsafeKeyword: unsafeTok,
expression: sub,
arena: self.arena
)
)
case (._borrow, let handle)?:
assert(self.experimentalFeatures.contains(.oldOwnershipOperatorSpellings))
fallthrough
case (.borrow, let handle)?:
if !atContextualExpressionModifier() {
break EXPR_PREFIX
}
let borrowTok = self.eat(handle)
let sub = self.parseSequenceExpressionElement(
flavor: flavor,
pattern: pattern
)
return RawExprSyntax(
RawBorrowExprSyntax(
borrowKeyword: borrowTok,
expression: sub,
arena: self.arena
)
)
case (.copy, let handle)?:
if !atContextualExpressionModifier() {
break EXPR_PREFIX
}
let copyTok = self.eat(handle)
let sub = self.parseSequenceExpressionElement(
flavor: flavor,
pattern: pattern
)
return RawExprSyntax(
RawCopyExprSyntax(
copyKeyword: copyTok,
expression: sub,
arena: self.arena
)
)
case (._move, let handle)?:
assert(self.experimentalFeatures.contains(.oldOwnershipOperatorSpellings))
fallthrough
case (.consume, let handle)?:
if !atContextualExpressionModifier() {
break EXPR_PREFIX
}
let consumeKeyword = self.eat(handle)
let sub = self.parseSequenceExpressionElement(
flavor: flavor,
pattern: pattern
)
return RawExprSyntax(
RawConsumeExprSyntax(
consumeKeyword: consumeKeyword,
expression: sub,
arena: self.arena
)
)
case (.repeat, let handle)?:
// 'repeat' is the start of a pack expansion expression.
return RawExprSyntax(parsePackExpansionExpr(repeatHandle: handle, flavor: flavor, pattern: pattern))
case (.each, let handle)?:
if !atContextualExpressionModifier() {
break EXPR_PREFIX
}
let each = self.eat(handle)
let pack = self.parseSequenceExpressionElement(flavor: flavor, pattern: pattern)
return RawExprSyntax(
RawPackElementExprSyntax(
eachKeyword: each,
pack: pack,
arena: self.arena
)
)
case (.any, _)?:
if !atContextualExpressionModifier() && !self.peek().isContextualPunctuator("~") {
break EXPR_PREFIX
}
// 'any' is parsed as a part of 'type'.
let type = self.parseType()
return RawExprSyntax(RawTypeExprSyntax(type: type, arena: self.arena))
case nil:
break
}
return self.parseUnaryExpression(flavor: flavor, pattern: pattern)
}
/// Parse an optional prefix operator followed by an expression.
mutating func parseUnaryExpression(
flavor: ExprFlavor,
pattern: PatternContext = .none
) -> RawExprSyntax {
// Try parse a single value statement as an expression (e.g do/if/switch).
// Note we do this here in parseUnaryExpression as we don't allow postfix
// syntax to be attached to such expressions to avoid ambiguities such as postfix
// '.member', which can currently be parsed as a static dot member for a
// result builder.
switch self.at(anyIn: SingleValueStatementExpression.self) {
case (.do, let handle)?:
return RawExprSyntax(self.parseDoExpression(doHandle: .noRecovery(handle)))
case (.if, let handle)?:
return RawExprSyntax(self.parseIfExpression(ifHandle: .noRecovery(handle)))
case (.switch, let handle)?:
return RawExprSyntax(self.parseSwitchExpression(switchHandle: .noRecovery(handle)))
default:
break
}
switch self.at(anyIn: ExpressionPrefixOperator.self) {
case (.prefixAmpersand, let handle)?:
let amp = self.eat(handle)
let expr = self.parseUnaryExpression(flavor: flavor, pattern: pattern)
return RawExprSyntax(
RawInOutExprSyntax(
ampersand: amp,
expression: expr,
arena: self.arena
)
)
case (.backslash, _)?:
return RawExprSyntax(self.parseKeyPathExpression(pattern: pattern))
case (.prefixOperator, let handle)?:
let op = self.eat(handle)
let postfix = self.parseUnaryExpression(flavor: flavor, pattern: pattern)
return RawExprSyntax(
RawPrefixOperatorExprSyntax(
operator: op,
expression: postfix,
arena: self.arena
)
)
default:
// If the next token is not an operator, just parse this as expr-postfix.
return self.parsePostfixExpression(
flavor: flavor,
pattern: pattern
)
}
}
/// Parse a postfix expression applied to another expression.
mutating func parsePostfixExpression(
flavor: ExprFlavor,
pattern: PatternContext
) -> RawExprSyntax {
let head = self.parsePrimaryExpression(pattern: pattern, flavor: flavor)
guard !head.is(RawMissingExprSyntax.self) else {
return head
}
return self.parsePostfixExpressionSuffix(
head,
flavor: flavor,
pattern: pattern
)
}
mutating func parseDottedExpressionSuffix(
previousNode: (some RawSyntaxNodeProtocol)?
) -> (
unexpectedPeriod: RawUnexpectedNodesSyntax?,
period: RawTokenSyntax,
declName: RawDeclReferenceExprSyntax,
generics: RawGenericArgumentClauseSyntax?
) {
precondition(self.at(.period))
let (unexpectedPeriod, period, skipMemberName) = self.consumeMemberPeriod(previousNode: previousNode)
if skipMemberName {
let missingIdentifier = missingToken(.identifier)
let declName = RawDeclReferenceExprSyntax(
baseName: missingIdentifier,
argumentNames: nil,
arena: self.arena
)
return (unexpectedPeriod, period, declName, nil)
}
// Parse the name portion.
let declName: RawDeclReferenceExprSyntax
if let indexOrSelf = self.consume(if: .integerLiteral, .keyword(.self)) {
// Handle "x.42" - a tuple index.
declName = RawDeclReferenceExprSyntax(
baseName: indexOrSelf,
argumentNames: nil,
arena: self.arena
)
} else {
// Handle an arbitrary declaration name.
declName = self.parseDeclReferenceExpr([.keywords, .compoundNames])
}
// Parse the generic arguments, if any.
let generics: RawGenericArgumentClauseSyntax?
if self.withLookahead({ $0.canParseAsGenericArgumentList() }) {
generics = self.parseGenericArguments()
} else {
generics = nil
}
return (unexpectedPeriod, period, declName, generics)
}
mutating func parseDottedExpressionSuffix(_ start: RawExprSyntax?) -> RawExprSyntax {
let (unexpectedPeriod, period, declName, generics) = parseDottedExpressionSuffix(previousNode: start)
let memberAccess = RawMemberAccessExprSyntax(
base: start,
unexpectedPeriod,
period: period,
declName: declName,
arena: self.arena
)
guard let generics = generics else {
return RawExprSyntax(memberAccess)
}
return RawExprSyntax(
RawGenericSpecializationExprSyntax(
expression: memberAccess,
genericArgumentClause: generics,
arena: self.arena
)
)
}
mutating func parseIfConfigExpressionSuffix(
_ start: RawExprSyntax?,
flavor: ExprFlavor
) -> RawExprSyntax {
precondition(self.at(.poundIf))
let config = self.parsePoundIfDirective { (parser, isFirstElement) -> RawExprSyntax? in
if !isFirstElement {
return nil
}
let head: RawExprSyntax
if parser.at(.period) {
head = parser.parseDottedExpressionSuffix(nil)
} else if parser.at(.poundIf) {
head = parser.parseIfConfigExpressionSuffix(nil, flavor: flavor)
} else {
// TODO: diagnose and skip.
return nil
}
let result = parser.parsePostfixExpressionSuffix(
head,
flavor: flavor,
pattern: .none
)
// TODO: diagnose and skip the remaining token in the current clause.
return result
} syntax: { (parser, elements) -> RawIfConfigClauseSyntax.Elements? in
switch elements.count {
case 0: return nil
case 1: return .postfixExpression(elements.first!)
default: fatalError("Postfix #if should only have one element")
}
}
return RawExprSyntax(
RawPostfixIfConfigExprSyntax(
base: start,
config: config,
arena: self.arena
)
)
}
/// Parse the suffix of a postfix expression.
mutating func parsePostfixExpressionSuffix(
_ start: RawExprSyntax,
flavor: ExprFlavor,
pattern: PatternContext
) -> RawExprSyntax {
// Handle suffix expressions.
var leadingExpr = start
var loopProgress = LoopProgressCondition()
while self.hasProgressed(&loopProgress) {
if flavor == .poundIfDirective && self.atStartOfLine {
return leadingExpr
}
// Check for a .foo suffix.
if self.at(.period) {
leadingExpr = self.parseDottedExpressionSuffix(leadingExpr)
continue
}
// If there is an expr-call-suffix, parse it and form a call.
if let lparen = self.consume(if: TokenSpec(.leftParen, allowAtStartOfLine: false)) {
let args = self.parseArgumentListElements(
pattern: pattern,
flavor: flavor.callArgumentFlavor,
allowTrailingComma: true
)
let (unexpectedBeforeRParen, rparen) = self.expect(.rightParen)
// If we can parse trailing closures, do so.
let trailingClosure: RawClosureExprSyntax?
let additionalTrailingClosures: RawMultipleTrailingClosureElementListSyntax
if case .basic = flavor, self.at(.leftBrace), self.withLookahead({ $0.atValidTrailingClosure(flavor: flavor) })
{
(trailingClosure, additionalTrailingClosures) = self.parseTrailingClosures(flavor: flavor)
} else {
trailingClosure = nil
additionalTrailingClosures = self.emptyCollection(RawMultipleTrailingClosureElementListSyntax.self)
}
leadingExpr = RawExprSyntax(
RawFunctionCallExprSyntax(
calledExpression: leadingExpr,
leftParen: lparen,
arguments: RawLabeledExprListSyntax(elements: args, arena: self.arena),
unexpectedBeforeRParen,
rightParen: rparen,
trailingClosure: trailingClosure,
additionalTrailingClosures: additionalTrailingClosures,
arena: self.arena
)
)
continue
}
// Check for a [expr] suffix.
// Note that this cannot be the start of a new line.
if let lsquare = self.consume(if: TokenSpec(.leftSquare, allowAtStartOfLine: false)) {
let args: [RawLabeledExprSyntax]
if self.at(.rightSquare) {
args = []
} else {
args = self.parseArgumentListElements(
pattern: pattern,
allowTrailingComma: true
)
}
let (unexpectedBeforeRSquare, rsquare) = self.expect(.rightSquare)
// If we can parse trailing closures, do so.
let trailingClosure: RawClosureExprSyntax?
let additionalTrailingClosures: RawMultipleTrailingClosureElementListSyntax
if case .basic = flavor, self.at(.leftBrace), self.withLookahead({ $0.atValidTrailingClosure(flavor: flavor) })
{
(trailingClosure, additionalTrailingClosures) = self.parseTrailingClosures(flavor: flavor)
} else {
trailingClosure = nil
additionalTrailingClosures = self.emptyCollection(RawMultipleTrailingClosureElementListSyntax.self)
}
leadingExpr = RawExprSyntax(
RawSubscriptCallExprSyntax(
calledExpression: leadingExpr,
leftSquare: lsquare,
arguments: RawLabeledExprListSyntax(elements: args, arena: self.arena),
unexpectedBeforeRSquare,
rightSquare: rsquare,
trailingClosure: trailingClosure,
additionalTrailingClosures: additionalTrailingClosures,
arena: self.arena
)
)
continue
}
// Check for a trailing closure, if allowed.
if self.at(.leftBrace) && !leadingExpr.raw.kind.isLiteral
&& self.withLookahead({ $0.atValidTrailingClosure(flavor: flavor) })
{
// Add dummy blank argument list to the call expression syntax.
let list = RawLabeledExprListSyntax(elements: [], arena: self.arena)
let (first, rest) = self.parseTrailingClosures(flavor: flavor)
leadingExpr = RawExprSyntax(
RawFunctionCallExprSyntax(
calledExpression: leadingExpr,
leftParen: nil,
arguments: list,
rightParen: nil,
trailingClosure: first,
additionalTrailingClosures: rest,
arena: self.arena
)
)
// We only allow a single trailing closure on a call. This could be
// generalized in the future, but needs further design.
if self.at(.leftBrace) {
break
}
continue
}
// Check for a ? suffix.
if let question = self.consume(if: .postfixQuestionMark) {
leadingExpr = RawExprSyntax(
RawOptionalChainingExprSyntax(
expression: leadingExpr,
questionMark: question,
arena: self.arena
)
)
continue
}
// Check for a ! suffix.
if let exlaim = self.consume(if: .exclamationMark) {
leadingExpr = RawExprSyntax(
RawForceUnwrapExprSyntax(
expression: leadingExpr,
exclamationMark: exlaim,
arena: self.arena
)
)
continue
}
// Check for a postfix-operator suffix.
if let op = self.consume(if: .postfixOperator) {
leadingExpr = RawExprSyntax(
RawPostfixOperatorExprSyntax(
expression: leadingExpr,
operator: op,
arena: self.arena
)
)
continue
}
if self.at(.poundIf) {
// Check if the first '#if' body starts with '.' <identifier>, and parse
// it as a "postfix ifconfig expression".
do {
var lookahead = self.lookahead()
// Skip to the first body. We may need to skip multiple '#if' directives
// since we support nested '#if's. e.g.
// baseExpr
// #if CONDITION_1
// #if CONDITION_2
// .someMember
var loopProgress = LoopProgressCondition()
repeat {
lookahead.eat(.poundIf)
while !lookahead.at(.endOfFile) && !lookahead.currentToken.isAtStartOfLine {
lookahead.skipSingle()
}
} while lookahead.at(.poundIf) && lookahead.hasProgressed(&loopProgress)
guard lookahead.atStartOfPostfixExprSuffix() else {
break
}
}
leadingExpr = self.parseIfConfigExpressionSuffix(
leadingExpr,
flavor: flavor
)
continue
}
// Otherwise, we don't know what this token is, it must end the expression.
break
}
return leadingExpr
}
}
extension Parser {
/// Determine if this is a key path postfix operator like ".?!?".
private func getNumOptionalKeyPathPostfixComponents(_ tokenText: SyntaxText, mayBeAfterTypeName: Bool) -> Int? {
var mayBeAfterTypeName = mayBeAfterTypeName
// Make sure every character is ".", "!", or "?", without two "."s in a row.
var numComponents = 0
var lastWasDot = false
for byte in tokenText {
if byte == UInt8(ascii: ".") {
if !mayBeAfterTypeName {
break
}
if lastWasDot {
return nil
}
lastWasDot = true
continue
}
if byte == UInt8(ascii: "!") || byte == UInt8(ascii: "?") {
mayBeAfterTypeName = false
lastWasDot = false
numComponents += 1
continue
}
return nil
}
return numComponents
}
/// Consume the optional key path postfix ino a set of key path components.
private mutating func consumeOptionalKeyPathPostfix(
numComponents: Int,
mayBeAfterTypeName: inout Bool
) -> [RawKeyPathComponentSyntax] {
var components: [RawKeyPathComponentSyntax] = []
for _ in 0..<numComponents {