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 | void clang::DiagnosticsTestHelper(DiagnosticsEngine &diag) { |
20 | unsigned delayedDiagID = 0U; |
21 | |
22 | EXPECT_EQ(diag.DelayedDiagID, delayedDiagID); |
23 | EXPECT_FALSE(diag.DiagStates.empty()); |
24 | EXPECT_TRUE(diag.DiagStatesByLoc.empty()); |
25 | EXPECT_TRUE(diag.DiagStateOnPushStack.empty()); |
26 | } |
27 | |
28 | namespace { |
29 | |
30 | // Check that DiagnosticErrorTrap works with SuppressAllDiagnostics. |
31 | TEST(DiagnosticTest, suppressAndTrap) { |
32 | DiagnosticsEngine Diags(new DiagnosticIDs(), |
33 | new DiagnosticOptions, |
34 | new IgnoringDiagConsumer()); |
35 | Diags.setSuppressAllDiagnostics(true); |
36 | |
37 | { |
38 | DiagnosticErrorTrap trap(Diags); |
39 | |
40 | // Diag that would set UncompilableErrorOccurred and ErrorOccurred. |
41 | Diags.Report(diag::err_target_unknown_triple) << "unknown" ; |
42 | |
43 | // Diag that would set UnrecoverableErrorOccurred and ErrorOccurred. |
44 | Diags.Report(diag::err_cannot_open_file) << "file" << "error" ; |
45 | |
46 | // Diag that would set FatalErrorOccurred |
47 | // (via non-note following a fatal error). |
48 | Diags.Report(diag::warn_mt_message) << "warning" ; |
49 | |
50 | EXPECT_TRUE(trap.hasErrorOccurred()); |
51 | EXPECT_TRUE(trap.hasUnrecoverableErrorOccurred()); |
52 | } |
53 | |
54 | EXPECT_FALSE(Diags.hasErrorOccurred()); |
55 | EXPECT_FALSE(Diags.hasFatalErrorOccurred()); |
56 | EXPECT_FALSE(Diags.hasUncompilableErrorOccurred()); |
57 | EXPECT_FALSE(Diags.hasUnrecoverableErrorOccurred()); |
58 | } |
59 | |
60 | // Check that FatalsAsError works as intended |
61 | TEST(DiagnosticTest, fatalsAsError) { |
62 | for (unsigned FatalsAsError = 0; FatalsAsError != 2; ++FatalsAsError) { |
63 | DiagnosticsEngine Diags(new DiagnosticIDs(), |
64 | new DiagnosticOptions, |
65 | new IgnoringDiagConsumer()); |
66 | Diags.setFatalsAsError(FatalsAsError); |
67 | |
68 | // Diag that would set UnrecoverableErrorOccurred and ErrorOccurred. |
69 | Diags.Report(diag::err_cannot_open_file) << "file" << "error" ; |
70 | |
71 | // Diag that would set FatalErrorOccurred |
72 | // (via non-note following a fatal error). |
73 | Diags.Report(diag::warn_mt_message) << "warning" ; |
74 | |
75 | EXPECT_TRUE(Diags.hasErrorOccurred()); |
76 | EXPECT_EQ(Diags.hasFatalErrorOccurred(), FatalsAsError ? 0u : 1u); |
77 | EXPECT_TRUE(Diags.hasUncompilableErrorOccurred()); |
78 | EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred()); |
79 | |
80 | // The warning should be emitted and counted only if we're not suppressing |
81 | // after fatal errors. |
82 | EXPECT_EQ(Diags.getNumWarnings(), FatalsAsError); |
83 | } |
84 | } |
85 | |
86 | // Check that soft RESET works as intended |
87 | TEST(DiagnosticTest, softReset) { |
88 | DiagnosticsEngine Diags(new DiagnosticIDs(), new DiagnosticOptions, |
89 | new IgnoringDiagConsumer()); |
90 | |
91 | unsigned numWarnings = 0U, numErrors = 0U; |
92 | |
93 | Diags.Reset(soft: true); |
94 | // Check For ErrorOccurred and TrapNumErrorsOccurred |
95 | EXPECT_FALSE(Diags.hasErrorOccurred()); |
96 | EXPECT_FALSE(Diags.hasFatalErrorOccurred()); |
97 | EXPECT_FALSE(Diags.hasUncompilableErrorOccurred()); |
98 | // Check for UnrecoverableErrorOccurred and TrapNumUnrecoverableErrorsOccurred |
99 | EXPECT_FALSE(Diags.hasUnrecoverableErrorOccurred()); |
100 | |
101 | EXPECT_EQ(Diags.getNumWarnings(), numWarnings); |
102 | EXPECT_EQ(Diags.getNumErrors(), numErrors); |
103 | |
104 | // Check for private variables of DiagnosticsEngine differentiating soft reset |
105 | DiagnosticsTestHelper(diag&: Diags); |
106 | |
107 | EXPECT_FALSE(Diags.isDiagnosticInFlight()); |
108 | EXPECT_TRUE(Diags.isLastDiagnosticIgnored()); |
109 | } |
110 | |
111 | TEST(DiagnosticTest, diagnosticError) { |
112 | DiagnosticsEngine Diags(new DiagnosticIDs(), new DiagnosticOptions, |
113 | new IgnoringDiagConsumer()); |
114 | PartialDiagnostic::DiagStorageAllocator Alloc; |
115 | llvm::Expected<std::pair<int, int>> Value = DiagnosticError::create( |
116 | SourceLocation(), PartialDiagnostic(diag::err_cannot_open_file, Alloc) |
117 | << "file" |
118 | << "error" ); |
119 | ASSERT_TRUE(!Value); |
120 | llvm::Error Err = Value.takeError(); |
121 | std::optional<PartialDiagnosticAt> ErrDiag = DiagnosticError::take(Err); |
122 | llvm::cantFail(Err: std::move(Err)); |
123 | ASSERT_FALSE(!ErrDiag); |
124 | EXPECT_EQ(ErrDiag->first, SourceLocation()); |
125 | EXPECT_EQ(ErrDiag->second.getDiagID(), diag::err_cannot_open_file); |
126 | |
127 | Value = std::make_pair(x: 20, y: 1); |
128 | ASSERT_FALSE(!Value); |
129 | EXPECT_EQ(*Value, std::make_pair(20, 1)); |
130 | EXPECT_EQ(Value->first, 20); |
131 | } |
132 | |
133 | TEST(DiagnosticTest, storedDiagEmptyWarning) { |
134 | DiagnosticsEngine Diags(new DiagnosticIDs(), new DiagnosticOptions); |
135 | |
136 | class CaptureDiagnosticConsumer : public DiagnosticConsumer { |
137 | public: |
138 | SmallVector<StoredDiagnostic> StoredDiags; |
139 | |
140 | void HandleDiagnostic(DiagnosticsEngine::Level level, |
141 | const Diagnostic &Info) override { |
142 | StoredDiags.push_back(Elt: StoredDiagnostic(level, Info)); |
143 | } |
144 | }; |
145 | |
146 | CaptureDiagnosticConsumer CaptureConsumer; |
147 | Diags.setClient(client: &CaptureConsumer, /*ShouldOwnClient=*/false); |
148 | Diags.Report(diag::pp_hash_warning) << "" ; |
149 | ASSERT_TRUE(CaptureConsumer.StoredDiags.size() == 1); |
150 | |
151 | // Make sure an empty warning can round-trip with \c StoredDiagnostic. |
152 | Diags.Report(storedDiag: CaptureConsumer.StoredDiags.front()); |
153 | } |
154 | } |
155 | |