brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.9 KiB · 574b64e Raw
242 lines · cpp
1//===---- Query.cpp - clang-query query -----------------------------------===//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#include "Query.h"10#include "QueryParser.h"11#include "QuerySession.h"12#include "clang/AST/ASTDumper.h"13#include "clang/ASTMatchers/ASTMatchFinder.h"14#include "clang/Frontend/ASTUnit.h"15#include "clang/Frontend/TextDiagnostic.h"16#include "llvm/Support/raw_ostream.h"17#include <optional>18 19using namespace clang::ast_matchers;20using namespace clang::ast_matchers::dynamic;21 22namespace clang {23namespace query {24 25Query::~Query() {}26 27bool InvalidQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {28  OS << ErrStr << "\n";29  return false;30}31 32bool NoOpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {33  return true;34}35 36bool HelpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {37  OS << "Available commands:\n\n"38        "  match MATCHER, m MATCHER          "39        "Match the loaded ASTs against the given matcher.\n"40        "  let NAME MATCHER, l NAME MATCHER  "41        "Give a matcher expression a name, to be used later\n"42        "                                    "43        "as part of other expressions.\n"44        "  set bind-root (true|false)        "45        "Set whether to bind the root matcher to \"root\".\n"46        "  set print-matcher (true|false)    "47        "Set whether to print the current matcher.\n"48        "  set enable-profile (true|false)   "49        "Set whether to enable matcher profiling.\n"50        "  set traversal <kind>              "51        "Set traversal kind of clang-query session. Available kinds are:\n"52        "    AsIs                            "53        "Print and match the AST as clang sees it.  This mode is the "54        "default.\n"55        "    IgnoreUnlessSpelledInSource     "56        "Omit AST nodes unless spelled in the source.\n"57        "  set output <feature>              "58        "Set whether to output only <feature> content.\n"59        "  enable output <feature>           "60        "Enable <feature> content non-exclusively.\n"61        "  disable output <feature>          "62        "Disable <feature> content non-exclusively.\n"63        "  quit, q                           "64        "Terminates the query session.\n\n"65        "Several commands accept a <feature> parameter. The available features "66        "are:\n\n"67        "  print                             "68        "Pretty-print bound nodes.\n"69        "  diag                              "70        "Diagnostic location for bound nodes.\n"71        "  detailed-ast                      "72        "Detailed AST output for bound nodes.\n"73        "  dump                              "74        "Detailed AST output for bound nodes (alias of detailed-ast).\n\n";75  return true;76}77 78bool QuitQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {79  QS.Terminate = true;80  return true;81}82 83namespace {84 85struct CollectBoundNodes : MatchFinder::MatchCallback {86  std::vector<BoundNodes> &Bindings;87  StringRef Unit;88  CollectBoundNodes(std::vector<BoundNodes> &Bindings, StringRef Unit)89      : Bindings(Bindings), Unit(Unit) {}90  void run(const MatchFinder::MatchResult &Result) override {91    Bindings.push_back(Result.Nodes);92  }93  StringRef getID() const override { return Unit; }94};95 96struct QueryProfiler {97  llvm::StringMap<llvm::TimeRecord> Records;98 99  ~QueryProfiler() {100    llvm::TimerGroup TG("clang-query", "clang-query matcher profiling",101                        Records);102    TG.print(llvm::errs());103    llvm::errs().flush();104  }105};106 107} // namespace108 109bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {110  unsigned MatchCount = 0;111 112  std::optional<QueryProfiler> Profiler;113  if (QS.EnableProfile)114    Profiler.emplace();115 116  for (auto &AST : QS.ASTs) {117    ast_matchers::MatchFinder::MatchFinderOptions FinderOptions;118    std::optional<llvm::StringMap<llvm::TimeRecord>> Records;119    if (QS.EnableProfile) {120      Records.emplace();121      FinderOptions.CheckProfiling.emplace(*Records);122    }123 124    MatchFinder Finder(FinderOptions);125    std::vector<BoundNodes> Matches;126    DynTypedMatcher MaybeBoundMatcher = Matcher;127    if (QS.BindRoot) {128      std::optional<DynTypedMatcher> M = Matcher.tryBind("root");129      if (M)130        MaybeBoundMatcher = *M;131    }132    StringRef OrigSrcName = AST->getOriginalSourceFileName();133    CollectBoundNodes Collect(Matches, OrigSrcName);134    if (!Finder.addDynamicMatcher(MaybeBoundMatcher, &Collect)) {135      OS << "Not a valid top-level matcher.\n";136      return false;137    }138 139    ASTContext &Ctx = AST->getASTContext();140    Ctx.getParentMapContext().setTraversalKind(QS.TK);141    Finder.matchAST(Ctx);142    if (QS.EnableProfile)143      Profiler->Records[OrigSrcName] += (*Records)[OrigSrcName];144 145    if (QS.PrintMatcher) {146      SmallVector<StringRef, 4> Lines;147      Source.split(Lines, "\n");148      auto FirstLine = Lines[0];149      Lines.erase(Lines.begin(), Lines.begin() + 1);150      while (!Lines.empty() && Lines.back().empty()) {151        Lines.resize(Lines.size() - 1);152      }153      unsigned MaxLength = FirstLine.size();154      std::string PrefixText = "Matcher: ";155      OS << "\n  " << PrefixText << FirstLine;156 157      for (auto Line : Lines) {158        OS << "\n" << std::string(PrefixText.size() + 2, ' ') << Line;159        MaxLength = std::max<int>(MaxLength, Line.rtrim().size());160      }161 162      OS << "\n"163         << "  " << std::string(PrefixText.size() + MaxLength, '=') << "\n\n";164    }165 166    for (auto MI = Matches.begin(), ME = Matches.end(); MI != ME; ++MI) {167      OS << "\nMatch #" << ++MatchCount << ":\n\n";168 169      for (auto BI = MI->getMap().begin(), BE = MI->getMap().end(); BI != BE;170           ++BI) {171        if (QS.DiagOutput) {172          clang::SourceRange R = BI->second.getSourceRange();173          if (R.isValid()) {174            TextDiagnostic TD(OS, AST->getASTContext().getLangOpts(),175                              AST->getDiagnostics().getDiagnosticOptions());176            TD.emitDiagnostic(177                FullSourceLoc(R.getBegin(), AST->getSourceManager()),178                DiagnosticsEngine::Note, "\"" + BI->first + "\" binds here",179                CharSourceRange::getTokenRange(R), {});180          }181        }182        if (QS.PrintOutput) {183          OS << "Binding for \"" << BI->first << "\":\n";184          BI->second.print(OS, AST->getASTContext().getPrintingPolicy());185          OS << "\n";186        }187        if (QS.DetailedASTOutput) {188          OS << "Binding for \"" << BI->first << "\":\n";189          ASTDumper Dumper(OS, Ctx, AST->getDiagnostics().getShowColors());190          Dumper.SetTraversalKind(QS.TK);191          Dumper.Visit(BI->second);192          OS << "\n";193        }194      }195 196      if (MI->getMap().empty())197        OS << "No bindings.\n";198    }199  }200 201  OS << MatchCount << (MatchCount == 1 ? " match.\n" : " matches.\n");202  return true;203}204 205bool LetQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {206  if (Value) {207    QS.NamedValues[Name] = Value;208  } else {209    QS.NamedValues.erase(Name);210  }211  return true;212}213 214#ifndef _MSC_VER215const QueryKind SetQueryKind<bool>::value;216const QueryKind SetQueryKind<OutputKind>::value;217#endif218 219bool FileQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {220  auto Buffer = llvm::MemoryBuffer::getFile(StringRef{File}.trim());221  if (!Buffer) {222    if (Prefix.has_value())223      llvm::errs() << *Prefix << ": ";224    llvm::errs() << "cannot open " << File << ": "225                 << Buffer.getError().message() << "\n";226    return false;227  }228 229  StringRef FileContentRef(Buffer.get()->getBuffer());230 231  while (!FileContentRef.empty()) {232    QueryRef Q = QueryParser::parse(FileContentRef, QS);233    if (!Q->run(llvm::outs(), QS))234      return false;235    FileContentRef = Q->RemainingContent;236  }237  return true;238}239 240} // namespace query241} // namespace clang242