169 lines · cpp
1//===---- ClangQuery.cpp - clang-query tool -------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This tool is for interactive exploration of the Clang AST using AST matchers.10// It currently allows the user to enter a matcher at an interactive prompt and11// view the resulting bindings as diagnostics, AST pretty prints or AST dumps.12// Example session:13//14// $ cat foo.c15// void foo(void) {}16// $ clang-query foo.c --17// clang-query> match functionDecl()18//19// Match #1:20//21// foo.c:1:1: note: "root" binds here22// void foo(void) {}23// ^~~~~~~~~~~~~~~~~24// 1 match.25//26//===----------------------------------------------------------------------===//27 28#include "Query.h"29#include "QueryParser.h"30#include "QuerySession.h"31#include "clang/Frontend/ASTUnit.h"32#include "clang/Tooling/CommonOptionsParser.h"33#include "clang/Tooling/Tooling.h"34#include "llvm/LineEditor/LineEditor.h"35#include "llvm/Support/CommandLine.h"36#include "llvm/Support/Error.h"37#include "llvm/Support/MemoryBuffer.h"38#include "llvm/Support/Signals.h"39#include "llvm/Support/WithColor.h"40#include <optional>41#include <string>42 43using namespace clang;44using namespace clang::ast_matchers;45using namespace clang::ast_matchers::dynamic;46using namespace clang::query;47using namespace clang::tooling;48using namespace llvm;49 50static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);51static cl::OptionCategory ClangQueryCategory("clang-query options");52 53static cl::opt<bool>54 UseColor("use-color",55 cl::desc(56 R"(Use colors in detailed AST output. If not set, colors57will be used if the terminal connected to58standard output supports colors.)"),59 cl::init(false), cl::cat(ClangQueryCategory));60 61static cl::list<std::string> Commands("c", cl::desc("Specify command to run"),62 cl::value_desc("command"),63 cl::cat(ClangQueryCategory));64 65static cl::list<std::string> CommandFiles("f",66 cl::desc("Read commands from file"),67 cl::value_desc("file"),68 cl::cat(ClangQueryCategory));69 70static cl::opt<std::string> PreloadFile(71 "preload",72 cl::desc("Preload commands from file and start interactive mode"),73 cl::value_desc("file"), cl::cat(ClangQueryCategory));74 75bool runCommandsInFile(const char *ExeName, std::string const &FileName,76 QuerySession &QS) {77 FileQuery Query(FileName, ExeName);78 return !Query.run(llvm::errs(), QS);79}80 81int main(int argc, const char **argv) {82 llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);83 84 llvm::Expected<CommonOptionsParser> OptionsParser =85 CommonOptionsParser::create(argc, argv, ClangQueryCategory,86 llvm::cl::OneOrMore);87 88 if (!OptionsParser) {89 llvm::WithColor::error() << llvm::toString(OptionsParser.takeError());90 return 1;91 }92 93 if (!Commands.empty() && !CommandFiles.empty()) {94 llvm::errs() << argv[0] << ": cannot specify both -c and -f\n";95 return 1;96 }97 98 if ((!Commands.empty() || !CommandFiles.empty()) && !PreloadFile.empty()) {99 llvm::errs() << argv[0]100 << ": cannot specify both -c or -f with --preload\n";101 return 1;102 }103 104 ClangTool Tool(OptionsParser->getCompilations(),105 OptionsParser->getSourcePathList());106 107 if (UseColor.getNumOccurrences() > 0) {108 ArgumentsAdjuster colorAdjustor = [](const CommandLineArguments &Args, StringRef /*unused*/) {109 CommandLineArguments AdjustedArgs = Args;110 if (UseColor)111 AdjustedArgs.push_back("-fdiagnostics-color");112 else113 AdjustedArgs.push_back("-fno-diagnostics-color");114 return AdjustedArgs;115 };116 Tool.appendArgumentsAdjuster(colorAdjustor);117 }118 119 std::vector<std::unique_ptr<ASTUnit>> ASTs;120 int ASTStatus = 0;121 switch (Tool.buildASTs(ASTs)) {122 case 0:123 break;124 case 1: // Building ASTs failed.125 return 1;126 case 2:127 ASTStatus |= 1;128 llvm::errs() << "Failed to build AST for some of the files, "129 << "results may be incomplete."130 << "\n";131 break;132 default:133 llvm_unreachable("Unexpected status returned");134 }135 136 QuerySession QS(ASTs);137 138 if (!Commands.empty()) {139 for (auto &Command : Commands) {140 QueryRef Q = QueryParser::parse(Command, QS);141 if (!Q->run(llvm::outs(), QS))142 return 1;143 }144 } else if (!CommandFiles.empty()) {145 for (auto &CommandFile : CommandFiles) {146 if (runCommandsInFile(argv[0], CommandFile, QS))147 return 1;148 }149 } else {150 if (!PreloadFile.empty()) {151 if (runCommandsInFile(argv[0], PreloadFile, QS))152 return 1;153 }154 LineEditor LE("clang-query");155 LE.setListCompleter([&QS](StringRef Line, size_t Pos) {156 return QueryParser::complete(Line, Pos, QS);157 });158 while (std::optional<std::string> Line = LE.readLine()) {159 QueryRef Q = QueryParser::parse(*Line, QS);160 Q->run(llvm::outs(), QS);161 llvm::outs().flush();162 if (QS.Terminate)163 break;164 }165 }166 167 return ASTStatus;168}169