[LLD][COFF] Emit tail merge pdata for delay load thunks on ARM64EC (#116810)
[llvm-project.git] / clang / unittests / Basic / DiagnosticTest.cpp
blob36a77c7247655fa706731ed77ed5c18e977166f8
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 //===----------------------------------------------------------------------===//
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 "clang/Basic/DiagnosticSema.h"
14 #include "clang/Basic/FileManager.h"
15 #include "clang/Basic/SourceManager.h"
16 #include "llvm/ADT/ArrayRef.h"
17 #include "llvm/ADT/IntrusiveRefCntPtr.h"
18 #include "llvm/ADT/StringRef.h"
19 #include "llvm/Support/MemoryBuffer.h"
20 #include "llvm/Support/VirtualFileSystem.h"
21 #include "gmock/gmock.h"
22 #include "gtest/gtest.h"
23 #include <optional>
24 #include <vector>
26 using namespace llvm;
27 using namespace clang;
29 // Declare DiagnosticsTestHelper to avoid GCC warning
30 namespace clang {
31 void DiagnosticsTestHelper(DiagnosticsEngine &diag);
34 void clang::DiagnosticsTestHelper(DiagnosticsEngine &diag) {
35 EXPECT_FALSE(diag.DiagStates.empty());
36 EXPECT_TRUE(diag.DiagStatesByLoc.empty());
37 EXPECT_TRUE(diag.DiagStateOnPushStack.empty());
40 namespace {
41 using testing::AllOf;
42 using testing::ElementsAre;
43 using testing::IsEmpty;
45 // Check that DiagnosticErrorTrap works with SuppressAllDiagnostics.
46 TEST(DiagnosticTest, suppressAndTrap) {
47 DiagnosticsEngine Diags(new DiagnosticIDs(),
48 new DiagnosticOptions,
49 new IgnoringDiagConsumer());
50 Diags.setSuppressAllDiagnostics(true);
53 DiagnosticErrorTrap trap(Diags);
55 // Diag that would set UncompilableErrorOccurred and ErrorOccurred.
56 Diags.Report(diag::err_target_unknown_triple) << "unknown";
58 // Diag that would set UnrecoverableErrorOccurred and ErrorOccurred.
59 Diags.Report(diag::err_cannot_open_file) << "file" << "error";
61 // Diag that would set FatalErrorOccurred
62 // (via non-note following a fatal error).
63 Diags.Report(diag::warn_mt_message) << "warning";
65 EXPECT_TRUE(trap.hasErrorOccurred());
66 EXPECT_TRUE(trap.hasUnrecoverableErrorOccurred());
69 EXPECT_FALSE(Diags.hasErrorOccurred());
70 EXPECT_FALSE(Diags.hasFatalErrorOccurred());
71 EXPECT_FALSE(Diags.hasUncompilableErrorOccurred());
72 EXPECT_FALSE(Diags.hasUnrecoverableErrorOccurred());
75 // Check that FatalsAsError works as intended
76 TEST(DiagnosticTest, fatalsAsError) {
77 for (unsigned FatalsAsError = 0; FatalsAsError != 2; ++FatalsAsError) {
78 DiagnosticsEngine Diags(new DiagnosticIDs(),
79 new DiagnosticOptions,
80 new IgnoringDiagConsumer());
81 Diags.setFatalsAsError(FatalsAsError);
83 // Diag that would set UnrecoverableErrorOccurred and ErrorOccurred.
84 Diags.Report(diag::err_cannot_open_file) << "file" << "error";
86 // Diag that would set FatalErrorOccurred
87 // (via non-note following a fatal error).
88 Diags.Report(diag::warn_mt_message) << "warning";
90 EXPECT_TRUE(Diags.hasErrorOccurred());
91 EXPECT_EQ(Diags.hasFatalErrorOccurred(), FatalsAsError ? 0u : 1u);
92 EXPECT_TRUE(Diags.hasUncompilableErrorOccurred());
93 EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred());
95 // The warning should be emitted and counted only if we're not suppressing
96 // after fatal errors.
97 EXPECT_EQ(Diags.getNumWarnings(), FatalsAsError);
101 TEST(DiagnosticTest, tooManyErrorsIsAlwaysFatal) {
102 DiagnosticsEngine Diags(new DiagnosticIDs(), new DiagnosticOptions,
103 new IgnoringDiagConsumer());
104 Diags.setFatalsAsError(true);
106 // Report a fatal_too_many_errors diagnostic to ensure that still
107 // acts as a fatal error despite downgrading fatal errors to errors.
108 Diags.Report(diag::fatal_too_many_errors);
109 EXPECT_TRUE(Diags.hasFatalErrorOccurred());
111 // Ensure that the severity of that diagnostic is really "fatal".
112 EXPECT_EQ(Diags.getDiagnosticLevel(diag::fatal_too_many_errors, {}),
113 DiagnosticsEngine::Level::Fatal);
116 // Check that soft RESET works as intended
117 TEST(DiagnosticTest, softReset) {
118 DiagnosticsEngine Diags(new DiagnosticIDs(), new DiagnosticOptions,
119 new IgnoringDiagConsumer());
121 unsigned numWarnings = 0U, numErrors = 0U;
123 Diags.Reset(true);
124 // Check For ErrorOccurred and TrapNumErrorsOccurred
125 EXPECT_FALSE(Diags.hasErrorOccurred());
126 EXPECT_FALSE(Diags.hasFatalErrorOccurred());
127 EXPECT_FALSE(Diags.hasUncompilableErrorOccurred());
128 // Check for UnrecoverableErrorOccurred and TrapNumUnrecoverableErrorsOccurred
129 EXPECT_FALSE(Diags.hasUnrecoverableErrorOccurred());
131 EXPECT_EQ(Diags.getNumWarnings(), numWarnings);
132 EXPECT_EQ(Diags.getNumErrors(), numErrors);
134 // Check for private variables of DiagnosticsEngine differentiating soft reset
135 DiagnosticsTestHelper(Diags);
137 EXPECT_TRUE(Diags.isLastDiagnosticIgnored());
140 TEST(DiagnosticTest, diagnosticError) {
141 DiagnosticsEngine Diags(new DiagnosticIDs(), new DiagnosticOptions,
142 new IgnoringDiagConsumer());
143 PartialDiagnostic::DiagStorageAllocator Alloc;
144 llvm::Expected<std::pair<int, int>> Value = DiagnosticError::create(
145 SourceLocation(), PartialDiagnostic(diag::err_cannot_open_file, Alloc)
146 << "file"
147 << "error");
148 ASSERT_TRUE(!Value);
149 llvm::Error Err = Value.takeError();
150 std::optional<PartialDiagnosticAt> ErrDiag = DiagnosticError::take(Err);
151 llvm::cantFail(std::move(Err));
152 ASSERT_FALSE(!ErrDiag);
153 EXPECT_EQ(ErrDiag->first, SourceLocation());
154 EXPECT_EQ(ErrDiag->second.getDiagID(), diag::err_cannot_open_file);
156 Value = std::make_pair(20, 1);
157 ASSERT_FALSE(!Value);
158 EXPECT_EQ(*Value, std::make_pair(20, 1));
159 EXPECT_EQ(Value->first, 20);
162 TEST(DiagnosticTest, storedDiagEmptyWarning) {
163 DiagnosticsEngine Diags(new DiagnosticIDs(), new DiagnosticOptions);
165 class CaptureDiagnosticConsumer : public DiagnosticConsumer {
166 public:
167 SmallVector<StoredDiagnostic> StoredDiags;
169 void HandleDiagnostic(DiagnosticsEngine::Level level,
170 const Diagnostic &Info) override {
171 StoredDiags.push_back(StoredDiagnostic(level, Info));
175 CaptureDiagnosticConsumer CaptureConsumer;
176 Diags.setClient(&CaptureConsumer, /*ShouldOwnClient=*/false);
177 Diags.Report(diag::pp_hash_warning) << "";
178 ASSERT_TRUE(CaptureConsumer.StoredDiags.size() == 1);
180 // Make sure an empty warning can round-trip with \c StoredDiagnostic.
181 Diags.Report(CaptureConsumer.StoredDiags.front());
184 class SuppressionMappingTest : public testing::Test {
185 public:
186 SuppressionMappingTest() {
187 Diags.setClient(&CaptureConsumer, /*ShouldOwnClient=*/false);
190 protected:
191 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS =
192 llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
193 DiagnosticsEngine Diags{new DiagnosticIDs(), new DiagnosticOptions};
195 llvm::ArrayRef<StoredDiagnostic> diags() {
196 return CaptureConsumer.StoredDiags;
199 private:
200 class CaptureDiagnosticConsumer : public DiagnosticConsumer {
201 public:
202 std::vector<StoredDiagnostic> StoredDiags;
204 void HandleDiagnostic(DiagnosticsEngine::Level level,
205 const Diagnostic &Info) override {
206 StoredDiags.push_back(StoredDiagnostic(level, Info));
209 CaptureDiagnosticConsumer CaptureConsumer;
212 MATCHER_P(WithMessage, Msg, "has diagnostic message") {
213 return arg.getMessage() == Msg;
215 MATCHER(IsError, "has error severity") {
216 return arg.getLevel() == DiagnosticsEngine::Level::Error;
219 TEST_F(SuppressionMappingTest, MissingMappingFile) {
220 Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
221 clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
222 EXPECT_THAT(diags(), ElementsAre(AllOf(
223 WithMessage("no such file or directory: 'foo.txt'"),
224 IsError())));
227 TEST_F(SuppressionMappingTest, MalformedFile) {
228 Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
229 FS->addFile("foo.txt", /*ModificationTime=*/{},
230 llvm::MemoryBuffer::getMemBuffer("asdf", "foo.txt"));
231 clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
232 EXPECT_THAT(diags(),
233 ElementsAre(AllOf(
234 WithMessage("failed to process suppression mapping file "
235 "'foo.txt': malformed line 1: 'asdf'"),
236 IsError())));
239 TEST_F(SuppressionMappingTest, UnknownDiagName) {
240 Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
241 FS->addFile("foo.txt", /*ModificationTime=*/{},
242 llvm::MemoryBuffer::getMemBuffer("[non-existing-warning]"));
243 clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
244 EXPECT_THAT(diags(), ElementsAre(WithMessage(
245 "unknown warning option 'non-existing-warning'")));
248 TEST_F(SuppressionMappingTest, SuppressesGroup) {
249 llvm::StringLiteral SuppressionMappingFile = R"(
250 [unused]
251 src:*)";
252 Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
253 FS->addFile("foo.txt", /*ModificationTime=*/{},
254 llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
255 clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
256 EXPECT_THAT(diags(), IsEmpty());
258 EXPECT_TRUE(
259 Diags.isSuppressedViaMapping(diag::warn_unused_function, "foo.cpp"));
260 EXPECT_FALSE(Diags.isSuppressedViaMapping(diag::warn_deprecated, "foo.cpp"));
263 TEST_F(SuppressionMappingTest, EmitCategoryIsExcluded) {
264 llvm::StringLiteral SuppressionMappingFile = R"(
265 [unused]
266 src:*
267 src:*foo.cpp=emit)";
268 Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
269 FS->addFile("foo.txt", /*ModificationTime=*/{},
270 llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
271 clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
272 EXPECT_THAT(diags(), IsEmpty());
274 EXPECT_TRUE(
275 Diags.isSuppressedViaMapping(diag::warn_unused_function, "bar.cpp"));
276 EXPECT_FALSE(
277 Diags.isSuppressedViaMapping(diag::warn_unused_function, "foo.cpp"));
280 TEST_F(SuppressionMappingTest, LongestMatchWins) {
281 llvm::StringLiteral SuppressionMappingFile = R"(
282 [unused]
283 src:*clang/*
284 src:*clang/lib/Sema/*=emit
285 src:*clang/lib/Sema/foo*)";
286 Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
287 FS->addFile("foo.txt", /*ModificationTime=*/{},
288 llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
289 clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
290 EXPECT_THAT(diags(), IsEmpty());
292 EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
293 "clang/lib/Basic/foo.h"));
294 EXPECT_FALSE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
295 "clang/lib/Sema/bar.h"));
296 EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
297 "clang/lib/Sema/foo.h"));
300 TEST_F(SuppressionMappingTest, IsIgnored) {
301 llvm::StringLiteral SuppressionMappingFile = R"(
302 [unused]
303 src:*clang/*)";
304 Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
305 Diags.getDiagnosticOptions().Warnings = {"unused"};
306 FS->addFile("foo.txt", /*ModificationTime=*/{},
307 llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
308 clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
309 ASSERT_THAT(diags(), IsEmpty());
311 FileManager FM({}, FS);
312 SourceManager SM(Diags, FM);
314 auto ClangID =
315 SM.createFileID(llvm::MemoryBuffer::getMemBuffer("", "clang/foo.h"));
316 auto NonClangID =
317 SM.createFileID(llvm::MemoryBuffer::getMemBuffer("", "llvm/foo.h"));
318 auto PresumedClangID =
319 SM.createFileID(llvm::MemoryBuffer::getMemBuffer("", "llvm/foo2.h"));
320 // Add a line directive to point into clang/foo.h
321 SM.AddLineNote(SM.getLocForStartOfFile(PresumedClangID), 42,
322 SM.getLineTableFilenameID("clang/foo.h"), false, false,
323 clang::SrcMgr::C_User);
325 EXPECT_TRUE(Diags.isIgnored(diag::warn_unused_function,
326 SM.getLocForStartOfFile(ClangID)));
327 EXPECT_FALSE(Diags.isIgnored(diag::warn_unused_function,
328 SM.getLocForStartOfFile(NonClangID)));
329 EXPECT_TRUE(Diags.isIgnored(diag::warn_unused_function,
330 SM.getLocForStartOfFile(PresumedClangID)));
332 // Pretend we have a clang-diagnostic pragma to enforce the warning. Make sure
333 // suppressing mapping doesn't take over.
334 Diags.setSeverity(diag::warn_unused_function, diag::Severity::Error,
335 SM.getLocForStartOfFile(ClangID));
336 EXPECT_FALSE(Diags.isIgnored(diag::warn_unused_function,
337 SM.getLocForStartOfFile(ClangID)));
339 } // namespace