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