//===---- tools/extra/ToolTemplate.cpp - Template for refactoring tool ----===// // // 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 // //===----------------------------------------------------------------------===// // // This file implements an empty refactoring tool using the clang tooling. // The goal is to lower the "barrier to entry" for writing refactoring tools. // // Usage: // tool-template ... // // Where is a CMake build directory in which a file named // compile_commands.json exists (enable -DCMAKE_EXPORT_COMPILE_COMMANDS in // CMake to get this output). // // ... specify the paths of files in the CMake source tree. This path // is looked up in the compile command database. If the path of a file is // absolute, it needs to point into CMake's source tree. If the path is // relative, the current working directory needs to be in the CMake source // tree and the file must be in a subdirectory of the current working // directory. "./" prefixes in the relative files will be automatically // removed, but the rest of a relative path must be a suffix of a path in // the compile command line database. // // For example, to use tool-template on all files in a subtree of the // source tree, use: // // /path/in/subtree $ find . -name '*.cpp'| // xargs tool-template /path/to/build // //===----------------------------------------------------------------------===// #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Basic/SourceManager.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Lex/Lexer.h" #include "clang/Tooling/CommonOptionsParser.h" #include "clang/Tooling/Execution.h" #include "clang/Tooling/Refactoring.h" #include "clang/Tooling/Refactoring/AtomicChange.h" #include "clang/Tooling/Tooling.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Signals.h" using namespace clang; using namespace clang::ast_matchers; using namespace clang::tooling; using namespace llvm; namespace { class ToolTemplateCallback : public MatchFinder::MatchCallback { public: ToolTemplateCallback(ExecutionContext &Context) : Context(Context) {} void run(const MatchFinder::MatchResult &Result) override { // TODO: This routine will get called for each thing that the matchers // find. // At this point, you can examine the match, and do whatever you want, // including replacing the matched text with other text auto *D = Result.Nodes.getNodeAs("decl"); assert(D); // Use AtomicChange to get a key. if (D->getBeginLoc().isValid()) { AtomicChange Change(*Result.SourceManager, D->getBeginLoc()); Context.reportResult(Change.getKey(), D->getQualifiedNameAsString()); } } void onStartOfTranslationUnit() override { Context.reportResult("START", "Start of TU."); } void onEndOfTranslationUnit() override { Context.reportResult("END", "End of TU."); } private: ExecutionContext &Context; }; } // end anonymous namespace // Set up the command line options static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage); static cl::OptionCategory ToolTemplateCategory("tool-template options"); int main(int argc, const char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); auto Executor = clang::tooling::createExecutorFromCommandLineArgs( argc, argv, ToolTemplateCategory); if (!Executor) { llvm::errs() << llvm::toString(Executor.takeError()) << "\n"; return 1; } ast_matchers::MatchFinder Finder; ToolTemplateCallback Callback(*Executor->get()->getExecutionContext()); // TODO: Put your matchers here. // Use Finder.addMatcher(...) to define the patterns in the AST that you // want to match against. You are not limited to just one matcher! // // This is a sample matcher: Finder.addMatcher( namedDecl(cxxRecordDecl(), isExpansionInMainFile()).bind("decl"), &Callback); auto Err = Executor->get()->execute(newFrontendActionFactory(&Finder)); if (Err) { llvm::errs() << llvm::toString(std::move(Err)) << "\n"; } Executor->get()->getToolResults()->forEachResult( [](llvm::StringRef key, llvm::StringRef value) { llvm::errs() << "----" << key.str() << "\n" << value.str() << "\n"; }); }