if (auto SS = Recognizer(&ND)) {
// FIXME: Also report forward decls from main-file, so that the caller
// can decide to insert/ignore a header.
- return CB({Loc, Symbol(*SS), RT}, findIncludeHeaders(*SS, SM, PI));
+ return CB({Loc, Symbol(*SS), RT}, findHeaders(*SS, SM, PI));
}
// FIXME: Extract locations from redecls.
- return CB({Loc, Symbol(ND), RT},
- findIncludeHeaders(ND.getLocation(), SM, PI));
+ return CB({Loc, Symbol(ND), RT}, findHeaders(ND.getLocation(), SM, PI));
});
}
for (const SymbolReference &MacroRef : MacroRefs) {
assert(MacroRef.Target.kind() == Symbol::Macro);
// FIXME: Handle macro locations.
return CB(MacroRef,
- findIncludeHeaders(MacroRef.Target.macro().Definition, SM, PI));
+ findHeaders(MacroRef.Target.macro().Definition, SM, PI));
}
}
-llvm::SmallVector<Header> findIncludeHeaders(const SymbolLocation &SLoc,
- const SourceManager &SM,
- const PragmaIncludes &PI) {
- llvm::SmallVector<Header> Results;
- if (auto *Loc = std::get_if<SourceLocation>(&SLoc)) {
- // FIXME: Handle non self-contained files.
- FileID FID = SM.getFileID(*Loc);
- const auto *FE = SM.getFileEntryForID(FID);
- if (!FE)
- return {};
-
- // We treat the spelling header in the IWYU pragma as the final public
- // header.
- // FIXME: look for exporters if the public header is exported by another
- // header.
- llvm::StringRef VerbatimSpelling = PI.getPublic(FE);
- if (!VerbatimSpelling.empty())
- return {Header(VerbatimSpelling)};
-
- Results = {Header(FE)};
- // FIXME: compute transitive exporter headers.
- for (const auto *Export : PI.getExporters(FE, SM.getFileManager()))
- Results.push_back(Export);
- return Results;
- }
- if (auto *Sym = std::get_if<tooling::stdlib::Symbol>(&SLoc)) {
- for (const auto &H : Sym->headers())
- Results.push_back(H);
- return Results;
- }
- llvm_unreachable("unhandled SymbolLocation kind!");
-}
-
} // namespace clang::include_cleaner
void walkAST(Decl &Root,
llvm::function_ref<void(SourceLocation, NamedDecl &, RefType)>);
-/// A location where a symbol can be provided.
+/// A place where a symbol can be provided.
/// It is either a physical file of the TU (SourceLocation) or a logical
/// location in the standard library (stdlib::Symbol).
-// FIXME: use a real Class!
-using SymbolLocation = std::variant<SourceLocation, tooling::stdlib::Symbol>;
+struct SymbolLocation {
+ enum Kind {
+ /// A position within a source file (or macro expansion) parsed by clang.
+ Physical,
+ /// A recognized standard library symbol, like std::string.
+ Standard,
+ };
+
+ SymbolLocation(SourceLocation S) : Storage(S) {}
+ SymbolLocation(tooling::stdlib::Symbol S) : Storage(S) {}
+
+ Kind kind() const { return static_cast<Kind>(Storage.index()); }
+ bool operator==(const SymbolLocation &RHS) const {
+ return Storage == RHS.Storage;
+ }
+
+ SourceLocation physical() const { return std::get<Physical>(Storage); }
+ tooling::stdlib::Symbol standard() const {
+ return std::get<Standard>(Storage);
+ }
+
+private:
+ // Order must match Kind enum!
+ std::variant<SourceLocation, tooling::stdlib::Symbol> Storage;
+};
+llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Header &);
/// Finds the headers that provide the symbol location.
// FIXME: expose signals
-llvm::SmallVector<Header> findIncludeHeaders(const SymbolLocation &Loc,
- const SourceManager &SM,
- const PragmaIncludes &PI);
+llvm::SmallVector<Header> findHeaders(const SymbolLocation &Loc,
+ const SourceManager &SM,
+ const PragmaIncludes &PI);
/// Write an HTML summary of the analysis to the given stream.
/// FIXME: Once analysis has a public API, this should be public too.
add_clang_library(clangIncludeCleaner
Analysis.cpp
+ FindHeaders.cpp
HTMLReport.cpp
+ LocateSymbol.cpp
Record.cpp
Types.cpp
WalkAST.cpp
--- /dev/null
+//===--- FindHeaders.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/SourceManager.h"
+
+namespace clang::include_cleaner {
+
+llvm::SmallVector<Header> findHeaders(const SymbolLocation &Loc,
+ const SourceManager &SM,
+ const PragmaIncludes &PI) {
+ llvm::SmallVector<Header> Results;
+ switch (Loc.kind()) {
+ case SymbolLocation::Physical: {
+ // FIXME: Handle non self-contained files.
+ FileID FID = SM.getFileID(Loc.physical());
+ const auto *FE = SM.getFileEntryForID(FID);
+ if (!FE)
+ return {};
+
+ // We treat the spelling header in the IWYU pragma as the final public
+ // header.
+ // FIXME: look for exporters if the public header is exported by another
+ // header.
+ llvm::StringRef VerbatimSpelling = PI.getPublic(FE);
+ if (!VerbatimSpelling.empty())
+ return {Header(VerbatimSpelling)};
+
+ Results = {Header(FE)};
+ // FIXME: compute transitive exporter headers.
+ for (const auto *Export : PI.getExporters(FE, SM.getFileManager()))
+ Results.push_back(Export);
+ return Results;
+ }
+ case SymbolLocation::Standard: {
+ for (const auto &H : Loc.standard().headers())
+ Results.push_back(H);
+ return Results;
+ }
+ }
+ llvm_unreachable("unhandled SymbolLocation kind!");
+}
+
+} // namespace clang::include_cleaner
\ No newline at end of file
--- /dev/null
+//===--- LocateSymbol.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 "llvm/ADT/StringExtras.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang::include_cleaner {
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const SymbolLocation &S) {
+ switch (S.kind()) {
+ case SymbolLocation::Physical:
+ // We can't decode the Location without SourceManager. Its raw
+ // representation isn't completely useless (and distinguishes
+ // SymbolReference from Symbol).
+ return OS << "@0x"
+ << llvm::utohexstr(
+ S.physical().getRawEncoding(), /*LowerCase=*/false,
+ /*Width=*/CHAR_BIT * sizeof(SourceLocation::UIntTy));
+ case SymbolLocation::Standard:
+ return OS << S.standard().scope() << S.standard().name();
+ }
+ llvm_unreachable("Unhandled Symbol kind");
+}
+
+} // namespace clang::include_cleaner
\ No newline at end of file
//===----------------------------------------------------------------------===//
#include "clang-include-cleaner/Analysis.h"
-#include "AnalysisInternal.h"
#include "clang-include-cleaner/Record.h"
#include "clang-include-cleaner/Types.h"
#include "clang/AST/ASTContext.h"
UnorderedElementsAre(Pair(Main.point(), UnorderedElementsAre(HdrFile))));
}
-TEST(FindIncludeHeaders, IWYU) {
- TestInputs Inputs;
- PragmaIncludes PI;
- Inputs.MakeAction = [&PI] {
- 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);
- };
-
- Inputs.Code = R"cpp(
- #include "header1.h"
- #include "header2.h"
- )cpp";
- Inputs.ExtraFiles["header1.h"] = R"cpp(
- // IWYU pragma: private, include "path/public.h"
- )cpp";
- Inputs.ExtraFiles["header2.h"] = R"cpp(
- #include "detail1.h" // IWYU pragma: export
-
- // IWYU pragma: begin_exports
- #include "detail2.h"
- // IWYU pragma: end_exports
-
- #include "normal.h"
- )cpp";
- Inputs.ExtraFiles["normal.h"] = Inputs.ExtraFiles["detail1.h"] =
- Inputs.ExtraFiles["detail2.h"] = "";
- TestAST AST(Inputs);
- const auto &SM = AST.sourceManager();
- auto &FM = SM.getFileManager();
- // Returns the source location for the start of the file.
- auto SourceLocFromFile = [&](llvm::StringRef FileName) {
- return SM.translateFileLineCol(FM.getFile(FileName).get(),
- /*Line=*/1, /*Col=*/1);
- };
-
- EXPECT_THAT(findIncludeHeaders(SourceLocFromFile("header1.h"), SM, PI),
- UnorderedElementsAre(Header("\"path/public.h\"")));
-
- EXPECT_THAT(findIncludeHeaders(SourceLocFromFile("detail1.h"), SM, PI),
- UnorderedElementsAre(Header(FM.getFile("header2.h").get()),
- Header(FM.getFile("detail1.h").get())));
- EXPECT_THAT(findIncludeHeaders(SourceLocFromFile("detail2.h"), SM, PI),
- UnorderedElementsAre(Header(FM.getFile("header2.h").get()),
- Header(FM.getFile("detail2.h").get())));
-
- EXPECT_THAT(findIncludeHeaders(SourceLocFromFile("normal.h"), SM, PI),
- UnorderedElementsAre(Header(FM.getFile("normal.h").get())));
-}
} // namespace
} // namespace clang::include_cleaner
add_custom_target(ClangIncludeCleanerUnitTests)
add_unittest(ClangIncludeCleanerUnitTests ClangIncludeCleanerTests
AnalysisTest.cpp
+ FindHeadersTest.cpp
RecordTest.cpp
WalkASTTest.cpp
)
--- /dev/null
+//===--- 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/Frontend/FrontendActions.h"
+#include "clang/Testing/TestAST.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang::include_cleaner {
+namespace {
+using testing::UnorderedElementsAre;
+
+TEST(FindIncludeHeaders, IWYU) {
+ TestInputs Inputs;
+ PragmaIncludes PI;
+ Inputs.MakeAction = [&PI] {
+ 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);
+ };
+
+ Inputs.Code = R"cpp(
+ #include "header1.h"
+ #include "header2.h"
+ )cpp";
+ Inputs.ExtraFiles["header1.h"] = R"cpp(
+ // IWYU pragma: private, include "path/public.h"
+ )cpp";
+ Inputs.ExtraFiles["header2.h"] = R"cpp(
+ #include "detail1.h" // IWYU pragma: export
+
+ // IWYU pragma: begin_exports
+ #include "detail2.h"
+ // IWYU pragma: end_exports
+
+ #include "normal.h"
+ )cpp";
+ Inputs.ExtraFiles["normal.h"] = Inputs.ExtraFiles["detail1.h"] =
+ Inputs.ExtraFiles["detail2.h"] = "";
+ TestAST AST(Inputs);
+ const auto &SM = AST.sourceManager();
+ auto &FM = SM.getFileManager();
+ // Returns the source location for the start of the file.
+ auto SourceLocFromFile = [&](llvm::StringRef FileName) {
+ return SM.translateFileLineCol(FM.getFile(FileName).get(),
+ /*Line=*/1, /*Col=*/1);
+ };
+
+ EXPECT_THAT(findHeaders(SourceLocFromFile("header1.h"), SM, PI),
+ UnorderedElementsAre(Header("\"path/public.h\"")));
+
+ EXPECT_THAT(findHeaders(SourceLocFromFile("detail1.h"), SM, PI),
+ UnorderedElementsAre(Header(FM.getFile("header2.h").get()),
+ Header(FM.getFile("detail1.h").get())));
+ EXPECT_THAT(findHeaders(SourceLocFromFile("detail2.h"), SM, PI),
+ UnorderedElementsAre(Header(FM.getFile("header2.h").get()),
+ Header(FM.getFile("detail2.h").get())));
+
+ EXPECT_THAT(findHeaders(SourceLocFromFile("normal.h"), SM, PI),
+ UnorderedElementsAre(Header(FM.getFile("normal.h").get())));
+}
+
+} // namespace
+} // namespace clang::include_cleaner
\ No newline at end of file