Sam McCall 6fa0e026c8 [include-cleaner] Add include-cleaner tool, with initial HTML report
The immediate goal is to start producing an HTML report to debug and explain
include-cleaner recommendations.
For now, this includes only the lowest-level piece: a list of the references
found in the source code.

How this fits into future ideas:
 - under refs we can also show the headers providing the symbol, which includes
   match those headers etc
 - we can also annotate the #include lines with which symbols they cover, and
   add whichever includes we're suggesting too
 - the include-cleaner tool will likely have modes where it emits diagnostics
   and/or applies edits, so the HTML report is behind a flag

Differential Revision: https://reviews.llvm.org/D135956
2022-10-18 18:09:41 +02:00

101 lines
3.3 KiB
C++

//===--- IncludeCleaner.cpp - standalone tool for include analysis --------===//
//
// 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/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
namespace clang {
namespace include_cleaner {
namespace {
namespace cl = llvm::cl;
llvm::StringRef Overview = llvm::StringLiteral(R"(
clang-include-cleaner analyzes the #include directives in source code.
It suggests removing headers that the code is not using.
It suggests inserting headers that the code relies on, but does not include.
These changes make the file more self-contained and (at scale) make the codebase
easier to reason about and modify.
The tool operates on *working* source code. This means it can suggest including
headers that are only indirectly included, but cannot suggest those that are
missing entirely. (clang-include-fixer can do this).
)")
.trim();
cl::OptionCategory IncludeCleaner("clang-include-cleaner");
cl::opt<std::string> HTMLReportPath{
"html",
cl::desc("Specify an output filename for an HTML report. "
"This describes both recommendations and reasons for changes."),
cl::cat(IncludeCleaner),
};
class HTMLReportAction : public clang::ASTFrontendAction {
RecordedAST AST;
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
StringRef File) override {
return AST.record();
}
void EndSourceFile() override {
std::error_code EC;
llvm::raw_fd_ostream OS(HTMLReportPath, EC);
if (EC) {
llvm::errs() << "Unable to write HTML report to " << HTMLReportPath
<< ": " << EC.message() << "\n";
exit(1);
}
writeHTMLReport(AST.Ctx->getSourceManager().getMainFileID(), AST.Roots,
*AST.Ctx, OS);
}
};
} // namespace
} // namespace include_cleaner
} // namespace clang
int main(int argc, const char **argv) {
using namespace clang::include_cleaner;
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
auto OptionsParser =
clang::tooling::CommonOptionsParser::create(argc, argv, IncludeCleaner);
if (!OptionsParser) {
llvm::errs() << toString(OptionsParser.takeError());
return 1;
}
std::unique_ptr<clang::tooling::FrontendActionFactory> Factory;
if (HTMLReportPath.getNumOccurrences()) {
if (OptionsParser->getSourcePathList().size() != 1) {
llvm::errs() << "-" << HTMLReportPath.ArgStr
<< " requires a single input file";
return 1;
}
Factory = clang::tooling::newFrontendActionFactory<HTMLReportAction>();
} else {
llvm::errs() << "Unimplemented\n";
return 1;
}
return clang::tooling::ClangTool(OptionsParser->getCompilations(),
OptionsParser->getSourcePathList())
.run(Factory.get());
}