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
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to