Stop parsing into IfConfiDecl nodes in the C++ parser

When parsing #if...#endif regions, parse the active clause directly into
place in the AST without ever producing an IfConfigDecl instance.
This commit is contained in:
Doug Gregor
2024-09-18 20:20:00 -07:00
parent 4bb9a587fa
commit d762dd53f8
11 changed files with 105 additions and 115 deletions

View File

@@ -977,7 +977,7 @@ public:
void consumeDecl(ParserPosition BeginParserPosition, bool IsTopLevel); void consumeDecl(ParserPosition BeginParserPosition, bool IsTopLevel);
ParserResult<Decl> parseDecl(bool IsAtStartOfLineOrPreviousHadSemi, ParserStatus parseDecl(bool IsAtStartOfLineOrPreviousHadSemi,
bool IfConfigsAreDeclAttrs, bool IfConfigsAreDeclAttrs,
llvm::function_ref<void(Decl *)> Handler, llvm::function_ref<void(Decl *)> Handler,
bool fromASTGen = false); bool fromASTGen = false);
@@ -1018,9 +1018,9 @@ public:
/// Parse a #if ... #endif directive. /// Parse a #if ... #endif directive.
/// Delegate callback function to parse elements in the blocks. /// Delegate callback function to parse elements in the blocks.
ParserResult<IfConfigDecl> parseIfConfig( ParserStatus parseIfConfig(
IfConfigContext ifConfigContext, IfConfigContext ifConfigContext,
llvm::function_ref<void(SmallVectorImpl<ASTNode> &, bool)> parseElements); llvm::function_ref<void(bool)> parseElements);
/// Parse an #if ... #endif containing only attributes. /// Parse an #if ... #endif containing only attributes.
ParserStatus parseIfConfigDeclAttributes( ParserStatus parseIfConfigDeclAttributes(

View File

@@ -35,6 +35,7 @@ BridgedExpr BridgedLegacyParser_parseExpr(BridgedLegacyParser p,
return result.getPtrOrNull(); return result.getPtrOrNull();
} }
// FIXME: We need to be able to return multiple declarations here.
BridgedDecl BridgedLegacyParser_parseDecl(BridgedLegacyParser p, BridgedDecl BridgedLegacyParser_parseDecl(BridgedLegacyParser p,
BridgedSourceLoc loc, BridgedSourceLoc loc,
BridgedDeclContext DC) { BridgedDeclContext DC) {
@@ -47,11 +48,16 @@ BridgedDecl BridgedLegacyParser_parseDecl(BridgedLegacyParser p,
// FIXME: IsAtStartOfLineOrPreviousHadSemi should be passed in from ASTGen. // FIXME: IsAtStartOfLineOrPreviousHadSemi should be passed in from ASTGen.
// IfConfigsAreDeclAttrs: true because ASTGen thinks the current location is // IfConfigsAreDeclAttrs: true because ASTGen thinks the current location is
// a start of a decl. // a start of a decl.
ParserResult<Decl> result = P.parseDecl( Decl *resultDecl = nullptr;
P.parseDecl(
/*IsAtStartOfLineOrPreviousHadSemi=*/true, /*IsAtStartOfLineOrPreviousHadSemi=*/true,
/*IfConfigsAreDeclAttrs=*/true, [&](Decl *decl) {}, /*IfConfigsAreDeclAttrs=*/true, [&](Decl *decl) {
// FIXME: We need to capture all of these declarations, not just
// the last one.
resultDecl = decl;
},
/*fromASTGen=*/true); /*fromASTGen=*/true);
return result.getPtrOrNull(); return resultDecl;
} }
BridgedStmt BridgedLegacyParser_parseStmt(BridgedLegacyParser p, BridgedStmt BridgedLegacyParser_parseStmt(BridgedLegacyParser p,

View File

@@ -6247,13 +6247,17 @@ static Parser::ParseDeclOptions getParseDeclOptions(DeclContext *DC) {
/// ///
/// \param fromASTGen If true , this function in called from ASTGen as the /// \param fromASTGen If true , this function in called from ASTGen as the
/// fallback, so do not attempt a callback to ASTGen. /// fallback, so do not attempt a callback to ASTGen.
ParserResult<Decl> Parser::parseDecl(bool IsAtStartOfLineOrPreviousHadSemi, ParserStatus Parser::parseDecl(bool IsAtStartOfLineOrPreviousHadSemi,
bool IfConfigsAreDeclAttrs, bool IfConfigsAreDeclAttrs,
llvm::function_ref<void(Decl *)> Handler, llvm::function_ref<void(Decl *)> Handler,
bool fromASTGen) { bool fromASTGen) {
#if SWIFT_BUILD_SWIFT_SYNTAX #if SWIFT_BUILD_SWIFT_SYNTAX
if (IsForASTGen && !fromASTGen) if (IsForASTGen && !fromASTGen) {
return parseDeclFromSyntaxTree(); auto result = parseDeclFromSyntaxTree();
if (auto resultDecl = result.getPtrOrNull())
Handler(resultDecl);
return result;
}
#endif #endif
ParseDeclOptions Flags = getParseDeclOptions(CurDeclContext); ParseDeclOptions Flags = getParseDeclOptions(CurDeclContext);
ParserPosition BeginParserPosition; ParserPosition BeginParserPosition;
@@ -6263,13 +6267,17 @@ ParserResult<Decl> Parser::parseDecl(bool IsAtStartOfLineOrPreviousHadSemi,
if (Tok.is(tok::pound_if) && !ifConfigContainsOnlyAttributes()) { if (Tok.is(tok::pound_if) && !ifConfigContainsOnlyAttributes()) {
auto IfConfigResult = parseIfConfig( auto IfConfigResult = parseIfConfig(
IfConfigContext::DeclItems, IfConfigContext::DeclItems,
[&](SmallVectorImpl<ASTNode> &Decls, bool IsActive) { [&](bool IsActive) {
ParserStatus Status; ParserStatus Status;
bool PreviousHadSemi = true; bool PreviousHadSemi = true;
while (Tok.isNot(tok::pound_else, tok::pound_endif, tok::pound_elseif, while (Tok.isNot(tok::pound_else, tok::pound_endif, tok::pound_elseif,
tok::r_brace, tok::eof)) { tok::r_brace, tok::eof)) {
Status |= parseDeclItem(PreviousHadSemi, Status |= parseDeclItem(PreviousHadSemi,
[&](Decl *D) { Decls.emplace_back(D); }); [&](Decl *D) {
if (IsActive) {
Handler(D);
}
});
} }
}); });
if (IfConfigResult.hasCodeCompletion() && isIDEInspectionFirstPass()) { if (IfConfigResult.hasCodeCompletion() && isIDEInspectionFirstPass()) {
@@ -6277,19 +6285,7 @@ ParserResult<Decl> Parser::parseDecl(bool IsAtStartOfLineOrPreviousHadSemi,
return makeParserError(); return makeParserError();
} }
if (auto ICD = IfConfigResult.getPtrOrNull()) { return makeParserSuccess();
// The IfConfigDecl is ahead of its members in source order.
Handler(ICD);
// Copy the active members into the entries list.
for (auto activeMember : ICD->getActiveClauseElements()) {
auto *D = activeMember.get<Decl*>();
if (isa<IfConfigDecl>(D))
// Don't hoist nested '#if'.
continue;
Handler(D);
}
}
return IfConfigResult;
} }
if (Tok.isAny(tok::pound_warning, tok::pound_error)) { if (Tok.isAny(tok::pound_warning, tok::pound_error)) {
auto Result = parseDeclPoundDiagnostic(); auto Result = parseDeclPoundDiagnostic();
@@ -7050,15 +7046,19 @@ ParserStatus Parser::parseDeclItem(bool &PreviousHadSemi,
return LineDirectiveStatus; return LineDirectiveStatus;
} }
ParserResult<Decl> Result = Decl *LastResultDecl = nullptr;
ParserStatus Result =
parseDecl(IsAtStartOfLineOrPreviousHadSemi, parseDecl(IsAtStartOfLineOrPreviousHadSemi,
/* IfConfigsAreDeclAttrs=*/false, handler); /* IfConfigsAreDeclAttrs=*/false, [&](Decl *decl) {
if (Result.isParseErrorOrHasCompletion()) handler(decl);
LastResultDecl = decl;
});
if (Result.isErrorOrHasCompletion())
skipUntilDeclRBrace(tok::semi, tok::pound_endif); skipUntilDeclRBrace(tok::semi, tok::pound_endif);
SourceLoc SemiLoc; SourceLoc SemiLoc;
PreviousHadSemi = consumeIf(tok::semi, SemiLoc); PreviousHadSemi = consumeIf(tok::semi, SemiLoc);
if (PreviousHadSemi && Result.isNonNull()) if (PreviousHadSemi && LastResultDecl)
Result.get()->TrailingSemiLoc = SemiLoc; LastResultDecl->TrailingSemiLoc = SemiLoc;
return Result; return Result;
} }

View File

@@ -1478,10 +1478,11 @@ Parser::parseExprPostfixSuffix(ParserResult<Expr> Result, bool isExprBasic,
.fixItInsert(getEndOfPreviousLoc(), "\n"); .fixItInsert(getEndOfPreviousLoc(), "\n");
} }
llvm::TinyPtrVector<ASTNode> activeElements;
llvm::SmallPtrSet<Expr *, 4> exprsWithBindOptional; llvm::SmallPtrSet<Expr *, 4> exprsWithBindOptional;
auto ICD = parseIfConfig( auto ICD = parseIfConfig(
IfConfigContext::PostfixExpr, IfConfigContext::PostfixExpr,
[&](SmallVectorImpl<ASTNode> &elements, bool isActive) { [&](bool isActive) {
// Although we know the '#if' body starts with period, // Although we know the '#if' body starts with period,
// '#elseif'/'#else' bodies might start with invalid tokens. // '#elseif'/'#else' bodies might start with invalid tokens.
if (isAtStartOfPostfixExprSuffix() || Tok.is(tok::pound_if)) { if (isAtStartOfPostfixExprSuffix() || Tok.is(tok::pound_if)) {
@@ -1491,13 +1492,14 @@ Parser::parseExprPostfixSuffix(ParserResult<Expr> Result, bool isExprBasic,
exprHasBindOptional); exprHasBindOptional);
if (exprHasBindOptional) if (exprHasBindOptional)
exprsWithBindOptional.insert(expr.get()); exprsWithBindOptional.insert(expr.get());
elements.push_back(expr.get());
if (isActive)
activeElements.push_back(expr.get());
} }
}); });
if (ICD.isNull()) if (ICD.isErrorOrHasCompletion())
break; break;
auto activeElements = ICD.get()->getActiveClauseElements();
if (activeElements.empty()) if (activeElements.empty())
// There's no active clause, or it was empty. Keep the current result. // There's no active clause, or it was empty. Keep the current result.
continue; continue;

View File

@@ -972,30 +972,21 @@ Result Parser::parseIfConfigRaw(
return finish(EndLoc, HadMissingEnd); return finish(EndLoc, HadMissingEnd);
} }
/// Parse and populate a #if ... #endif directive. // Parse and populate a #if ... #endif directive.
/// Delegate callback function to parse elements in the blocks. /// Delegate callback function to parse elements in the blocks.
ParserResult<IfConfigDecl> Parser::parseIfConfig( ParserStatus Parser::parseIfConfig(
IfConfigContext ifConfigContext, IfConfigContext ifConfigContext,
llvm::function_ref<void(SmallVectorImpl<ASTNode> &, bool)> parseElements) { llvm::function_ref<void(bool)> parseElements) {
SmallVector<IfConfigClause, 4> clauses; ParserStatus status = makeParserSuccess();
return parseIfConfigRaw<ParserResult<IfConfigDecl>>( return parseIfConfigRaw<ParserStatus>(
ifConfigContext, ifConfigContext,
[&](SourceLoc clauseLoc, Expr *condition, bool isActive, [&](SourceLoc clauseLoc, Expr *condition, bool isActive,
IfConfigElementsRole role) { IfConfigElementsRole role) {
SmallVector<ASTNode, 16> elements;
if (role != IfConfigElementsRole::Skipped) if (role != IfConfigElementsRole::Skipped)
parseElements(elements, isActive); parseElements(isActive);
if (role == IfConfigElementsRole::SyntaxOnly)
elements.clear();
clauses.emplace_back(
clauseLoc, condition, Context.AllocateCopy(elements), isActive);
}, },
[&](SourceLoc endLoc, bool hadMissingEnd) { [&](SourceLoc endLoc, bool hadMissingEnd) {
auto *ICD = new (Context) IfConfigDecl(CurDeclContext, return status;
Context.AllocateCopy(clauses),
endLoc, hadMissingEnd);
return makeParserResult(ICD);
}); });
} }

View File

@@ -369,35 +369,31 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl<ASTNode> &Entries,
// Parse the decl, stmt, or expression. // Parse the decl, stmt, or expression.
PreviousHadSemi = false; PreviousHadSemi = false;
if (Tok.is(tok::pound_if) && !isStartOfSwiftDecl()) { if (Tok.is(tok::pound_if) && !isStartOfSwiftDecl()) {
SmallVector<ASTNode, 16> activeElements;
auto IfConfigResult = parseIfConfig( auto IfConfigResult = parseIfConfig(
IfConfigContext::BraceItems, IfConfigContext::BraceItems,
[&](SmallVectorImpl<ASTNode> &Elements, bool IsActive) { [&](bool IsActive) {
parseBraceItems(Elements, Kind, SmallVector<ASTNode, 16> elements;
parseBraceItems(elements, Kind,
IsActive IsActive
? BraceItemListKind::ActiveConditionalBlock ? BraceItemListKind::ActiveConditionalBlock
: BraceItemListKind::InactiveConditionalBlock, : BraceItemListKind::InactiveConditionalBlock,
IsFollowingGuard); IsFollowingGuard);
if (IsActive)
activeElements = std::move(elements);
}); });
if (IfConfigResult.hasCodeCompletion() && isIDEInspectionFirstPass()) { if (IfConfigResult.hasCodeCompletion() && isIDEInspectionFirstPass()) {
consumeDecl(BeginParserPosition, IsTopLevel); consumeDecl(BeginParserPosition, IsTopLevel);
return IfConfigResult; return IfConfigResult;
} }
BraceItemsStatus |= IfConfigResult; BraceItemsStatus |= IfConfigResult;
if (auto ICD = IfConfigResult.getPtrOrNull()) { if (IfConfigResult.isError()) {
Result = ICD;
// Add the #if block itself
Entries.push_back(ICD);
for (auto &Entry : ICD->getActiveClauseElements()) {
if (Entry.is<Decl *>() && isa<IfConfigDecl>(Entry.get<Decl *>()))
// Don't hoist nested '#if'.
continue;
Entries.push_back(Entry);
}
} else {
NeedParseErrorRecovery = true; NeedParseErrorRecovery = true;
continue; continue;
} }
Entries.append(activeElements);
} else if (Tok.is(tok::pound_line)) { } else if (Tok.is(tok::pound_line)) {
ParserStatus Status = parseLineDirective(true); ParserStatus Status = parseLineDirective(true);
BraceItemsStatus |= Status; BraceItemsStatus |= Status;
@@ -408,7 +404,7 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl<ASTNode> &Entries,
NeedParseErrorRecovery = Status.isErrorOrHasCompletion(); NeedParseErrorRecovery = Status.isErrorOrHasCompletion();
} else if (isStartOfSwiftDecl()) { } else if (isStartOfSwiftDecl()) {
SmallVector<Decl*, 8> TmpDecls; SmallVector<Decl*, 8> TmpDecls;
ParserResult<Decl> DeclResult = ParserStatus DeclResult =
parseDecl(IsAtStartOfLineOrPreviousHadSemi, parseDecl(IsAtStartOfLineOrPreviousHadSemi,
/*IfConfigsAreDeclAttrs=*/true, [&](Decl *D) { /*IfConfigsAreDeclAttrs=*/true, [&](Decl *D) {
TmpDecls.push_back(D); TmpDecls.push_back(D);
@@ -423,7 +419,7 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl<ASTNode> &Entries,
FD->setHasTopLevelLocalContextCaptures(); FD->setHasTopLevelLocalContextCaptures();
}); });
BraceItemsStatus |= DeclResult; BraceItemsStatus |= DeclResult;
if (DeclResult.isParseErrorOrHasCompletion()) { if (DeclResult.isErrorOrHasCompletion()) {
NeedParseErrorRecovery = true; NeedParseErrorRecovery = true;
if (DeclResult.hasCodeCompletion() && IsTopLevel && if (DeclResult.hasCodeCompletion() && IsTopLevel &&
isIDEInspectionFirstPass()) { isIDEInspectionFirstPass()) {
@@ -431,8 +427,14 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl<ASTNode> &Entries,
return DeclResult; return DeclResult;
} }
} }
Result = DeclResult.getPtrOrNull();
Entries.append(TmpDecls.begin(), TmpDecls.end()); Entries.append(TmpDecls.begin(), TmpDecls.end());
// HACK: If any declarations were parsed, make the last one the "result".
// We should know whether this was in an #if or not.
if (!TmpDecls.empty()) {
Result = TmpDecls.back();
}
} else if (IsTopLevel) { } else if (IsTopLevel) {
// If this is a statement or expression at the top level of the module, // If this is a statement or expression at the top level of the module,
// Parse it as a child of a TopLevelCodeDecl. // Parse it as a child of a TopLevelCodeDecl.
@@ -2661,25 +2663,15 @@ Parser::parseStmtCases(SmallVectorImpl<ASTNode> &cases, bool IsActive) {
// clauses. // clauses.
auto IfConfigResult = auto IfConfigResult =
parseIfConfig(IfConfigContext::SwitchStmt, parseIfConfig(IfConfigContext::SwitchStmt,
[&](SmallVectorImpl<ASTNode> &Elements, bool IsActive) { [&](bool IsActive) {
parseStmtCases(Elements, IsActive); SmallVector<ASTNode, 16> elements;
parseStmtCases(elements, IsActive);
if (IsActive) {
cases.append(elements);
}
}); });
Status |= IfConfigResult; Status |= IfConfigResult;
if (auto ICD = IfConfigResult.getPtrOrNull()) {
cases.emplace_back(ICD);
for (auto &Entry : ICD->getActiveClauseElements()) {
if (Entry.is<Decl*>() &&
(isa<IfConfigDecl>(Entry.get<Decl*>())))
// Don't hoist nested '#if'.
continue;
assert((Entry.is<Stmt*>() && isa<CaseStmt>(Entry.get<Stmt*>())) ||
(Entry.is<Decl*>() &&
isa<PoundDiagnosticDecl>(Entry.get<Decl*>())));
cases.push_back(Entry);
}
}
} else if (Tok.is(tok::pound_warning) || Tok.is(tok::pound_error)) { } else if (Tok.is(tok::pound_warning) || Tok.is(tok::pound_error)) {
auto PoundDiagnosticResult = parseDeclPoundDiagnostic(); auto PoundDiagnosticResult = parseDeclPoundDiagnostic();
Status |= PoundDiagnosticResult; Status |= PoundDiagnosticResult;

View File

@@ -822,9 +822,11 @@ bool Parser::parseEndIfDirective(SourceLoc &Loc) {
Loc = PreviousLoc; Loc = PreviousLoc;
skipUntilConditionalBlockClose(); skipUntilConditionalBlockClose();
return true; return true;
} else if (!Tok.isAtStartOfLine() && Tok.isNot(tok::eof)) } else if (!Tok.isAtStartOfLine() && Tok.isNot(tok::eof)) {
diagnose(Tok.getLoc(), diagnose(Tok.getLoc(),
diag::extra_tokens_conditional_compilation_directive); diag::extra_tokens_conditional_compilation_directive);
skipUntilTokenOrEndOfLine(tok::NUM_TOKENS);
}
return false; return false;
} }

View File

@@ -31,7 +31,7 @@ func bar() {
// CHECK-LABEL: (enum_decl{{.*}}trailing_semi "TrailingSemi" // CHECK-LABEL: (enum_decl{{.*}}trailing_semi "TrailingSemi"
enum TrailingSemi { enum TrailingSemi {
// CHECK-LABEL: (enum_case_decl{{.*}}trailing_semi // CHECK-LABEL: (enum_case_decl
// CHECK-NOT: (enum_element_decl{{.*}}trailing_semi // CHECK-NOT: (enum_element_decl{{.*}}trailing_semi
// CHECK: (enum_element_decl{{.*}}"A") // CHECK: (enum_element_decl{{.*}}"A")
// CHECK: (enum_element_decl{{.*}}"B") // CHECK: (enum_element_decl{{.*}}"B")
@@ -41,7 +41,7 @@ enum TrailingSemi {
// CHECK-NOT: (func_decl{{.*}}trailing_semi <anonymous @ 0x{{[0-9a-f]+}}> get for="subscript(_:)" // CHECK-NOT: (func_decl{{.*}}trailing_semi <anonymous @ 0x{{[0-9a-f]+}}> get for="subscript(_:)"
// CHECK: (accessor_decl{{.*}} <anonymous @ 0x{{[0-9a-f]+}}> get for="subscript(_:)" // CHECK: (accessor_decl{{.*}} <anonymous @ 0x{{[0-9a-f]+}}> get for="subscript(_:)"
subscript(x: Int) -> Int { subscript(x: Int) -> Int {
// CHECK-LABEL: (pattern_binding_decl{{.*}}trailing_semi // CHECK-LABEL: (pattern_binding_decl{{.*}}
// CHECK-NOT: (var_decl{{.*}}trailing_semi "y" // CHECK-NOT: (var_decl{{.*}}trailing_semi "y"
// CHECK: (var_decl{{.*}}"y" // CHECK: (var_decl{{.*}}"y"
var y = 1; var y = 1;

View File

@@ -3,15 +3,13 @@
// More blah blah. // More blah blah.
#if FOO
import Swift import Swift
#if FOO
class FooEnabled {} class FooEnabled {}
typealias MyN = Int typealias MyN = Int
#else #else
import Swift
class FooDisabled {} class FooDisabled {}
typealias MyN = Int typealias MyN = Int

View File

@@ -710,12 +710,12 @@ func returnBranches6PoundIf() -> Int {
func returnBranches6PoundIf2() -> Int { func returnBranches6PoundIf2() -> Int {
// We don't allow multiple expressions. // We don't allow multiple expressions.
let i = if .random() { let i = if .random() { // expected-error{{expected expression in branch of 'if' expression}}
#if false #if false
print("hello") print("hello")
0 0
#endif #endif
} else { // expected-error {{non-expression branch of 'if' expression may only end with a 'throw'}} } else {
1 1
} }
return i return i
@@ -849,11 +849,11 @@ func testPoundIfBranch2() -> Int {
} }
func testPoundIfBranch3() -> Int { func testPoundIfBranch3() -> Int {
let x = if .random() { let x = if .random() { // expected-error{{expected expression in branch of 'if' expression}}
#if false #if false
0 0
#endif #endif
} else { // expected-error {{non-expression branch of 'if' expression may only end with a 'throw'}} } else {
0 0
} }
return x return x
@@ -872,25 +872,25 @@ func testPoundIfBranch4() -> Int {
} }
func testPoundIfBranch5() -> Int { func testPoundIfBranch5() -> Int {
// Not allowed (matches the behavior of implict expression returns) // Inactive #if regions don't count
if .random() { if .random() {
#if false #if false
0 0
#endif #endif
0 // expected-warning {{integer literal is unused}} 0
} else { } else {
1 // expected-warning {{integer literal is unused}} 1
} }
} }
func testPoundIfBranch6() -> Int { func testPoundIfBranch6() -> Int {
// Not allowed (matches the behavior of implict expression returns) // Inactive #if regions don't count
let x = if .random() { let x = if .random() {
#if false #if false
0 0
#endif #endif
0 // expected-warning {{integer literal is unused}} 0
} else { // expected-error {{non-expression branch of 'if' expression may only end with a 'throw'}} } else {
1 1
} }
return x return x

View File

@@ -855,14 +855,14 @@ func returnBranches6PoundIf() -> Int {
} }
func returnBranches6PoundIf2() -> Int { func returnBranches6PoundIf2() -> Int {
// We don't allow multiple expressions. // We don't allow multiple expressions, but inactive #ifs don't count.
let i = switch Bool.random() { let i = switch Bool.random() {
case true: case true:
#if false #if false
print("hello") print("hello")
0 0
#endif #endif
// expected-error@-1 {{non-expression branch of 'switch' expression may only end with a 'throw' or 'fallthrough'}} // expected-error@-1 {{expected expression in branch of 'switch' expression}}
case false: case false:
1 1
} }
@@ -1071,7 +1071,7 @@ func testPoundIfBranch3() -> Int {
#if false #if false
0 0
#endif #endif
// expected-error@-1 {{non-expression branch of 'switch' expression may only end with a 'throw' or 'fallthrough'}} // expected-error@-1 {{expected expression in branch of 'switch' expression}}
case false: case false:
0 0
} }
@@ -1092,27 +1092,26 @@ func testPoundIfBranch4() -> Int {
} }
func testPoundIfBranch5() -> Int { func testPoundIfBranch5() -> Int {
// Not allowed (matches the behavior of implict expression returns) // Okay, inactive #ifs don't count.
switch Bool.random() { switch Bool.random() {
case true: case true:
#if false #if false
0 0
#endif #endif
0 // expected-warning {{integer literal is unused}} 0
case false: case false:
1 // expected-warning {{integer literal is unused}} 1
} }
} }
func testPoundIfBranch6() -> Int { func testPoundIfBranch6() -> Int {
// Not allowed (matches the behavior of implict expression returns) // Okay, inactive #ifs don't count.
let x = switch Bool.random() { let x = switch Bool.random() {
case true: case true:
#if false #if false
0 0
#endif #endif
0 // expected-warning {{integer literal is unused}} 0
// expected-error@-1 {{non-expression branch of 'switch' expression may only end with a 'throw' or 'fallthrough'}}
case false: case false:
1 1
} }