//===--- StmtDumper.cpp - Dumping implementation for Stmt 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 Stmt::dump/Stmt::print methods, which dump out the // AST in a form that exposes type details and other fields. // //===----------------------------------------------------------------------===// #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; //===----------------------------------------------------------------------===// // StmtDumper Visitor //===----------------------------------------------------------------------===// namespace { class StmtDumper : public StmtVisitor<StmtDumper> { SourceManager *SM; 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; } void DumpSubTree(Stmt *S) { // Prune the recursion if not using dump all. if (MaxDepth == 0) return; ++IndentLevel; 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++); } } } OS << ')'; } else { Indent(); OS << "<<<NULL>>>"; } --IndentLevel; } void DumpDeclarator(Decl *D); void Indent() const { for (int i = 0, e = IndentLevel; i < e; ++i) OS << " "; } 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) << "'"; } } void DumpDeclRef(Decl *node); void DumpStmt(const Stmt *Node) { Indent(); OS << "(" << Node->getStmtClassName() << " " << (const void*)Node; DumpSourceRange(Node); } void DumpValueKind(ExprValueKind K) { switch (K) { case VK_RValue: break; case VK_LValue: OS << " lvalue"; break; case VK_XValue: OS << " xvalue"; break; } } void DumpObjectKind(ExprObjectKind K) { switch (K) { case OK_Ordinary: break; case OK_BitField: OS << " bitfield"; break; case OK_ObjCProperty: OS << " objcproperty"; break; case OK_ObjCSubscript: OS << " objcsubscript"; break; case OK_VectorComponent: OS << " vectorcomponent"; break; } } void DumpExpr(const Expr *Node) { DumpStmt(Node); OS << ' '; DumpType(Node->getType()); DumpValueKind(Node->getValueKind()); DumpObjectKind(Node->getObjectKind()); } void DumpSourceRange(const Stmt *Node); void DumpLocation(SourceLocation Loc); // Stmts. void VisitStmt(Stmt *Node); void VisitDeclStmt(DeclStmt *Node); void VisitLabelStmt(LabelStmt *Node); void VisitGotoStmt(GotoStmt *Node); // Exprs void VisitExpr(Expr *Node); void VisitCastExpr(CastExpr *Node); void VisitDeclRefExpr(DeclRefExpr *Node); void VisitPredefinedExpr(PredefinedExpr *Node); void VisitCharacterLiteral(CharacterLiteral *Node); void VisitIntegerLiteral(IntegerLiteral *Node); void VisitFloatingLiteral(FloatingLiteral *Node); void VisitStringLiteral(StringLiteral *Str); void VisitUnaryOperator(UnaryOperator *Node); void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node); void VisitMemberExpr(MemberExpr *Node); void VisitExtVectorElementExpr(ExtVectorElementExpr *Node); void VisitBinaryOperator(BinaryOperator *Node); void VisitCompoundAssignOperator(CompoundAssignOperator *Node); void VisitAddrLabelExpr(AddrLabelExpr *Node); void VisitBlockExpr(BlockExpr *Node); void VisitOpaqueValueExpr(OpaqueValueExpr *Node); // C++ void VisitCXXNamedCastExpr(CXXNamedCastExpr *Node); void VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *Node); void VisitCXXThisExpr(CXXThisExpr *Node); void VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *Node); void VisitCXXConstructExpr(CXXConstructExpr *Node); void VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *Node); void VisitExprWithCleanups(ExprWithCleanups *Node); void VisitUnresolvedLookupExpr(UnresolvedLookupExpr *Node); void DumpCXXTemporary(CXXTemporary *Temporary); // ObjC void VisitObjCAtCatchStmt(ObjCAtCatchStmt *Node); void VisitObjCEncodeExpr(ObjCEncodeExpr *Node); void VisitObjCMessageExpr(ObjCMessageExpr* Node); void VisitObjCBoxedExpr(ObjCBoxedExpr* Node); void VisitObjCSelectorExpr(ObjCSelectorExpr *Node); void VisitObjCProtocolExpr(ObjCProtocolExpr *Node); void VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *Node); void VisitObjCSubscriptRefExpr(ObjCSubscriptRefExpr *Node); void VisitObjCIvarRefExpr(ObjCIvarRefExpr *Node); void VisitObjCBoolLiteralExpr(ObjCBoolLiteralExpr *Node); }; } //===----------------------------------------------------------------------===// // 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; } } void StmtDumper::VisitLabelStmt(LabelStmt *Node) { DumpStmt(Node); OS << " '" << Node->getName() << "'"; } void StmtDumper::VisitGotoStmt(GotoStmt *Node) { DumpStmt(Node); OS << " '" << Node->getLabel()->getName() << "':" << (void*)Node->getLabel(); } //===----------------------------------------------------------------------===// // Expr printing methods. //===----------------------------------------------------------------------===// void StmtDumper::VisitExpr(Expr *Node) { DumpExpr(Node); } static void DumpBasePath(raw_ostream &OS, CastExpr *Node) { if (Node->path_empty()) return; OS << " ("; bool First = true; for (CastExpr::path_iterator I = Node->path_begin(), E = Node->path_end(); I != E; ++I) { const CXXBaseSpecifier *Base = *I; if (!First) OS << " -> "; const CXXRecordDecl *RD = cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl()); if (Base->isVirtual()) OS << "virtual "; OS << RD->getName(); First = false; } OS << ')'; } void StmtDumper::VisitCastExpr(CastExpr *Node) { DumpExpr(Node); OS << " <" << Node->getCastKindName(); DumpBasePath(OS, Node); OS << ">"; } void StmtDumper::VisitDeclRefExpr(DeclRefExpr *Node) { DumpExpr(Node); OS << " "; DumpDeclRef(Node->getDecl()); if (Node->getDecl() != Node->getFoundDecl()) { OS << " ("; DumpDeclRef(Node->getFoundDecl()); OS << ")"; } } void StmtDumper::DumpDeclRef(Decl *d) { OS << d->getDeclKindName() << ' ' << (void*) d; if (NamedDecl *nd = dyn_cast<NamedDecl>(d)) { OS << " '"; nd->getDeclName().printName(OS); OS << "'"; } if (ValueDecl *vd = dyn_cast<ValueDecl>(d)) { OS << ' '; DumpType(vd->getType()); } } void StmtDumper::VisitUnresolvedLookupExpr(UnresolvedLookupExpr *Node) { DumpExpr(Node); OS << " ("; if (!Node->requiresADL()) OS << "no "; OS << "ADL) = '" << Node->getName() << '\''; UnresolvedLookupExpr::decls_iterator I = Node->decls_begin(), E = Node->decls_end(); if (I == E) OS << " empty"; for (; I != E; ++I) OS << " " << (void*) *I; } void StmtDumper::VisitObjCIvarRefExpr(ObjCIvarRefExpr *Node) { DumpExpr(Node); OS << " " << Node->getDecl()->getDeclKindName() << "Decl='" << *Node->getDecl() << "' " << (void*)Node->getDecl(); if (Node->isFreeIvar()) OS << " isFreeIvar"; } void StmtDumper::VisitPredefinedExpr(PredefinedExpr *Node) { DumpExpr(Node); switch (Node->getIdentType()) { default: llvm_unreachable("unknown case"); case PredefinedExpr::Func: OS << " __func__"; break; case PredefinedExpr::Function: OS << " __FUNCTION__"; break; case PredefinedExpr::LFunction: OS << " L__FUNCTION__"; break; case PredefinedExpr::PrettyFunction: OS << " __PRETTY_FUNCTION__";break; } } void StmtDumper::VisitCharacterLiteral(CharacterLiteral *Node) { DumpExpr(Node); OS << " " << Node->getValue(); } void StmtDumper::VisitIntegerLiteral(IntegerLiteral *Node) { DumpExpr(Node); bool isSigned = Node->getType()->isSignedIntegerType(); OS << " " << Node->getValue().toString(10, isSigned); } void StmtDumper::VisitFloatingLiteral(FloatingLiteral *Node) { DumpExpr(Node); OS << " " << Node->getValueAsApproximateDouble(); } void StmtDumper::VisitStringLiteral(StringLiteral *Str) { DumpExpr(Str); OS << " "; Str->outputString(OS); } void StmtDumper::VisitUnaryOperator(UnaryOperator *Node) { DumpExpr(Node); OS << " " << (Node->isPostfix() ? "postfix" : "prefix") << " '" << UnaryOperator::getOpcodeStr(Node->getOpcode()) << "'"; } void StmtDumper::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) { DumpExpr(Node); switch(Node->getKind()) { case UETT_SizeOf: OS << " sizeof "; break; case UETT_AlignOf: OS << " alignof "; break; case UETT_VecStep: OS << " vec_step "; break; } if (Node->isArgumentType()) DumpType(Node->getArgumentType()); } void StmtDumper::VisitMemberExpr(MemberExpr *Node) { DumpExpr(Node); OS << " " << (Node->isArrow() ? "->" : ".") << *Node->getMemberDecl() << ' ' << (void*)Node->getMemberDecl(); } void StmtDumper::VisitExtVectorElementExpr(ExtVectorElementExpr *Node) { DumpExpr(Node); OS << " " << Node->getAccessor().getNameStart(); } void StmtDumper::VisitBinaryOperator(BinaryOperator *Node) { DumpExpr(Node); OS << " '" << BinaryOperator::getOpcodeStr(Node->getOpcode()) << "'"; } void StmtDumper::VisitCompoundAssignOperator(CompoundAssignOperator *Node) { DumpExpr(Node); OS << " '" << BinaryOperator::getOpcodeStr(Node->getOpcode()) << "' ComputeLHSTy="; DumpType(Node->getComputationLHSType()); OS << " ComputeResultTy="; DumpType(Node->getComputationResultType()); } void StmtDumper::VisitBlockExpr(BlockExpr *Node) { DumpExpr(Node); BlockDecl *block = Node->getBlockDecl(); OS << " decl=" << block; IndentLevel++; if (block->capturesCXXThis()) { OS << '\n'; Indent(); OS << "(capture this)"; } for (BlockDecl::capture_iterator i = block->capture_begin(), e = block->capture_end(); i != e; ++i) { OS << '\n'; 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 << ")"; } IndentLevel--; OS << '\n'; DumpSubTree(block->getBody()); } void StmtDumper::VisitOpaqueValueExpr(OpaqueValueExpr *Node) { DumpExpr(Node); if (Expr *Source = Node->getSourceExpr()) { OS << '\n'; DumpSubTree(Source); } } // GNU extensions. void StmtDumper::VisitAddrLabelExpr(AddrLabelExpr *Node) { DumpExpr(Node); OS << " " << Node->getLabel()->getName() << " " << (void*)Node->getLabel(); } //===----------------------------------------------------------------------===// // C++ Expressions //===----------------------------------------------------------------------===// void StmtDumper::VisitCXXNamedCastExpr(CXXNamedCastExpr *Node) { DumpExpr(Node); OS << " " << Node->getCastName() << "<" << Node->getTypeAsWritten().getAsString() << ">" << " <" << Node->getCastKindName(); DumpBasePath(OS, Node); OS << ">"; } void StmtDumper::VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *Node) { DumpExpr(Node); OS << " " << (Node->getValue() ? "true" : "false"); } void StmtDumper::VisitCXXThisExpr(CXXThisExpr *Node) { DumpExpr(Node); OS << " this"; } void StmtDumper::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *Node) { DumpExpr(Node); OS << " functional cast to " << Node->getTypeAsWritten().getAsString() << " <" << Node->getCastKindName() << ">"; } void StmtDumper::VisitCXXConstructExpr(CXXConstructExpr *Node) { DumpExpr(Node); CXXConstructorDecl *Ctor = Node->getConstructor(); DumpType(Ctor->getType()); if (Node->isElidable()) OS << " elidable"; if (Node->requiresZeroInitialization()) OS << " zeroing"; } void StmtDumper::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *Node) { DumpExpr(Node); OS << " "; DumpCXXTemporary(Node->getTemporary()); } void StmtDumper::VisitExprWithCleanups(ExprWithCleanups *Node) { DumpExpr(Node); ++IndentLevel; for (unsigned i = 0, e = Node->getNumObjects(); i != e; ++i) { OS << "\n"; Indent(); OS << "(cleanup "; DumpDeclRef(Node->getObject(i)); OS << ")"; } --IndentLevel; } void StmtDumper::DumpCXXTemporary(CXXTemporary *Temporary) { OS << "(CXXTemporary " << (void *)Temporary << ")"; } //===----------------------------------------------------------------------===// // Obj-C Expressions //===----------------------------------------------------------------------===// void StmtDumper::VisitObjCMessageExpr(ObjCMessageExpr* Node) { DumpExpr(Node); OS << " selector=" << Node->getSelector().getAsString(); switch (Node->getReceiverKind()) { case ObjCMessageExpr::Instance: break; case ObjCMessageExpr::Class: OS << " class="; DumpType(Node->getClassReceiver()); break; case ObjCMessageExpr::SuperInstance: OS << " super (instance)"; break; case ObjCMessageExpr::SuperClass: OS << " super (class)"; break; } } void StmtDumper::VisitObjCBoxedExpr(ObjCBoxedExpr* Node) { DumpExpr(Node); OS << " selector=" << Node->getBoxingMethod()->getSelector().getAsString(); } void StmtDumper::VisitObjCAtCatchStmt(ObjCAtCatchStmt *Node) { DumpStmt(Node); if (VarDecl *CatchParam = Node->getCatchParamDecl()) { OS << " catch parm = "; DumpDeclarator(CatchParam); } else { OS << " catch all"; } } void StmtDumper::VisitObjCEncodeExpr(ObjCEncodeExpr *Node) { DumpExpr(Node); OS << " "; DumpType(Node->getEncodedType()); } void StmtDumper::VisitObjCSelectorExpr(ObjCSelectorExpr *Node) { DumpExpr(Node); OS << " " << Node->getSelector().getAsString(); } void StmtDumper::VisitObjCProtocolExpr(ObjCProtocolExpr *Node) { DumpExpr(Node); OS << ' ' <<* Node->getProtocol(); } void StmtDumper::VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *Node) { DumpExpr(Node); if (Node->isImplicitProperty()) { OS << " Kind=MethodRef Getter=\""; if (Node->getImplicitPropertyGetter()) OS << Node->getImplicitPropertyGetter()->getSelector().getAsString(); else OS << "(null)"; OS << "\" Setter=\""; if (ObjCMethodDecl *Setter = Node->getImplicitPropertySetter()) OS << Setter->getSelector().getAsString(); else OS << "(null)"; OS << "\""; } else { OS << " Kind=PropertyRef Property=\"" << *Node->getExplicitProperty() <<'"'; } if (Node->isSuperReceiver()) OS << " super"; OS << " Messaging="; if (Node->isMessagingGetter() && Node->isMessagingSetter()) OS << "Getter&Setter"; else if (Node->isMessagingGetter()) OS << "Getter"; else if (Node->isMessagingSetter()) OS << "Setter"; } void StmtDumper::VisitObjCSubscriptRefExpr(ObjCSubscriptRefExpr *Node) { DumpExpr(Node); if (Node->isArraySubscriptRefExpr()) OS << " Kind=ArraySubscript GetterForArray=\""; else OS << " Kind=DictionarySubscript GetterForDictionary=\""; if (Node->getAtIndexMethodDecl()) OS << Node->getAtIndexMethodDecl()->getSelector().getAsString(); else OS << "(null)"; if (Node->isArraySubscriptRefExpr()) OS << "\" SetterForArray=\""; else OS << "\" SetterForDictionary=\""; if (Node->setAtIndexMethodDecl()) OS << Node->setAtIndexMethodDecl()->getSelector().getAsString(); else OS << "(null)"; } void StmtDumper::VisitObjCBoolLiteralExpr(ObjCBoolLiteralExpr *Node) { DumpExpr(Node); OS << " " << (Node->getValue() ? "__objc_yes" : "__objc_no"); } //===----------------------------------------------------------------------===// // 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 Stmt::dump(SourceManager &SM) const { dump(llvm::errs(), SM); } void Stmt::dump(raw_ostream &OS, SourceManager &SM) const { StmtDumper P(&SM, OS, 4); P.DumpSubTree(const_cast<Stmt*>(this)); OS << "\n"; } /// 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 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"; }