@@ -16,18 +16,21 @@ extension Parser {
1616 private enum IfConfigContinuationClauseStartKeyword : TokenSpecSet {
1717 case poundElseifKeyword
1818 case poundElseKeyword
19+ case pound
1920
2021 var spec : TokenSpec {
2122 switch self {
2223 case . poundElseifKeyword: return . poundElseifKeyword
2324 case . poundElseKeyword: return . poundElseKeyword
25+ case . pound: return TokenSpec ( . pound, recoveryPrecedence: . openingPoundIf)
2426 }
2527 }
2628
2729 init ? ( lexeme: Lexer . Lexeme ) {
2830 switch PrepareForKeywordMatch ( lexeme) {
2931 case TokenSpec ( . poundElseifKeyword) : self = . poundElseifKeyword
3032 case TokenSpec ( . poundElseKeyword) : self = . poundElseKeyword
33+ case TokenSpec ( . pound) : self = . pound
3134 default : return nil
3235 }
3336 }
@@ -103,32 +106,59 @@ extension Parser {
103106 do {
104107 var firstIteration = true
105108 var loopProgress = LoopProgressCondition ( )
106- while let poundIfHandle = firstIteration ? self . canRecoverTo ( . poundIfKeyword) : self . canRecoverTo ( anyIn: IfConfigContinuationClauseStartKeyword . self) ? . handle,
109+ LOOP: while let poundIfHandle = firstIteration ? self . canRecoverTo ( . poundIfKeyword) : self . canRecoverTo ( anyIn: IfConfigContinuationClauseStartKeyword . self) ? . handle,
107110 loopProgress. evaluate ( self . currentToken)
108111 {
109- var ( unexpectedBeforePoundIf, poundIf) = self . eat ( poundIfHandle)
110- firstIteration = false
111- // Parse the condition.
112+ var unexpectedBeforePound : RawUnexpectedNodesSyntax ?
113+ var pound : RawTokenSyntax
112114 let condition : RawExprSyntax ?
113- switch poundIf. tokenKind {
114- case . poundIfKeyword, . poundElseifKeyword:
115+ var atElifTypo : Bool {
116+ guard self . at ( TokenSpec ( . pound) ) , self . currentToken. trailingTriviaText. isEmpty else {
117+ return false
118+ }
119+ let identifierSpec = TokenSpec ( . identifier, allowAtStartOfLine: false )
120+ var lookahead = self . lookahead ( )
121+ lookahead. consumeAnyToken ( )
122+ guard lookahead. at ( identifierSpec) , lookahead. currentToken. tokenText == " elif " , lookahead. currentToken. leadingTriviaText. isEmpty else {
123+ return false
124+ }
125+ lookahead. consumeAnyToken ( )
126+ return lookahead. at ( identifierSpec)
127+ }
128+
129+ if atElifTypo {
130+ ( unexpectedBeforePound, pound) = self . eat ( poundIfHandle)
131+ guard let identifier = self . consume ( if: TokenSpec ( . identifier, allowAtStartOfLine: false ) ) else {
132+ preconditionFailure ( " The current token should be an identifier, guaranteed by the previous if statement " )
133+ }
134+ unexpectedBeforePound = RawUnexpectedNodesSyntax ( combining: unexpectedBeforePound, pound, identifier, arena: self . arena)
135+ pound = self . missingToken ( . poundElseifKeyword)
115136 condition = RawExprSyntax ( self . parseSequenceExpression ( . basic, forDirective: true ) )
116- case . poundElseKeyword:
117- 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)
137+ } else {
138+ ( unexpectedBeforePound, pound) = self . eat ( poundIfHandle)
139+ firstIteration = false
140+ switch pound. tokenKind {
141+ case . poundIfKeyword, . poundElseifKeyword:
120142 condition = RawExprSyntax ( self . parseSequenceExpression ( . basic, forDirective: true ) )
121- } else {
122- condition = nil
143+ case . poundElseKeyword:
144+ if let ifToken = self . consume ( if: . init( . if, allowAtStartOfLine: false ) ) {
145+ unexpectedBeforePound = RawUnexpectedNodesSyntax ( combining: unexpectedBeforePound, pound, ifToken, arena: self . arena)
146+ pound = self . missingToken ( . poundElseifKeyword)
147+ condition = RawExprSyntax ( self . parseSequenceExpression ( . basic, forDirective: true ) )
148+ } else {
149+ condition = nil
150+ }
151+ case . pound:
152+ break LOOP
153+ default :
154+ preconditionFailure ( " The loop condition should guarantee that we are at one of these tokens " )
123155 }
124- default :
125- preconditionFailure ( " The loop condition should guarantee that we are at one of these tokens " )
126156 }
127157
128158 var elements = [ Element] ( )
129159 do {
130160 var elementsProgress = LoopProgressCondition ( )
131- while !self . at ( . eof) && !self . at ( . poundElseKeyword, . poundElseifKeyword, . poundEndifKeyword) && elementsProgress. evaluate ( currentToken) {
161+ while !self . at ( . eof) && !self . at ( . poundElseKeyword, . poundElseifKeyword, . poundEndifKeyword) && !atElifTypo && elementsProgress. evaluate ( currentToken) {
132162 let newItemAtStartOfLine = self . currentToken. isAtStartOfLine
133163 guard let element = parseElement ( & self , elements. isEmpty) , !element. isEmpty else {
134164 break
@@ -142,8 +172,8 @@ extension Parser {
142172
143173 clauses. append (
144174 RawIfConfigClauseSyntax (
145- unexpectedBeforePoundIf ,
146- poundKeyword: poundIf ,
175+ unexpectedBeforePound ,
176+ poundKeyword: pound ,
147177 condition: condition,
148178 elements: syntax ( & self , elements) ,
149179 arena: self . arena
0 commit comments