1313@_spi ( RawSyntax) import SwiftSyntax
1414
1515extension Parser {
16- private enum IfConfigContinuationClauseStartKeyword : TokenSpecSet {
16+ private enum PoundIfDirectiveKeywords : TokenSpecSet {
17+ case poundIfKeyword
1718 case poundElseifKeyword
1819 case poundElseKeyword
20+ case pound
1921
2022 var spec : TokenSpec {
2123 switch self {
24+ case . poundIfKeyword: return . poundIfKeyword
2225 case . poundElseifKeyword: return . poundElseifKeyword
2326 case . poundElseKeyword: return . poundElseKeyword
27+ case . pound: return TokenSpec ( . pound, recoveryPrecedence: . openingPoundIf)
2428 }
2529 }
2630
2731 init ? ( lexeme: Lexer . Lexeme ) {
2832 switch PrepareForKeywordMatch ( lexeme) {
33+ case TokenSpec ( . poundIfKeyword) : self = . poundIfKeyword
2934 case TokenSpec ( . poundElseifKeyword) : self = . poundElseifKeyword
3035 case TokenSpec ( . poundElseKeyword) : self = . poundElseKeyword
36+ case TokenSpec ( . pound) : self = . pound
3137 default : return nil
3238 }
3339 }
@@ -103,32 +109,72 @@ extension Parser {
103109 do {
104110 var firstIteration = true
105111 var loopProgress = LoopProgressCondition ( )
106- while let poundIfHandle = firstIteration ? self . canRecoverTo ( . poundIfKeyword) : self . canRecoverTo ( anyIn: IfConfigContinuationClauseStartKeyword . self) ? . handle,
107- loopProgress. evaluate ( self . currentToken)
108- {
109- var ( unexpectedBeforePoundIf, poundIf) = self . eat ( poundIfHandle)
110- firstIteration = false
111- // Parse the condition.
112+ LOOP: while let ( match, handle) = self . canRecoverTo ( anyIn: PoundIfDirectiveKeywords . self) , loopProgress. evaluate ( self . currentToken) {
113+ var unexpectedBeforePound : RawUnexpectedNodesSyntax ?
114+ var poundKeyword : RawTokenSyntax
112115 let condition : RawExprSyntax ?
113- switch poundIf. tokenKind {
114- case . poundIfKeyword, . poundElseifKeyword:
116+ var atElifTypo : Bool {
117+ guard self . at ( TokenSpec ( . pound) ) , self . currentToken. trailingTriviaText. isEmpty else {
118+ return false
119+ }
120+ var lookahead = self . lookahead ( ) // consume `#`
121+ lookahead. consumeAnyToken ( )
122+ guard lookahead. at ( TokenSpec ( . identifier, allowAtStartOfLine: false ) ) , lookahead. currentToken. tokenText == " elif " , lookahead. currentToken. leadingTriviaText. isEmpty else {
123+ return false // `#` and `elif` must not be separated by trivia
124+ }
125+ lookahead. consumeAnyToken ( ) // consume `elif`
126+ // We are only at a `elif` typo if it’s followed by an identifier for the condition.
127+ // `#elif` or `#elif(…)` could be macro invocations.
128+ return lookahead. at ( TokenSpec ( . identifier, allowAtStartOfLine: false ) )
129+ }
130+
131+ switch match {
132+ case . poundIfKeyword:
133+ if !firstIteration {
134+ break LOOP
135+ }
136+ firstIteration = false
137+ ( unexpectedBeforePound, poundKeyword) = self . eat ( handle)
138+ condition = RawExprSyntax ( self . parseSequenceExpression ( . basic, forDirective: true ) )
139+ case . poundElseifKeyword:
140+ if firstIteration {
141+ break LOOP
142+ }
143+ ( unexpectedBeforePound, poundKeyword) = self . eat ( handle)
115144 condition = RawExprSyntax ( self . parseSequenceExpression ( . basic, forDirective: true ) )
116145 case . poundElseKeyword:
146+ if firstIteration {
147+ break LOOP
148+ }
149+ ( unexpectedBeforePound, poundKeyword) = self . eat ( handle)
117150 if let ifToken = self . consume ( if: . init( . if, allowAtStartOfLine: false ) ) {
118- unexpectedBeforePoundIf = RawUnexpectedNodesSyntax ( combining: unexpectedBeforePoundIf , poundIf , ifToken, arena: self . arena)
119- poundIf = self . missingToken ( . poundElseifKeyword)
151+ unexpectedBeforePound = RawUnexpectedNodesSyntax ( combining: unexpectedBeforePound , poundKeyword , ifToken, arena: self . arena)
152+ poundKeyword = self . missingToken ( . poundElseifKeyword)
120153 condition = RawExprSyntax ( self . parseSequenceExpression ( . basic, forDirective: true ) )
121154 } else {
122155 condition = nil
123156 }
124- default :
125- preconditionFailure ( " The loop condition should guarantee that we are at one of these tokens " )
157+ case . pound:
158+ if firstIteration {
159+ break LOOP
160+ }
161+ if atElifTypo {
162+ ( unexpectedBeforePound, poundKeyword) = self . eat ( handle)
163+ guard let elif = self . consume ( if: TokenSpec ( . identifier, allowAtStartOfLine: false ) ) else {
164+ preconditionFailure ( " The current token should be an identifier, guaranteed by the `atElifTypo` check. " )
165+ }
166+ unexpectedBeforePound = RawUnexpectedNodesSyntax ( combining: unexpectedBeforePound, poundKeyword, elif, arena: self . arena)
167+ poundKeyword = self . missingToken ( . poundElseifKeyword)
168+ condition = RawExprSyntax ( self . parseSequenceExpression ( . basic, forDirective: true ) )
169+ } else {
170+ break LOOP
171+ }
126172 }
127173
128174 var elements = [ Element] ( )
129175 do {
130176 var elementsProgress = LoopProgressCondition ( )
131- while !self . at ( . eof) && !self . at ( . poundElseKeyword, . poundElseifKeyword, . poundEndifKeyword) && elementsProgress. evaluate ( currentToken) {
177+ while !self . at ( . eof) && !self . at ( . poundElseKeyword, . poundElseifKeyword, . poundEndifKeyword) && !atElifTypo && elementsProgress. evaluate ( currentToken) {
132178 let newItemAtStartOfLine = self . currentToken. isAtStartOfLine
133179 guard let element = parseElement ( & self , elements. isEmpty) , !element. isEmpty else {
134180 break
@@ -142,8 +188,8 @@ extension Parser {
142188
143189 clauses. append (
144190 RawIfConfigClauseSyntax (
145- unexpectedBeforePoundIf ,
146- poundKeyword: poundIf ,
191+ unexpectedBeforePound ,
192+ poundKeyword: poundKeyword ,
147193 condition: condition,
148194 elements: syntax ( & self , elements) ,
149195 arena: self . arena
0 commit comments