xref: /llvm-project/clang-tools-extra/clang-query/tool/ClangQuery.cpp (revision 85e6e8717145115bcb6ad67cde29a1f5eb352dab)
1 //===---- ClangQuery.cpp - clang-query tool -------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This tool is for interactive exploration of the Clang AST using AST matchers.
11 // It currently allows the user to enter a matcher at an interactive prompt and
12 // view the resulting bindings as diagnostics, AST pretty prints or AST dumps.
13 // Example session:
14 //
15 // $ cat foo.c
16 // void foo(void) {}
17 // $ clang-query foo.c --
18 // clang-query> match functionDecl()
19 //
20 // Match #1:
21 //
22 // foo.c:1:1: note: "root" binds here
23 // void foo(void) {}
24 // ^~~~~~~~~~~~~~~~~
25 // 1 match.
26 //
27 //===----------------------------------------------------------------------===//
28 
29 #include "Query.h"
30 #include "QueryParser.h"
31 #include "QuerySession.h"
32 #include "clang/Frontend/ASTUnit.h"
33 #include "clang/Tooling/CompilationDatabase.h"
34 #include "clang/Tooling/Tooling.h"
35 #include "llvm/ADT/OwningPtr.h"
36 #include "llvm/Support/CommandLine.h"
37 #include "llvm/Support/MemoryBuffer.h"
38 #include "llvm/Support/Signals.h"
39 #include <fstream>
40 #include <histedit.h>
41 #include <string>
42 
43 using namespace clang;
44 using namespace clang::ast_matchers;
45 using namespace clang::ast_matchers::dynamic;
46 using namespace clang::query;
47 using namespace clang::tooling;
48 using namespace llvm;
49 
50 static cl::opt<std::string> BuildPath("b", cl::desc("Specify build path"),
51                                       cl::value_desc("<path>"));
52 
53 static cl::list<std::string> Commands("c", cl::desc("Specify command to run"),
54                                       cl::value_desc("<command>"));
55 
56 static cl::list<std::string> CommandFiles("f",
57                                           cl::desc("Read commands from file"),
58                                           cl::value_desc("<file>"));
59 
60 static cl::list<std::string> SourcePaths(cl::Positional,
61                                          cl::desc("<source0> [... <sourceN>]"),
62                                          cl::OneOrMore);
63 
64 static char *ReturnPrompt(EditLine *EL) {
65   static char Prompt[] = "clang-query> ";
66   return Prompt;
67 }
68 
69 int main(int argc, const char **argv) {
70   llvm::sys::PrintStackTraceOnErrorSignal();
71   cl::ParseCommandLineOptions(argc, argv);
72 
73   if (!Commands.empty() && !CommandFiles.empty()) {
74     llvm::errs() << argv[0] << ": cannot specify both -c and -f\n";
75     return 1;
76   }
77 
78   llvm::OwningPtr<CompilationDatabase> Compilations(
79         FixedCompilationDatabase::loadFromCommandLine(argc, argv));
80   if (!Compilations) {  // Couldn't find a compilation DB from the command line
81     std::string ErrorMessage;
82     Compilations.reset(
83       !BuildPath.empty() ?
84         CompilationDatabase::autoDetectFromDirectory(BuildPath, ErrorMessage) :
85         CompilationDatabase::autoDetectFromSource(SourcePaths[0], ErrorMessage)
86       );
87 
88     // Still no compilation DB? - bail.
89     if (!Compilations)
90       llvm::report_fatal_error(ErrorMessage);
91   }
92 
93   ClangTool Tool(*Compilations, SourcePaths);
94   std::vector<ASTUnit *> ASTs;
95   if (Tool.buildASTs(ASTs) != 0)
96     return 1;
97 
98   QuerySession QS(ASTs);
99 
100   if (!Commands.empty()) {
101     for (cl::list<std::string>::iterator I = Commands.begin(),
102                                          E = Commands.end();
103          I != E; ++I) {
104       QueryRef Q = ParseQuery(I->c_str());
105       if (!Q->run(llvm::outs(), QS))
106         return 1;
107     }
108   } else if (!CommandFiles.empty()) {
109     for (cl::list<std::string>::iterator I = CommandFiles.begin(),
110                                          E = CommandFiles.end();
111          I != E; ++I) {
112       std::ifstream Input(I->c_str());
113       if (!Input.is_open()) {
114         llvm::errs() << argv[0] << ": cannot open " << *I << "\n";
115         return 1;
116       }
117       while (Input.good()) {
118         std::string Line;
119         std::getline(Input, Line);
120 
121         QueryRef Q = ParseQuery(Line.c_str());
122         if (!Q->run(llvm::outs(), QS))
123           return 1;
124       }
125     }
126   } else {
127     History *Hist = history_init();
128     HistEvent Event;
129     history(Hist, &Event, H_SETSIZE, 100);
130 
131     EditLine *EL = el_init("clang-query", stdin, stdout, stderr);
132     el_set(EL, EL_PROMPT, ReturnPrompt);
133     el_set(EL, EL_EDITOR, "emacs");
134     el_set(EL, EL_HIST, history, Hist);
135 
136     int Count;
137     while (const char *Line = el_gets(EL, &Count)) {
138       if (Count == 0)
139         break;
140 
141       history(Hist, &Event, H_ENTER, Line);
142 
143       QueryRef Q = ParseQuery(Line);
144       Q->run(llvm::outs(), QS);
145     }
146 
147     history_end(Hist);
148     el_end(EL);
149 
150     llvm::outs() << "\n";
151   }
152 
153   llvm::DeleteContainerPointers(ASTs);
154 
155   return 0;
156 }
157