[clang-query] Add non-exclusive output API

Summary:
Add granular options for AST dumping, text printing and diagnostics.

This makes it possible to

* Have both diag and dump active at once
* Extend the output with other queryable content in the future.

Reviewers: aaron.ballman, pcc, ioeric, ilya-biryukov, klimek, sammccall

Reviewed By: aaron.ballman

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D52857

llvm-svn: 345522
diff --git a/clang-tools-extra/clang-query/Query.cpp b/clang-tools-extra/clang-query/Query.cpp
index 48e2004..ffa3b45 100644
--- a/clang-tools-extra/clang-query/Query.cpp
+++ b/clang-tools-extra/clang-query/Query.cpp
@@ -45,6 +45,10 @@
         "Set whether to print the current matcher,\n"
         "  set output <feature>              "
         "Set whether to output only <feature> content.\n"
+        "  enable output <feature>           "
+        "Enable <feature> content non-exclusively.\n"
+        "  disable output <feature>          "
+        "Disable <feature> content non-exclusively.\n"
         "  quit, q                           "
         "Terminates the query session.\n\n"
         "Several commands accept a <feature> parameter. The available features "
diff --git a/clang-tools-extra/clang-query/Query.h b/clang-tools-extra/clang-query/Query.h
index 1f5a25d..b4ff1d0 100644
--- a/clang-tools-extra/clang-query/Query.h
+++ b/clang-tools-extra/clang-query/Query.h
@@ -29,6 +29,8 @@
   QK_Match,
   QK_SetBool,
   QK_SetOutputKind,
+  QK_EnableOutputKind,
+  QK_DisableOutputKind,
   QK_Quit
 };
 
@@ -151,6 +153,36 @@
   bool QuerySession::*Var;
 };
 
+// Implements the non-exclusive 'set output dump|diag|print' options.
+struct SetNonExclusiveOutputQuery : Query {
+  SetNonExclusiveOutputQuery(QueryKind Kind, bool QuerySession::*Var,
+                             bool Value)
+      : Query(Kind), Var(Var), Value(Value) {}
+  bool run(llvm::raw_ostream &OS, QuerySession &QS) const override {
+    QS.*Var = Value;
+    return true;
+  }
+
+  bool QuerySession::*Var;
+  bool Value;
+};
+
+struct EnableOutputQuery : SetNonExclusiveOutputQuery {
+  EnableOutputQuery(bool QuerySession::*Var)
+      : SetNonExclusiveOutputQuery(QK_EnableOutputKind, Var, true) {}
+
+  static bool classof(const Query *Q) { return Q->Kind == QK_EnableOutputKind; }
+};
+
+struct DisableOutputQuery : SetNonExclusiveOutputQuery {
+  DisableOutputQuery(bool QuerySession::*Var)
+      : SetNonExclusiveOutputQuery(QK_DisableOutputKind, Var, false) {}
+
+  static bool classof(const Query *Q) {
+    return Q->Kind == QK_DisableOutputKind;
+  }
+};
+
 } // namespace query
 } // namespace clang
 
diff --git a/clang-tools-extra/clang-query/QueryParser.cpp b/clang-tools-extra/clang-query/QueryParser.cpp
index b0dacea..7c12e3b 100644
--- a/clang-tools-extra/clang-query/QueryParser.cpp
+++ b/clang-tools-extra/clang-query/QueryParser.cpp
@@ -106,7 +106,7 @@
   return new SetQuery<bool>(Var, Value);
 }
 
-QueryRef QueryParser::parseSetOutputKind() {
+template <typename QueryType> QueryRef QueryParser::parseSetOutputKind() {
   StringRef ValStr;
   unsigned OutKind = LexOrCompleteWord<unsigned>(this, ValStr)
                          .Case("diag", OK_Diag)
@@ -122,11 +122,11 @@
 
   switch (OutKind) {
   case OK_DetailedAST:
-    return new SetExclusiveOutputQuery(&QuerySession::DetailedASTOutput);
+    return new QueryType(&QuerySession::DetailedASTOutput);
   case OK_Diag:
-    return new SetExclusiveOutputQuery(&QuerySession::DiagOutput);
+    return new QueryType(&QuerySession::DiagOutput);
   case OK_Print:
-    return new SetExclusiveOutputQuery(&QuerySession::PrintOutput);
+    return new QueryType(&QuerySession::PrintOutput);
   }
 
   llvm_unreachable("Invalid output kind");
@@ -151,7 +151,9 @@
   PQK_Match,
   PQK_Set,
   PQK_Unlet,
-  PQK_Quit
+  PQK_Quit,
+  PQK_Enable,
+  PQK_Disable
 };
 
 enum ParsedQueryVariable {
@@ -193,6 +195,8 @@
                               .Case("q", PQK_Quit,  /*IsCompletion=*/false)
                               .Case("quit", PQK_Quit)
                               .Case("set", PQK_Set)
+                              .Case("enable", PQK_Enable)
+                              .Case("disable", PQK_Disable)
                               .Case("unlet", PQK_Unlet)
                               .Default(PQK_Invalid);
 
@@ -256,7 +260,7 @@
     QueryRef Q;
     switch (Var) {
     case PQV_Output:
-      Q = parseSetOutputKind();
+      Q = parseSetOutputKind<SetExclusiveOutputQuery>();
       break;
     case PQV_BindRoot:
       Q = parseSetBool(&QuerySession::BindRoot);
@@ -270,6 +274,28 @@
 
     return endQuery(Q);
   }
+  case PQK_Enable:
+  case PQK_Disable: {
+    StringRef VarStr;
+    ParsedQueryVariable Var =
+        LexOrCompleteWord<ParsedQueryVariable>(this, VarStr)
+            .Case("output", PQV_Output)
+            .Default(PQV_Invalid);
+    if (VarStr.empty())
+      return new InvalidQuery("expected variable name");
+    if (Var == PQV_Invalid)
+      return new InvalidQuery("unknown variable: '" + VarStr + "'");
+
+    QueryRef Q;
+
+    if (QKind == PQK_Enable)
+      Q = parseSetOutputKind<EnableOutputQuery>();
+    else if (QKind == PQK_Disable)
+      Q = parseSetOutputKind<DisableOutputQuery>();
+    else
+      llvm_unreachable("Invalid query kind");
+    return endQuery(Q);
+  }
 
   case PQK_Unlet: {
     StringRef Name = lexWord();
diff --git a/clang-tools-extra/clang-query/QueryParser.h b/clang-tools-extra/clang-query/QueryParser.h
index 67f907d..6730f99 100644
--- a/clang-tools-extra/clang-query/QueryParser.h
+++ b/clang-tools-extra/clang-query/QueryParser.h
@@ -44,7 +44,7 @@
   template <typename T> struct LexOrCompleteWord;
 
   QueryRef parseSetBool(bool QuerySession::*Var);
-  QueryRef parseSetOutputKind();
+  template <typename QueryType> QueryRef parseSetOutputKind();
   QueryRef completeMatcherExpression();
 
   QueryRef endQuery(QueryRef Q);