1193326Sed//===- InheritViz.cpp - Graphviz visualization for inheritance --*- C++ -*-===// 2193326Sed// 3193326Sed// The LLVM Compiler Infrastructure 4193326Sed// 5193326Sed// This file is distributed under the University of Illinois Open Source 6193326Sed// License. See LICENSE.TXT for details. 7193326Sed// 8193326Sed//===----------------------------------------------------------------------===// 9193326Sed// 10193326Sed// This file implements CXXRecordDecl::viewInheritance, which 11193326Sed// generates a GraphViz DOT file that depicts the class inheritance 12193326Sed// diagram and then calls Graphviz/dot+gv on it. 13193326Sed// 14193326Sed//===----------------------------------------------------------------------===// 15193326Sed 16193326Sed#include "clang/AST/ASTContext.h" 17193326Sed#include "clang/AST/Decl.h" 18193326Sed#include "clang/AST/DeclCXX.h" 19193326Sed#include "clang/AST/TypeOrdering.h" 20193326Sed#include "llvm/Support/GraphWriter.h" 21193326Sed#include "llvm/Support/raw_ostream.h" 22193326Sed#include <map> 23193326Sed 24193326Sedusing namespace llvm; 25193326Sed 26193326Sednamespace clang { 27193326Sed 28193326Sed/// InheritanceHierarchyWriter - Helper class that writes out a 29193326Sed/// GraphViz file that diagrams the inheritance hierarchy starting at 30193326Sed/// a given C++ class type. Note that we do not use LLVM's 31193326Sed/// GraphWriter, because the interface does not permit us to properly 32193326Sed/// differentiate between uses of types as virtual bases 33193326Sed/// vs. non-virtual bases. 34193326Sedclass InheritanceHierarchyWriter { 35193326Sed ASTContext& Context; 36226633Sdim raw_ostream &Out; 37193326Sed std::map<QualType, int, QualTypeOrdering> DirectBaseCount; 38193326Sed std::set<QualType, QualTypeOrdering> KnownVirtualBases; 39193326Sed 40193326Sedpublic: 41226633Sdim InheritanceHierarchyWriter(ASTContext& Context, raw_ostream& Out) 42193326Sed : Context(Context), Out(Out) { } 43193326Sed 44193326Sed void WriteGraph(QualType Type) { 45193326Sed Out << "digraph \"" << DOT::EscapeString(Type.getAsString()) << "\" {\n"; 46193326Sed WriteNode(Type, false); 47193326Sed Out << "}\n"; 48193326Sed } 49193326Sed 50193326Sedprotected: 51193326Sed /// WriteNode - Write out the description of node in the inheritance 52193326Sed /// diagram, which may be a base class or it may be the root node. 53193326Sed void WriteNode(QualType Type, bool FromVirtual); 54193326Sed 55193326Sed /// WriteNodeReference - Write out a reference to the given node, 56193326Sed /// using a unique identifier for each direct base and for the 57193326Sed /// (only) virtual base. 58226633Sdim raw_ostream& WriteNodeReference(QualType Type, bool FromVirtual); 59193326Sed}; 60193326Sed 61193326Sedvoid InheritanceHierarchyWriter::WriteNode(QualType Type, bool FromVirtual) { 62193326Sed QualType CanonType = Context.getCanonicalType(Type); 63193326Sed 64193326Sed if (FromVirtual) { 65193326Sed if (KnownVirtualBases.find(CanonType) != KnownVirtualBases.end()) 66193326Sed return; 67193326Sed 68193326Sed // We haven't seen this virtual base before, so display it and 69193326Sed // its bases. 70193326Sed KnownVirtualBases.insert(CanonType); 71193326Sed } 72193326Sed 73193326Sed // Declare the node itself. 74193326Sed Out << " "; 75193326Sed WriteNodeReference(Type, FromVirtual); 76193326Sed 77193326Sed // Give the node a label based on the name of the class. 78193326Sed std::string TypeName = Type.getAsString(); 79193326Sed Out << " [ shape=\"box\", label=\"" << DOT::EscapeString(TypeName); 80193326Sed 81193326Sed // If the name of the class was a typedef or something different 82193326Sed // from the "real" class name, show the real class name in 83193326Sed // parentheses so we don't confuse ourselves. 84193326Sed if (TypeName != CanonType.getAsString()) { 85193326Sed Out << "\\n(" << CanonType.getAsString() << ")"; 86193326Sed } 87193326Sed 88193326Sed // Finished describing the node. 89193326Sed Out << " \"];\n"; 90193326Sed 91193326Sed // Display the base classes. 92198092Srdivacky const CXXRecordDecl *Decl 93198092Srdivacky = static_cast<const CXXRecordDecl *>(Type->getAs<RecordType>()->getDecl()); 94193326Sed for (CXXRecordDecl::base_class_const_iterator Base = Decl->bases_begin(); 95193326Sed Base != Decl->bases_end(); ++Base) { 96193326Sed QualType CanonBaseType = Context.getCanonicalType(Base->getType()); 97193326Sed 98193326Sed // If this is not virtual inheritance, bump the direct base 99193326Sed // count for the type. 100193326Sed if (!Base->isVirtual()) 101193326Sed ++DirectBaseCount[CanonBaseType]; 102193326Sed 103193326Sed // Write out the node (if we need to). 104193326Sed WriteNode(Base->getType(), Base->isVirtual()); 105193326Sed 106193326Sed // Write out the edge. 107193326Sed Out << " "; 108193326Sed WriteNodeReference(Type, FromVirtual); 109193326Sed Out << " -> "; 110193326Sed WriteNodeReference(Base->getType(), Base->isVirtual()); 111193326Sed 112193326Sed // Write out edge attributes to show the kind of inheritance. 113193326Sed if (Base->isVirtual()) { 114193326Sed Out << " [ style=\"dashed\" ]"; 115193326Sed } 116193326Sed Out << ";"; 117193326Sed } 118193326Sed} 119193326Sed 120193326Sed/// WriteNodeReference - Write out a reference to the given node, 121193326Sed/// using a unique identifier for each direct base and for the 122193326Sed/// (only) virtual base. 123226633Sdimraw_ostream& 124198092SrdivackyInheritanceHierarchyWriter::WriteNodeReference(QualType Type, 125193326Sed bool FromVirtual) { 126193326Sed QualType CanonType = Context.getCanonicalType(Type); 127193326Sed 128193326Sed Out << "Class_" << CanonType.getAsOpaquePtr(); 129193326Sed if (!FromVirtual) 130193326Sed Out << "_" << DirectBaseCount[CanonType]; 131193326Sed return Out; 132193326Sed} 133193326Sed 134193326Sed/// viewInheritance - Display the inheritance hierarchy of this C++ 135193326Sed/// class using GraphViz. 136193326Sedvoid CXXRecordDecl::viewInheritance(ASTContext& Context) const { 137249423Sdim QualType Self = Context.getTypeDeclType(this); 138221345Sdim std::string ErrMsg; 139221345Sdim sys::Path Filename = sys::Path::GetTemporaryDirectory(&ErrMsg); 140221345Sdim if (Filename.isEmpty()) { 141221345Sdim llvm::errs() << "Error: " << ErrMsg << "\n"; 142193326Sed return; 143193326Sed } 144221345Sdim Filename.appendComponent(Self.getAsString() + ".dot"); 145221345Sdim if (Filename.makeUnique(true,&ErrMsg)) { 146221345Sdim llvm::errs() << "Error: " << ErrMsg << "\n"; 147221345Sdim return; 148221345Sdim } 149193326Sed 150221345Sdim llvm::errs() << "Writing '" << Filename.c_str() << "'... "; 151193326Sed 152221345Sdim llvm::raw_fd_ostream O(Filename.c_str(), ErrMsg); 153193326Sed 154221345Sdim if (ErrMsg.empty()) { 155221345Sdim InheritanceHierarchyWriter Writer(Context, O); 156221345Sdim Writer.WriteGraph(Self); 157221345Sdim llvm::errs() << " done. \n"; 158193326Sed 159221345Sdim O.close(); 160221345Sdim 161221345Sdim // Display the graph 162221345Sdim DisplayGraph(Filename); 163221345Sdim } else { 164221345Sdim llvm::errs() << "error opening file for writing!\n"; 165221345Sdim } 166193326Sed} 167193326Sed 168193326Sed} 169