xref: /llvm-project/clang/unittests/Basic/DiagnosticTest.cpp (revision 6a623e8484fe713d7074acb7ceab520458bfa89c)
1 //===- unittests/Basic/DiagnosticTest.cpp -- Diagnostic engine tests ------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "clang/Basic/Diagnostic.h"
10 #include "clang/Basic/DiagnosticError.h"
11 #include "clang/Basic/DiagnosticIDs.h"
12 #include "clang/Basic/DiagnosticLex.h"
13 #include "gtest/gtest.h"
14 #include <optional>
15 
16 using namespace llvm;
17 using namespace clang;
18 
19 // Declare DiagnosticsTestHelper to avoid GCC warning
20 namespace clang {
21 void DiagnosticsTestHelper(DiagnosticsEngine &diag);
22 }
23 
24 void clang::DiagnosticsTestHelper(DiagnosticsEngine &diag) {
25   EXPECT_FALSE(diag.DiagStates.empty());
26   EXPECT_TRUE(diag.DiagStatesByLoc.empty());
27   EXPECT_TRUE(diag.DiagStateOnPushStack.empty());
28 }
29 
30 namespace {
31 
32 // Check that DiagnosticErrorTrap works with SuppressAllDiagnostics.
33 TEST(DiagnosticTest, suppressAndTrap) {
34   DiagnosticsEngine Diags(new DiagnosticIDs(),
35                           new DiagnosticOptions,
36                           new IgnoringDiagConsumer());
37   Diags.setSuppressAllDiagnostics(true);
38 
39   {
40     DiagnosticErrorTrap trap(Diags);
41 
42     // Diag that would set UncompilableErrorOccurred and ErrorOccurred.
43     Diags.Report(diag::err_target_unknown_triple) << "unknown";
44 
45     // Diag that would set UnrecoverableErrorOccurred and ErrorOccurred.
46     Diags.Report(diag::err_cannot_open_file) << "file" << "error";
47 
48     // Diag that would set FatalErrorOccurred
49     // (via non-note following a fatal error).
50     Diags.Report(diag::warn_mt_message) << "warning";
51 
52     EXPECT_TRUE(trap.hasErrorOccurred());
53     EXPECT_TRUE(trap.hasUnrecoverableErrorOccurred());
54   }
55 
56   EXPECT_FALSE(Diags.hasErrorOccurred());
57   EXPECT_FALSE(Diags.hasFatalErrorOccurred());
58   EXPECT_FALSE(Diags.hasUncompilableErrorOccurred());
59   EXPECT_FALSE(Diags.hasUnrecoverableErrorOccurred());
60 }
61 
62 // Check that FatalsAsError works as intended
63 TEST(DiagnosticTest, fatalsAsError) {
64   for (unsigned FatalsAsError = 0; FatalsAsError != 2; ++FatalsAsError) {
65     DiagnosticsEngine Diags(new DiagnosticIDs(),
66                             new DiagnosticOptions,
67                             new IgnoringDiagConsumer());
68     Diags.setFatalsAsError(FatalsAsError);
69 
70     // Diag that would set UnrecoverableErrorOccurred and ErrorOccurred.
71     Diags.Report(diag::err_cannot_open_file) << "file" << "error";
72 
73     // Diag that would set FatalErrorOccurred
74     // (via non-note following a fatal error).
75     Diags.Report(diag::warn_mt_message) << "warning";
76 
77     EXPECT_TRUE(Diags.hasErrorOccurred());
78     EXPECT_EQ(Diags.hasFatalErrorOccurred(), FatalsAsError ? 0u : 1u);
79     EXPECT_TRUE(Diags.hasUncompilableErrorOccurred());
80     EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred());
81 
82     // The warning should be emitted and counted only if we're not suppressing
83     // after fatal errors.
84     EXPECT_EQ(Diags.getNumWarnings(), FatalsAsError);
85   }
86 }
87 
88 TEST(DiagnosticTest, tooManyErrorsIsAlwaysFatal) {
89   DiagnosticsEngine Diags(new DiagnosticIDs(), new DiagnosticOptions,
90                           new IgnoringDiagConsumer());
91   Diags.setFatalsAsError(true);
92 
93   // Report a fatal_too_many_errors diagnostic to ensure that still
94   // acts as a fatal error despite downgrading fatal errors to errors.
95   Diags.Report(diag::fatal_too_many_errors);
96   EXPECT_TRUE(Diags.hasFatalErrorOccurred());
97 
98   // Ensure that the severity of that diagnostic is really "fatal".
99   EXPECT_EQ(Diags.getDiagnosticLevel(diag::fatal_too_many_errors, {}),
100             DiagnosticsEngine::Level::Fatal);
101 }
102 
103 // Check that soft RESET works as intended
104 TEST(DiagnosticTest, softReset) {
105   DiagnosticsEngine Diags(new DiagnosticIDs(), new DiagnosticOptions,
106                           new IgnoringDiagConsumer());
107 
108   unsigned numWarnings = 0U, numErrors = 0U;
109 
110   Diags.Reset(true);
111   // Check For ErrorOccurred and TrapNumErrorsOccurred
112   EXPECT_FALSE(Diags.hasErrorOccurred());
113   EXPECT_FALSE(Diags.hasFatalErrorOccurred());
114   EXPECT_FALSE(Diags.hasUncompilableErrorOccurred());
115   // Check for UnrecoverableErrorOccurred and TrapNumUnrecoverableErrorsOccurred
116   EXPECT_FALSE(Diags.hasUnrecoverableErrorOccurred());
117 
118   EXPECT_EQ(Diags.getNumWarnings(), numWarnings);
119   EXPECT_EQ(Diags.getNumErrors(), numErrors);
120 
121   // Check for private variables of DiagnosticsEngine differentiating soft reset
122   DiagnosticsTestHelper(Diags);
123 
124   EXPECT_TRUE(Diags.isLastDiagnosticIgnored());
125 }
126 
127 TEST(DiagnosticTest, diagnosticError) {
128   DiagnosticsEngine Diags(new DiagnosticIDs(), new DiagnosticOptions,
129                           new IgnoringDiagConsumer());
130   PartialDiagnostic::DiagStorageAllocator Alloc;
131   llvm::Expected<std::pair<int, int>> Value = DiagnosticError::create(
132       SourceLocation(), PartialDiagnostic(diag::err_cannot_open_file, Alloc)
133                             << "file"
134                             << "error");
135   ASSERT_TRUE(!Value);
136   llvm::Error Err = Value.takeError();
137   std::optional<PartialDiagnosticAt> ErrDiag = DiagnosticError::take(Err);
138   llvm::cantFail(std::move(Err));
139   ASSERT_FALSE(!ErrDiag);
140   EXPECT_EQ(ErrDiag->first, SourceLocation());
141   EXPECT_EQ(ErrDiag->second.getDiagID(), diag::err_cannot_open_file);
142 
143   Value = std::make_pair(20, 1);
144   ASSERT_FALSE(!Value);
145   EXPECT_EQ(*Value, std::make_pair(20, 1));
146   EXPECT_EQ(Value->first, 20);
147 }
148 
149 TEST(DiagnosticTest, storedDiagEmptyWarning) {
150   DiagnosticsEngine Diags(new DiagnosticIDs(), new DiagnosticOptions);
151 
152   class CaptureDiagnosticConsumer : public DiagnosticConsumer {
153   public:
154     SmallVector<StoredDiagnostic> StoredDiags;
155 
156     void HandleDiagnostic(DiagnosticsEngine::Level level,
157                           const Diagnostic &Info) override {
158       StoredDiags.push_back(StoredDiagnostic(level, Info));
159     }
160   };
161 
162   CaptureDiagnosticConsumer CaptureConsumer;
163   Diags.setClient(&CaptureConsumer, /*ShouldOwnClient=*/false);
164   Diags.Report(diag::pp_hash_warning) << "";
165   ASSERT_TRUE(CaptureConsumer.StoredDiags.size() == 1);
166 
167   // Make sure an empty warning can round-trip with \c StoredDiagnostic.
168   Diags.Report(CaptureConsumer.StoredDiags.front());
169 }
170 }
171