@@ -1087,6 +1087,9 @@ Parser::parseExprPostfixSuffix(ParserResult<Expr> Result, bool isExprBasic,
10871087 if (Result.isNull ())
10881088 return Result;
10891089
1090+ if (InPoundIfEnvironment && Tok.isAtStartOfLine ())
1091+ return Result;
1092+
10901093 if (Result.hasCodeCompletion () &&
10911094 SourceMgr.getCodeCompletionLoc () == PreviousLoc) {
10921095 // Don't parse suffixes if the expression ended with code completion
@@ -1305,6 +1308,95 @@ Parser::parseExprPostfixSuffix(ParserResult<Expr> Result, bool isExprBasic,
13051308 continue ;
13061309 }
13071310
1311+ if (Tok.is (tok::pound_if)) {
1312+
1313+ // Helper function to see if we can parse member reference like suffixes
1314+ // inside '#if'.
1315+ auto isAtStartOfPostfixExprSuffix = [&]() {
1316+ if (!Tok.isAny (tok::period, tok::period_prefix)) {
1317+ return false ;
1318+ }
1319+ if (!peekToken ().isAny (tok::identifier, tok::kw_Self, tok::kw_self,
1320+ tok::integer_literal, tok::code_complete) &&
1321+ !peekToken ().isKeyword ()) {
1322+ return false ;
1323+ }
1324+ return true ;
1325+ };
1326+
1327+ // Check if the first '#if' body starts with '.' <identifier>, and parse
1328+ // it as a "postfix ifconfig expression".
1329+ bool isPostfixIfConfigExpr = false ;
1330+ {
1331+ llvm::SaveAndRestore<Optional<StableHasher>> H (CurrentTokenHash, None);
1332+ Parser::BacktrackingScope Backtrack (*this );
1333+ // Skip to the first body. We may need to skip multiple '#if' directives
1334+ // since we support nested '#if's. e.g.
1335+ // baseExpr
1336+ // #if CONDITION_1
1337+ // #if CONDITION_2
1338+ // .someMember
1339+ do {
1340+ consumeToken (tok::pound_if);
1341+ skipUntilTokenOrEndOfLine (tok::NUM_TOKENS);
1342+ } while (Tok.is (tok::pound_if));
1343+ isPostfixIfConfigExpr = isAtStartOfPostfixExprSuffix ();
1344+ }
1345+ if (!isPostfixIfConfigExpr)
1346+ break ;
1347+
1348+ if (!Tok.isAtStartOfLine ()) {
1349+ diagnose (Tok, diag::statement_same_line_without_newline)
1350+ .fixItInsert (getEndOfPreviousLoc (), " \n " );
1351+ }
1352+
1353+ llvm::SmallPtrSet<Expr *, 4 > exprsWithBindOptional;
1354+ auto ICD =
1355+ parseIfConfig ([&](SmallVectorImpl<ASTNode> &elements, bool isActive) {
1356+ SyntaxParsingContext postfixCtx (SyntaxContext,
1357+ SyntaxContextKind::Expr);
1358+ // Although we know the '#if' body starts with period,
1359+ // '#elseif'/'#else' bodies might start with invalid tokens.
1360+ if (isAtStartOfPostfixExprSuffix () || Tok.is (tok::pound_if)) {
1361+ bool exprHasBindOptional = false ;
1362+ auto expr = parseExprPostfixSuffix (Result, isExprBasic,
1363+ periodHasKeyPathBehavior,
1364+ exprHasBindOptional);
1365+ if (exprHasBindOptional)
1366+ exprsWithBindOptional.insert (expr.get ());
1367+ elements.push_back (expr.get ());
1368+ }
1369+
1370+ // Don't allow any character other than the postfix expression.
1371+ if (!Tok.isAny (tok::pound_elseif, tok::pound_else, tok::pound_endif,
1372+ tok::eof)) {
1373+ diagnose (Tok, diag::expr_postfix_ifconfig_unexpectedtoken);
1374+ skipUntilConditionalBlockClose ();
1375+ }
1376+ });
1377+ if (ICD.isNull ())
1378+ break ;
1379+
1380+ SyntaxContext->createNodeInPlace (SyntaxKind::PostfixIfConfigExpr);
1381+
1382+ auto activeElements = ICD.get ()->getActiveClauseElements ();
1383+ if (activeElements.empty ())
1384+ // There's no active clause, or it was empty. Keep the current result.
1385+ continue ;
1386+
1387+ // Extract the parsed expression as the result.
1388+ assert (activeElements.size () == 1 && activeElements[0 ].is <Expr *>());
1389+ auto expr = activeElements[0 ].get <Expr *>();
1390+ ParserStatus status (ICD);
1391+ if (SourceMgr.getCodeCompletionLoc ().isValid () &&
1392+ SourceMgr.rangeContainsTokenLoc (expr->getSourceRange (),
1393+ SourceMgr.getCodeCompletionLoc ()))
1394+ status.setHasCodeCompletion ();
1395+ hasBindOptional |= exprsWithBindOptional.contains (expr);
1396+ Result = makeParserResult (status, expr);
1397+ continue ;
1398+ }
1399+
13081400 if (Tok.is (tok::code_complete)) {
13091401 if (InSwiftKeyPath)
13101402 return Result;
0 commit comments