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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "Query.h"
10 #include "QuerySession.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Frontend/ASTUnit.h"
13 #include "clang/Frontend/TextDiagnostic.h"
14 #include "llvm/Support/raw_ostream.h"
15 
16 using namespace clang::ast_matchers;
17 using namespace clang::ast_matchers::dynamic;
18 
19 namespace clang {
20 namespace query {
21 
22 Query::~Query() {}
23 
24 bool InvalidQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
25   OS << ErrStr << "\n";
26   return false;
27 }
28 
29 bool NoOpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
30   return true;
31 }
32 
33 bool HelpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
34   OS << "Available commands:\n\n"
35         "  match MATCHER, m MATCHER          "
36         "Match the loaded ASTs against the given matcher.\n"
37         "  let NAME MATCHER, l NAME MATCHER  "
38         "Give a matcher expression a name, to be used later\n"
39         "                                    "
40         "as part of other expressions.\n"
41         "  set bind-root (true|false)        "
42         "Set whether to bind the root matcher to \"root\".\n"
43         "  set print-matcher (true|false)    "
44         "Set whether to print the current matcher,\n"
45         "  set output <feature>              "
46         "Set whether to output only <feature> content.\n"
47         "  enable output <feature>           "
48         "Enable <feature> content non-exclusively.\n"
49         "  disable output <feature>          "
50         "Disable <feature> content non-exclusively.\n"
51         "  quit, q                           "
52         "Terminates the query session.\n\n"
53         "Several commands accept a <feature> parameter. The available features "
54         "are:\n\n"
55         "  print                             "
56         "Pretty-print bound nodes.\n"
57         "  diag                              "
58         "Diagnostic location for bound nodes.\n"
59         "  detailed-ast                      "
60         "Detailed AST output for bound nodes.\n"
61         "  dump                              "
62         "Detailed AST output for bound nodes (alias of detailed-ast).\n\n";
63   return true;
64 }
65 
66 bool QuitQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
67   QS.Terminate = true;
68   return true;
69 }
70 
71 namespace {
72 
73 struct CollectBoundNodes : MatchFinder::MatchCallback {
74   std::vector<BoundNodes> &Bindings;
75   CollectBoundNodes(std::vector<BoundNodes> &Bindings) : Bindings(Bindings) {}
76   void run(const MatchFinder::MatchResult &Result) override {
77     Bindings.push_back(Result.Nodes);
78   }
79 };
80 
81 } // namespace
82 
83 bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
84   unsigned MatchCount = 0;
85 
86   for (auto &AST : QS.ASTs) {
87     MatchFinder Finder;
88     std::vector<BoundNodes> Matches;
89     DynTypedMatcher MaybeBoundMatcher = Matcher;
90     if (QS.BindRoot) {
91       llvm::Optional<DynTypedMatcher> M = Matcher.tryBind("root");
92       if (M)
93         MaybeBoundMatcher = *M;
94     }
95     CollectBoundNodes Collect(Matches);
96     if (!Finder.addDynamicMatcher(MaybeBoundMatcher, &Collect)) {
97       OS << "Not a valid top-level matcher.\n";
98       return false;
99     }
100     Finder.matchAST(AST->getASTContext());
101 
102     if (QS.PrintMatcher) {
103       std::string prefixText = "Matcher: ";
104       OS << "\n  " << prefixText << Source << "\n";
105       OS << "  " << std::string(prefixText.size() + Source.size(), '=') << '\n';
106     }
107 
108     for (auto MI = Matches.begin(), ME = Matches.end(); MI != ME; ++MI) {
109       OS << "\nMatch #" << ++MatchCount << ":\n\n";
110 
111       for (auto BI = MI->getMap().begin(), BE = MI->getMap().end(); BI != BE;
112            ++BI) {
113         if (QS.DiagOutput) {
114           clang::SourceRange R = BI->second.getSourceRange();
115           if (R.isValid()) {
116             TextDiagnostic TD(OS, AST->getASTContext().getLangOpts(),
117                               &AST->getDiagnostics().getDiagnosticOptions());
118             TD.emitDiagnostic(
119                 FullSourceLoc(R.getBegin(), AST->getSourceManager()),
120                 DiagnosticsEngine::Note, "\"" + BI->first + "\" binds here",
121                 CharSourceRange::getTokenRange(R), None);
122           }
123         }
124         if (QS.PrintOutput) {
125           OS << "Binding for \"" << BI->first << "\":\n";
126           BI->second.print(OS, AST->getASTContext().getPrintingPolicy());
127           OS << "\n";
128         }
129         if (QS.DetailedASTOutput) {
130           OS << "Binding for \"" << BI->first << "\":\n";
131           BI->second.dump(OS, AST->getSourceManager());
132           OS << "\n";
133         }
134       }
135 
136       if (MI->getMap().empty())
137         OS << "No bindings.\n";
138     }
139   }
140 
141   OS << MatchCount << (MatchCount == 1 ? " match.\n" : " matches.\n");
142   return true;
143 }
144 
145 bool LetQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
146   if (Value) {
147     QS.NamedValues[Name] = Value;
148   } else {
149     QS.NamedValues.erase(Name);
150   }
151   return true;
152 }
153 
154 #ifndef _MSC_VER
155 const QueryKind SetQueryKind<bool>::value;
156 const QueryKind SetQueryKind<OutputKind>::value;
157 #endif
158 
159 } // namespace query
160 } // namespace clang
161