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