* dump types for FunctionDecl and VarDecl
  * added simple test cases for FunctionDecl, VarDecl and DeclStmt

Hi alexfh, doug.gregor,

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

CHANGE SINCE LAST DIFF
  http://llvm-reviews.chandlerc.com/D52?vs=139&id=141#toc

Files:
  include/clang/AST/Stmt.h
  lib/AST/ASTDumper.cpp
  lib/AST/ASTDumper.h
  lib/AST/CMakeLists.txt
  lib/AST/DeclDumper.cpp
  lib/AST/DeclPrinter.cpp
  lib/AST/StmtDumper.cpp
  unittests/AST/CMakeLists.txt
  unittests/AST/DeclDumperTest.cpp
  unittests/AST/StmtDumperTest.cpp
Index: include/clang/AST/Stmt.h
===================================================================
--- include/clang/AST/Stmt.h
+++ include/clang/AST/Stmt.h
@@ -360,17 +360,12 @@
   static void EnableStatistics();
   static void PrintStats();
 
-  /// dump - This does a local dump of the specified AST fragment.  It dumps the
-  /// specified node and a few nodes underneath it, but not the whole subtree.
+  /// dump - This does a dump of the specified AST fragment and all subtrees.
   /// 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;
 
-  /// dumpAll - This does a dump of the specified AST fragment and all subtrees.
-  void dumpAll() const;
-  void dumpAll(SourceManager &SM) const;
-
   /// dumpPretty/printPretty - These two methods do a "pretty print" of the AST
   /// back to its original source language syntax.
   void dumpPretty(ASTContext &Context) const;
Index: lib/AST/ASTDumper.cpp
===================================================================
--- /dev/null
+++ lib/AST/ASTDumper.cpp
@@ -0,0 +1,96 @@
+//===--- ASTDumper.cpp - Dumping implementation for ASTs ------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the ASTDumper class, which dumps out the
+// AST in a form that exposes type details and other fields.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTDumper.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/Support/raw_ostream.h"
+using namespace clang;
+
+void ASTDumper::flush() {
+  if (NeedNewLine)
+    OS << "\n";
+  NeedNewLine = false;
+}
+
+void ASTDumper::indent() {
+  if (NeedNewLine)
+    OS << "\n";
+
+  NeedNewLine = true;
+  for (int i = 0, e = IndentLevel; i < e; ++i)
+    OS << "  ";
+  OS << "(";
+  IndentLevel++;
+}
+
+void ASTDumper::unindent() {
+  OS << ")";
+  IndentLevel--;
+}
+
+void ASTDumper::dumpType(QualType T) {
+  SplitQualType T_split = T.split();
+  OS << "'" << QualType::getAsString(T_split) << "'";
+
+  if (!T.isNull()) {
+    // If the type is sugared, also dump a (shallow) desugared type.
+    SplitQualType D_split = T.getSplitDesugaredType();
+    if (T_split != D_split)
+      OS << ":'" << QualType::getAsString(D_split) << "'";
+  }
+}
+
+void ASTDumper::dumpLocation(SourceLocation Loc) {
+  SourceLocation SpellingLoc = SM->getSpellingLoc(Loc);
+
+  // The general format we print out is filename:line:col, but we drop pieces
+  // that haven't changed since the last loc printed.
+  PresumedLoc PLoc = SM->getPresumedLoc(SpellingLoc);
+
+  if (PLoc.isInvalid()) {
+    OS << "<invalid sloc>";
+    return;
+  }
+
+  if (strcmp(PLoc.getFilename(), LastLocFilename) != 0) {
+    OS << PLoc.getFilename() << ':' << PLoc.getLine()
+       << ':' << PLoc.getColumn();
+    LastLocFilename = PLoc.getFilename();
+    LastLocLine = PLoc.getLine();
+  } else if (PLoc.getLine() != LastLocLine) {
+    OS << "line" << ':' << PLoc.getLine()
+       << ':' << PLoc.getColumn();
+    LastLocLine = PLoc.getLine();
+  } else {
+    OS << "col" << ':' << PLoc.getColumn();
+  }
+}
+
+void ASTDumper::dumpSourceRange(SourceRange R) {
+  // Can't translate locations if a SourceManager isn't available.
+  if (SM == 0)
+    return;
+
+  // TODO: If the parent expression is available, we can print a delta vs its
+  // location.
+
+  OS << " <";
+  dumpLocation(R.getBegin());
+  if (R.getBegin() != R.getEnd()) {
+    OS << ", ";
+    dumpLocation(R.getEnd());
+  }
+  OS << ">";
+}
Index: lib/AST/ASTDumper.h
===================================================================
--- /dev/null
+++ lib/AST/ASTDumper.h
@@ -0,0 +1,62 @@
+//===--- ASTDumper.h - Dumping implementation for ASTs ----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the ASTDumper class, which dumps out the
+// AST in a form that exposes type details and other fields.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_ASTDUMPER_H
+#define LLVM_CLANG_AST_ASTDUMPER_H
+
+#include "clang/Basic/LLVM.h"
+
+namespace clang {
+
+class SourceManager;
+class SourceRange;
+class SourceLocation;
+class QualType;
+class Decl;
+class Stmt;
+
+class ASTDumper {
+public:
+  ASTDumper(SourceManager *SM, raw_ostream &OS)
+    : SM(SM), OS(OS), IndentLevel(0), LastLocFilename(""), LastLocLine(~0U),
+    NeedNewLine(false) {
+  }
+
+  void flush();
+  void indent();
+  void unindent();
+
+  void dumpDecl(Decl *D);
+  void dumpStmt(Stmt *S);
+  void dumpType(QualType T);
+  void dumpSourceRange(SourceRange R);
+
+private:
+  void dumpLocation(SourceLocation Loc);
+
+  SourceManager *SM;
+  raw_ostream &OS;
+  unsigned IndentLevel;
+
+  /// 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;
+  unsigned LastLocLine;
+
+  bool NeedNewLine;
+};
+
+}
+
+#endif
Index: lib/AST/CMakeLists.txt
===================================================================
--- lib/AST/CMakeLists.txt
+++ lib/AST/CMakeLists.txt
@@ -5,6 +5,7 @@
   ASTConsumer.cpp
   ASTContext.cpp
   ASTDiagnostic.cpp
+  ASTDumper.cpp
   ASTImporter.cpp
   AttrImpl.cpp
   CXXInheritance.cpp
@@ -19,6 +20,7 @@
   DeclarationName.cpp
   DeclBase.cpp
   DeclCXX.cpp
+  DeclDumper.cpp
   DeclFriend.cpp
   DeclGroup.cpp
   DeclObjC.cpp
Index: lib/AST/DeclDumper.cpp
===================================================================
--- /dev/null
+++ lib/AST/DeclDumper.cpp
@@ -0,0 +1,114 @@
+//===--- DeclDumper.cpp - Dumping implementation for Decl ASTs ------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the Decl::dump method, which dumps out the
+// AST in a form that exposes type details and other fields.
+//
+//===----------------------------------------------------------------------===//
+#include "ASTDumper.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclVisitor.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+#include "llvm/Support/raw_ostream.h"
+using namespace clang;
+
+namespace {
+  class DeclDumper : public DeclVisitor<DeclDumper> {
+    ASTDumper &Dumper;
+    raw_ostream &OS;
+
+  public:
+    DeclDumper(ASTDumper &Dumper, raw_ostream &OS)
+      : Dumper(Dumper), OS(OS) {
+    }
+
+    void dumpDecl(Decl *D);
+    void dumpDeclContext(DeclContext *DC);
+
+    void VisitEnumConstantDecl(EnumConstantDecl *D);
+    void VisitFunctionDecl(FunctionDecl *D);
+    void VisitFieldDecl(FieldDecl *D);
+    void VisitVarDecl(VarDecl *D);
+  };
+}
+
+void DeclDumper::dumpDecl(Decl *D) {
+  Dumper.indent();
+  if (D) {
+    OS << D->getDeclKindName() << "Decl"
+       << " " << (const void*)D;
+    Dumper.dumpSourceRange(D->getSourceRange());
+    if (isa<NamedDecl>(D))
+      OS << " " << cast<NamedDecl>(D)->getNameAsString();
+    Visit(D);
+    dumpDeclContext(dyn_cast<DeclContext>(D));
+  } else {
+    OS << "<<<NULL>>>";
+  }
+  Dumper.unindent();
+}
+
+void DeclDumper::dumpDeclContext(DeclContext *DC) {
+  // Decls within functions are visited by the body
+  if (!DC || isa<FunctionDecl>(*DC))
+    return;
+
+  for (DeclContext::decl_iterator D = DC->decls_begin(), DEnd = DC->decls_end();
+       D != DEnd; ++D)
+    dumpDecl(*D);
+}
+
+//----------------------------------------------------------------------------
+// Common C declarations
+//----------------------------------------------------------------------------
+
+void DeclDumper::VisitEnumConstantDecl(EnumConstantDecl *D) {
+  if (Expr *Init = D->getInitExpr())
+    Dumper.dumpStmt(Init);
+}
+
+void DeclDumper::VisitFunctionDecl(FunctionDecl *D) {
+  OS << ' ';
+  Dumper.dumpType(D->getType());
+  if (D->doesThisDeclarationHaveABody())
+    Dumper.dumpStmt(D->getBody());
+}
+
+void DeclDumper::VisitFieldDecl(FieldDecl *D) {
+  if (Expr *Init = D->getInClassInitializer())
+    Dumper.dumpStmt(Init);
+}
+
+void DeclDumper::VisitVarDecl(VarDecl *D) {
+  OS << ' ';
+  Dumper.dumpType(D->getType());
+  if (Expr *Init = D->getInit())
+    Dumper.dumpStmt(Init);
+}
+
+//===----------------------------------------------------------------------===//
+// Decl method implementations
+//===----------------------------------------------------------------------===//
+
+void ASTDumper::dumpDecl(Decl *D) {
+  DeclDumper Dumper(*this, OS);
+  Dumper.dumpDecl(D);
+}
+
+void Decl::dump(raw_ostream &OS) const {
+  ASTDumper Dumper(&getASTContext().getSourceManager(), OS);
+  Dumper.dumpDecl(const_cast<Decl*>(this));
+  Dumper.flush();
+}
+
+void Decl::dump() const {
+  dump(llvm::errs());
+}
Index: lib/AST/DeclPrinter.cpp
===================================================================
--- lib/AST/DeclPrinter.cpp
+++ lib/AST/DeclPrinter.cpp
@@ -7,7 +7,7 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// This file implements the Decl::dump method, which pretty print the
+// This file implements the Decl::print method, which pretty prints the
 // AST back out to C/Objective-C/C++/Objective-C++ code.
 //
 //===----------------------------------------------------------------------===//
@@ -172,16 +172,6 @@
   Printer.VisitDeclContext(const_cast<DeclContext *>(this), /*Indent=*/false);
 }
 
-void Decl::dump() const {
-  dump(llvm::errs());
-}
-
-void Decl::dump(raw_ostream &Out) const {
-  PrintingPolicy Policy = getASTContext().getPrintingPolicy();
-  Policy.DumpSourceManager = &getASTContext().getSourceManager();
-  print(Out, Policy, /*Indentation*/ 0, /*PrintInstantiation*/ true);
-}
-
 raw_ostream& DeclPrinter::Indent(unsigned Indentation) {
   for (unsigned i = 0; i != Indentation; ++i)
     Out << "  ";
Index: lib/AST/StmtDumper.cpp
===================================================================
--- lib/AST/StmtDumper.cpp
+++ lib/AST/StmtDumper.cpp
@@ -7,17 +7,15 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// This file implements the Stmt::dump/Stmt::print methods, which dump out the
+// This file implements the Stmt::dump method, which dumps out the
 // AST in a form that exposes type details and other fields.
 //
 //===----------------------------------------------------------------------===//
 
+#include "ASTDumper.h"
 #include "clang/AST/StmtVisitor.h"
-#include "clang/AST/ASTContext.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclCXX.h"
-#include "clang/AST/PrettyPrinter.h"
-#include "clang/Basic/SourceManager.h"
 #include "llvm/Support/raw_ostream.h"
 using namespace clang;
 
@@ -27,79 +25,41 @@
 
 namespace  {
   class StmtDumper : public StmtVisitor<StmtDumper> {
-    SourceManager *SM;
+    ASTDumper &Dumper;
     raw_ostream &OS;
-    unsigned IndentLevel;
-
-    /// 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.
-    unsigned MaxDepth;
-
-    /// 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;
-    unsigned LastLocLine;
 
   public:
-    StmtDumper(SourceManager *sm, raw_ostream &os, unsigned maxDepth)
-      : SM(sm), OS(os), IndentLevel(0-1), MaxDepth(maxDepth) {
-      LastLocFilename = "";
-      LastLocLine = ~0U;
+    StmtDumper(ASTDumper &Dumper, raw_ostream &OS)
+      : Dumper(Dumper), OS(OS) {
     }
 
     void DumpSubTree(Stmt *S) {
-      // Prune the recursion if not using dump all.
-      if (MaxDepth == 0) return;
-
-      ++IndentLevel;
+      Dumper.indent();
       if (S) {
         if (DeclStmt* DS = dyn_cast<DeclStmt>(S))
           VisitDeclStmt(DS);
         else {
           Visit(S);
 
           // Print out children.
           Stmt::child_range CI = S->children();
-          if (CI) {
-            while (CI) {
-              OS << '\n';
-              DumpSubTree(*CI++);
-            }
-          }
+          while (CI)
+            DumpSubTree(*CI++);
         }
-        OS << ')';
       } else {
-        Indent();
         OS << "<<<NULL>>>";
       }
-      --IndentLevel;
-    }
-
-    void DumpDeclarator(Decl *D);
-
-    void Indent() const {
-      for (int i = 0, e = IndentLevel; i < e; ++i)
-        OS << "  ";
+      Dumper.unindent();
     }
 
     void DumpType(QualType T) {
-      SplitQualType T_split = T.split();
-      OS << "'" << QualType::getAsString(T_split) << "'";
-
-      if (!T.isNull()) {
-        // If the type is sugared, also dump a (shallow) desugared type.
-        SplitQualType D_split = T.getSplitDesugaredType();
-        if (T_split != D_split)
-          OS << ":'" << QualType::getAsString(D_split) << "'";
-      }
+      Dumper.dumpType(T);
     }
     void DumpDeclRef(Decl *node);
     void DumpStmt(const Stmt *Node) {
-      Indent();
-      OS << "(" << Node->getStmtClassName()
+      OS << Node->getStmtClassName()
          << " " << (const void*)Node;
-      DumpSourceRange(Node);
+      Dumper.dumpSourceRange(Node->getSourceRange());
     }
     void DumpValueKind(ExprValueKind K) {
       switch (K) {
@@ -124,8 +84,6 @@
       DumpValueKind(Node->getValueKind());
       DumpObjectKind(Node->getObjectKind());
     }
-    void DumpSourceRange(const Stmt *Node);
-    void DumpLocation(SourceLocation Loc);
 
     // Stmts.
     void VisitStmt(Stmt *Node);
@@ -178,146 +136,18 @@
 }
 
 //===----------------------------------------------------------------------===//
-//  Utilities
-//===----------------------------------------------------------------------===//
-
-void StmtDumper::DumpLocation(SourceLocation Loc) {
-  SourceLocation SpellingLoc = SM->getSpellingLoc(Loc);
-
-  // The general format we print out is filename:line:col, but we drop pieces
-  // that haven't changed since the last loc printed.
-  PresumedLoc PLoc = SM->getPresumedLoc(SpellingLoc);
-
-  if (PLoc.isInvalid()) {
-    OS << "<invalid sloc>";
-    return;
-  }
-
-  if (strcmp(PLoc.getFilename(), LastLocFilename) != 0) {
-    OS << PLoc.getFilename() << ':' << PLoc.getLine()
-       << ':' << PLoc.getColumn();
-    LastLocFilename = PLoc.getFilename();
-    LastLocLine = PLoc.getLine();
-  } else if (PLoc.getLine() != LastLocLine) {
-    OS << "line" << ':' << PLoc.getLine()
-       << ':' << PLoc.getColumn();
-    LastLocLine = PLoc.getLine();
-  } else {
-    OS << "col" << ':' << PLoc.getColumn();
-  }
-}
-
-void StmtDumper::DumpSourceRange(const Stmt *Node) {
-  // Can't translate locations if a SourceManager isn't available.
-  if (SM == 0) return;
-
-  // TODO: If the parent expression is available, we can print a delta vs its
-  // location.
-  SourceRange R = Node->getSourceRange();
-
-  OS << " <";
-  DumpLocation(R.getBegin());
-  if (R.getBegin() != R.getEnd()) {
-    OS << ", ";
-    DumpLocation(R.getEnd());
-  }
-  OS << ">";
-
-  // <t2.c:123:421[blah], t2.c:412:321>
-
-}
-
-
-//===----------------------------------------------------------------------===//
 //  Stmt printing methods.
 //===----------------------------------------------------------------------===//
 
 void StmtDumper::VisitStmt(Stmt *Node) {
   DumpStmt(Node);
 }
 
-void StmtDumper::DumpDeclarator(Decl *D) {
-  // FIXME: Need to complete/beautify this... this code simply shows the
-  // nodes are where they need to be.
-  if (TypedefDecl *localType = dyn_cast<TypedefDecl>(D)) {
-    OS << "\"typedef " << localType->getUnderlyingType().getAsString()
-       << ' ' << *localType << '"';
-  } else if (TypeAliasDecl *localType = dyn_cast<TypeAliasDecl>(D)) {
-    OS << "\"using " << *localType << " = "
-       << localType->getUnderlyingType().getAsString() << '"';
-  } else if (ValueDecl *VD = dyn_cast<ValueDecl>(D)) {
-    OS << "\"";
-    // Emit storage class for vardecls.
-    if (VarDecl *V = dyn_cast<VarDecl>(VD)) {
-      if (V->getStorageClass() != SC_None)
-        OS << VarDecl::getStorageClassSpecifierString(V->getStorageClass())
-           << " ";
-    }
-
-    std::string Name = VD->getNameAsString();
-    VD->getType().getAsStringInternal(Name,
-                          PrintingPolicy(VD->getASTContext().getLangOpts()));
-    OS << Name;
-
-    // If this is a vardecl with an initializer, emit it.
-    if (VarDecl *V = dyn_cast<VarDecl>(VD)) {
-      if (V->getInit()) {
-        OS << " =\n";
-        DumpSubTree(V->getInit());
-      }
-    }
-    OS << '"';
-  } else if (TagDecl *TD = dyn_cast<TagDecl>(D)) {
-    // print a free standing tag decl (e.g. "struct x;").
-    const char *tagname;
-    if (const IdentifierInfo *II = TD->getIdentifier())
-      tagname = II->getNameStart();
-    else
-      tagname = "<anonymous>";
-    OS << '"' << TD->getKindName() << ' ' << tagname << ";\"";
-    // FIXME: print tag bodies.
-  } else if (UsingDirectiveDecl *UD = dyn_cast<UsingDirectiveDecl>(D)) {
-    // print using-directive decl (e.g. "using namespace x;")
-    const char *ns;
-    if (const IdentifierInfo *II = UD->getNominatedNamespace()->getIdentifier())
-      ns = II->getNameStart();
-    else
-      ns = "<anonymous>";
-    OS << '"' << UD->getDeclKindName() << ns << ";\"";
-  } else if (UsingDecl *UD = dyn_cast<UsingDecl>(D)) {
-    // print using decl (e.g. "using std::string;")
-    const char *tn = UD->isTypeName() ? "typename " : "";
-    OS << '"' << UD->getDeclKindName() << tn;
-    UD->getQualifier()->print(OS,
-                        PrintingPolicy(UD->getASTContext().getLangOpts()));
-    OS << ";\"";
-  } else if (LabelDecl *LD = dyn_cast<LabelDecl>(D)) {
-    OS << "label " << *LD;
-  } else if (StaticAssertDecl *SAD = dyn_cast<StaticAssertDecl>(D)) {
-    OS << "\"static_assert(\n";
-    DumpSubTree(SAD->getAssertExpr());
-    OS << ",\n";
-    DumpSubTree(SAD->getMessage());
-    OS << ");\"";
-  } else {
-    llvm_unreachable("Unexpected decl");
-  }
-}
-
 void StmtDumper::VisitDeclStmt(DeclStmt *Node) {
   DumpStmt(Node);
-  OS << "\n";
   for (DeclStmt::decl_iterator DI = Node->decl_begin(), DE = Node->decl_end();
-       DI != DE; ++DI) {
-    Decl* D = *DI;
-    ++IndentLevel;
-    Indent();
-    OS << (void*) D << " ";
-    DumpDeclarator(D);
-    if (DI+1 != DE)
-      OS << "\n";
-    --IndentLevel;
-  }
+       DI != DE; ++DI)
+    Dumper.dumpDecl(*DI);
 }
 
 void StmtDumper::VisitLabelStmt(LabelStmt *Node) {
@@ -503,35 +333,31 @@
   BlockDecl *block = Node->getBlockDecl();
   OS << " decl=" << block;
 
-  IndentLevel++;
   if (block->capturesCXXThis()) {
-    OS << '\n'; Indent(); OS << "(capture this)";
+    Dumper.indent();
+    OS << "capture this";
+    Dumper.unindent();
   }
   for (BlockDecl::capture_iterator
          i = block->capture_begin(), e = block->capture_end(); i != e; ++i) {
-    OS << '\n';
-    Indent();
-    OS << "(capture ";
+    Dumper.indent();
+    OS << "capture ";
     if (i->isByRef()) OS << "byref ";
     if (i->isNested()) OS << "nested ";
     if (i->getVariable())
       DumpDeclRef(i->getVariable());
     if (i->hasCopyExpr()) DumpSubTree(i->getCopyExpr());
-    OS << ")";
+    Dumper.unindent();
   }
-  IndentLevel--;
 
-  OS << '\n';
   DumpSubTree(block->getBody());
 }
 
 void StmtDumper::VisitOpaqueValueExpr(OpaqueValueExpr *Node) {
   DumpExpr(Node);
 
-  if (Expr *Source = Node->getSourceExpr()) {
-    OS << '\n';
+  if (Expr *Source = Node->getSourceExpr())
     DumpSubTree(Source);
-  }
 }
 
 // GNU extensions.
@@ -589,15 +415,12 @@
 
 void StmtDumper::VisitExprWithCleanups(ExprWithCleanups *Node) {
   DumpExpr(Node);
-  ++IndentLevel;
   for (unsigned i = 0, e = Node->getNumObjects(); i != e; ++i) {
-    OS << "\n";
-    Indent();
-    OS << "(cleanup ";
+    Dumper.indent();
+    OS << "cleanup ";
     DumpDeclRef(Node->getObject(i));
-    OS << ")";
+    Dumper.unindent();
   }
-  --IndentLevel;
 }
 
 void StmtDumper::DumpCXXTemporary(CXXTemporary *Temporary) {
@@ -638,8 +461,7 @@
 void StmtDumper::VisitObjCAtCatchStmt(ObjCAtCatchStmt *Node) {
   DumpStmt(Node);
   if (VarDecl *CatchParam = Node->getCatchParamDecl()) {
-    OS << " catch parm = ";
-    DumpDeclarator(CatchParam);
+    Dumper.dumpDecl(CatchParam);
   } else {
     OS << " catch all";
   }
@@ -724,38 +546,26 @@
 // Stmt method implementations
 //===----------------------------------------------------------------------===//
 
-/// dump - This does a local dump of the specified AST fragment.  It dumps the
-/// specified node and a few nodes underneath it, but not the whole subtree.
-/// This is useful in a debugger.
+void ASTDumper::dumpStmt(Stmt *S) {
+  StmtDumper P(*this, OS);
+  P.DumpSubTree(S);
+}
+
+/// \brief This does a dump of the specified AST fragment and all subtrees.
 void Stmt::dump(SourceManager &SM) const {
   dump(llvm::errs(), SM);
 }
 
+/// \brief This does a dump of the specified AST fragment and all subtrees.
 void Stmt::dump(raw_ostream &OS, SourceManager &SM) const {
-  StmtDumper P(&SM, OS, 4);
-  P.DumpSubTree(const_cast<Stmt*>(this));
-  OS << "\n";
+  ASTDumper Dumper(&SM, OS);
+  Dumper.dumpStmt(const_cast<Stmt*>(this));
+  Dumper.flush();
 }
 
-/// dump - This does a local dump of the specified AST fragment.  It dumps the
-/// specified node and a few nodes underneath it, but not the whole subtree.
-/// This is useful in a debugger.
+/// \brief This does a dump of the specified AST fragment and all subtrees.
 void Stmt::dump() const {
-  StmtDumper P(0, llvm::errs(), 4);
-  P.DumpSubTree(const_cast<Stmt*>(this));
-  llvm::errs() << "\n";
-}
-
-/// 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);
-  P.DumpSubTree(const_cast<Stmt*>(this));
-  llvm::errs() << "\n";
-}
-
-/// dumpAll - This does a dump of the specified AST fragment and all subtrees.
-void Stmt::dumpAll() const {
-  StmtDumper P(0, llvm::errs(), ~0U);
-  P.DumpSubTree(const_cast<Stmt*>(this));
-  llvm::errs() << "\n";
+  ASTDumper Dumper(0, llvm::errs());
+  Dumper.dumpStmt(const_cast<Stmt*>(this));
+  Dumper.flush();
 }
Index: unittests/AST/CMakeLists.txt
===================================================================
--- unittests/AST/CMakeLists.txt
+++ unittests/AST/CMakeLists.txt
@@ -1,7 +1,9 @@
 add_clang_unittest(ASTTests
   CommentLexer.cpp
   CommentParser.cpp
+  DeclDumperTest.cpp
   DeclPrinterTest.cpp
+  StmtDumperTest.cpp
   StmtPrinterTest.cpp
   )
 
Index: unittests/AST/DeclDumperTest.cpp
===================================================================
--- /dev/null
+++ unittests/AST/DeclDumperTest.cpp
@@ -0,0 +1,126 @@
+//===- unittests/AST/DeclDumperTest.cpp --- Declaration dumper tests ------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains tests for Decl::dump() and related methods.
+//
+// These tests have a coding convention:
+// * declaration to be dumped is named 'A' unless it should have some special
+// name (e.g., 'operator+');
+// * additional helper declarations are 'Z', 'Y', 'X' and so on.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Regex.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace ast_matchers;
+using namespace tooling;
+
+namespace {
+
+class DumpMatch : public MatchFinder::MatchCallback {
+  SmallString<1024> Dumped;
+  unsigned NumFoundDecls;
+
+public:
+  DumpMatch() : NumFoundDecls(0) {}
+
+  virtual void run(const MatchFinder::MatchResult &Result) {
+    const Decl *D = Result.Nodes.getDeclAs<Decl>("id");
+    if (!D || D->isImplicit())
+      return;
+    NumFoundDecls++;
+    if (NumFoundDecls > 1)
+      return;
+
+    llvm::raw_svector_ostream Out(Dumped);
+    D->dump(Out);
+  }
+
+  StringRef getDumped() const {
+    return Dumped;
+  }
+
+  unsigned getNumFoundDecls() const {
+    return NumFoundDecls;
+  }
+};
+
+::testing::AssertionResult DumpedDeclMatches(
+                                  StringRef Code,
+                                  const std::vector<std::string> &Args,
+                                  const DeclarationMatcher &NodeMatch,
+                                  StringRef ExpectedDumped) {
+  DumpMatch Dumper;
+  MatchFinder Finder;
+  Finder.addMatcher(NodeMatch, &Dumper);
+  OwningPtr<FrontendActionFactory> Factory(newFrontendActionFactory(&Finder));
+
+  if (!runToolOnCodeWithArgs(Factory->create(), Code, Args))
+    return testing::AssertionFailure() << "Parsing error in \"" << Code << "\"";
+
+  if (Dumper.getNumFoundDecls() == 0)
+    return testing::AssertionFailure()
+        << "Matcher didn't find any declarations";
+
+  if (Dumper.getNumFoundDecls() > 1)
+    return testing::AssertionFailure()
+        << "Matcher should match only one declaration "
+           "(found " << Dumper.getNumFoundDecls() << ")";
+
+  std::string Dumped = Dumper.getDumped();
+  llvm::Regex AddressRegex("0x[^X ]+");
+  while (AddressRegex.match(Dumped))
+    Dumped = AddressRegex.sub("0xXXXXXXXX", Dumped);
+
+  if (Dumped != ExpectedDumped)
+    return ::testing::AssertionFailure()
+      << "Expected \"" << ExpectedDumped << "\", got \"" << Dumped << "\"";
+
+  return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult DumpedDeclCXX98Matches(StringRef Code,
+                                                   StringRef DeclName,
+                                                   StringRef ExpectedDumped) {
+  std::vector<std::string> Args(1, "-std=c++98");
+  return DumpedDeclMatches(Code,
+                           Args,
+                           namedDecl(hasName(DeclName)).bind("id"),
+                           ExpectedDumped);
+}
+
+} // unnamed namespace
+
+TEST(DeclDumper, TestFunctionDecl) {
+  ASSERT_TRUE(DumpedDeclCXX98Matches(
+    "int A(int a) {\n"
+    "  return a;\n"
+    "}\n",
+    "A",
+    "(FunctionDecl 0xXXXXXXXX <input.cc:1:1, line:3:1> A 'int (int)'\n"
+    "  (CompoundStmt 0xXXXXXXXX <line:1:14, line:3:1>\n"
+    "    (ReturnStmt 0xXXXXXXXX <line:2:3, col:10>\n"
+    "      (ImplicitCastExpr 0xXXXXXXXX <col:10> 'int' <LValueToRValue>\n"
+    "        (DeclRefExpr 0xXXXXXXXX <col:10> 'int' lvalue ParmVar 0xXXXXXXXX 'a' 'int')))))\n"));
+}
+
+
+TEST(DeclDumper, TestVarDecl) {
+  ASSERT_TRUE(DumpedDeclCXX98Matches(
+    "int A = 1;",
+    "A",
+    "(VarDecl 0xXXXXXXXX <input.cc:1:1, col:9> A 'int'\n"
+    "  (IntegerLiteral 0xXXXXXXXX <col:9> 'int' 1))\n"));
+}
Index: unittests/AST/StmtDumperTest.cpp
===================================================================
--- /dev/null
+++ unittests/AST/StmtDumperTest.cpp
@@ -0,0 +1,121 @@
+//===- unittests/AST/StmtDumperTest.cpp --- Statement dumper tests --------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains tests for Stmt::dump() and related methods.
+//
+// These tests have a coding convention:
+// * statements to be dumped should be contained within a function named 'A'
+//   unless it should have some special name (e.g., 'operator+');
+// * additional helper declarations are 'Z', 'Y', 'X' and so on.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Regex.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace ast_matchers;
+using namespace tooling;
+
+namespace {
+
+class DumpMatch : public MatchFinder::MatchCallback {
+  SmallString<1024> Dumped;
+  unsigned NumFoundStmts;
+
+public:
+  DumpMatch() : NumFoundStmts(0) {}
+
+  virtual void run(const MatchFinder::MatchResult &Result) {
+    const Stmt *S = Result.Nodes.getStmtAs<Stmt>("id");
+    if (!S)
+      return;
+    NumFoundStmts++;
+    if (NumFoundStmts > 1)
+      return;
+
+    llvm::raw_svector_ostream Out(Dumped);
+    S->dump(Out, Result.Context->getSourceManager());
+  }
+
+  StringRef getDumped() const {
+    return Dumped;
+  }
+
+  unsigned getNumFoundStmts() const {
+    return NumFoundStmts;
+  }
+};
+
+::testing::AssertionResult DumpedStmtMatches(
+                                        StringRef Code,
+                                        const std::vector<std::string> &Args,
+                                        const DeclarationMatcher &NodeMatch,
+                                        StringRef ExpectedDumped) {
+
+  DumpMatch Dumper;
+  MatchFinder Finder;
+  Finder.addMatcher(NodeMatch, &Dumper);
+  OwningPtr<FrontendActionFactory> Factory(newFrontendActionFactory(&Finder));
+
+  if (!runToolOnCodeWithArgs(Factory->create(), Code, Args))
+    return testing::AssertionFailure() << "Parsing error in \"" << Code << "\"";
+
+  if (Dumper.getNumFoundStmts() == 0)
+    return testing::AssertionFailure()
+        << "Matcher didn't find any statements";
+
+  if (Dumper.getNumFoundStmts() > 1)
+    return testing::AssertionFailure()
+        << "Matcher should match only one statement "
+           "(found " << Dumper.getNumFoundStmts() << ")";
+
+  std::string Dumped = Dumper.getDumped();
+  llvm::Regex AddressRegex("0x[^X ]+");
+  while (AddressRegex.match(Dumped))
+    Dumped = AddressRegex.sub("0xXXXXXXXX", Dumped);
+
+  if (Dumped != ExpectedDumped)
+    return ::testing::AssertionFailure()
+      << "Expected \"" << ExpectedDumped << "\", got \"" << Dumped << "\"";
+
+  return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult DumpedStmtCXX98Matches(
+                                              StringRef Code,
+                                              StringRef ContainingFunction,
+                                              StringRef ExpectedDumped) {
+  std::vector<std::string> Args;
+  Args.push_back("-std=c++98");
+  Args.push_back("-Wno-unused-value");
+  return DumpedStmtMatches(Code,
+                           Args,
+                           functionDecl(hasName(ContainingFunction),
+                                        has(compoundStmt(has(stmt().bind("id"))))),
+                           ExpectedDumped);
+}
+
+} // unnamed namespace
+
+TEST(StmtDumper, TestDeclStmt) {
+  ASSERT_TRUE(DumpedStmtCXX98Matches(
+    "void A() {\n"
+    "  int a = 1, b;\n"
+    "}",
+    "A",
+    "(DeclStmt 0xXXXXXXXX <input.cc:2:3, col:15>\n"
+    "  (VarDecl 0xXXXXXXXX <col:3, col:11> a 'int'\n"
+    "    (IntegerLiteral 0xXXXXXXXX <col:11> 'int' 1))\n"
+    "  (VarDecl 0xXXXXXXXX <col:3, col:14> b 'int'))\n"));
+}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to