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
16using namespace llvm;
17using namespace clang;
18
19void 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
28namespace {
29
30// Check that DiagnosticErrorTrap works with SuppressAllDiagnostics.
31TEST(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
61TEST(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
87TEST(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
111TEST(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
133TEST(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

source code of clang/unittests/Basic/DiagnosticTest.cpp