diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 1337925dfd3d7..7ce47a5c4cc29 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -44,6 +44,7 @@ namespace swift { class GenericEnvironment; class ArchetypeType; class ASTContext; + struct ASTNode; class ASTPrinter; class ASTWalker; class ConstructorDecl; @@ -2023,34 +2024,33 @@ class SerializedTopLevelCodeDeclContext : public SerializedLocalDeclContext { } }; -/// IfConfigDecl - This class represents the declaration-side representation of -/// #if/#else/#endif blocks. Active and inactive block members are stored -/// separately, with the intention being that active members will be handed -/// back to the enclosing declaration. +/// IfConfigDecl - This class represents #if/#else/#endif blocks. +/// Active and inactive block members are stored separately, with the intention +/// being that active members will be handed back to the enclosing context. class IfConfigDecl : public Decl { /// An array of clauses controlling each of the #if/#elseif/#else conditions. /// The array is ASTContext allocated. - ArrayRef> Clauses; + ArrayRef Clauses; SourceLoc EndLoc; public: - IfConfigDecl(DeclContext *Parent, ArrayRef> Clauses, + IfConfigDecl(DeclContext *Parent, ArrayRef Clauses, SourceLoc EndLoc, bool HadMissingEnd) : Decl(DeclKind::IfConfig, Parent), Clauses(Clauses), EndLoc(EndLoc) { IfConfigDeclBits.HadMissingEnd = HadMissingEnd; } - ArrayRef> getClauses() const { return Clauses; } + ArrayRef getClauses() const { return Clauses; } /// Return the active clause, or null if there is no active one. - const IfConfigClause *getActiveClause() const { + const IfConfigClause *getActiveClause() const { for (auto &Clause : Clauses) if (Clause.isActive) return &Clause; return nullptr; } - const ArrayRef getActiveMembers() const { + const ArrayRef getActiveClauseElements() const { if (auto *Clause = getActiveClause()) return Clause->Elements; return {}; diff --git a/include/swift/AST/IfConfigClause.h b/include/swift/AST/IfConfigClause.h index 7c8c1cf44d23b..a3ce67401dbcf 100644 --- a/include/swift/AST/IfConfigClause.h +++ b/include/swift/AST/IfConfigClause.h @@ -22,11 +22,11 @@ namespace swift { class Expr; class SourceLoc; + struct ASTNode; /// This represents one part of a #if block. If the condition field is /// non-null, then this represents a #if or a #elseif, otherwise it represents /// an #else block. -template struct IfConfigClause { /// The location of the #if, #elseif, or #else keyword. SourceLoc Loc; @@ -36,13 +36,13 @@ struct IfConfigClause { Expr *Cond; /// Elements inside the clause - ArrayRef Elements; + ArrayRef Elements; /// True if this is the active clause of the #if block. bool isActive; - IfConfigClause(SourceLoc Loc, Expr *Cond, - ArrayRef Elements, bool isActive) + IfConfigClause(SourceLoc Loc, Expr *Cond, + ArrayRef Elements, bool isActive) : Loc(Loc), Cond(Cond), Elements(Elements), isActive(isActive) { } }; diff --git a/include/swift/AST/PrintOptions.h b/include/swift/AST/PrintOptions.h index b91170692ca07..2d528f4375b3d 100644 --- a/include/swift/AST/PrintOptions.h +++ b/include/swift/AST/PrintOptions.h @@ -279,7 +279,7 @@ struct PrintOptions { /// Print all decls that have at least this level of access. Accessibility AccessibilityFilter = Accessibility::Private; - /// Print IfConfigDecls and IfConfigStmts. + /// Print IfConfigDecls. bool PrintIfConfig = true; /// Whether we are printing for sil. diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index fe6ca397a8dc6..afb0d457228b7 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -655,46 +655,6 @@ class GuardStmt : public LabeledConditionalStmt { static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Guard; } }; -/// IfConfigStmt - This class models the statement-side representation of -/// #if/#else/#endif blocks. -class IfConfigStmt : public Stmt { - /// An array of clauses controlling each of the #if/#elseif/#else conditions. - /// The array is ASTContext allocated. - ArrayRef> Clauses; - SourceLoc EndLoc; - bool HadMissingEnd; - -public: - IfConfigStmt(ArrayRef> Clauses, SourceLoc EndLoc, - bool HadMissingEnd) - : Stmt(StmtKind::IfConfig, /*implicit=*/false), - Clauses(Clauses), EndLoc(EndLoc), HadMissingEnd(HadMissingEnd) {} - - SourceLoc getIfLoc() const { return Clauses[0].Loc; } - - SourceLoc getStartLoc() const { return getIfLoc(); } - SourceLoc getEndLoc() const { return EndLoc; } - - bool hadMissingEnd() const { return HadMissingEnd; } - - const ArrayRef> &getClauses() const { - return Clauses; - } - - ArrayRef getActiveClauseElements() const { - for (auto &Clause : Clauses) - if (Clause.isActive) - return Clause.Elements; - return ArrayRef(); - } - - // Implement isa/cast/dyncast/etc. - static bool classof(const Stmt *S) { - return S->getKind() == StmtKind::IfConfig; - } -}; - - /// WhileStmt - while statement. After type-checking, the condition is of /// type Builtin.Int1. class WhileStmt : public LabeledConditionalStmt { diff --git a/include/swift/AST/StmtNodes.def b/include/swift/AST/StmtNodes.def index 301f636c219bf..6c718acd1576d 100644 --- a/include/swift/AST/StmtNodes.def +++ b/include/swift/AST/StmtNodes.def @@ -61,7 +61,6 @@ STMT(Catch, Stmt) STMT(Break, Stmt) STMT(Continue, Stmt) STMT(Fallthrough, Stmt) -STMT(IfConfig, Stmt) STMT(Fail, Stmt) STMT(Throw, Stmt) diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index bd129a28545c9..b83a3a6bcd1ad 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -716,7 +716,11 @@ class Parser { ParserResult parseDeclAssociatedType(ParseDeclOptions Flags, DeclAttributes &Attributes); - ParserResult parseDeclIfConfig(ParseDeclOptions Flags); + /// Parse a #if ... #endif directive. + /// Delegate callback function to parse elements in the blocks. + ParserResult parseIfConfig( + llvm::function_ref &, bool)> parseElements); + /// Parse a #line/#sourceLocation directive. /// 'isLine = true' indicates parsing #line instead of #sourcelocation ParserStatus parseLineDirective(bool isLine = false); @@ -1277,8 +1281,6 @@ class Parser { ParserResult parseStmtConditionPoundAvailable(); ParserResult parseStmtIf(LabeledStmtInfo LabelInfo); ParserResult parseStmtGuard(); - ParserResult parseStmtIfConfig(BraceItemListKind Kind - = BraceItemListKind::Brace); ParserResult parseStmtWhile(LabeledStmtInfo LabelInfo); ParserResult parseStmtRepeat(LabeledStmtInfo LabelInfo); ParserResult parseStmtDo(LabeledStmtInfo LabelInfo); diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 4eaa7d7054b53..8b733f02002ee 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -1030,33 +1030,41 @@ namespace { } PrintWithColorRAII(OS, ParenthesisColor) << ')'; } + + void printASTNodes(const ArrayRef &Elements, StringRef Name) { + OS.indent(Indent); + PrintWithColorRAII(OS, ParenthesisColor) << "("; + PrintWithColorRAII(OS, ASTNodeColor) << Name; + for (auto Elt : Elements) { + OS << '\n'; + if (auto *SubExpr = Elt.dyn_cast()) + printRec(SubExpr); + else if (auto *SubStmt = Elt.dyn_cast()) + printRec(SubStmt); + else + printRec(Elt.get()); + } + PrintWithColorRAII(OS, ParenthesisColor) << ')'; + } void visitIfConfigDecl(IfConfigDecl *ICD) { - OS.indent(Indent); - PrintWithColorRAII(OS, ParenthesisColor) << '('; - OS << "#if_decl\n"; + printCommon(ICD, "if_config_decl"); Indent += 2; for (auto &Clause : ICD->getClauses()) { + OS << '\n'; + OS.indent(Indent); + PrintWithColorRAII(OS, StmtColor) << (Clause.Cond ? "#if:" : "#else:"); + if (Clause.isActive) + PrintWithColorRAII(OS, DeclModifierColor) << " active"; if (Clause.Cond) { - PrintWithColorRAII(OS, ParenthesisColor) << '('; - OS << "#if:"; - if (Clause.isActive) OS << " active"; OS << "\n"; printRec(Clause.Cond); - } else { - OS << '\n'; - PrintWithColorRAII(OS, ParenthesisColor) << '('; - OS << "#else:"; - if (Clause.isActive) OS << " active"; - OS << "\n"; } - for (auto D : Clause.Elements) { - OS << '\n'; - printRec(D); - } - - PrintWithColorRAII(OS, ParenthesisColor) << ')'; + OS << '\n'; + Indent += 2; + printASTNodes(Clause.Elements, "elements"); + Indent -= 2; } Indent -= 2; @@ -1416,35 +1424,6 @@ class PrintStmt : public StmtVisitor { PrintWithColorRAII(OS, ParenthesisColor) << ')'; } - void visitIfConfigStmt(IfConfigStmt *S) { - printCommon(S, "#if_stmt"); - Indent += 2; - for (auto &Clause : S->getClauses()) { - OS << '\n'; - OS.indent(Indent); - if (Clause.Cond) { - PrintWithColorRAII(OS, ParenthesisColor) << '('; - PrintWithColorRAII(OS, StmtColor) << "#if:"; - if (Clause.isActive) - PrintWithColorRAII(OS, DeclModifierColor) << " active"; - OS << '\n'; - printRec(Clause.Cond); - } else { - PrintWithColorRAII(OS, StmtColor) << "#else"; - if (Clause.isActive) - PrintWithColorRAII(OS, DeclModifierColor) << " active"; - } - - OS << '\n'; - Indent += 2; - printASTNodes(Clause.Elements, "elements"); - Indent -= 2; - } - - Indent -= 2; - PrintWithColorRAII(OS, ParenthesisColor) << ')'; - } - void visitDoStmt(DoStmt *S) { printCommon(S, "do_stmt") << '\n'; printRec(S->getBody()); diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 8d8133792acd1..4084086559a60 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -2360,7 +2360,21 @@ void PrintAST::visitTopLevelCodeDecl(TopLevelCodeDecl *decl) { } void PrintAST::visitIfConfigDecl(IfConfigDecl *ICD) { - // FIXME: Pretty print #if decls + if (!Options.PrintIfConfig) + return; + + for (auto &Clause : ICD->getClauses()) { + if (&Clause == &*ICD->getClauses().begin()) + Printer << tok::pound_if << " /* condition */"; // FIXME: print condition + else if (Clause.Cond) + Printer << tok::pound_elseif << " /* condition */"; // FIXME: print condition + else + Printer << tok::pound_else; + printASTNodes(Clause.Elements); + Printer.printNewline(); + indent(); + } + Printer << tok::pound_endif; } void PrintAST::visitTypeAliasDecl(TypeAliasDecl *decl) { @@ -3267,27 +3281,6 @@ void PrintAST::visitGuardStmt(GuardStmt *stmt) { visit(stmt->getBody()); } -void PrintAST::visitIfConfigStmt(IfConfigStmt *stmt) { - if (!Options.PrintIfConfig) - return; - - for (auto &Clause : stmt->getClauses()) { - if (&Clause == &*stmt->getClauses().begin()) - Printer << tok::pound_if << " "; // FIXME: print condition - else if (Clause.Cond) - Printer << tok::pound_elseif << ""; // FIXME: print condition - else - Printer << tok::pound_else; - Printer.printNewline(); - if (printASTNodes(Clause.Elements)) { - Printer.printNewline(); - indent(); - } - } - Printer.printNewline(); - Printer << tok::pound_endif; -} - void PrintAST::visitWhileStmt(WhileStmt *stmt) { Printer << tok::kw_while << " "; // FIXME: print condition diff --git a/lib/AST/ASTScope.cpp b/lib/AST/ASTScope.cpp index 2ca2f4416dced..a9a07680dfe58 100644 --- a/lib/AST/ASTScope.cpp +++ b/lib/AST/ASTScope.cpp @@ -188,24 +188,6 @@ static bool hasAccessors(AbstractStorageDecl *asd) { llvm_unreachable("Unhandled ContinuationKind in switch."); } -/// Determine whether this is a top-level code declaration that isn't just -/// wrapping an #if. -static bool isRealTopLevelCodeDecl(Decl *decl) { - auto topLevelCode = dyn_cast(decl); - if (!topLevelCode) return false; - - // Drop top-level statements containing just an IfConfigStmt. - // FIXME: The modeling of IfConfig is weird. - auto braceStmt = topLevelCode->getBody(); - auto elements = braceStmt->getElements(); - if (elements.size() == 1 && - elements[0].is() && - isa(elements[0].get())) - return false; - - return true; -} - void ASTScope::expand() const { assert(!isExpanded() && "Already expanded the children of this node"); ASTContext &ctx = getASTContext(); @@ -313,7 +295,7 @@ void ASTScope::expand() const { // If the declaration is a top-level code declaration, turn the source // file into a continuation. We're done. - if (isRealTopLevelCodeDecl(decl)) { + if (isa(decl)) { addActiveContinuation(this); break; } @@ -950,7 +932,6 @@ ASTScope *ASTScope::createIfNeeded(const ASTScope *parent, Decl *decl) { } case DeclKind::TopLevelCode: - if (!isRealTopLevelCodeDecl(decl)) return nullptr; return new (ctx) ASTScope(parent, cast(decl)); case DeclKind::Protocol: @@ -1153,7 +1134,6 @@ ASTScope *ASTScope::createIfNeeded(const ASTScope *parent, Stmt *stmt) { case StmtKind::Break: case StmtKind::Continue: case StmtKind::Fallthrough: - case StmtKind::IfConfig: case StmtKind::Fail: case StmtKind::Throw: // Nothing to do for these statements. diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index e3eef72b114b9..23f2275c9dcfa 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -2917,23 +2917,23 @@ class Verifier : public ASTWalker { [&]{ S->print(Out); }); } - void checkSourceRanges(IfConfigStmt *S) { - checkSourceRangesBase(S); + void checkSourceRanges(IfConfigDecl *ICD) { + checkSourceRangesBase(ICD); - SourceLoc Location = S->getStartLoc(); - for (auto &Clause : S->getClauses()) { + SourceLoc Location = ICD->getStartLoc(); + for (auto &Clause : ICD->getClauses()) { // Clause start, note that the first clause start location is the // same as that of the whole statement - if (Location == S->getStartLoc()) { + if (Location == ICD->getStartLoc()) { if (Location != Clause.Loc) { - Out << "bad start location of IfConfigStmt first clause\n"; - S->print(Out); + Out << "bad start location of IfConfigDecl first clause\n"; + ICD->print(Out); abort(); } } else { if (!Ctx.SourceMgr.isBeforeInBuffer(Location, Clause.Loc)) { - Out << "bad start location of IfConfigStmt clause\n"; - S->print(Out); + Out << "bad start location of IfConfigDecl clause\n"; + ICD->print(Out); abort(); } } @@ -2943,8 +2943,8 @@ class Verifier : public ASTWalker { Expr *Cond = Clause.Cond; if (Cond) { if (!Ctx.SourceMgr.isBeforeInBuffer(Location, Cond->getStartLoc())) { - Out << "invalid IfConfigStmt clause condition start location\n"; - S->print(Out); + Out << "invalid IfConfigDecl clause condition start location\n"; + ICD->print(Out); abort(); } Location = Cond->getEndLoc(); @@ -2959,8 +2959,8 @@ class Verifier : public ASTWalker { } if (!Ctx.SourceMgr.isBeforeInBuffer(StoredLoc, StartLocation)) { - Out << "invalid IfConfigStmt clause element start location\n"; - S->print(Out); + Out << "invalid IfConfigDecl clause element start location\n"; + ICD->print(Out); abort(); } @@ -2972,9 +2972,9 @@ class Verifier : public ASTWalker { } } - if (Ctx.SourceMgr.isBeforeInBuffer(S->getEndLoc(), Location)) { - Out << "invalid IfConfigStmt end location\n"; - S->print(Out); + if (Ctx.SourceMgr.isBeforeInBuffer(ICD->getEndLoc(), Location)) { + Out << "invalid IfConfigDecl end location\n"; + ICD->print(Out); abort(); } } diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index f87eab3c37f67..546cef5b09712 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -177,7 +177,7 @@ class Traversal : public ASTVisitorgetBody())) DS->setBody(S2); diff --git a/lib/AST/NameLookupImpl.h b/lib/AST/NameLookupImpl.h index 22da51be1c9b2..0d8ff5a4e62b8 100644 --- a/lib/AST/NameLookupImpl.h +++ b/lib/AST/NameLookupImpl.h @@ -151,10 +151,6 @@ class FindLocalVal : public StmtVisitor { visit(S->getBody()); } - void visitIfConfigStmt(IfConfigStmt * S) { - // Active members are attached to the enclosing declaration, so there's no - // need to walk anything within. - } void visitWhileStmt(WhileStmt *S) { if (!isReferencePointInRange(S->getSourceRange())) return; diff --git a/lib/AST/SourceEntityWalker.cpp b/lib/AST/SourceEntityWalker.cpp index 9b423d43ee1d4..19383e645735b 100644 --- a/lib/AST/SourceEntityWalker.cpp +++ b/lib/AST/SourceEntityWalker.cpp @@ -141,6 +141,15 @@ bool SemaAnnotator::walkToDeclPre(Decl *D) { if (Loc.isValid()) NameLen = PrecD->getName().getLength(); + } else if (auto *ICD = dyn_cast(D)) { + if (SEWalker.shouldWalkInactiveConfigRegion()) { + for (auto Clause : ICD->getClauses()) { + for (auto Member : Clause.Elements) { + Member.walk(*this); + } + } + return false; + } } else { return true; } @@ -198,17 +207,6 @@ bool SemaAnnotator::walkToDeclPost(Decl *D) { std::pair SemaAnnotator::walkToStmtPre(Stmt *S) { bool TraverseChildren = SEWalker.walkToStmtPre(S); if (TraverseChildren) { - if (SEWalker.shouldWalkInactiveConfigRegion()) { - if (auto *ICS = dyn_cast(S)) { - TraverseChildren = false; - for (auto Clause : ICS->getClauses()) { - for (auto Member : Clause.Elements) { - Member.walk(*this); - } - } - } - } - if (auto *DeferS = dyn_cast(S)) { if (auto *FD = DeferS->getTempDecl()) { auto *RetS = FD->getBody()->walk(*this); diff --git a/lib/IDE/Formatting.cpp b/lib/IDE/Formatting.cpp index 35499c1980d93..2952cbcf881e9 100644 --- a/lib/IDE/Formatting.cpp +++ b/lib/IDE/Formatting.cpp @@ -652,7 +652,7 @@ class FormatWalker : public SourceEntityWalker { /// Sometimes, target is a part of "parent", for instance, "#else" is a part /// of an ifconfigstmt, so that ifconfigstmt is not really the parent of "#else". bool isTargetPartOf(swift::ASTWalker::ParentTy Parent) { - if (auto Conf = dyn_cast_or_null(Parent.getAsStmt())) { + if (auto Conf = dyn_cast_or_null(Parent.getAsDecl())) { for (auto Clause : Conf->getClauses()) { if (Clause.Loc == TargetLocation) return true; diff --git a/lib/IDE/SyntaxModel.cpp b/lib/IDE/SyntaxModel.cpp index 048cb56a10769..ad413b35ea6da 100644 --- a/lib/IDE/SyntaxModel.cpp +++ b/lib/IDE/SyntaxModel.cpp @@ -336,7 +336,7 @@ class ModelASTWalker : public ASTWalker { bool searchForURL(CharSourceRange Range); bool findFieldsInDocCommentLine(SyntaxNode Node); bool findFieldsInDocCommentBlock(SyntaxNode Node); - bool isVisitedBeforeInIfConfigStmt(ASTNode Node) { + bool isVisitedBeforeInIfConfig(ASTNode Node) { return VisitedNodesInsideIfConfig.count(Node) > 0; } }; @@ -438,7 +438,7 @@ void ModelASTWalker::visitSourceFile(SourceFile &SrcFile, } std::pair ModelASTWalker::walkToExprPre(Expr *E) { - if (isVisitedBeforeInIfConfigStmt(E)) + if (isVisitedBeforeInIfConfig(E)) return {false, E}; if (E->isImplicit()) @@ -556,7 +556,7 @@ void ModelASTWalker::handleStmtCondition(StmtCondition cond) { std::pair ModelASTWalker::walkToStmtPre(Stmt *S) { - if (isVisitedBeforeInIfConfigStmt(S)) { + if (isVisitedBeforeInIfConfig(S)) { return {false, S}; } auto addExprElem = [&](SyntaxStructureElementKind K, const Expr *Elem, @@ -699,39 +699,6 @@ std::pair ModelASTWalker::walkToStmtPre(Stmt *S) { pushStructureNode(SN, SW); } - } else if (auto ConfigS = dyn_cast(S)) { - for (auto &Clause : ConfigS->getClauses()) { - unsigned TokLen; - if (&Clause == &*ConfigS->getClauses().begin()) - TokLen = 3; // '#if' - else if (Clause.Cond == nullptr) - TokLen = 5; // '#else' - else - TokLen = 7; // '#elseif' - if (!passNonTokenNode({SyntaxNodeKind::BuildConfigKeyword, - CharSourceRange(Clause.Loc, TokLen) })) - return { false, nullptr }; - - if (Clause.Cond && !annotateIfConfigConditionIdentifiers(Clause.Cond)) - return { false, nullptr }; - - for (auto &Element : Clause.Elements) { - if (auto *E = Element.dyn_cast()) { - E->walk(*this); - } else if (auto *S = Element.dyn_cast()) { - S->walk(*this); - } else { - Element.get()->walk(*this); - } - VisitedNodesInsideIfConfig.insert(Element); - } - } - - if (!ConfigS->hadMissingEnd()) - if (!passNonTokenNode({ SyntaxNodeKind::BuildConfigKeyword, - CharSourceRange(ConfigS->getEndLoc(), 6/*'#endif'*/) })) - return { false, nullptr }; - } else if (auto *DeferS = dyn_cast(S)) { if (auto *FD = DeferS->getTempDecl()) { auto *RetS = FD->getBody()->walk(*this); @@ -752,7 +719,7 @@ Stmt *ModelASTWalker::walkToStmtPost(Stmt *S) { } bool ModelASTWalker::walkToDeclPre(Decl *D) { - if (isVisitedBeforeInIfConfigStmt(D)) + if (isVisitedBeforeInIfConfig(D)) return false; if (D->isImplicit()) return false; @@ -916,9 +883,16 @@ bool ModelASTWalker::walkToDeclPre(Decl *D) { if (Clause.Cond && !annotateIfConfigConditionIdentifiers(Clause.Cond)) return false; - for (auto *D : Clause.Elements) - if (D->walk(*this)) - return false; + for (auto &Element : Clause.Elements) { + if (auto *E = Element.dyn_cast()) { + E->walk(*this); + } else if (auto *S = Element.dyn_cast()) { + S->walk(*this); + } else { + Element.get()->walk(*this); + } + VisitedNodesInsideIfConfig.insert(Element); + } } if (!ConfigD->hadMissingEnd()) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 20983e57b1226..013778adcaccd 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2100,13 +2100,40 @@ Parser::parseDecl(ParseDeclOptions Flags, } if (Tok.is(tok::pound_if)) { - auto IfConfigResult = parseDeclIfConfig(Flags); + auto IfConfigResult = parseIfConfig( + [&](SmallVectorImpl &Decls, bool IsActive) { + Optional scope; + if (!IsActive) + scope.emplace(this, getScopeInfo().getCurrentScope()->getKind(), + /*inactiveConfigBlock=*/true); + + ParserStatus Status; + bool PreviousHadSemi = true; + while (Tok.isNot(tok::pound_else, tok::pound_endif, tok::pound_elseif, + tok::eof)) { + if (Tok.is(tok::r_brace)) { + diagnose(Tok.getLoc(), + diag::unexpected_rbrace_in_conditional_compilation_block); + // If we see '}', following declarations don't look like belong to + // the current decl context; skip them. + skipUntilConditionalBlockClose(); + break; + } + Status |= parseDeclItem(PreviousHadSemi, Flags, + [&](Decl *D) {Decls.emplace_back(D);}); + } + }); + if (auto ICD = IfConfigResult.getPtrOrNull()) { // The IfConfigDecl is ahead of its members in source order. Handler(ICD); // Copy the active members into the entries list. - for (auto activeMember : ICD->getActiveMembers()) { - Handler(activeMember); + for (auto activeMember : ICD->getActiveClauseElements()) { + auto *D = activeMember.get(); + if (isa(D)) + // Don't hoist nested '#if'. + continue; + Handler(D); } } return IfConfigResult; diff --git a/lib/Parse/ParseIfConfig.cpp b/lib/Parse/ParseIfConfig.cpp index c0071c9cc134c..9f8b5d661da9b 100644 --- a/lib/Parse/ParseIfConfig.cpp +++ b/lib/Parse/ParseIfConfig.cpp @@ -549,21 +549,20 @@ static bool isVersionIfConfigCondition(Expr *Condition) { } // end anonymous namespace -/// Parse and populate a list of #if/#elseif/#else/#endif clauses. +/// Parse and populate a #if ... #endif directive. /// Delegate callback function to parse elements in the blocks. -template -static ParserStatus parseIfConfig( - Parser &P, SmallVectorImpl> &Clauses, - SourceLoc &EndLoc, bool HadMissingEnd, - llvm::function_ref &, bool)> parseElements) { +ParserResult Parser::parseIfConfig( + llvm::function_ref &, bool)> parseElements) { + + SmallVector Clauses; Parser::StructureMarkerRAII ParsingDecl( - P, P.Tok.getLoc(), Parser::StructureMarkerKind::IfConfig); + *this, Tok.getLoc(), Parser::StructureMarkerKind::IfConfig); bool foundActive = false; bool isVersionCondition = false; while (1) { - bool isElse = P.Tok.is(tok::pound_else); - SourceLoc ClauseLoc = P.consumeToken(); + bool isElse = Tok.is(tok::pound_else); + SourceLoc ClauseLoc = consumeToken(); Expr *Condition = nullptr; bool isActive = false; @@ -571,110 +570,56 @@ static ParserStatus parseIfConfig( if (isElse) { isActive = !foundActive; } else { - llvm::SaveAndRestore S(P.InPoundIfEnvironment, true); - ParserResult Result = P.parseExprSequence(diag::expected_expr, + llvm::SaveAndRestore S(InPoundIfEnvironment, true); + ParserResult Result = parseExprSequence(diag::expected_expr, /*isBasic*/true, /*isForDirective*/true); if (Result.isNull()) return makeParserError(); Condition = Result.get(); - if (validateIfConfigCondition(Condition, P.Context, P.Diags)) { + if (validateIfConfigCondition(Condition, Context, Diags)) { // Error in the condition; isActive = false; isVersionCondition = false; } else if (!foundActive) { // Evaludate the condition only if we haven't found any active one. - isActive = evaluateIfConfigCondition(Condition, P.Context); + isActive = evaluateIfConfigCondition(Condition, Context); isVersionCondition = isVersionIfConfigCondition(Condition); } } foundActive |= isActive; - if (!P.Tok.isAtStartOfLine() && P.Tok.isNot(tok::eof)) { - P.diagnose(P.Tok.getLoc(), - diag::extra_tokens_conditional_compilation_directive); + if (!Tok.isAtStartOfLine() && Tok.isNot(tok::eof)) { + diagnose(Tok.getLoc(), + diag::extra_tokens_conditional_compilation_directive); } // Parse elements - SmallVector Elements; + SmallVector Elements; if (isActive || !isVersionCondition) { parseElements(Elements, isActive); } else { - DiagnosticTransaction DT(P.Diags); - P.skipUntilConditionalBlockClose(); + DiagnosticTransaction DT(Diags); + skipUntilConditionalBlockClose(); DT.abort(); } - Clauses.push_back(IfConfigClause(ClauseLoc, Condition, - P.Context.AllocateCopy(Elements), - isActive)); + Clauses.emplace_back(ClauseLoc, Condition, + Context.AllocateCopy(Elements), isActive); - if (P.Tok.isNot(tok::pound_elseif, tok::pound_else)) + if (Tok.isNot(tok::pound_elseif, tok::pound_else)) break; if (isElse) - P.diagnose(P.Tok, diag::expected_close_after_else_directive); + diagnose(Tok, diag::expected_close_after_else_directive); } - HadMissingEnd = P.parseEndIfDirective(EndLoc); - return makeParserSuccess(); -} - -/// Parse #if ... #endif in declarations position. -ParserResult Parser::parseDeclIfConfig(ParseDeclOptions Flags) { - SmallVector, 4> Clauses; SourceLoc EndLoc; - bool HadMissingEnd = false; - auto Status = parseIfConfig( - *this, Clauses, EndLoc, HadMissingEnd, - [&](SmallVectorImpl &Decls, bool IsActive) { - Optional scope; - if (!IsActive) - scope.emplace(this, getScopeInfo().getCurrentScope()->getKind(), - /*inactiveConfigBlock=*/true); - - ParserStatus Status; - bool PreviousHadSemi = true; - while (Tok.isNot(tok::pound_else, tok::pound_endif, tok::pound_elseif, - tok::eof)) { - if (Tok.is(tok::r_brace)) { - diagnose(Tok.getLoc(), - diag::unexpected_rbrace_in_conditional_compilation_block); - // If we see '}', following declarations don't look like belong to - // the current decl context; skip them. - skipUntilConditionalBlockClose(); - break; - } - Status |= parseDeclItem(PreviousHadSemi, Flags, - [&](Decl *D) {Decls.push_back(D);}); - } - }); - if (Status.isError()) - return makeParserErrorResult(); + bool HadMissingEnd = parseEndIfDirective(EndLoc); - IfConfigDecl *ICD = new (Context) IfConfigDecl(CurDeclContext, - Context.AllocateCopy(Clauses), - EndLoc, HadMissingEnd); - return makeParserResult(ICD); -} - -/// Parse #if ... #endif in statements position. -ParserResult Parser::parseStmtIfConfig(BraceItemListKind Kind) { - SmallVector, 4> Clauses; - SourceLoc EndLoc; - bool HadMissingEnd = false; - auto Status = parseIfConfig( - *this, Clauses, EndLoc, HadMissingEnd, - [&](SmallVectorImpl &Elements, bool IsActive) { - parseBraceItems(Elements, Kind, IsActive - ? BraceItemListKind::ActiveConditionalBlock - : BraceItemListKind::InactiveConditionalBlock); - }); - if (Status.isError()) - return makeParserErrorResult(); - - auto *ICS = new (Context) IfConfigStmt(Context.AllocateCopy(Clauses), + auto *ICD = new (Context) IfConfigDecl(CurDeclContext, + Context.AllocateCopy(Clauses), EndLoc, HadMissingEnd); - return makeParserResult(ICS); + return makeParserResult(ICD); } diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index cd13134b41772..1591ef3678f95 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -293,11 +293,12 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl &Entries, Entries.push_back(D); TmpDecls.clear(); } else if (Tok.is(tok::pound_if)) { - SourceLoc StartLoc = Tok.getLoc(); - - // We'll want to parse the #if block, but not wrap it in a top-level - // code declaration immediately. - auto IfConfigResult = parseStmtIfConfig(Kind); + auto IfConfigResult = parseIfConfig( + [&](SmallVectorImpl &Elements, bool IsActive) { + parseBraceItems(Elements, Kind, IsActive + ? BraceItemListKind::ActiveConditionalBlock + : BraceItemListKind::InactiveConditionalBlock); + }); if (IfConfigResult.isParseError()) { NeedParseErrorRecovery = true; @@ -311,19 +312,14 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl &Entries, continue; } - // Add the #if block itself as a TLCD if necessary - if (Kind == BraceItemListKind::TopLevelCode) { - auto *TLCD = new (Context) TopLevelCodeDecl(CurDeclContext); - auto Brace = BraceStmt::create(Context, StartLoc, - {Result}, PreviousLoc); - TLCD->setBody(Brace); - Entries.push_back(TLCD); - } else { - Entries.push_back(Result); - } + // Add the #if block itself + Entries.push_back(Result); - IfConfigStmt *ICS = cast(Result.get()); - for (auto &Entry : ICS->getActiveClauseElements()) { + IfConfigDecl *ICD = cast(Result.get()); + for (auto &Entry : ICD->getActiveClauseElements()) { + if (Entry.is() && isa(Entry.get())) + // Don't hoist nested '#if'. + continue; Entries.push_back(Entry); if (Entry.is()) { Entry.get()->setEscapedFromIfConfig(true); diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index d76e0dade1423..56110a52b9f55 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -200,8 +200,8 @@ void StmtEmitter::visitBraceStmt(BraceStmt *S) { for (auto &ESD : S->getElements()) { - if (auto S = ESD.dyn_cast()) - if (isa(S)) + if (auto D = ESD.dyn_cast()) + if (isa(D)) continue; // If we ever reach an unreachable point, stop emitting statements and issue @@ -541,12 +541,6 @@ void StmtEmitter::visitGuardStmt(GuardStmt *S) { SGF.emitStmtCondition(S->getCond(), bodyBB, S); } - -void StmtEmitter::visitIfConfigStmt(IfConfigStmt *S) { - // Active members are attached to the enclosing declaration, so there's no - // need to walk anything within. -} - void StmtEmitter::visitWhileStmt(WhileStmt *S) { LexicalScope condBufferScope(SGF, S); diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index e597c39463c53..55deeda328afa 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -1975,6 +1975,11 @@ class VarDeclUsageChecker : public ASTWalker { if (isa(D)) return false; + // The body of #if clauses are not walked into, we need custom processing + // for them. + if (auto *ICD = dyn_cast(D)) + handleIfConfig(ICD); + // If this is a VarDecl, then add it to our list of things to track. if (auto *vd = dyn_cast(D)) if (shouldTrackVarDecl(vd)) { @@ -2045,15 +2050,10 @@ class VarDeclUsageChecker : public ASTWalker { std::pair walkToExprPre(Expr *E) override; /// handle #if directives. - void handleIfConfig(IfConfigStmt *ICS); + void handleIfConfig(IfConfigDecl *ICD); /// Custom handling for statements. std::pair walkToStmtPre(Stmt *S) override { - // The body of #if statements are not walked into, we need custom processing - // for them. - if (auto *ICS = dyn_cast(S)) - handleIfConfig(ICS); - // Keep track of an association between vardecls and the StmtCondition that // they are bound in for IfStmt, GuardStmt, WhileStmt, etc. if (auto LCS = dyn_cast(S)) { @@ -2396,7 +2396,7 @@ std::pair VarDeclUsageChecker::walkToExprPre(Expr *E) { /// handle #if directives. All of the active clauses are already walked by the /// AST walker, but we also want to handle the inactive ones to avoid false /// positives. -void VarDeclUsageChecker::handleIfConfig(IfConfigStmt *ICS) { +void VarDeclUsageChecker::handleIfConfig(IfConfigDecl *ICD) { struct ConservativeDeclMarker : public ASTWalker { VarDeclUsageChecker &VDUC; ConservativeDeclMarker(VarDeclUsageChecker &VDUC) : VDUC(VDUC) {} @@ -2411,7 +2411,7 @@ void VarDeclUsageChecker::handleIfConfig(IfConfigStmt *ICS) { } }; - for (auto &clause : ICS->getClauses()) { + for (auto &clause : ICD->getClauses()) { // Active clauses are handled by the normal AST walk. if (clause.isActive) continue; diff --git a/lib/Sema/TypeCheckError.cpp b/lib/Sema/TypeCheckError.cpp index aaa82a231a08c..4271e0f38207c 100644 --- a/lib/Sema/TypeCheckError.cpp +++ b/lib/Sema/TypeCheckError.cpp @@ -192,9 +192,14 @@ class ErrorHandlingWalker : public ASTWalker { Impl &asImpl() { return *static_cast(this); } public: bool walkToDeclPre(Decl *D) override { + ShouldRecurse_t recurse = ShouldRecurse; // Skip the implementations of all local declarations... except // PBD. We should really just have a PatternBindingStmt. - return isa(D); + if (auto ic = dyn_cast(D)) + recurse = asImpl().checkIfConfig(ic); + else if (!isa(D)) + recurse = ShouldNotRecurse; + return bool(recurse); } std::pair walkToExprPre(Expr *E) override { @@ -230,8 +235,6 @@ class ErrorHandlingWalker : public ASTWalker { recurse = asImpl().checkDoCatch(doCatch); } else if (auto thr = dyn_cast(S)) { recurse = asImpl().checkThrow(thr); - } else if (auto ic = dyn_cast(S)) { - recurse = asImpl().checkIfConfig(ic); } else { assert(!isa(S)); } @@ -606,7 +609,7 @@ class ApplyClassifier { return ShouldRecurse; } - ShouldRecurse_t checkIfConfig(IfConfigStmt *S) { + ShouldRecurse_t checkIfConfig(IfConfigDecl *D) { return ShouldRecurse; } @@ -1431,7 +1434,7 @@ class CheckErrorCoverage : public ErrorHandlingWalker { return !type || type->hasError() ? ShouldNotRecurse : ShouldRecurse; } - ShouldRecurse_t checkIfConfig(IfConfigStmt *S) { + ShouldRecurse_t checkIfConfig(IfConfigDecl *ICD) { // Check the inactive regions of a #if block to disable warnings that may // be due to platform specific code. struct ConservativeThrowChecker : public ASTWalker { @@ -1452,7 +1455,7 @@ class CheckErrorCoverage : public ErrorHandlingWalker { } }; - for (auto &clause : S->getClauses()) { + for (auto &clause : ICD->getClauses()) { // Active clauses are handled by the normal AST walk. if (clause.isActive) continue; diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index c881364476de7..fa4705b1e7826 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -512,14 +512,6 @@ class StmtChecker : public StmtVisitor { return GS; } - Stmt *visitIfConfigStmt(IfConfigStmt *ICS) { - - // Active members are attached to the enclosing declaration, so there's no - // need to walk anything within. - - return ICS; - } - Stmt *visitDoStmt(DoStmt *DS) { AddLabeledStmt loopNest(*this, DS); Stmt *S = DS->getBody(); diff --git a/lib/Syntax/LegacyASTTransformer.cpp b/lib/Syntax/LegacyASTTransformer.cpp index 57372611f3ceb..26f9bc8b040cd 100644 --- a/lib/Syntax/LegacyASTTransformer.cpp +++ b/lib/Syntax/LegacyASTTransformer.cpp @@ -598,13 +598,6 @@ LegacyASTTransformer::visitFallthroughStmt(FallthroughStmt *S, return SyntaxFactory::makeFallthroughStmt(FallthroughToken).Root; } -RC -LegacyASTTransformer::visitIfConfigStmt(IfConfigStmt *S, - const SyntaxData *Parent, - const CursorIndex IndexInParent) { - return getUnknownStmt(S); -} - RC LegacyASTTransformer::visitFailStmt(FailStmt *S, const SyntaxData *Parent, diff --git a/test/IDE/print_ast_non_typechecked.swift b/test/IDE/print_ast_non_typechecked.swift index 90b2d463059dc..dfc4c56522f86 100644 --- a/test/IDE/print_ast_non_typechecked.swift +++ b/test/IDE/print_ast_non_typechecked.swift @@ -4,3 +4,24 @@ class C { // RUN: %target-swift-ide-test -print-ast-not-typechecked -source-filename %s | %FileCheck %s -check-prefix=CHECK1 // CHECK1: func foo(s: Int) + +#if BAR +func bar() {} +#elseif BAZ +func baz() {} +#else +func qux() {} +#endif + +// CHECK1: {{^}}#if /* condition */ +// CHECK1: {{^}} func bar() { +// CHECK1: {{^}} } +// CHECK1: {{^}}#elseif /* condition */ +// CHECK1: {{^}} func baz() { +// CHECK1: {{^}} } +// CHECK1: {{^}}#else +// CHECK1: {{^}} func qux() { +// CHECK1: {{^}} } +// CHECK1: {{^}}#endif +// CHECK1: {{^}}func qux() { +// CHECK1: {{^}}} diff --git a/test/IDE/print_source_file_interface_1.swift.result b/test/IDE/print_source_file_interface_1.swift.result index f1f48735252e2..fbf08d5f43ce3 100644 --- a/test/IDE/print_source_file_interface_1.swift.result +++ b/test/IDE/print_source_file_interface_1.swift.result @@ -3,8 +3,6 @@ // More blah blah. - - import Swift internal class FooDisabled { diff --git a/test/Parse/ConditionalCompilation/pound-if-top-level-4.swift b/test/Parse/ConditionalCompilation/pound-if-top-level-4.swift new file mode 100644 index 0000000000000..c6d53e1e3d7b7 --- /dev/null +++ b/test/Parse/ConditionalCompilation/pound-if-top-level-4.swift @@ -0,0 +1,21 @@ +// RUN: %target-typecheck-verify-swift + +// https://bugs.swift.org/browse/SR-4426 +// '#if' in top-level code that contains only decls should not disturb forward reference. + +typealias A = B + +#if false +func foo() {} +#endif + +struct B {} + +// If '#if' contains active non-decls, we don't support forward reference. +typealias C = D // expected-error {{use of undeclared type 'D'}} + +#if true +print("ok") +#endif + +struct D {} diff --git a/test/Parse/ConditionalCompilation/stmt_in_type.swift b/test/Parse/ConditionalCompilation/stmt_in_type.swift new file mode 100644 index 0000000000000..154856a648b7d --- /dev/null +++ b/test/Parse/ConditionalCompilation/stmt_in_type.swift @@ -0,0 +1,13 @@ +// RUN: %target-typecheck-verify-swift -D FOO + +// Test case for statements in #if block in types. + +func foo() {} + +struct S1 { // expected-note 2 {{in declaration of 'S1'}} +#if FOO + return 1; // expected-error {{expected declaration}} +#elseif BAR + foo(); // expected-error {{expected declaration}} +#endif +} diff --git a/test/Parse/ConditionalCompilation/toplevel_parseaslibrary.swift b/test/Parse/ConditionalCompilation/toplevel_parseaslibrary.swift new file mode 100644 index 0000000000000..d946620825a5f --- /dev/null +++ b/test/Parse/ConditionalCompilation/toplevel_parseaslibrary.swift @@ -0,0 +1,11 @@ +// RUN: %target-typecheck-verify-swift -parse-as-library -D FOO + +// '-parse-as-library' doesn't allow exprssions nor statements in #if blocks. + +func foo() {} + +#if FOO + foo() // expected-error {{expressions are not allowed at the top level}} +#else + if true { foo() } // expected-error {{statements are not allowed at the top level}} +#endif