llvm-project/clang-tools-extra/include-cleaner/unittests/FindHeadersTest.cpp

157 lines
4.9 KiB
C++

//===--- FindHeadersTest.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 "AnalysisInternal.h"
#include "clang-include-cleaner/Record.h"
#include "clang/Basic/FileEntry.h"
#include "clang/Basic/FileManager.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Testing/TestAST.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <memory>
namespace clang::include_cleaner {
namespace {
using testing::UnorderedElementsAre;
std::string guard(llvm::StringRef Code) {
return "#pragma once\n" + Code.str();
}
class FindHeadersTest : public testing::Test {
protected:
TestInputs Inputs;
PragmaIncludes PI;
std::unique_ptr<TestAST> AST;
FindHeadersTest() {
Inputs.MakeAction = [this] {
struct Hook : public PreprocessOnlyAction {
public:
Hook(PragmaIncludes *Out) : Out(Out) {}
bool BeginSourceFileAction(clang::CompilerInstance &CI) override {
Out->record(CI);
return true;
}
PragmaIncludes *Out;
};
return std::make_unique<Hook>(&PI);
};
}
void buildAST() { AST = std::make_unique<TestAST>(Inputs); }
llvm::SmallVector<Header> findHeaders(llvm::StringRef FileName) {
return include_cleaner::findHeaders(
AST->sourceManager().translateFileLineCol(
AST->fileManager().getFile(FileName).get(),
/*Line=*/1, /*Col=*/1),
AST->sourceManager(), &PI);
}
const FileEntry *physicalHeader(llvm::StringRef FileName) {
return AST->fileManager().getFile(FileName).get();
};
};
TEST_F(FindHeadersTest, IWYUPrivateToPublic) {
Inputs.Code = R"cpp(
#include "private.h"
)cpp";
Inputs.ExtraFiles["private.h"] = guard(R"cpp(
// IWYU pragma: private, include "path/public.h"
)cpp");
buildAST();
EXPECT_THAT(findHeaders("private.h"),
UnorderedElementsAre(physicalHeader("private.h"),
Header("\"path/public.h\"")));
}
TEST_F(FindHeadersTest, IWYUExport) {
Inputs.Code = R"cpp(
#include "exporter.h"
)cpp";
Inputs.ExtraFiles["exporter.h"] = guard(R"cpp(
#include "exported1.h" // IWYU pragma: export
// IWYU pragma: begin_exports
#include "exported2.h"
// IWYU pragma: end_exports
#include "normal.h"
)cpp");
Inputs.ExtraFiles["exported1.h"] = guard("");
Inputs.ExtraFiles["exported2.h"] = guard("");
Inputs.ExtraFiles["normal.h"] = guard("");
buildAST();
EXPECT_THAT(findHeaders("exported1.h"),
UnorderedElementsAre(physicalHeader("exported1.h"),
physicalHeader("exporter.h")));
EXPECT_THAT(findHeaders("exported2.h"),
UnorderedElementsAre(physicalHeader("exported2.h"),
physicalHeader("exporter.h")));
EXPECT_THAT(findHeaders("normal.h"),
UnorderedElementsAre(physicalHeader("normal.h")));
EXPECT_THAT(findHeaders("exporter.h"),
UnorderedElementsAre(physicalHeader("exporter.h")));
}
TEST_F(FindHeadersTest, SelfContained) {
Inputs.Code = R"cpp(
#include "header.h"
)cpp";
Inputs.ExtraFiles["header.h"] = guard(R"cpp(
#include "fragment.inc"
)cpp");
Inputs.ExtraFiles["fragment.inc"] = "";
buildAST();
EXPECT_THAT(findHeaders("fragment.inc"),
UnorderedElementsAre(physicalHeader("fragment.inc"),
physicalHeader("header.h")));
}
TEST_F(FindHeadersTest, NonSelfContainedTraversePrivate) {
Inputs.Code = R"cpp(
#include "header.h"
)cpp";
Inputs.ExtraFiles["header.h"] = guard(R"cpp(
#include "fragment.inc"
)cpp");
Inputs.ExtraFiles["fragment.inc"] = R"cpp(
// IWYU pragma: private, include "public.h"
)cpp";
buildAST();
// There is a IWYU private mapping in the non self-contained header, verify
// that we don't emit its includer.
EXPECT_THAT(findHeaders("fragment.inc"),
UnorderedElementsAre(physicalHeader("fragment.inc"),
Header("\"public.h\"")));
}
TEST_F(FindHeadersTest, NonSelfContainedTraverseExporter) {
Inputs.Code = R"cpp(
#include "exporter.h"
)cpp";
Inputs.ExtraFiles["exporter.h"] = guard(R"cpp(
#include "exported.h" // IWYU pragma: export
)cpp");
Inputs.ExtraFiles["exported.h"] = guard(R"cpp(
#include "fragment.inc"
)cpp");
Inputs.ExtraFiles["fragment.inc"] = "";
buildAST();
// Verify that we emit exporters for each header on the path.
EXPECT_THAT(findHeaders("fragment.inc"),
UnorderedElementsAre(physicalHeader("fragment.inc"),
physicalHeader("exported.h"),
physicalHeader("exporter.h")));
}
} // namespace
} // namespace clang::include_cleaner