xref: /llvm-project/clang-tools-extra/clang-query/tool/ClangQuery.cpp (revision d9a0f254bcd7e550bd504358fcda7a8e5b9906b3)
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/LineEditor/LineEditor.h"
37 #include "llvm/Support/CommandLine.h"
38 #include "llvm/Support/MemoryBuffer.h"
39 #include "llvm/Support/Signals.h"
40 #include <fstream>
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 int main(int argc, const char **argv) {
65   llvm::sys::PrintStackTraceOnErrorSignal();
66   cl::ParseCommandLineOptions(argc, argv);
67 
68   if (!Commands.empty() && !CommandFiles.empty()) {
69     llvm::errs() << argv[0] << ": cannot specify both -c and -f\n";
70     return 1;
71   }
72 
73   llvm::OwningPtr<CompilationDatabase> Compilations(
74         FixedCompilationDatabase::loadFromCommandLine(argc, argv));
75   if (!Compilations) {  // Couldn't find a compilation DB from the command line
76     std::string ErrorMessage;
77     Compilations.reset(
78       !BuildPath.empty() ?
79         CompilationDatabase::autoDetectFromDirectory(BuildPath, ErrorMessage) :
80         CompilationDatabase::autoDetectFromSource(SourcePaths[0], ErrorMessage)
81       );
82 
83     // Still no compilation DB? - bail.
84     if (!Compilations)
85       llvm::report_fatal_error(ErrorMessage);
86   }
87 
88   ClangTool Tool(*Compilations, SourcePaths);
89   std::vector<ASTUnit *> ASTs;
90   if (Tool.buildASTs(ASTs) != 0)
91     return 1;
92 
93   QuerySession QS(ASTs);
94 
95   if (!Commands.empty()) {
96     for (cl::list<std::string>::iterator I = Commands.begin(),
97                                          E = Commands.end();
98          I != E; ++I) {
99       QueryRef Q = QueryParser::parse(I->c_str());
100       if (!Q->run(llvm::outs(), QS))
101         return 1;
102     }
103   } else if (!CommandFiles.empty()) {
104     for (cl::list<std::string>::iterator I = CommandFiles.begin(),
105                                          E = CommandFiles.end();
106          I != E; ++I) {
107       std::ifstream Input(I->c_str());
108       if (!Input.is_open()) {
109         llvm::errs() << argv[0] << ": cannot open " << *I << "\n";
110         return 1;
111       }
112       while (Input.good()) {
113         std::string Line;
114         std::getline(Input, Line);
115 
116         QueryRef Q = QueryParser::parse(Line.c_str());
117         if (!Q->run(llvm::outs(), QS))
118           return 1;
119       }
120     }
121   } else {
122     LineEditor LE("clang-query");
123     LE.setListCompleter(QueryParser::complete);
124     while (llvm::Optional<std::string> Line = LE.readLine()) {
125       QueryRef Q = QueryParser::parse(*Line);
126       Q->run(llvm::outs(), QS);
127     }
128   }
129 
130   llvm::DeleteContainerPointers(ASTs);
131 
132   return 0;
133 }
134