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 "clang/Tooling/NodeIntrospection.h" 16 #include "llvm/Support/raw_ostream.h" 17 18 using namespace clang::ast_matchers; 19 using namespace clang::ast_matchers::dynamic; 20 21 namespace clang { 22 namespace query { 23 24 Query::~Query() {} 25 26 bool InvalidQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const { 27 OS << ErrStr << "\n"; 28 return false; 29 } 30 31 bool NoOpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const { 32 return true; 33 } 34 35 bool HelpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const { 36 OS << "Available commands:\n\n" 37 " match MATCHER, m MATCHER " 38 "Match the loaded ASTs against the given matcher.\n" 39 " let NAME MATCHER, l NAME MATCHER " 40 "Give a matcher expression a name, to be used later\n" 41 " " 42 "as part of other expressions.\n" 43 " set bind-root (true|false) " 44 "Set whether to bind the root matcher to \"root\".\n" 45 " set print-matcher (true|false) " 46 "Set whether to print the current matcher,\n" 47 " set traversal <kind> " 48 "Set traversal kind of clang-query session. Available kinds are:\n" 49 " AsIs " 50 "Print and match the AST as clang sees it. This mode is the " 51 "default.\n" 52 " IgnoreUnlessSpelledInSource " 53 "Omit AST nodes unless spelled in the source.\n" 54 " set output <feature> " 55 "Set whether to output only <feature> content.\n" 56 " enable output <feature> " 57 "Enable <feature> content non-exclusively.\n" 58 " disable output <feature> " 59 "Disable <feature> content non-exclusively.\n" 60 " quit, q " 61 "Terminates the query session.\n\n" 62 "Several commands accept a <feature> parameter. The available features " 63 "are:\n\n" 64 " print " 65 "Pretty-print bound nodes.\n" 66 " diag " 67 "Diagnostic location for bound nodes.\n" 68 " detailed-ast " 69 "Detailed AST output for bound nodes.\n" 70 " srcloc " 71 "Source locations and ranges for bound nodes.\n" 72 " dump " 73 "Detailed AST output for bound nodes (alias of detailed-ast).\n\n"; 74 return true; 75 } 76 77 bool QuitQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const { 78 QS.Terminate = true; 79 return true; 80 } 81 82 namespace { 83 84 struct CollectBoundNodes : MatchFinder::MatchCallback { 85 std::vector<BoundNodes> &Bindings; 86 CollectBoundNodes(std::vector<BoundNodes> &Bindings) : Bindings(Bindings) {} 87 void run(const MatchFinder::MatchResult &Result) override { 88 Bindings.push_back(Result.Nodes); 89 } 90 }; 91 92 void dumpLocations(llvm::raw_ostream &OS, DynTypedNode Node, ASTContext &Ctx, 93 const DiagnosticsEngine &Diags, SourceManager const &SM) { 94 auto Locs = clang::tooling::NodeIntrospection::GetLocations(Node); 95 96 auto PrintLocations = [](llvm::raw_ostream &OS, auto Iter, auto End) { 97 auto CommonEntry = Iter->first; 98 auto Scout = Iter; 99 SmallVector<std::string> LocationStrings; 100 while (Scout->first == CommonEntry) { 101 LocationStrings.push_back( 102 tooling::LocationCallFormatterCpp::format(*Iter->second)); 103 if (Scout == End) 104 break; 105 ++Scout; 106 if (Scout->first == CommonEntry) 107 ++Iter; 108 } 109 llvm::sort(LocationStrings); 110 for (auto &LS : LocationStrings) { 111 OS << " * \"" << LS << "\"\n"; 112 } 113 return Iter; 114 }; 115 116 TextDiagnostic TD(OS, Ctx.getLangOpts(), &Diags.getDiagnosticOptions()); 117 118 for (auto Iter = Locs.LocationAccessors.begin(); 119 Iter != Locs.LocationAccessors.end(); ++Iter) { 120 if (!Iter->first.isValid()) 121 continue; 122 123 TD.emitDiagnostic(FullSourceLoc(Iter->first, SM), DiagnosticsEngine::Note, 124 "source locations here", None, None); 125 126 Iter = PrintLocations(OS, Iter, Locs.LocationAccessors.end()); 127 OS << '\n'; 128 } 129 130 for (auto Iter = Locs.RangeAccessors.begin(); 131 Iter != Locs.RangeAccessors.end(); ++Iter) { 132 133 if (!Iter->first.getBegin().isValid()) 134 continue; 135 136 if (SM.getPresumedLineNumber(Iter->first.getBegin()) != 137 SM.getPresumedLineNumber(Iter->first.getEnd())) 138 continue; 139 140 TD.emitDiagnostic(FullSourceLoc(Iter->first.getBegin(), SM), 141 DiagnosticsEngine::Note, 142 "source ranges here " + Iter->first.printToString(SM), 143 CharSourceRange::getTokenRange(Iter->first), None); 144 145 Iter = PrintLocations(OS, Iter, Locs.RangeAccessors.end()); 146 } 147 for (auto Iter = Locs.RangeAccessors.begin(); 148 Iter != Locs.RangeAccessors.end(); ++Iter) { 149 150 if (!Iter->first.getBegin().isValid()) 151 continue; 152 153 if (SM.getPresumedLineNumber(Iter->first.getBegin()) == 154 SM.getPresumedLineNumber(Iter->first.getEnd())) 155 continue; 156 157 TD.emitDiagnostic( 158 FullSourceLoc(Iter->first.getBegin(), SM), DiagnosticsEngine::Note, 159 "source range " + Iter->first.printToString(SM) + " starting here...", 160 CharSourceRange::getTokenRange(Iter->first), None); 161 162 auto ColNum = SM.getPresumedColumnNumber(Iter->first.getEnd()); 163 auto LastLineLoc = Iter->first.getEnd().getLocWithOffset(-(ColNum - 1)); 164 165 TD.emitDiagnostic(FullSourceLoc(Iter->first.getEnd(), SM), 166 DiagnosticsEngine::Note, "... ending here", 167 CharSourceRange::getTokenRange( 168 SourceRange(LastLineLoc, Iter->first.getEnd())), 169 None); 170 171 Iter = PrintLocations(OS, Iter, Locs.RangeAccessors.end()); 172 } 173 OS << "\n"; 174 } 175 176 } // namespace 177 178 bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const { 179 unsigned MatchCount = 0; 180 181 for (auto &AST : QS.ASTs) { 182 MatchFinder Finder; 183 std::vector<BoundNodes> Matches; 184 DynTypedMatcher MaybeBoundMatcher = Matcher; 185 if (QS.BindRoot) { 186 llvm::Optional<DynTypedMatcher> M = Matcher.tryBind("root"); 187 if (M) 188 MaybeBoundMatcher = *M; 189 } 190 CollectBoundNodes Collect(Matches); 191 if (!Finder.addDynamicMatcher(MaybeBoundMatcher, &Collect)) { 192 OS << "Not a valid top-level matcher.\n"; 193 return false; 194 } 195 196 auto &Ctx = AST->getASTContext(); 197 const auto &SM = Ctx.getSourceManager(); 198 Ctx.getParentMapContext().setTraversalKind(QS.TK); 199 Finder.matchAST(Ctx); 200 201 if (QS.PrintMatcher) { 202 SmallVector<StringRef, 4> Lines; 203 Source.split(Lines, "\n"); 204 auto FirstLine = Lines[0]; 205 Lines.erase(Lines.begin(), Lines.begin() + 1); 206 while (!Lines.empty() && Lines.back().empty()) { 207 Lines.resize(Lines.size() - 1); 208 } 209 unsigned MaxLength = FirstLine.size(); 210 std::string PrefixText = "Matcher: "; 211 OS << "\n " << PrefixText << FirstLine; 212 213 for (auto Line : Lines) { 214 OS << "\n" << std::string(PrefixText.size() + 2, ' ') << Line; 215 MaxLength = std::max<int>(MaxLength, Line.rtrim().size()); 216 } 217 218 OS << "\n" 219 << " " << std::string(PrefixText.size() + MaxLength, '=') << "\n\n"; 220 } 221 222 for (auto MI = Matches.begin(), ME = Matches.end(); MI != ME; ++MI) { 223 OS << "\nMatch #" << ++MatchCount << ":\n\n"; 224 225 for (auto BI = MI->getMap().begin(), BE = MI->getMap().end(); BI != BE; 226 ++BI) { 227 if (QS.DiagOutput) { 228 clang::SourceRange R = BI->second.getSourceRange(); 229 if (R.isValid()) { 230 TextDiagnostic TD(OS, AST->getASTContext().getLangOpts(), 231 &AST->getDiagnostics().getDiagnosticOptions()); 232 TD.emitDiagnostic( 233 FullSourceLoc(R.getBegin(), AST->getSourceManager()), 234 DiagnosticsEngine::Note, "\"" + BI->first + "\" binds here", 235 CharSourceRange::getTokenRange(R), None); 236 } 237 } 238 if (QS.PrintOutput) { 239 OS << "Binding for \"" << BI->first << "\":\n"; 240 BI->second.print(OS, AST->getASTContext().getPrintingPolicy()); 241 OS << "\n"; 242 } 243 if (QS.DetailedASTOutput) { 244 OS << "Binding for \"" << BI->first << "\":\n"; 245 const ASTContext &Ctx = AST->getASTContext(); 246 ASTDumper Dumper(OS, Ctx, AST->getDiagnostics().getShowColors()); 247 Dumper.SetTraversalKind(QS.TK); 248 Dumper.Visit(BI->second); 249 OS << "\n"; 250 } 251 if (QS.SrcLocOutput) { 252 OS << "\n \"" << BI->first << "\" Source locations\n"; 253 OS << " " << std::string(19 + BI->first.size(), '-') << '\n'; 254 255 dumpLocations(OS, BI->second, Ctx, AST->getDiagnostics(), SM); 256 OS << "\n"; 257 } 258 } 259 260 if (MI->getMap().empty()) 261 OS << "No bindings.\n"; 262 } 263 } 264 265 OS << MatchCount << (MatchCount == 1 ? " match.\n" : " matches.\n"); 266 return true; 267 } 268 269 bool LetQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const { 270 if (Value) { 271 QS.NamedValues[Name] = Value; 272 } else { 273 QS.NamedValues.erase(Name); 274 } 275 return true; 276 } 277 278 #ifndef _MSC_VER 279 const QueryKind SetQueryKind<bool>::value; 280 const QueryKind SetQueryKind<OutputKind>::value; 281 #endif 282 283 } // namespace query 284 } // namespace clang 285