njames93 created this revision.
njames93 added reviewers: aaron.ballman, steveire.
njames93 published this revision for review.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Add option `set match-scope (include-headers|main-file-only)` to control only 
printing matches from the main file.
This can be set once and then avoids the need to pollute all your match 
expressions with `isExpansionInMainFile()`
By default it is enabled to only show match results from the main file.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D97805

Files:
  clang-tools-extra/clang-query/Query.cpp
  clang-tools-extra/clang-query/QueryParser.cpp
  clang-tools-extra/clang-query/QueryParser.h
  clang-tools-extra/clang-query/QuerySession.h
  clang-tools-extra/docs/ReleaseNotes.rst
  clang-tools-extra/test/clang-query/Inputs/foo.h
  clang-tools-extra/test/clang-query/headers.cpp

Index: clang-tools-extra/test/clang-query/headers.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-query/headers.cpp
@@ -0,0 +1,37 @@
+// RUN: clang-query -c "set match-scope include-headers" -c "match functionDecl(hasName('foo'))" %s -- | FileCheck %s --check-prefix=CHECK-ALL-FOO
+// RUN: clang-query -c "set match-scope main-file-only" -c "match functionDecl(hasName('foo'))" %s -- | FileCheck %s --check-prefix=CHECK-MAIN-FOO
+// RUN: clang-query -c "set match-scope include-headers" -c "match functionDecl(hasName('bar'))" %s -- | FileCheck %s --check-prefix=CHECK-ALL-BAR
+// RUN: clang-query -c "set match-scope main-file-only" -c "match functionDecl(hasName('bar'))" %s -- | FileCheck %s --check-prefix=CHECK-MAIN-BAR
+// RUN: clang-query -c "set match-scope include-headers" -c "match functionDecl()" %s -- | FileCheck %s --check-prefix=CHECK-ALL-ANY
+// RUN: clang-query -c "set match-scope main-file-only" -c "match functionDecl()" %s -- | FileCheck %s --check-prefix=CHECK-MAIN-ANY
+
+// Test to ensure default behavious only includes main file matches.
+// RUN: clang-query -c "match functionDecl()" %s -- | FileCheck %s --check-prefix=CHECK-MAIN-ANY
+
+#include "Inputs/foo.h"
+
+void foo() {}
+
+// CHECK-ALL-FOO: /Inputs/foo.h:4:1: note: "root" binds here
+// CHECK-ALL-FOO: /headers.cpp:13:1: note: "root" binds here
+// CHECK-ALL-FOO: 2 matches.
+
+// CHECK-MAIN-FOO: /headers.cpp:13:1: note: "root" binds here
+// CHECK-MAIN-FOO: 1 match.
+// CHECK-MAIN-FOO-Next: Skipped 1 match from header files.
+// CHECK-MAIN-FOO-Next: Use 'set match-mode include-headers' to display them.
+
+// CHECK-ALL-BAR: /Inputs/foo.h:5:1: note: "root" binds here
+// CHECK-ALL-BAR: 1 match.
+
+// CHECK-MAIN-BAR: 0 matches.
+// CHECK-MAIN-BAR-Next: Skipped 1 match from header files.
+
+// CHECK-ALL-ANY: /Inputs/foo.h:4:1: note: "root" binds here
+// CHECK-ALL-ANY: /Inputs/foo.h:5:1: note: "root" binds here
+// CHECK-ALL-ANY: /headers.cpp:13:1: note: "root" binds here
+// CHECK-ALL-ANY: 3 matches.
+
+// CHECK-MAIN-ANY: /headers.cpp:13:1: note: "root" binds here
+// CHECK-MAIN-ANY: 1 match.
+// CHECK-MAIN-BAR-Next: Skipped 2 matches from header files.
Index: clang-tools-extra/test/clang-query/Inputs/foo.h
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-query/Inputs/foo.h
@@ -0,0 +1,7 @@
+#ifndef INPUTS_FOO_HH
+#define INPUTS_FOO_HH
+
+void foo();
+void bar();
+
+#endif // INPUTS_FOO_HH
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -57,7 +57,8 @@
 Improvements to clang-query
 ---------------------------
 
-The improvements are...
+ - Added a option `set match-scope` to control displaying matches that occur 
+   in header files.
 
 Improvements to clang-rename
 ----------------------------
Index: clang-tools-extra/clang-query/QuerySession.h
===================================================================
--- clang-tools-extra/clang-query/QuerySession.h
+++ clang-tools-extra/clang-query/QuerySession.h
@@ -26,7 +26,7 @@
   QuerySession(llvm::ArrayRef<std::unique_ptr<ASTUnit>> ASTs)
       : ASTs(ASTs), PrintOutput(false), DiagOutput(true),
         DetailedASTOutput(false), BindRoot(true), PrintMatcher(false),
-        Terminate(false), TK(TK_AsIs) {}
+        OnlyMainFileMatches(true), Terminate(false), TK(TK_AsIs) {}
 
   llvm::ArrayRef<std::unique_ptr<ASTUnit>> ASTs;
 
@@ -36,6 +36,7 @@
 
   bool BindRoot;
   bool PrintMatcher;
+  bool OnlyMainFileMatches;
   bool Terminate;
 
   TraversalKind TK;
Index: clang-tools-extra/clang-query/QueryParser.h
===================================================================
--- clang-tools-extra/clang-query/QueryParser.h
+++ clang-tools-extra/clang-query/QueryParser.h
@@ -44,6 +44,7 @@
 
   QueryRef parseSetBool(bool QuerySession::*Var);
   QueryRef parseSetTraversalKind(TraversalKind QuerySession::*Var);
+  QueryRef parseMatchScope(bool QuerySession::*Var);
   template <typename QueryType> QueryRef parseSetOutputKind();
   QueryRef completeMatcherExpression();
 
Index: clang-tools-extra/clang-query/QueryParser.cpp
===================================================================
--- clang-tools-extra/clang-query/QueryParser.cpp
+++ clang-tools-extra/clang-query/QueryParser.cpp
@@ -141,6 +141,18 @@
   return new SetQuery<TraversalKind>(Var, static_cast<TraversalKind>(Value));
 }
 
+QueryRef QueryParser::parseMatchScope(bool QuerySession::*Var) {
+  StringRef ValStr;
+  unsigned Value = LexOrCompleteWord<unsigned>(this, ValStr)
+                       .Case("main-file-only", 1)
+                       .Case("include-headers", 0)
+                       .Default(~0U);
+  if (Value == ~0U) {
+    return new InvalidQuery("expected match scope, got '" + ValStr + "'");
+  }
+  return new SetQuery<bool>(Var, Value);
+}
+
 QueryRef QueryParser::endQuery(QueryRef Q) {
   StringRef Extra = Line;
   StringRef ExtraTrimmed = Extra.drop_while(
@@ -185,7 +197,8 @@
   PQV_Output,
   PQV_BindRoot,
   PQV_PrintMatcher,
-  PQV_Traversal
+  PQV_Traversal,
+  PQV_OnlyMainFileMatches
 };
 
 QueryRef makeInvalidQueryFromDiagnostics(const Diagnostics &Diag) {
@@ -287,6 +300,7 @@
             .Case("bind-root", PQV_BindRoot)
             .Case("print-matcher", PQV_PrintMatcher)
             .Case("traversal", PQV_Traversal)
+            .Case("match-scope", PQV_OnlyMainFileMatches)
             .Default(PQV_Invalid);
     if (VarStr.empty())
       return new InvalidQuery("expected variable name");
@@ -307,6 +321,9 @@
     case PQV_Traversal:
       Q = parseSetTraversalKind(&QuerySession::TK);
       break;
+    case PQV_OnlyMainFileMatches:
+      Q = parseMatchScope(&QuerySession::OnlyMainFileMatches);
+      break;
     case PQV_Invalid:
       llvm_unreachable("Invalid query kind");
     }
Index: clang-tools-extra/clang-query/Query.cpp
===================================================================
--- clang-tools-extra/clang-query/Query.cpp
+++ clang-tools-extra/clang-query/Query.cpp
@@ -52,6 +52,13 @@
         "Omit implicit casts and parens in matching and dumping.\n"
         "    IgnoreUnlessSpelledInSource     "
         "Omit AST nodes unless spelled in the source.\n"
+        "  set match-scope <kind>            "
+        "Set the scope of matchers. Available kinds are:\n"
+        "    include-headers                 "
+        "Match nodes included from head files.\n"
+        "    main-file-only                  "
+        "Only match nodes from the main source file. This mode is the "
+        "default.\n"
         "  set output <feature>              "
         "Set whether to output only <feature> content.\n"
         "  enable output <feature>           "
@@ -90,20 +97,43 @@
 
 } // namespace
 
+static void printMatch(llvm::raw_ostream &OS, QuerySession &QS, ASTUnit &AST,
+                       const DynTypedNode &Node, StringRef BindName) {
+  if (QS.DiagOutput) {
+    clang::SourceRange R = Node.getSourceRange();
+    if (R.isValid()) {
+      TextDiagnostic TD(OS, AST.getASTContext().getLangOpts(),
+                        &AST.getDiagnostics().getDiagnosticOptions());
+      TD.emitDiagnostic(FullSourceLoc(R.getBegin(), AST.getSourceManager()),
+                        DiagnosticsEngine::Note,
+                        ("\"" + BindName + "\" binds here").str(),
+                        CharSourceRange::getTokenRange(R), None);
+    }
+  }
+  if (QS.PrintOutput) {
+    OS << "Binding for \"" << BindName << "\":\n";
+    Node.print(OS, AST.getASTContext().getPrintingPolicy());
+    OS << "\n";
+  }
+  if (QS.DetailedASTOutput) {
+    OS << "Binding for \"" << BindName << "\":\n";
+    const ASTContext &Ctx = AST.getASTContext();
+    ASTDumper Dumper(OS, Ctx, AST.getDiagnostics().getShowColors());
+    Dumper.SetTraversalKind(QS.TK);
+    Dumper.Visit(Node);
+    OS << "\n";
+  }
+}
+
 bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
   unsigned MatchCount = 0;
+  unsigned SkippedMatchersInHeader = 0;
 
   for (auto &AST : QS.ASTs) {
     MatchFinder Finder;
     std::vector<BoundNodes> Matches;
-    DynTypedMatcher MaybeBoundMatcher = Matcher;
-    if (QS.BindRoot) {
-      llvm::Optional<DynTypedMatcher> M = Matcher.tryBind("root");
-      if (M)
-        MaybeBoundMatcher = *M;
-    }
     CollectBoundNodes Collect(Matches);
-    if (!Finder.addDynamicMatcher(MaybeBoundMatcher, &Collect)) {
+    if (!Finder.addDynamicMatcher(Matcher, &Collect)) {
       OS << "Not a valid top-level matcher.\n";
       return false;
     }
@@ -132,43 +162,38 @@
          << "  " << std::string(PrefixText.size() + MaxLength, '=') << "\n\n";
     }
 
-    for (auto MI = Matches.begin(), ME = Matches.end(); MI != ME; ++MI) {
-      OS << "\nMatch #" << ++MatchCount << ":\n\n";
-
-      for (auto BI = MI->getMap().begin(), BE = MI->getMap().end(); BI != BE;
-           ++BI) {
-        if (QS.DiagOutput) {
-          clang::SourceRange R = BI->second.getSourceRange();
-          if (R.isValid()) {
-            TextDiagnostic TD(OS, AST->getASTContext().getLangOpts(),
-                              &AST->getDiagnostics().getDiagnosticOptions());
-            TD.emitDiagnostic(
-                FullSourceLoc(R.getBegin(), AST->getSourceManager()),
-                DiagnosticsEngine::Note, "\"" + BI->first + "\" binds here",
-                CharSourceRange::getTokenRange(R), None);
+    for (const auto &Match : Matches) {
+      const DynTypedNode &RootNode = Match.getRoot();
+      if (QS.OnlyMainFileMatches) {
+        SourceRange SR = RootNode.getSourceRange();
+        if (SR.isValid()) {
+          const SourceManager &SM = AST->getSourceManager();
+          if (!SM.isInMainFile(SM.getExpansionLoc(SR.getBegin())) ||
+              !SM.isInMainFile(SM.getExpansionLoc(SR.getEnd()))) {
+            ++SkippedMatchersInHeader;
+            continue;
           }
         }
-        if (QS.PrintOutput) {
-          OS << "Binding for \"" << BI->first << "\":\n";
-          BI->second.print(OS, AST->getASTContext().getPrintingPolicy());
-          OS << "\n";
-        }
-        if (QS.DetailedASTOutput) {
-          OS << "Binding for \"" << BI->first << "\":\n";
-          const ASTContext &Ctx = AST->getASTContext();
-          ASTDumper Dumper(OS, Ctx, AST->getDiagnostics().getShowColors());
-          Dumper.SetTraversalKind(QS.TK);
-          Dumper.Visit(BI->second);
-          OS << "\n";
-        }
+      }
+      OS << "\nMatch #" << ++MatchCount << ":\n\n";
+
+      for (const auto &NameAndNode : Match.getMap()) {
+        printMatch(OS, QS, *AST, NameAndNode.second, NameAndNode.first);
       }
 
-      if (MI->getMap().empty())
+      if (QS.BindRoot)
+        printMatch(OS, QS, *AST, RootNode, "root");
+      else if (Match.getMap().empty())
         OS << "No bindings.\n";
     }
   }
 
   OS << MatchCount << (MatchCount == 1 ? " match.\n" : " matches.\n");
+  if (SkippedMatchersInHeader > 0)
+    OS << "Skipped " << SkippedMatchersInHeader
+       << (SkippedMatchersInHeader == 1 ? " match" : " matches")
+       << " from header files.\n"
+       << "Use 'set match-mode include-headers' to display them.\n";
   return true;
 }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to