[AMDGPU][AsmParser][NFC] Translate parsed MIMG instructions to MCInsts automatically.
[llvm-project.git] / clang-tools-extra / include-cleaner / unittests / FindHeadersTest.cpp
blob9f9ac11a93eb85a8736833ed290b22646a1d3267
1 //===--- FindHeadersTest.cpp ----------------------------------------------===//
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 "AnalysisInternal.h"
10 #include "TypesInternal.h"
11 #include "clang-include-cleaner/Analysis.h"
12 #include "clang-include-cleaner/Record.h"
13 #include "clang-include-cleaner/Types.h"
14 #include "clang/AST/RecursiveASTVisitor.h"
15 #include "clang/Basic/FileEntry.h"
16 #include "clang/Basic/FileManager.h"
17 #include "clang/Basic/LLVM.h"
18 #include "clang/Frontend/FrontendActions.h"
19 #include "clang/Testing/TestAST.h"
20 #include "clang/Tooling/Inclusions/StandardLibrary.h"
21 #include "llvm/ADT/SmallVector.h"
22 #include "llvm/ADT/StringRef.h"
23 #include "gmock/gmock.h"
24 #include "gtest/gtest.h"
25 #include <cassert>
26 #include <memory>
28 namespace clang::include_cleaner {
29 namespace {
30 using testing::ElementsAre;
31 using testing::UnorderedElementsAre;
33 std::string guard(llvm::StringRef Code) {
34 return "#pragma once\n" + Code.str();
37 class FindHeadersTest : public testing::Test {
38 protected:
39 TestInputs Inputs;
40 PragmaIncludes PI;
41 std::unique_ptr<TestAST> AST;
42 FindHeadersTest() {
43 Inputs.MakeAction = [this] {
44 struct Hook : public SyntaxOnlyAction {
45 public:
46 Hook(PragmaIncludes *Out) : Out(Out) {}
47 bool BeginSourceFileAction(clang::CompilerInstance &CI) override {
48 Out->record(CI);
49 return true;
52 PragmaIncludes *Out;
54 return std::make_unique<Hook>(&PI);
57 void buildAST() { AST = std::make_unique<TestAST>(Inputs); }
59 llvm::SmallVector<Hinted<Header>> findHeaders(llvm::StringRef FileName) {
60 return include_cleaner::findHeaders(
61 AST->sourceManager().translateFileLineCol(
62 AST->fileManager().getFile(FileName).get(),
63 /*Line=*/1, /*Col=*/1),
64 AST->sourceManager(), &PI);
66 const FileEntry *physicalHeader(llvm::StringRef FileName) {
67 return AST->fileManager().getFile(FileName).get();
71 TEST_F(FindHeadersTest, IWYUPrivateToPublic) {
72 Inputs.Code = R"cpp(
73 #include "private.h"
74 )cpp";
75 Inputs.ExtraFiles["private.h"] = guard(R"cpp(
76 // IWYU pragma: private, include "path/public.h"
77 )cpp");
78 buildAST();
79 EXPECT_THAT(findHeaders("private.h"),
80 UnorderedElementsAre(physicalHeader("private.h"),
81 Header("\"path/public.h\"")));
84 TEST_F(FindHeadersTest, IWYUExport) {
85 Inputs.Code = R"cpp(
86 #include "exporter.h"
87 )cpp";
88 Inputs.ExtraFiles["exporter.h"] = guard(R"cpp(
89 #include "exported1.h" // IWYU pragma: export
91 // IWYU pragma: begin_exports
92 #include "exported2.h"
93 // IWYU pragma: end_exports
95 #include "normal.h"
96 )cpp");
97 Inputs.ExtraFiles["exported1.h"] = guard("");
98 Inputs.ExtraFiles["exported2.h"] = guard("");
99 Inputs.ExtraFiles["normal.h"] = guard("");
101 buildAST();
102 EXPECT_THAT(findHeaders("exported1.h"),
103 UnorderedElementsAre(physicalHeader("exported1.h"),
104 physicalHeader("exporter.h")));
105 EXPECT_THAT(findHeaders("exported2.h"),
106 UnorderedElementsAre(physicalHeader("exported2.h"),
107 physicalHeader("exporter.h")));
108 EXPECT_THAT(findHeaders("normal.h"),
109 UnorderedElementsAre(physicalHeader("normal.h")));
110 EXPECT_THAT(findHeaders("exporter.h"),
111 UnorderedElementsAre(physicalHeader("exporter.h")));
114 TEST_F(FindHeadersTest, IWYUExportForStandardHeaders) {
115 Inputs.Code = R"cpp(
116 #include "exporter.h"
117 )cpp";
118 Inputs.ExtraFiles["exporter.h"] = guard(R"cpp(
119 #include <string> // IWYU pragma: export
120 )cpp");
121 Inputs.ExtraFiles["string"] = guard("");
122 Inputs.ExtraArgs.push_back("-isystem.");
123 buildAST();
124 tooling::stdlib::Symbol StdString =
125 *tooling::stdlib::Symbol::named("std::", "string");
126 EXPECT_THAT(
127 include_cleaner::findHeaders(StdString, AST->sourceManager(), &PI),
128 UnorderedElementsAre(physicalHeader("exporter.h"), StdString.header()));
131 TEST_F(FindHeadersTest, SelfContained) {
132 Inputs.Code = R"cpp(
133 #include "header.h"
134 )cpp";
135 Inputs.ExtraFiles["header.h"] = guard(R"cpp(
136 #include "fragment.inc"
137 )cpp");
138 Inputs.ExtraFiles["fragment.inc"] = "";
139 buildAST();
140 EXPECT_THAT(findHeaders("fragment.inc"),
141 UnorderedElementsAre(physicalHeader("fragment.inc"),
142 physicalHeader("header.h")));
145 TEST_F(FindHeadersTest, NonSelfContainedTraversePrivate) {
146 Inputs.Code = R"cpp(
147 #include "header.h"
148 )cpp";
149 Inputs.ExtraFiles["header.h"] = guard(R"cpp(
150 #include "fragment.inc"
151 )cpp");
152 Inputs.ExtraFiles["fragment.inc"] = R"cpp(
153 // IWYU pragma: private, include "public.h"
154 )cpp";
156 buildAST();
157 // There is a IWYU private mapping in the non self-contained header, verify
158 // that we don't emit its includer.
159 EXPECT_THAT(findHeaders("fragment.inc"),
160 UnorderedElementsAre(physicalHeader("fragment.inc"),
161 Header("\"public.h\"")));
164 TEST_F(FindHeadersTest, NonSelfContainedTraverseExporter) {
165 Inputs.Code = R"cpp(
166 #include "exporter.h"
167 )cpp";
168 Inputs.ExtraFiles["exporter.h"] = guard(R"cpp(
169 #include "exported.h" // IWYU pragma: export
170 )cpp");
171 Inputs.ExtraFiles["exported.h"] = guard(R"cpp(
172 #include "fragment.inc"
173 )cpp");
174 Inputs.ExtraFiles["fragment.inc"] = "";
175 buildAST();
176 // Verify that we emit exporters for each header on the path.
177 EXPECT_THAT(findHeaders("fragment.inc"),
178 UnorderedElementsAre(physicalHeader("fragment.inc"),
179 physicalHeader("exported.h"),
180 physicalHeader("exporter.h")));
183 TEST_F(FindHeadersTest, TargetIsExpandedFromMacroInHeader) {
184 struct CustomVisitor : RecursiveASTVisitor<CustomVisitor> {
185 const Decl *Out = nullptr;
186 bool VisitNamedDecl(const NamedDecl *ND) {
187 if (ND->getName() == "FLAG_foo" || ND->getName() == "Foo") {
188 EXPECT_TRUE(Out == nullptr);
189 Out = ND;
191 return true;
195 struct {
196 llvm::StringRef MacroHeader;
197 llvm::StringRef DeclareHeader;
198 } TestCases[] = {
199 {/*MacroHeader=*/R"cpp(
200 #define DEFINE_CLASS(name) class name {};
201 )cpp",
202 /*DeclareHeader=*/R"cpp(
203 #include "macro.h"
204 DEFINE_CLASS(Foo)
205 )cpp"},
206 {/*MacroHeader=*/R"cpp(
207 #define DEFINE_Foo class Foo {};
208 )cpp",
209 /*DeclareHeader=*/R"cpp(
210 #include "macro.h"
211 DEFINE_Foo
212 )cpp"},
213 {/*MacroHeader=*/R"cpp(
214 #define DECLARE_FLAGS(name) extern int FLAG_##name
215 )cpp",
216 /*DeclareHeader=*/R"cpp(
217 #include "macro.h"
218 DECLARE_FLAGS(foo);
219 )cpp"},
222 for (const auto &T : TestCases) {
223 Inputs.Code = R"cpp(#include "declare.h")cpp";
224 Inputs.ExtraFiles["declare.h"] = guard(T.DeclareHeader);
225 Inputs.ExtraFiles["macro.h"] = guard(T.MacroHeader);
226 buildAST();
228 CustomVisitor Visitor;
229 Visitor.TraverseDecl(AST->context().getTranslationUnitDecl());
231 auto Headers = clang::include_cleaner::findHeaders(
232 Visitor.Out->getLocation(), AST->sourceManager(),
233 /*PragmaIncludes=*/nullptr);
234 EXPECT_THAT(Headers, UnorderedElementsAre(physicalHeader("declare.h")));
238 MATCHER_P2(HintedHeader, Header, Hint, "") {
239 return std::tie(arg.Hint, arg) == std::tie(Hint, Header);
242 TEST_F(FindHeadersTest, PublicHeaderHint) {
243 Inputs.Code = R"cpp(
244 #include "public.h"
245 )cpp";
246 Inputs.ExtraFiles["public.h"] = guard(R"cpp(
247 #include "private.h"
248 #include "private.inc"
249 )cpp");
250 Inputs.ExtraFiles["private.h"] = guard(R"cpp(
251 // IWYU pragma: private
252 )cpp");
253 Inputs.ExtraFiles["private.inc"] = "";
254 buildAST();
255 // Non self-contained files and headers marked with IWYU private pragma
256 // shouldn't have PublicHeader hint.
257 EXPECT_THAT(
258 findHeaders("private.inc"),
259 UnorderedElementsAre(
260 HintedHeader(physicalHeader("private.inc"), Hints::OriginHeader),
261 HintedHeader(physicalHeader("public.h"), Hints::PublicHeader)));
262 EXPECT_THAT(findHeaders("private.h"),
263 UnorderedElementsAre(HintedHeader(physicalHeader("private.h"),
264 Hints::OriginHeader)));
267 TEST_F(FindHeadersTest, PreferredHeaderHint) {
268 Inputs.Code = R"cpp(
269 #include "private.h"
270 )cpp";
271 Inputs.ExtraFiles["private.h"] = guard(R"cpp(
272 // IWYU pragma: private, include "public.h"
273 )cpp");
274 buildAST();
275 // Headers explicitly marked should've preferred signal.
276 EXPECT_THAT(
277 findHeaders("private.h"),
278 UnorderedElementsAre(
279 HintedHeader(physicalHeader("private.h"), Hints::OriginHeader),
280 HintedHeader(Header("\"public.h\""),
281 Hints::PreferredHeader | Hints::PublicHeader)));
284 class HeadersForSymbolTest : public FindHeadersTest {
285 protected:
286 llvm::SmallVector<Header> headersFor(llvm::StringRef Name) {
287 struct Visitor : public RecursiveASTVisitor<Visitor> {
288 const NamedDecl *Out = nullptr;
289 llvm::StringRef Name;
290 Visitor(llvm::StringRef Name) : Name(Name) {}
291 bool VisitNamedDecl(const NamedDecl *ND) {
292 if (auto *TD = ND->getDescribedTemplate())
293 ND = TD;
295 if (ND->getName() == Name) {
296 EXPECT_TRUE(Out == nullptr || Out == ND->getCanonicalDecl())
297 << "Found multiple matches for " << Name << ".";
298 Out = cast<NamedDecl>(ND->getCanonicalDecl());
300 return true;
303 Visitor V(Name);
304 V.TraverseDecl(AST->context().getTranslationUnitDecl());
305 if (!V.Out)
306 ADD_FAILURE() << "Couldn't find any decls named " << Name << ".";
307 assert(V.Out);
308 return headersForSymbol(*V.Out, AST->sourceManager(), &PI);
310 llvm::SmallVector<Header> headersForFoo() { return headersFor("foo"); }
313 TEST_F(HeadersForSymbolTest, Deduplicates) {
314 Inputs.Code = R"cpp(
315 #include "foo.h"
316 )cpp";
317 Inputs.ExtraFiles["foo.h"] = guard(R"cpp(
318 // IWYU pragma: private, include "foo.h"
319 void foo();
320 void foo();
321 )cpp");
322 buildAST();
323 EXPECT_THAT(
324 headersForFoo(),
325 UnorderedElementsAre(physicalHeader("foo.h"),
326 // FIXME: de-duplicate across different kinds.
327 Header("\"foo.h\"")));
330 TEST_F(HeadersForSymbolTest, RankByName) {
331 Inputs.Code = R"cpp(
332 #include "fox.h"
333 #include "bar.h"
334 )cpp";
335 Inputs.ExtraFiles["fox.h"] = guard(R"cpp(
336 void foo();
337 )cpp");
338 Inputs.ExtraFiles["bar.h"] = guard(R"cpp(
339 void foo();
340 )cpp");
341 buildAST();
342 EXPECT_THAT(headersForFoo(),
343 ElementsAre(physicalHeader("bar.h"), physicalHeader("fox.h")));
346 TEST_F(HeadersForSymbolTest, Ranking) {
347 // Sorting is done over (canonical, public, complete, origin)-tuple.
348 Inputs.Code = R"cpp(
349 #include "private.h"
350 #include "public.h"
351 #include "public_complete.h"
352 #include "exporter.h"
353 )cpp";
354 Inputs.ExtraFiles["public.h"] = guard(R"cpp(
355 struct foo;
356 )cpp");
357 Inputs.ExtraFiles["private.h"] = guard(R"cpp(
358 // IWYU pragma: private, include "canonical.h"
359 struct foo;
360 )cpp");
361 Inputs.ExtraFiles["exporter.h"] = guard(R"cpp(
362 #include "private.h" // IWYU pragma: export
363 )cpp");
364 Inputs.ExtraFiles["public_complete.h"] = guard("struct foo {};");
365 buildAST();
366 EXPECT_THAT(headersForFoo(), ElementsAre(Header("\"canonical.h\""),
367 physicalHeader("public_complete.h"),
368 physicalHeader("public.h"),
369 physicalHeader("exporter.h"),
370 physicalHeader("private.h")));
373 TEST_F(HeadersForSymbolTest, PreferPublicOverComplete) {
374 Inputs.Code = R"cpp(
375 #include "complete_private.h"
376 #include "public.h"
377 )cpp";
378 Inputs.ExtraFiles["complete_private.h"] = guard(R"cpp(
379 // IWYU pragma: private
380 struct foo {};
381 )cpp");
382 Inputs.ExtraFiles["public.h"] = guard("struct foo;");
383 buildAST();
384 EXPECT_THAT(headersForFoo(),
385 ElementsAre(physicalHeader("public.h"),
386 physicalHeader("complete_private.h")));
389 TEST_F(HeadersForSymbolTest, PreferNameMatch) {
390 Inputs.Code = R"cpp(
391 #include "public_complete.h"
392 #include "test/foo.fwd.h"
393 )cpp";
394 Inputs.ExtraFiles["public_complete.h"] = guard(R"cpp(
395 struct foo {};
396 )cpp");
397 Inputs.ExtraFiles["test/foo.fwd.h"] = guard("struct foo;");
398 buildAST();
399 EXPECT_THAT(headersForFoo(),
400 ElementsAre(physicalHeader("test/foo.fwd.h"),
401 physicalHeader("public_complete.h")));
404 TEST_F(HeadersForSymbolTest, MainFile) {
405 Inputs.Code = R"cpp(
406 #include "public_complete.h"
407 struct foo;
408 )cpp";
409 Inputs.ExtraFiles["public_complete.h"] = guard(R"cpp(
410 struct foo {};
411 )cpp");
412 buildAST();
413 auto &SM = AST->sourceManager();
414 // FIXME: Symbols provided by main file should be treated specially.
415 EXPECT_THAT(headersForFoo(),
416 ElementsAre(physicalHeader("public_complete.h"),
417 Header(SM.getFileEntryForID(SM.getMainFileID()))));
420 TEST_F(HeadersForSymbolTest, PreferExporterOfPrivate) {
421 Inputs.Code = R"cpp(
422 #include "private.h"
423 #include "exporter.h"
424 )cpp";
425 Inputs.ExtraFiles["private.h"] = guard(R"cpp(
426 // IWYU pragma: private
427 struct foo {};
428 )cpp");
429 Inputs.ExtraFiles["exporter.h"] = guard(R"cpp(
430 #include "private.h" // IWYU pragma: export
431 )cpp");
432 buildAST();
433 EXPECT_THAT(headersForFoo(), ElementsAre(physicalHeader("exporter.h"),
434 physicalHeader("private.h")));
437 TEST_F(HeadersForSymbolTest, ExporterIsDownRanked) {
438 Inputs.Code = R"cpp(
439 #include "exporter.h"
440 #include "zoo.h"
441 )cpp";
442 // Deliberately named as zoo to make sure it doesn't get name-match boost and
443 // also gets lexicographically bigger order than "exporter".
444 Inputs.ExtraFiles["zoo.h"] = guard(R"cpp(
445 struct foo {};
446 )cpp");
447 Inputs.ExtraFiles["exporter.h"] = guard(R"cpp(
448 #include "zoo.h" // IWYU pragma: export
449 )cpp");
450 buildAST();
451 EXPECT_THAT(headersForFoo(), ElementsAre(physicalHeader("zoo.h"),
452 physicalHeader("exporter.h")));
455 TEST_F(HeadersForSymbolTest, PreferPublicOverNameMatchOnPrivate) {
456 Inputs.Code = R"cpp(
457 #include "foo.h"
458 )cpp";
459 Inputs.ExtraFiles["foo.h"] = guard(R"cpp(
460 // IWYU pragma: private, include "public.h"
461 struct foo {};
462 )cpp");
463 buildAST();
464 EXPECT_THAT(headersForFoo(), ElementsAre(Header(StringRef("\"public.h\"")),
465 physicalHeader("foo.h")));
468 TEST_F(HeadersForSymbolTest, AmbiguousStdSymbols) {
469 struct {
470 llvm::StringRef Code;
471 llvm::StringRef Name;
473 llvm::StringRef ExpectedHeader;
474 } TestCases[] = {
476 R"cpp(
477 namespace std {
478 template <typename InputIt, typename OutputIt>
479 constexpr OutputIt move(InputIt first, InputIt last, OutputIt dest);
480 })cpp",
481 "move",
482 "<algorithm>",
485 R"cpp(
486 namespace std {
487 template<typename T> constexpr T move(T&& t) noexcept;
488 })cpp",
489 "move",
490 "<utility>",
493 R"cpp(
494 namespace std {
495 template<class ForwardIt, class T>
496 ForwardIt remove(ForwardIt first, ForwardIt last, const T& value);
497 })cpp",
498 "remove",
499 "<algorithm>",
502 "namespace std { int remove(const char*); }",
503 "remove",
504 "<cstdio>",
508 for (const auto &T : TestCases) {
509 Inputs.Code = T.Code;
510 buildAST();
511 EXPECT_THAT(headersFor(T.Name),
512 UnorderedElementsAre(
513 Header(*tooling::stdlib::Header::named(T.ExpectedHeader))));
517 TEST_F(HeadersForSymbolTest, StandardHeaders) {
518 Inputs.Code = "void assert();";
519 buildAST();
520 EXPECT_THAT(
521 headersFor("assert"),
522 // Respect the ordering from the stdlib mapping.
523 UnorderedElementsAre(tooling::stdlib::Header::named("<cassert>"),
524 tooling::stdlib::Header::named("<assert.h>")));
527 } // namespace
528 } // namespace clang::include_cleaner