diff --git a/clang-tools-extra/clangd/CMakeLists.txt b/clang-tools-extra/clangd/CMakeLists.txt --- a/clang-tools-extra/clangd/CMakeLists.txt +++ b/clang-tools-extra/clangd/CMakeLists.txt @@ -180,6 +180,7 @@ add_subdirectory(fuzzer) endif() add_subdirectory(tool) +add_subdirectory(include-cleaner) add_subdirectory(indexer) if (LLVM_INCLUDE_BENCHMARKS) diff --git a/clang-tools-extra/clangd/include-cleaner/CMakeLists.txt b/clang-tools-extra/clangd/include-cleaner/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/include-cleaner/CMakeLists.txt @@ -0,0 +1,29 @@ +add_clang_tool(clang-include-cleaner + ClangIncludeCleanerMain.cpp + ) + +set(LLVM_LINK_COMPONENTS + support + ) + +clang_target_link_libraries(clang-include-cleaner + PRIVATE + clangAST + clangBasic + clangFormat + clangFrontend + clangLex + clangSema + clangTooling + clangToolingCore + clangToolingRefactoring + clangToolingSyntax + ) + +target_link_libraries(clang-include-cleaner + PRIVATE + + clangDaemon + clangdRemoteIndex + clangdSupport + ) diff --git a/clang-tools-extra/clangd/include-cleaner/ClangIncludeCleanerMain.cpp b/clang-tools-extra/clangd/include-cleaner/ClangIncludeCleanerMain.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/include-cleaner/ClangIncludeCleanerMain.cpp @@ -0,0 +1,69 @@ +#include "ClangdServer.h" +#include "support/Logger.h" +#include "llvm/Support/Path.h" + +using namespace clang::clangd; + +struct report { + PathRef File; + void operator()(llvm::Expected> C) { + if (C) { + auto &Added = C.get(); + if (Added.empty()) + return; + llvm::errs() << "Found " << Added.size() << " unused include" + << (Added.size() == 1 ? " " : "s ") << "in: " << File + << "\n"; + for (auto const *I : Added) { + llvm::outs() << "- " << I->Resolved << "\n"; + } + } else + llvm::errs() << C.takeError(); + } +}; + +class NullLogger : public Logger { + void log(Level, const char *Fmt, + const llvm::formatv_object_base &Message) final {} +}; + +int main(int argc, char **argv) { + if (argc < 2) { + llvm::errs() + << "usage: clang-include-cleaner compile_commands_dir files...\n"; + return 1; + } + RealThreadsafeFS TFS; + DirectoryBasedGlobalCompilationDatabase::Options CDBOpts(TFS); + llvm::SmallString<64> CCD; + auto errc = llvm::sys::fs::real_path(argv[1], CCD); + if (errc) { + llvm::errs() << argv[1] << ": " << errc.message() << "\n"; + return 2; + } + CDBOpts.CompileCommandsDir = CCD.str().str(); + DirectoryBasedGlobalCompilationDatabase CDB(CDBOpts); + + StreamLogger SLog(llvm::errs(), Logger::Error); + clang::clangd::LoggingSession LoggingSession(SLog); + + ClangdServer::Options CSOpts; + ClangdServer CS(CDB, TFS, CSOpts); + + for (int i = 2; i < argc; ++i) { + PathRef File = argv[i]; + auto MB = llvm::MemoryBuffer::getFile(File); + if (!MB) { + llvm::errs() << "failed to open " << File; + return 1; + } + CS.addDocument(File, MB.get()->getBuffer()); + CS.findUnusedIncludes(File, report{File}); + + // We're using clangd in synchronous mode + while (CS.blockUntilIdleForTest(.1)) + ; + } + + return 0; +}