//===-- RecordTest.cpp ----------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "clang-include-cleaner/Types.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/FileSystemOptions.h" #include "clang/Tooling/Inclusions/StandardLibrary.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/Support/VirtualFileSystem.h" #include "gmock/gmock.h" #include "gtest/gtest.h" namespace clang::include_cleaner { namespace { using testing::ElementsAre; using testing::IsEmpty; using testing::UnorderedElementsAre; // Matches an Include* on the specified line; MATCHER_P(line, N, "") { return arg->Line == (unsigned)N; } TEST(RecordedIncludesTest, Match) { // We're using synthetic data, but need a FileManager to obtain FileEntry*s. // Ensure it doesn't do any actual IO. auto FS = llvm::makeIntrusiveRefCnt(); FileManager FM(FileSystemOptions{}); FileEntryRef A = FM.getVirtualFileRef("/path/a", /*Size=*/0, time_t{}); FileEntryRef B = FM.getVirtualFileRef("/path/b", /*Size=*/0, time_t{}); Includes Inc; Inc.add(Include{"a", A, SourceLocation(), 1}); Inc.add(Include{"a2", A, SourceLocation(), 2}); Inc.add(Include{"b", B, SourceLocation(), 3}); Inc.add(Include{"vector", B, SourceLocation(), 4}); Inc.add(Include{"vector", B, SourceLocation(), 5}); Inc.add(Include{"missing", std::nullopt, SourceLocation(), 6}); EXPECT_THAT(Inc.match(A), ElementsAre(line(1), line(2))); EXPECT_THAT(Inc.match(B), ElementsAre(line(3), line(4), line(5))); EXPECT_THAT(Inc.match(*tooling::stdlib::Header::named("")), ElementsAre(line(4), line(5))); } TEST(RecordedIncludesTest, MatchVerbatim) { auto FS = llvm::makeIntrusiveRefCnt(); FileManager FM(FileSystemOptions{}); Includes Inc; // By default, a verbatim header only matches includes with the same spelling. auto Foo = FM.getVirtualFileRef("repo/lib/include/rel/foo.h", /*Size=*/0, time_t{}); Inc.add(Include{"lib/include/rel/foo.h", Foo, SourceLocation(), 1}); Inc.add(Include{"rel/foo.h", Foo, SourceLocation(), 2}); EXPECT_THAT(Inc.match(Header("")), ElementsAre(line(2))); // A verbatim header can match another spelling if the search path // suggests it's equivalent. auto Bar = FM.getVirtualFileRef("repo/lib/include/rel/bar.h", /*Size=*/0, time_t{}); Inc.addSearchDirectory("repo/"); Inc.addSearchDirectory("repo/lib/include"); Inc.add(Include{"lib/include/rel/bar.h", Bar, SourceLocation(), 3}); Inc.add(Include{"rel/bar.h", Bar, SourceLocation(), 4}); EXPECT_THAT(Inc.match(Header("")), UnorderedElementsAre(line(3), line(4))); // We don't apply this logic to system headers, though. auto Vector = FM.getVirtualFileRef("repo/lib/include/vector", /*Size=*/0, time_t{}); Inc.add(Include{"lib/include/vector", Vector, SourceLocation(), 5}); EXPECT_THAT(Inc.match(Header(*tooling::stdlib::Header::named(""))), IsEmpty()); } TEST(RecordedIncludesTest, MatchVerbatimMixedAbsoluteRelative) { auto FS = llvm::makeIntrusiveRefCnt(); FS->setCurrentWorkingDirectory("/working"); FileManager FM(FileSystemOptions{}); Includes Inc; auto Foo = FM.getVirtualFileRef("/working/rel1/rel2/foo.h", /*Size=*/0, time_t{}); Inc.addSearchDirectory("rel1"); Inc.addSearchDirectory("rel1/rel2"); Inc.add(Include{"rel2/foo.h", Foo, SourceLocation(), 1}); EXPECT_THAT(Inc.match(Header("")), IsEmpty()); Inc = Includes{}; auto Bar = FM.getVirtualFileRef("rel1/rel2/bar.h", /*Size=*/0, time_t{}); Inc.addSearchDirectory("/working/rel1"); Inc.addSearchDirectory("/working/rel1/rel2"); Inc.add(Include{"rel2/bar.h", Bar, SourceLocation(), 1}); EXPECT_THAT(Inc.match(Header("")), IsEmpty()); } } // namespace } // namespace clang::include_cleaner