Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -10423,6 +10423,7 @@ assert(ClassDecl && "DefineImplicitDefaultConstructor - invalid constructor"); SynthesizedFunctionScope Scope(*this, Constructor); + DiagnosticErrorTrap Trap(Diags); // The exception specification is needed because we are defining the // function. @@ -10433,7 +10434,8 @@ // Add a context note for diagnostics produced after this point. Scope.addContextNote(CurrentLocation); - if (SetCtorInitializers(Constructor, /*AnyErrors=*/false)) { + if (SetCtorInitializers(Constructor, /*AnyErrors=*/false) || + Trap.hasErrorOccurred()) { Constructor->setInvalidDecl(); return; } @@ -10558,6 +10560,7 @@ // Initializations are performed "as if by a defaulted default constructor", // so enter the appropriate scope. SynthesizedFunctionScope Scope(*this, Constructor); + DiagnosticErrorTrap Trap(Diags); // The exception specification is needed because we are defining the // function. @@ -10612,7 +10615,8 @@ // We now proceed as if for a defaulted default constructor, with the relevant // initializers replaced. - if (SetCtorInitializers(Constructor, /*AnyErrors*/false, Inits)) { + if (SetCtorInitializers(Constructor, /*AnyErrors*/false, Inits) || + Trap.hasErrorOccurred()) { Constructor->setInvalidDecl(); return; } @@ -10701,6 +10705,7 @@ assert(ClassDecl && "DefineImplicitDestructor - invalid destructor"); SynthesizedFunctionScope Scope(*this, Destructor); + DiagnosticErrorTrap Trap(Diags); // The exception specification is needed because we are defining the // function. @@ -10714,7 +10719,7 @@ MarkBaseAndMemberDestructorsReferenced(Destructor->getLocation(), Destructor->getParent()); - if (CheckDestructor(Destructor)) { + if (CheckDestructor(Destructor) || Trap.hasErrorOccurred()) { Destructor->setInvalidDecl(); return; } @@ -11351,6 +11356,7 @@ } SynthesizedFunctionScope Scope(*this, CopyAssignOperator); + DiagnosticErrorTrap Trap(Diags); // The exception specification is needed because we are defining the // function. @@ -11513,8 +11519,11 @@ StmtResult Return = BuildReturnStmt(Loc, ThisObj.get()); if (Return.isInvalid()) Invalid = true; - else + else { Statements.push_back(Return.getAs()); + if (Trap.hasErrorOccurred()) + Invalid = true; + } } if (Invalid) { @@ -11725,6 +11734,7 @@ checkMoveAssignmentForRepeatedMove(*this, ClassDecl, CurrentLocation); SynthesizedFunctionScope Scope(*this, MoveAssignOperator); + DiagnosticErrorTrap Trap(Diags); // The exception specification is needed because we are defining the // function. @@ -11884,8 +11894,11 @@ StmtResult Return = BuildReturnStmt(Loc, ThisObj.get()); if (Return.isInvalid()) Invalid = true; - else + else { Statements.push_back(Return.getAs()); + if (Trap.hasErrorOccurred()) + Invalid = true; + } } if (Invalid) { @@ -12003,6 +12016,7 @@ assert(ClassDecl && "DefineImplicitCopyConstructor - invalid constructor"); SynthesizedFunctionScope Scope(*this, CopyConstructor); + DiagnosticErrorTrap Trap(Diags); // The exception specification is needed because we are defining the // function. @@ -12020,7 +12034,8 @@ if (getLangOpts().CPlusPlus11 && CopyConstructor->isImplicit()) diagnoseDeprecatedCopyOperation(*this, CopyConstructor); - if (SetCtorInitializers(CopyConstructor, /*AnyErrors=*/false)) { + if (SetCtorInitializers(CopyConstructor, /*AnyErrors=*/false) || + Trap.hasErrorOccurred()) { CopyConstructor->setInvalidDecl(); } else { SourceLocation Loc = CopyConstructor->getLocEnd().isValid() @@ -12126,6 +12141,7 @@ assert(ClassDecl && "DefineImplicitMoveConstructor - invalid constructor"); SynthesizedFunctionScope Scope(*this, MoveConstructor); + DiagnosticErrorTrap Trap(Diags); // The exception specification is needed because we are defining the // function. @@ -12136,7 +12152,8 @@ // Add a context note for diagnostics produced after this point. Scope.addContextNote(CurrentLocation); - if (SetCtorInitializers(MoveConstructor, /*AnyErrors=*/false)) { + if (SetCtorInitializers(MoveConstructor, /*AnyErrors=*/false) || + Trap.hasErrorOccurred()) { MoveConstructor->setInvalidDecl(); } else { SourceLocation Loc = MoveConstructor->getLocEnd().isValid() Index: unittests/Sema/CMakeLists.txt =================================================================== --- unittests/Sema/CMakeLists.txt +++ unittests/Sema/CMakeLists.txt @@ -4,6 +4,7 @@ add_clang_unittest(SemaTests ExternalSemaSourceTest.cpp + SuppressAllDiagnosticsTest.cpp ) target_link_libraries(SemaTests Index: unittests/Sema/SuppressAllDiagnosticsTest.cpp =================================================================== --- /dev/null +++ unittests/Sema/SuppressAllDiagnosticsTest.cpp @@ -0,0 +1,205 @@ +//=== unittests/Sema/SuppressAllDiagnosticsTest.cpp -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Sema/Sema.h" +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" + +#include + +namespace { + +// \brief ASTConsumer that forces definition of special members. +class ForceImplicitMembersASTConsumer : public clang::ASTConsumer { + clang::CompilerInstance &CI; + std::queue Classes; + +public: + ForceImplicitMembersASTConsumer(clang::CompilerInstance &CI) : CI(CI) {} + + void AddImplicitMembers(clang::CXXRecordDecl *RD) { + auto &Sema = CI.getSema(); + + // Declare implicit members. + Sema.ForceDeclarationOfImplicitMembers(RD); + + // Force definition of implicit members. + for (auto *D : RD->decls()) { + auto *M = clang::dyn_cast(D); + if (M && !M->isDeleted() && !M->isInvalidDecl()) { + bool Mark = false; + auto *C = clang::dyn_cast(M); + if (C) { + Mark = (C->isDefaultConstructor() || C->isCopyConstructor() || + C->isMoveConstructor()); + } else if (clang::dyn_cast(M)) { + Mark = true; + } else { + Mark = + (M->isCopyAssignmentOperator() || M->isMoveAssignmentOperator()); + } + if (Mark) { + // Ensure the member is defined. + Sema.MarkFunctionReferenced(clang::SourceLocation(), M); + if (C && C->isDefaulted() && C->isDefaultConstructor() && + C->isTrivial() && !C->isUsed(false)) { + // Clang does not build the definition of trivial constructors + // until they are used. Force semantic checking. + Sema.DefineImplicitDefaultConstructor(clang::SourceLocation(), C); + } + // Finish implicitly instantiated member. + Sema.PerformPendingInstantiations(); + } + } + } + } + + void HandleTagDeclDefinition(clang::TagDecl *D) { + if (auto *RD = clang::dyn_cast(D)) { + Classes.push(RD); + } + } + + void HandleTranslationUnit(clang::ASTContext &CTX) { + auto &Sema = CI.getSema(); + + // Finish the translation unit as written in the source. + Sema.PerformPendingInstantiations(); + + // Suppress diagnostics while we force more definitions. + Sema.getDiagnostics().setSuppressAllDiagnostics(true); + + // Force addition of implicit members to classes. + while (!Classes.empty()) { + auto *RD = Classes.front(); + Classes.pop(); + AddImplicitMembers(RD); + } + Sema.ActOnEndOfTranslationUnit(); + + // Verify that the "derived" class has the implicit members we expect. + auto const *TU = CTX.getTranslationUnitDecl(); + auto &Ids = CI.getPreprocessor().getIdentifierTable(); + auto const &result = + TU->lookup(clang::DeclarationName(&Ids.get("derived"))); + bool CheckedRecordDecl = false; + for (auto const *N : result) { + if (auto const *RD = clang::dyn_cast(N)) { + CheckedRecordDecl = true; + CheckImplicitMembers(RD); + } + } + ASSERT_TRUE(CheckedRecordDecl); + } + + void CheckImplicitMembers(clang::CXXRecordDecl const *RD) { + bool CheckedDefaultConstructor = false; + bool CheckedDestructor = false; + bool CheckedCopyConstructor = false; + bool CheckedMoveConstructor = false; + bool CheckedCopyAssignment = false; + bool CheckedMoveAssignment = false; + for (auto const *D : RD->decls()) { + if (auto const *M = clang::dyn_cast(D)) { + if (auto const *C = + clang::dyn_cast(M)) { + if (C->isDefaultConstructor()) { + CheckedDefaultConstructor = true; + ASSERT_FALSE(C->isDeleted()); + ASSERT_FALSE(C->isInvalidDecl()); + } else if (C->isCopyConstructor()) { + CheckedCopyConstructor = true; + if (CI.getLangOpts().CPlusPlus11) + ASSERT_TRUE(C->isDeleted()); + else + ASSERT_TRUE(C->isInvalidDecl()); + } else if (C->isMoveConstructor()) { + CheckedMoveConstructor = true; + ASSERT_TRUE(C->isDeleted()); + } + } else if (clang::dyn_cast(M)) { + CheckedDestructor = true; + ASSERT_FALSE(M->isDeleted()); + ASSERT_FALSE(M->isInvalidDecl()); + } else if (M->isCopyAssignmentOperator()) { + CheckedCopyAssignment = true; + ASSERT_TRUE(M->isDeleted() || M->isInvalidDecl()); + } else if (M->isMoveAssignmentOperator()) { + CheckedMoveAssignment = true; + if (CI.getLangOpts().CPlusPlus11) + ASSERT_TRUE(M->isDeleted()); + else + ASSERT_TRUE(M->isInvalidDecl()); + } + } + } + ASSERT_TRUE(CheckedDefaultConstructor); + ASSERT_TRUE(CheckedDestructor); + ASSERT_TRUE(CheckedCopyConstructor); + ASSERT_TRUE(CheckedCopyAssignment); + if (CI.getLangOpts().CPlusPlus11) { + ASSERT_TRUE(CheckedMoveConstructor); + ASSERT_TRUE(CheckedMoveAssignment); + } + } +}; + +// \brief Creates a ForceImplicitMembersASTConsumer. +class ForceImplicitMembersAction : public clang::SyntaxOnlyAction { + +protected: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &Compiler, + llvm::StringRef /* dummy */) override { + return llvm::make_unique(Compiler); + } +}; + +// Test semantic checking of C++98 implicit members +// when diagnostics are suppressed. +TEST(SuppressAllDiagnostics, ForceImplicitMembers98) { + auto Action = llvm::make_unique(); + std::vector Args(1, "-std=c++98"); + ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs( + Action.release(), "class base {\n" + "protected:\n" + " base();\n" + " ~base();\n" + "private:\n" + " base(base const&);\n" + " base& operator=(base const&);\n" + "};\n" + "class derived : public base {};\n", + Args)); +} + +// Test semantic checking of C++11 implicit members +// when diagnostics are suppressed. +TEST(SuppressAllDiagnostics, ForceImplicitMembers11) { + auto Action = llvm::make_unique(); + std::vector Args(1, "-std=c++11"); + ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs( + Action.release(), "class base {\n" + "protected:\n" + " base();\n" + " ~base();\n" + " base(base&&) = delete;\n" + " base(base const&) = delete;\n" + " base& operator=(base&&) = delete;\n" + " base& operator=(base const&) = delete;\n" + "};\n" + "class derived : public base {};\n", + Args)); +} + +} // anonymous namespace