Hi doug.gregor,

StmtDumper has some incomplete support for limiting the maximum dump depth. 
This patch completes that support, and exposes it as an -ast-dump-depth option.

By completing the max depth support, this patch changes the behavior of 
Stmt::dump() to limit the depth to 4 by default. I don't know whether current 
users of dump() want this change or not. The behavior of Stmt::dumpAll() is 
unchanged, as is the default behavior of -ast-dump.

I originally thought this option would be useful for testing Decl dumping, but 
now I'm not finding many places where it helps much. So alternatively, I could 
submit a patch that removes the incomplete support in StmtDumper.

http://llvm-reviews.chandlerc.com/D98

Files:
  test/Misc/ast-dump-stmt.c
  test/Misc/ast-dump-stmt.m
  include/clang/AST/DeclBase.h
  include/clang/AST/PrettyPrinter.h
  include/clang/AST/Stmt.h
  include/clang/Driver/CC1Options.td
  include/clang/Frontend/ASTConsumers.h
  include/clang/Frontend/FrontendOptions.h
  lib/AST/DeclPrinter.cpp
  lib/AST/DumpXML.cpp
  lib/AST/StmtDumper.cpp
  lib/AST/StmtPrinter.cpp
  lib/Frontend/ASTConsumers.cpp
  lib/Frontend/CompilerInvocation.cpp
  lib/Frontend/FrontendActions.cpp
  test/Tooling/clang-check-ast-dump.cpp
  tools/clang-check/ClangCheck.cpp
Index: test/Misc/ast-dump-stmt.c
===================================================================
--- test/Misc/ast-dump-stmt.c
+++ test/Misc/ast-dump-stmt.c
@@ -1,16 +1,36 @@
 // RUN: %clang_cc1 -ast-dump -ast-dump-filter Test %s | FileCheck -strict-whitespace %s
+// RUN: %clang_cc1 -ast-dump -ast-dump-depth 1 -ast-dump-filter Test %s | FileCheck -check-prefix CHECK1 -strict-whitespace %s
+// RUN: %clang_cc1 -ast-dump -ast-dump-depth 2 -ast-dump-filter Test %s | FileCheck -check-prefix CHECK2 -strict-whitespace %s
 
 int TestLocation = 0;
 // CHECK:      Dumping TestLocation
-// CHECK-NEXT:   IntegerLiteral 0x{{[^ ]*}} <{{.*}}:3:20> 'int' 0
+// CHECK-NEXT:   IntegerLiteral 0x{{[^ ]*}} <{{.*}}:5:20> 'int' 0
 
 int TestIndent = 1 + (1);
 // CHECK:      Dumping TestIndent
 // CHECK-NEXT: {{\(BinaryOperator[^()]*$}}
 // CHECK-NEXT: {{^  \(IntegerLiteral.*0[^()]*\)$}}
 // CHECK-NEXT: {{^  \(ParenExpr.*0[^()]*$}}
 // CHECK-NEXT: {{^    \(IntegerLiteral.*0[^()]*\)\)\)$}}
 
+int TestChildren = 1 + (1);
+// CHECK:      Dumping TestChildren
+// CHECK-NEXT: BinaryOperator
+// CHECK-NEXT:   IntegerLiteral
+// CHECK-NEXT:   ParenExpr
+// CHECK-NEXT:     IntegerLiteral
+
+// CHECK1:      Dumping TestChildren
+// CHECK1-NEXT: BinaryOperator{{.*}} ...)
+// CHECK1-NEXT-NOT: IntegerLiteral
+// CHECK1-NEXT-NOT: ParenExpr
+
+// CHECK2:      Dumping TestChildren
+// CHECK2-NEXT: BinaryOperator
+// CHECK2-NEXT:   IntegerLiteral
+// CHECK2-NEXT:   ParenExpr{{.*}} ...)
+// CHECK2-NEXT-NOT: IntegerLiteral
+
 void TestDeclStmt() {
   int x = 0;
   int y, z;
@@ -24,6 +44,11 @@
 // CHECK-NEXT:     int y
 // CHECK-NEXT:     int z
 
+// CHECK2:      Dumping TestDeclStmt
+// CHECK2-NEXT: CompoundStmt
+// CHECK2-NEXT:   DeclStmt
+// CHECK2-NEXT:   DeclStmt
+
 int TestOpaqueValueExpr = 0 ?: 1;
 // CHECK:      Dumping TestOpaqueValueExpr
 // CHECK-NEXT: BinaryConditionalOperator
@@ -33,3 +58,10 @@
 // CHECK-NEXT:   OpaqueValueExpr
 // CHECK-NEXT:     IntegerLiteral
 // CHECK-NEXT:   IntegerLiteral
+
+// CHECK2:      Dumping TestOpaqueValueExpr
+// CHECK2-NEXT: BinaryConditionalOperator
+// CHECK2-NEXT:   IntegerLiteral
+// CHECK2-NEXT:   OpaqueValueExpr
+// CHECK2-NEXT:   OpaqueValueExpr
+// CHECK2-NEXT:   IntegerLiteral
Index: test/Misc/ast-dump-stmt.m
===================================================================
--- test/Misc/ast-dump-stmt.m
+++ test/Misc/ast-dump-stmt.m
@@ -1,4 +1,6 @@
 // RUN: %clang_cc1 -Wno-unused -fblocks -fobjc-exceptions -ast-dump -ast-dump-filter Test %s | FileCheck -strict-whitespace %s
+// RUN: %clang_cc1 -Wno-unused -fblocks -fobjc-exceptions -ast-dump -ast-dump-depth 2 -ast-dump-filter Test %s | FileCheck -check-prefix CHECK2 -strict-whitespace %s
+// RUN: %clang_cc1 -Wno-unused -fblocks -fobjc-exceptions -ast-dump -ast-dump-depth 3 -ast-dump-filter Test %s | FileCheck -check-prefix CHECK3 -strict-whitespace %s
 
 void TestBlockExpr(int x) {
   ^{ x; };
@@ -8,14 +10,24 @@
 // CHECK-NEXT:   capture ParmVar
 // CHECK-NEXT:   CompoundStmt
 
+// CHECK3:      Dumping TestBlockExpr
+// CHECK3:      BlockExpr{{.*}} decl=
+// CHECK3-NEXT-NOT: capture
+// CHECK3-NEXT-NOT: CompoundStmt
+
 void TestExprWithCleanup(int x) {
   ^{ x; };
 }
 // CHECK:      Dumping TestExprWithCleanup
 // CHECK:      ExprWithCleanups
 // CHECK-NEXT:   cleanup Block
 // CHECK-NEXT:   BlockExpr
 
+// CHECK2:      Dumping TestExprWithCleanup
+// CHECK2:      ExprWithCleanups
+// CHECK2-NEXT-NOT: cleanup
+// CHECK2-NEXT-NOT: BlockExpr
+
 @interface A
 @end
 
@@ -34,3 +46,10 @@
 // CHECK-NEXT:   ObjCAtCatchStmt{{.*}} catch all
 // CHECK-NEXT:     CompoundStmt
 // CHECK-NEXT:   ObjCAtFinallyStmt
+
+// CHECK3:      Dumping TestObjCAtCatchStmt
+// CHECK3:      ObjCAtTryStmt
+// CHECK3-NEXT:   CompoundStmt
+// CHECK3-NEXT:   ObjCAtCatchStmt{{.*}} catch parm = "A *a"
+// CHECK3-NEXT:   ObjCAtCatchStmt{{.*}} catch all
+// CHECK3-NEXT:   ObjCAtFinallyStmt
Index: include/clang/AST/DeclBase.h
===================================================================
--- include/clang/AST/DeclBase.h
+++ include/clang/AST/DeclBase.h
@@ -851,7 +851,7 @@
                          unsigned Indentation = 0);
   // Debuggers don't usually respect default arguments.
   LLVM_ATTRIBUTE_USED void dump() const;
-  void dump(raw_ostream &Out) const;
+  void dump(raw_ostream &Out, unsigned MaxDepth) const;
   // Debuggers don't usually respect default arguments.
   LLVM_ATTRIBUTE_USED void dumpXML() const;
   void dumpXML(raw_ostream &OS) const;
Index: include/clang/AST/PrettyPrinter.h
===================================================================
--- include/clang/AST/PrettyPrinter.h
+++ include/clang/AST/PrettyPrinter.h
@@ -40,7 +40,7 @@
       ConstantArraySizeAsWritten(false), AnonymousTagLocations(true),
       SuppressStrongLifetime(false), Bool(LO.Bool),
       TerseOutput(false), SuppressAttributes(false),
-      DumpSourceManager(0) { }
+      DumpSourceManager(0), DumpDepth(0) { }
 
   /// \brief What language we're printing.
   LangOptions LangOpts;
@@ -151,6 +151,10 @@
   /// involves printing the internal details of the AST and pretty-printing
   /// involves printing something similar to source code.
   SourceManager *DumpSourceManager;
+
+  /// \brief If we are "dumping" rather than "pretty-printing", this is the
+  /// maximum AST traversal depth.
+  unsigned DumpDepth;
 };
 
 } // end namespace clang
Index: include/clang/AST/Stmt.h
===================================================================
--- include/clang/AST/Stmt.h
+++ include/clang/AST/Stmt.h
@@ -365,7 +365,7 @@
   /// This is useful in a debugger.
   LLVM_ATTRIBUTE_USED void dump() const;
   LLVM_ATTRIBUTE_USED void dump(SourceManager &SM) const;
-  void dump(raw_ostream &OS, SourceManager &SM) const;
+  void dump(raw_ostream &OS, SourceManager &SM, unsigned MaxDepth) const;
 
   /// dumpAll - This does a dump of the specified AST fragment and all subtrees.
   void dumpAll() const;
Index: include/clang/Driver/CC1Options.td
===================================================================
--- include/clang/Driver/CC1Options.td
+++ include/clang/Driver/CC1Options.td
@@ -294,6 +294,8 @@
   HelpText<"Use with -ast-dump or -ast-print to dump/print only AST declaration"
            " nodes having a certain substring in a qualified name. Use"
            " -ast-list to list all filterable declaration node names.">;
+def ast_dump_depth : Separate<["-"], "ast-dump-depth">,
+  HelpText<"Maximum AST traversal depth while dumping with -ast-dump.">;
 
 let Group = Action_Group in {
 
Index: include/clang/Frontend/ASTConsumers.h
===================================================================
--- include/clang/Frontend/ASTConsumers.h
+++ include/clang/Frontend/ASTConsumers.h
@@ -37,7 +37,7 @@
 
 // AST dumper: dumps the raw AST in human-readable form to stderr; this is
 // intended for debugging.
-ASTConsumer *CreateASTDumper(StringRef FilterString);
+ASTConsumer *CreateASTDumper(StringRef FilterString, unsigned MaxDepth);
 
 // AST Decl node lister: prints qualified names of all filterable AST Decl
 // nodes.
Index: include/clang/Frontend/FrontendOptions.h
===================================================================
--- include/clang/Frontend/FrontendOptions.h
+++ include/clang/Frontend/FrontendOptions.h
@@ -145,6 +145,9 @@
   /// If given, filter dumped AST Decl nodes by this substring.
   std::string ASTDumpFilter;
 
+  /// If nonzero, the maximum AST traversal depth while dumping.
+  unsigned ASTDumpDepth;
+
   /// If given, enable code completion at the provided location.
   ParsedSourceLocation CodeCompletionAt;
 
Index: lib/AST/DeclPrinter.cpp
===================================================================
--- lib/AST/DeclPrinter.cpp
+++ lib/AST/DeclPrinter.cpp
@@ -175,12 +175,13 @@
 }
 
 void Decl::dump() const {
-  dump(llvm::errs());
+  dump(llvm::errs(), 0);
 }
 
-void Decl::dump(raw_ostream &Out) const {
+void Decl::dump(raw_ostream &Out, unsigned MaxDepth) const {
   PrintingPolicy Policy = getASTContext().getPrintingPolicy();
   Policy.DumpSourceManager = &getASTContext().getSourceManager();
+  Policy.DumpDepth = MaxDepth;
   print(Out, Policy, /*Indentation*/ 0, /*PrintInstantiation*/ true);
 }
 
Index: lib/AST/DumpXML.cpp
===================================================================
--- lib/AST/DumpXML.cpp
+++ lib/AST/DumpXML.cpp
@@ -1028,7 +1028,7 @@
     push("Stmt");
     out << ">\n";
     Stack.back().State = NS_Children; // explicitly become non-lazy
-    S->dump(out, Context.getSourceManager());
+    S->dump(out, Context.getSourceManager(), 0);
     out << '\n';
     pop();
   }
Index: lib/AST/StmtDumper.cpp
===================================================================
--- lib/AST/StmtDumper.cpp
+++ lib/AST/StmtDumper.cpp
@@ -32,11 +32,19 @@
     unsigned IndentLevel;
     bool IsFirstLine;
 
-    /// MaxDepth - When doing a normal dump (not dumpAll) we only want to dump
-    /// the first few levels of an AST.  This keeps track of how many ast levels
-    /// are left.
+    /// \brief When doing a normal dump (not dumpAll) we only want to dump
+    /// the first few levels of an AST. This is the maximum indentation
+    /// level to reach. If this is zero then the depth is unlimited.
     unsigned MaxDepth;
 
+    /// \brief True if any children of the current node were pruned due to
+    /// reaching the maximum depth.
+    ///
+    /// Used to determine if an indication of the truncation needs to be
+    /// displayed. This is not displayed immediately because that may result
+    /// in the indication being displayed more than once.
+    bool Truncated;
+
     /// LastLocFilename/LastLocLine - Keep track of the last location we print
     /// out so that we can print out deltas from then on out.
     const char *LastLocFilename;
@@ -55,7 +63,8 @@
 
   public:
     StmtDumper(SourceManager *sm, raw_ostream &os, unsigned maxDepth)
-      : SM(sm), OS(os), IndentLevel(0), IsFirstLine(true), MaxDepth(maxDepth) {
+      : SM(sm), OS(os), IndentLevel(0), IsFirstLine(true), MaxDepth(maxDepth),
+        Truncated(false) {
       LastLocFilename = "";
       LastLocLine = ~0U;
     }
@@ -66,8 +75,8 @@
 
     void DumpSubTree(Stmt *S) {
       // Prune the recursion if not using dump all.
-      if (MaxDepth == 0) return;
-
+      if (atMaxDepth())
+        return;
       IndentScope Indent(*this);
 
       if (!S) {
@@ -98,10 +107,21 @@
     }
 
     void unindent() {
+      if (Truncated) {
+        OS << " ...";
+        Truncated = false;
+      }
       OS << ")";
       IndentLevel--;
     }
 
+    bool atMaxDepth() {
+      if (!MaxDepth || MaxDepth > IndentLevel)
+        return false;
+      Truncated = true;
+      return true;
+    }
+
     void DumpType(QualType T) {
       SplitQualType T_split = T.split();
       OS << "'" << QualType::getAsString(T_split) << "'";
@@ -324,6 +344,9 @@
 
 void StmtDumper::VisitDeclStmt(DeclStmt *Node) {
   DumpStmt(Node);
+  if (atMaxDepth())
+    return;
+
   for (DeclStmt::decl_iterator DI = Node->decl_begin(), DE = Node->decl_end();
        DI != DE; ++DI) {
     IndentScope Indent(*this);
@@ -516,6 +539,9 @@
   BlockDecl *block = Node->getBlockDecl();
   OS << " decl=" << block;
 
+  if (atMaxDepth())
+    return;
+
   if (block->capturesCXXThis()) {
     IndentScope Indent(*this);
     OS << "capture this";
@@ -536,7 +562,6 @@
 
 void StmtDumper::VisitOpaqueValueExpr(OpaqueValueExpr *Node) {
   DumpExpr(Node);
-
   if (Expr *Source = Node->getSourceExpr())
     DumpSubTree(Source);
 }
@@ -596,6 +621,9 @@
 
 void StmtDumper::VisitExprWithCleanups(ExprWithCleanups *Node) {
   DumpExpr(Node);
+  if (atMaxDepth())
+    return;
+
   for (unsigned i = 0, e = Node->getNumObjects(); i != e; ++i) {
     IndentScope Indent(*this);
     OS << "cleanup ";
@@ -731,11 +759,11 @@
 /// specified node and a few nodes underneath it, but not the whole subtree.
 /// This is useful in a debugger.
 void Stmt::dump(SourceManager &SM) const {
-  dump(llvm::errs(), SM);
+  dump(llvm::errs(), SM, 4);
 }
 
-void Stmt::dump(raw_ostream &OS, SourceManager &SM) const {
-  StmtDumper P(&SM, OS, 4);
+void Stmt::dump(raw_ostream &OS, SourceManager &SM, unsigned MaxDepth) const {
+  StmtDumper P(&SM, OS, MaxDepth);
   P.DumpSubTree(const_cast<Stmt*>(this));
 }
 
@@ -749,12 +777,12 @@
 
 /// dumpAll - This does a dump of the specified AST fragment and all subtrees.
 void Stmt::dumpAll(SourceManager &SM) const {
-  StmtDumper P(&SM, llvm::errs(), ~0U);
+  StmtDumper P(&SM, llvm::errs(), 0);
   P.DumpSubTree(const_cast<Stmt*>(this));
 }
 
 /// dumpAll - This does a dump of the specified AST fragment and all subtrees.
 void Stmt::dumpAll() const {
-  StmtDumper P(0, llvm::errs(), ~0U);
+  StmtDumper P(0, llvm::errs(), 0);
   P.DumpSubTree(const_cast<Stmt*>(this));
 }
Index: lib/AST/StmtPrinter.cpp
===================================================================
--- lib/AST/StmtPrinter.cpp
+++ lib/AST/StmtPrinter.cpp
@@ -1859,7 +1859,7 @@
   }
 
   if (Policy.DumpSourceManager) {
-    dump(OS, *Policy.DumpSourceManager);
+    dump(OS, *Policy.DumpSourceManager, Policy.DumpDepth);
     return;
   }
 
Index: lib/Frontend/ASTConsumers.cpp
===================================================================
--- lib/Frontend/ASTConsumers.cpp
+++ lib/Frontend/ASTConsumers.cpp
@@ -37,16 +37,16 @@
 
   public:
     ASTPrinter(raw_ostream *Out = NULL, bool Dump = false,
-               StringRef FilterString = "")
+               StringRef FilterString = "", unsigned MaxDepth = 0)
         : Out(Out ? *Out : llvm::outs()), Dump(Dump),
-          FilterString(FilterString) {}
+          FilterString(FilterString), MaxDepth(MaxDepth) {}
 
     virtual void HandleTranslationUnit(ASTContext &Context) {
       TranslationUnitDecl *D = Context.getTranslationUnitDecl();
 
       if (FilterString.empty()) {
         if (Dump)
-          D->dump(Out);
+          D->dump(Out, MaxDepth);
         else
           D->print(Out, /*Indentation=*/0, /*PrintInstantiation=*/true);
         return;
@@ -65,7 +65,7 @@
             (Dump ? "Dumping " : "Printing ") << getName(D) << ":\n";
         Out.resetColor();
         if (Dump)
-          D->dump(Out);
+          D->dump(Out, MaxDepth);
         else
           D->print(Out, /*Indentation=*/0, /*PrintInstantiation=*/true);
         Out << "\n";
@@ -88,6 +88,7 @@
     raw_ostream &Out;
     bool Dump;
     std::string FilterString;
+    unsigned MaxDepth;
   };
 
   class ASTDeclNodeLister : public ASTConsumer,
@@ -117,8 +118,8 @@
   return new ASTPrinter(Out, /*Dump=*/ false, FilterString);
 }
 
-ASTConsumer *clang::CreateASTDumper(StringRef FilterString) {
-  return new ASTPrinter(0, /*Dump=*/ true, FilterString);
+ASTConsumer *clang::CreateASTDumper(StringRef FilterString, unsigned MaxDepth) {
+  return new ASTPrinter(0, /*Dump=*/ true, FilterString, MaxDepth);
 }
 
 ASTConsumer *clang::CreateASTDeclNodeLister() {
Index: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp
+++ lib/Frontend/CompilerInvocation.cpp
@@ -564,6 +564,8 @@
   }
   if (!Opts.ASTDumpFilter.empty())
     Res.push_back("-ast-dump-filter", Opts.ASTDumpFilter);
+  if (Opts.ASTDumpDepth != 0)
+    Res.push_back("-ast-dump-depth", llvm::utostr(Opts.ASTDumpDepth));
   for (unsigned i = 0, e = Opts.Plugins.size(); i != e; ++i)
     Res.push_back("-load", Opts.Plugins[i]);
   for (unsigned i = 0, e = Opts.AddPluginActions.size(); i != e; ++i) {
@@ -1585,6 +1587,7 @@
   Opts.FixAndRecompile = Args.hasArg(OPT_fixit_recompile);
   Opts.FixToTemporaries = Args.hasArg(OPT_fixit_to_temp);
   Opts.ASTDumpFilter = Args.getLastArgValue(OPT_ast_dump_filter);
+  Opts.ASTDumpDepth = Args.getLastArgIntValue(OPT_ast_dump_depth, 0, Diags);
 
   Opts.CodeCompleteOpts.IncludeMacros
     = Args.hasArg(OPT_code_completion_macros);
Index: lib/Frontend/FrontendActions.cpp
===================================================================
--- lib/Frontend/FrontendActions.cpp
+++ lib/Frontend/FrontendActions.cpp
@@ -53,7 +53,8 @@
 
 ASTConsumer *ASTDumpAction::CreateASTConsumer(CompilerInstance &CI,
                                               StringRef InFile) {
-  return CreateASTDumper(CI.getFrontendOpts().ASTDumpFilter);
+  return CreateASTDumper(CI.getFrontendOpts().ASTDumpFilter,
+                         CI.getFrontendOpts().ASTDumpDepth);
 }
 
 ASTConsumer *ASTDeclListAction::CreateASTConsumer(CompilerInstance &CI,
Index: test/Tooling/clang-check-ast-dump.cpp
===================================================================
--- test/Tooling/clang-check-ast-dump.cpp
+++ test/Tooling/clang-check-ast-dump.cpp
@@ -12,6 +12,11 @@
 // CHECK-FILTER-NEXT:   (ReturnStmt
 // CHECK-FILTER-NEXT:     (BinaryOperator
 //
+// RUN: clang-check -ast-dump -ast-dump-filter test_namespace::TheClass::theMethod -ast-dump-depth 2 "%s" -- 2>&1 | FileCheck -check-prefix CHECK-DEPTH %s
+// CHECK-DEPTH: int theMethod(int x) (CompoundStmt
+// CHECK-DEPTH-NEXT:   (ReturnStmt{{.*}} ...)
+// CHECK-DEPTH-NOT:     (BinaryOperator
+//
 // RUN: clang-check -ast-print "%s" -- 2>&1 | FileCheck -check-prefix CHECK-PRINT %s
 // CHECK-PRINT: namespace test_namespace
 // CHECK-PRINT: class TheClass
Index: tools/clang-check/ClangCheck.cpp
===================================================================
--- tools/clang-check/ClangCheck.cpp
+++ tools/clang-check/ClangCheck.cpp
@@ -61,6 +61,9 @@
 static cl::opt<std::string> ASTDumpFilter(
     "ast-dump-filter",
     cl::desc(Options->getOptionHelpText(options::OPT_ast_dump_filter)));
+static cl::opt<unsigned> ASTDumpDepth(
+    "ast-dump-depth",
+    cl::desc(Options->getOptionHelpText(options::OPT_ast_dump_depth)));
 
 static cl::opt<bool> Fixit(
     "fixit",
@@ -133,7 +136,7 @@
     if (ASTList)
       return clang::CreateASTDeclNodeLister();
     if (ASTDump)
-      return clang::CreateASTDumper(ASTDumpFilter);
+      return clang::CreateASTDumper(ASTDumpFilter, ASTDumpDepth);
     if (ASTPrint)
       return clang::CreateASTPrinter(&llvm::outs(), ASTDumpFilter);
     return new clang::ASTConsumer();
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to