Hi Folks,

I'm building a tool based on Clang that needs to test whether certain
constructs can be compiled without error (e.g. implicit class members).
In order to do this without issuing diagnostics or stopping the Clang
operation, I need to be able to suppress emission of diagnostics but
still know whether any would have been issued.

Here is a patch adding a RAII class to achieve this.  See its commit
message for details.  Basically the class saves the diagnostic state
members on construction and restores them on destruction.  While in
scope a new member tells the diagnostic engine not to emit anything
but to still maintain counts so that error traps can work.

The patch includes a new test case to cover the new functionality.
I've tested this patch on several SVN revisions of Clang for the last
few months, and most recently on r220679.

Thanks,
-Brad

>From 7aafeaeb21b367a358c74fa1a353e677cdb90a7e Mon Sep 17 00:00:00 2001
Message-Id: <7aafeaeb21b367a358c74fa1a353e677cdb90a7e.1414420325.git.brad.k...@kitware.com>
From: Brad King <[email protected]>
Date: Mon, 27 Oct 2014 10:29:37 -0400
Subject: [PATCH] Add DiagnosticSuppressionScope RAII class

Create a RAII class to suppress emission of diagnostics and reset their
counts on scope exit.  Maintain counts within the scope to allow nested
DiagnosticErrorTrap instances to work.  Assert that no one tries to
reset a DiagnosticsEngine within such a scope.

This RAII class will allow clients to test the validity of code
constructs without leaking diagnostics to surrounding contexts.

Create a DiagnosticTest unit test to cover usage of this RAII class.
---
 include/clang/Basic/Diagnostic.h   | 51 ++++++++++++++++++++++++++++++++++
 lib/Basic/Diagnostic.cpp           |  4 +++
 lib/Basic/DiagnosticIDs.cpp        |  4 +++
 unittests/Basic/CMakeLists.txt     |  1 +
 unittests/Basic/DiagnosticTest.cpp | 57 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 117 insertions(+)
 create mode 100644 unittests/Basic/DiagnosticTest.cpp

diff --git a/include/clang/Basic/Diagnostic.h b/include/clang/Basic/Diagnostic.h
index 556219c..d98b4f6 100644
--- a/include/clang/Basic/Diagnostic.h
+++ b/include/clang/Basic/Diagnostic.h
@@ -303,6 +303,10 @@ private:
   unsigned NumWarnings;         ///< Number of warnings reported
   unsigned NumErrors;           ///< Number of errors reported
 
+  /// \brief Indicates we are in a scope where diagnostics are not emitted,
+  /// but are temporarily counted and reset at scope exit.
+  bool InDiagnosticSuppressionScope;
+
   /// \brief A function pointer that converts an opaque diagnostic
   /// argument to a strings.
   ///
@@ -724,6 +728,7 @@ private:
   friend class Diagnostic;
   friend class PartialDiagnostic;
   friend class DiagnosticErrorTrap;
+  friend class DiagnosticSuppressionScope;
   
   /// \brief The location of the current diagnostic that is in flight.
   SourceLocation CurDiagLoc;
@@ -849,6 +854,52 @@ public:
   }
 };
 
+/// \brief RAII class that suppresses emission of diagnostic messages
+/// and resets their counts on destruction.  Counts are maintained within
+/// the scope so that nested DiagnosticErrorTrap instances still work.
+class DiagnosticSuppressionScope {
+  DiagnosticsEngine &Diag;
+  bool PrevErrorOccurred;
+  bool PrevUncompilableErrorOccurred;
+  bool PrevFatalErrorOccurred;
+  bool PrevUnrecoverableErrorOccurred;
+  DiagnosticIDs::Level PrevLastDiagLevel;
+  unsigned PrevTrapNumErrorsOccurred;
+  unsigned PrevTrapNumUnrecoverableErrorsOccurred;
+  unsigned PrevNumWarnings;
+  unsigned PrevNumErrors;
+  bool PrevInDiagnosticSuppressionScope;
+public:
+  DiagnosticSuppressionScope(DiagnosticsEngine &Diag)
+    : Diag(Diag),
+      PrevErrorOccurred(Diag.ErrorOccurred),
+      PrevUncompilableErrorOccurred(Diag.UncompilableErrorOccurred),
+      PrevFatalErrorOccurred(Diag.FatalErrorOccurred),
+      PrevUnrecoverableErrorOccurred(Diag.UnrecoverableErrorOccurred),
+      PrevLastDiagLevel(Diag.LastDiagLevel),
+      PrevTrapNumErrorsOccurred(Diag.TrapNumErrorsOccurred),
+      PrevTrapNumUnrecoverableErrorsOccurred(
+        Diag.TrapNumUnrecoverableErrorsOccurred),
+      PrevNumWarnings(Diag.NumWarnings),
+      PrevNumErrors(Diag.NumErrors),
+      PrevInDiagnosticSuppressionScope(Diag.InDiagnosticSuppressionScope) {
+    Diag.InDiagnosticSuppressionScope = true;
+  }
+  ~DiagnosticSuppressionScope() {
+    Diag.ErrorOccurred = PrevErrorOccurred;
+    Diag.UncompilableErrorOccurred = PrevUncompilableErrorOccurred;
+    Diag.FatalErrorOccurred = PrevFatalErrorOccurred;
+    Diag.UnrecoverableErrorOccurred = PrevUnrecoverableErrorOccurred;
+    Diag.LastDiagLevel = PrevLastDiagLevel;
+    Diag.TrapNumErrorsOccurred = PrevTrapNumErrorsOccurred;
+    Diag.TrapNumUnrecoverableErrorsOccurred =
+      PrevTrapNumUnrecoverableErrorsOccurred;
+    Diag.NumWarnings = PrevNumWarnings;
+    Diag.NumErrors = PrevNumErrors;
+    Diag.InDiagnosticSuppressionScope = PrevInDiagnosticSuppressionScope;
+  }
+};
+
 //===----------------------------------------------------------------------===//
 // DiagnosticBuilder
 //===----------------------------------------------------------------------===//
diff --git a/lib/Basic/Diagnostic.cpp b/lib/Basic/Diagnostic.cpp
index 59a75df..f0dc96a 100644
--- a/lib/Basic/Diagnostic.cpp
+++ b/lib/Basic/Diagnostic.cpp
@@ -55,6 +55,7 @@ DiagnosticsEngine::DiagnosticsEngine(
   ShowColors = false;
   ShowOverloads = Ovl_All;
   ExtBehavior = diag::Severity::Ignored;
+  InDiagnosticSuppressionScope = false;
 
   ErrorLimit = 0;
   TemplateBacktraceLimit = 0;
@@ -94,6 +95,9 @@ bool DiagnosticsEngine::popMappings(SourceLocation Loc) {
 }
 
 void DiagnosticsEngine::Reset() {
+  assert(!InDiagnosticSuppressionScope &&
+         "DiagnosticsEngine::Reset in DiagnosticSuppressionScope");
+
   ErrorOccurred = false;
   UncompilableErrorOccurred = false;
   FatalErrorOccurred = false;
diff --git a/lib/Basic/DiagnosticIDs.cpp b/lib/Basic/DiagnosticIDs.cpp
index 282e75e..5cbfe3b 100644
--- a/lib/Basic/DiagnosticIDs.cpp
+++ b/lib/Basic/DiagnosticIDs.cpp
@@ -668,12 +668,16 @@ bool DiagnosticIDs::ProcessDiag(DiagnosticsEngine &Diag) const {
     // If we've emitted a lot of errors, emit a fatal error instead of it to 
     // stop a flood of bogus errors.
     if (Diag.ErrorLimit && Diag.NumErrors > Diag.ErrorLimit &&
+        !Diag.InDiagnosticSuppressionScope &&
         DiagLevel == DiagnosticIDs::Error) {
       Diag.SetDelayedDiagnostic(diag::fatal_too_many_errors);
       return false;
     }
   }
 
+  if (Diag.InDiagnosticSuppressionScope)
+    return false;
+
   // Finally, report it.
   EmitDiag(Diag, DiagLevel);
   return true;
diff --git a/unittests/Basic/CMakeLists.txt b/unittests/Basic/CMakeLists.txt
index b8f69bf..3cb3cb8 100644
--- a/unittests/Basic/CMakeLists.txt
+++ b/unittests/Basic/CMakeLists.txt
@@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
 
 add_clang_unittest(BasicTests
   CharInfoTest.cpp
+  DiagnosticTest.cpp
   FileManagerTest.cpp
   SourceManagerTest.cpp
   VirtualFileSystemTest.cpp
diff --git a/unittests/Basic/DiagnosticTest.cpp b/unittests/Basic/DiagnosticTest.cpp
new file mode 100644
index 0000000..8636dbf
--- /dev/null
+++ b/unittests/Basic/DiagnosticTest.cpp
@@ -0,0 +1,57 @@
+//===- unittests/Basic/DiagnosticTest.cpp -- Diagnostic engine tests ------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticIDs.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace clang;
+
+namespace {
+
+// Check that DiagnosticSuppressionScope works.
+TEST(DiagnosticTest, suppressionScope) {
+  clang::DiagnosticsEngine Diags(new clang::DiagnosticIDs(),
+                                 new clang::DiagnosticOptions,
+                                 new clang::IgnoringDiagConsumer());
+  clang::DiagnosticErrorTrap outerTrap(Diags);
+
+  {
+    clang::DiagnosticSuppressionScope supp(Diags);
+    clang::DiagnosticErrorTrap innerTrap(Diags);
+
+    // Set UncompilableErrorOccurred and ErrorOccurred
+    Diags.Report(clang::diag::err_target_unknown_triple) << "unknown";
+
+    // Set UnrecoverableErrorOccurred and ErrorOccurred
+    Diags.Report(clang::diag::err_cannot_open_file) << "file" << "error";
+
+    // Set FatalErrorOccurred (via non-note following a fatal error)
+    Diags.Report(clang::diag::warn_mt_message) << "warning";
+
+    EXPECT_TRUE(innerTrap.hasErrorOccurred());
+    EXPECT_TRUE(innerTrap.hasUnrecoverableErrorOccurred());
+    EXPECT_TRUE(outerTrap.hasErrorOccurred());
+    EXPECT_TRUE(outerTrap.hasUnrecoverableErrorOccurred());
+    EXPECT_TRUE(Diags.hasErrorOccurred());
+    EXPECT_TRUE(Diags.hasFatalErrorOccurred());
+    EXPECT_TRUE(Diags.hasUncompilableErrorOccurred());
+    EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred());
+  }
+
+  EXPECT_FALSE(outerTrap.hasErrorOccurred());
+  EXPECT_FALSE(outerTrap.hasUnrecoverableErrorOccurred());
+  EXPECT_FALSE(Diags.hasErrorOccurred());
+  EXPECT_FALSE(Diags.hasFatalErrorOccurred());
+  EXPECT_FALSE(Diags.hasUncompilableErrorOccurred());
+  EXPECT_FALSE(Diags.hasUnrecoverableErrorOccurred());
+}
+
+}
-- 
2.1.1

_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to