1 //===---- QueryParser.cpp - clang-query command parser --------------------===//
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 #include "QueryParser.h"
11 #include "Query.h"
12 #include "QuerySession.h"
13 #include "llvm/ADT/StringRef.h"
14 #include "llvm/ADT/StringSwitch.h"
15 #include "clang/ASTMatchers/Dynamic/Parser.h"
16 #include "clang/Basic/CharInfo.h"
17 
18 using namespace llvm;
19 using namespace clang::ast_matchers::dynamic;
20 
21 namespace clang {
22 namespace query {
23 
24 // Lex any amount of whitespace followed by a "word" (any sequence of
25 // non-whitespace characters) from the start of region [Begin,End).  If no word
26 // is found before End, return StringRef().  Begin is adjusted to exclude the
27 // lexed region.
28 static StringRef LexWord(const char *&Begin, const char *End) {
29   while (true) {
30     if (Begin == End)
31       return StringRef();
32 
33     if (!isWhitespace(*Begin))
34       break;
35 
36     ++Begin;
37   }
38 
39   const char *WordBegin = Begin;
40 
41   while (true) {
42     ++Begin;
43 
44     if (Begin == End || isWhitespace(*Begin))
45       return StringRef(WordBegin, Begin - WordBegin);
46   }
47 }
48 
49 static QueryRef ParseSetBool(bool QuerySession::*Var, StringRef ValStr) {
50   unsigned Value = StringSwitch<unsigned>(ValStr)
51                       .Case("false", 0)
52                       .Case("true", 1)
53                       .Default(~0u);
54   if (Value == ~0u) {
55     return new InvalidQuery("expected 'true' or 'false', got '" + ValStr + "'");
56   }
57   return new SetQuery<bool>(Var, Value);
58 }
59 
60 static QueryRef ParseSetOutputKind(StringRef ValStr) {
61   unsigned OutKind = StringSwitch<unsigned>(ValStr)
62                          .Case("diag", OK_Diag)
63                          .Case("print", OK_Print)
64                          .Case("dump", OK_Dump)
65                          .Default(~0u);
66   if (OutKind == ~0u) {
67     return new InvalidQuery("expected 'diag', 'print' or 'dump', got '" +
68                             ValStr + "'");
69   }
70   return new SetQuery<OutputKind>(&QuerySession::OutKind, OutputKind(OutKind));
71 }
72 
73 static QueryRef EndQuery(const char *Begin, const char *End, QueryRef Q) {
74   const char *Extra = Begin;
75   if (!LexWord(Begin, End).empty())
76     return new InvalidQuery("unexpected extra input: '" +
77                             StringRef(Extra, End - Extra) + "'");
78   return Q;
79 }
80 
81 enum ParsedQueryKind {
82   PQK_Invalid,
83   PQK_NoOp,
84   PQK_Help,
85   PQK_Match,
86   PQK_Set
87 };
88 
89 enum ParsedQueryVariable {
90   PQV_Invalid,
91   PQV_Output,
92   PQV_BindRoot
93 };
94 
95 QueryRef ParseQuery(StringRef Line) {
96   const char *Begin = Line.data();
97   const char *End = Line.data() + Line.size();
98 
99   StringRef CommandStr = LexWord(Begin, End);
100   ParsedQueryKind QKind = StringSwitch<ParsedQueryKind>(CommandStr)
101                               .Case("", PQK_NoOp)
102                               .Case("help", PQK_Help)
103                               .Case("m", PQK_Match)
104                               .Case("match", PQK_Match)
105                               .Case("set", PQK_Set)
106                               .Default(PQK_Invalid);
107 
108   switch (QKind) {
109   case PQK_NoOp:
110     return new NoOpQuery;
111 
112   case PQK_Help:
113     return EndQuery(Begin, End, new HelpQuery);
114 
115   case PQK_Match: {
116     Diagnostics Diag;
117     Optional<DynTypedMatcher> Matcher =
118         Parser::parseMatcherExpression(StringRef(Begin, End - Begin), &Diag);
119     if (!Matcher) {
120       std::string ErrStr;
121       llvm::raw_string_ostream OS(ErrStr);
122       Diag.printToStreamFull(OS);
123       return new InvalidQuery(OS.str());
124     }
125     return new MatchQuery(*Matcher);
126   }
127 
128   case PQK_Set: {
129     StringRef VarStr = LexWord(Begin, End);
130     if (VarStr.empty())
131       return new InvalidQuery("expected variable name");
132 
133     ParsedQueryVariable Var = StringSwitch<ParsedQueryVariable>(VarStr)
134                 .Case("output", PQV_Output)
135                 .Case("bind-root", PQV_BindRoot)
136                 .Default(PQV_Invalid);
137     if (Var == PQV_Invalid)
138       return new InvalidQuery("unknown variable: '" + VarStr + "'");
139 
140     StringRef ValStr = LexWord(Begin, End);
141     if (ValStr.empty())
142       return new InvalidQuery("expected variable value");
143 
144     QueryRef Q;
145     switch (Var) {
146     case PQV_Output:
147       Q = ParseSetOutputKind(ValStr);
148       break;
149     case PQV_BindRoot:
150       Q = ParseSetBool(&QuerySession::BindRoot, ValStr);
151       break;
152     case PQV_Invalid:
153       llvm_unreachable("Invalid query kind");
154     }
155 
156     return EndQuery(Begin, End, Q);
157   }
158 
159   case PQK_Invalid:
160     return new InvalidQuery("unknown command: " + CommandStr);
161   }
162 
163   llvm_unreachable("Invalid query kind");
164 }
165 
166 } // namespace query
167 } // namespace clang
168