brad.king updated this revision to Diff 113561.
brad.king edited the summary of this revision.
brad.king added a comment.
Herald added a subscriber: mgorny.
Updated diff to add a test case.
https://reviews.llvm.org/D37381
Files:
lib/Sema/SemaDeclCXX.cpp
unittests/Sema/CMakeLists.txt
unittests/Sema/SuppressAllDiagnosticsTest.cpp
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 <queue>
+
+namespace {
+
+// \brief ASTConsumer that forces definition of special members.
+class ForceImplicitMembersASTConsumer : public clang::ASTConsumer {
+ clang::CompilerInstance &CI;
+ std::queue<clang::CXXRecordDecl *> 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<clang::CXXMethodDecl>(D);
+ if (M && !M->isDeleted() && !M->isInvalidDecl()) {
+ bool Mark = false;
+ auto *C = clang::dyn_cast<clang::CXXConstructorDecl>(M);
+ if (C) {
+ Mark = (C->isDefaultConstructor() || C->isCopyConstructor() ||
+ C->isMoveConstructor());
+ } else if (clang::dyn_cast<clang::CXXDestructorDecl>(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<clang::CXXRecordDecl>(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<clang::CXXRecordDecl const>(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<clang::CXXMethodDecl const>(D)) {
+ if (auto const *C =
+ clang::dyn_cast<clang::CXXConstructorDecl const>(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<clang::CXXDestructorDecl>(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<clang::ASTConsumer>
+ CreateASTConsumer(clang::CompilerInstance &Compiler,
+ llvm::StringRef /* dummy */) override {
+ return llvm::make_unique<ForceImplicitMembersASTConsumer>(Compiler);
+ }
+};
+
+// Test semantic checking of C++98 implicit members
+// when diagnostics are suppressed.
+TEST(SuppressAllDiagnostics, ForceImplicitMembers98) {
+ auto Action = llvm::make_unique<ForceImplicitMembersAction>();
+ std::vector<std::string> 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<ForceImplicitMembersAction>();
+ std::vector<std::string> 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
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: 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<Stmt>());
+ 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<Stmt>());
+ 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()
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits