blob: 609563da488e3b500d95365506a02cc3b6fdf4b9 [file] [log] [blame]
//===-- 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<llvm::vfs::InMemoryFileSystem>();
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("<vector>")),
ElementsAre(line(4), line(5)));
}
TEST(RecordedIncludesTest, MatchVerbatim) {
auto FS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
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("<rel/foo.h>")), 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("<rel/bar.h>")),
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("<vector>"))),
IsEmpty());
}
TEST(RecordedIncludesTest, MatchVerbatimMixedAbsoluteRelative) {
auto FS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
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("<foo.h>")), 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("<bar.h>")), IsEmpty());
}
} // namespace
} // namespace clang::include_cleaner