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" 20263508Sdim#include "llvm/Support/FileSystem.h" 21193326Sed#include "llvm/Support/GraphWriter.h" 22193326Sed#include "llvm/Support/raw_ostream.h" 23193326Sed#include <map> 24263508Sdim#include <set> 25193326Sed 26193326Sedusing namespace llvm; 27193326Sed 28193326Sednamespace clang { 29193326Sed 30193326Sed/// InheritanceHierarchyWriter - Helper class that writes out a 31193326Sed/// GraphViz file that diagrams the inheritance hierarchy starting at 32193326Sed/// a given C++ class type. Note that we do not use LLVM's 33193326Sed/// GraphWriter, because the interface does not permit us to properly 34193326Sed/// differentiate between uses of types as virtual bases 35193326Sed/// vs. non-virtual bases. 36193326Sedclass InheritanceHierarchyWriter { 37193326Sed ASTContext& Context; 38226633Sdim raw_ostream &Out; 39193326Sed std::map<QualType, int, QualTypeOrdering> DirectBaseCount; 40193326Sed std::set<QualType, QualTypeOrdering> KnownVirtualBases; 41193326Sed 42193326Sedpublic: 43226633Sdim InheritanceHierarchyWriter(ASTContext& Context, raw_ostream& Out) 44193326Sed : Context(Context), Out(Out) { } 45193326Sed 46193326Sed void WriteGraph(QualType Type) { 47193326Sed Out << "digraph \"" << DOT::EscapeString(Type.getAsString()) << "\" {\n"; 48193326Sed WriteNode(Type, false); 49193326Sed Out << "}\n"; 50193326Sed } 51193326Sed 52193326Sedprotected: 53193326Sed /// WriteNode - Write out the description of node in the inheritance 54193326Sed /// diagram, which may be a base class or it may be the root node. 55193326Sed void WriteNode(QualType Type, bool FromVirtual); 56193326Sed 57193326Sed /// WriteNodeReference - Write out a reference to the given node, 58193326Sed /// using a unique identifier for each direct base and for the 59193326Sed /// (only) virtual base. 60226633Sdim raw_ostream& WriteNodeReference(QualType Type, bool FromVirtual); 61193326Sed}; 62193326Sed 63193326Sedvoid InheritanceHierarchyWriter::WriteNode(QualType Type, bool FromVirtual) { 64193326Sed QualType CanonType = Context.getCanonicalType(Type); 65193326Sed 66193326Sed if (FromVirtual) { 67193326Sed if (KnownVirtualBases.find(CanonType) != KnownVirtualBases.end()) 68193326Sed return; 69193326Sed 70193326Sed // We haven't seen this virtual base before, so display it and 71193326Sed // its bases. 72193326Sed KnownVirtualBases.insert(CanonType); 73193326Sed } 74193326Sed 75193326Sed // Declare the node itself. 76193326Sed Out << " "; 77193326Sed WriteNodeReference(Type, FromVirtual); 78193326Sed 79193326Sed // Give the node a label based on the name of the class. 80193326Sed std::string TypeName = Type.getAsString(); 81193326Sed Out << " [ shape=\"box\", label=\"" << DOT::EscapeString(TypeName); 82193326Sed 83193326Sed // If the name of the class was a typedef or something different 84193326Sed // from the "real" class name, show the real class name in 85193326Sed // parentheses so we don't confuse ourselves. 86193326Sed if (TypeName != CanonType.getAsString()) { 87193326Sed Out << "\\n(" << CanonType.getAsString() << ")"; 88193326Sed } 89193326Sed 90193326Sed // Finished describing the node. 91193326Sed Out << " \"];\n"; 92193326Sed 93193326Sed // Display the base classes. 94198092Srdivacky const CXXRecordDecl *Decl 95198092Srdivacky = static_cast<const CXXRecordDecl *>(Type->getAs<RecordType>()->getDecl()); 96193326Sed for (CXXRecordDecl::base_class_const_iterator Base = Decl->bases_begin(); 97193326Sed Base != Decl->bases_end(); ++Base) { 98193326Sed QualType CanonBaseType = Context.getCanonicalType(Base->getType()); 99193326Sed 100193326Sed // If this is not virtual inheritance, bump the direct base 101193326Sed // count for the type. 102193326Sed if (!Base->isVirtual()) 103193326Sed ++DirectBaseCount[CanonBaseType]; 104193326Sed 105193326Sed // Write out the node (if we need to). 106193326Sed WriteNode(Base->getType(), Base->isVirtual()); 107193326Sed 108193326Sed // Write out the edge. 109193326Sed Out << " "; 110193326Sed WriteNodeReference(Type, FromVirtual); 111193326Sed Out << " -> "; 112193326Sed WriteNodeReference(Base->getType(), Base->isVirtual()); 113193326Sed 114193326Sed // Write out edge attributes to show the kind of inheritance. 115193326Sed if (Base->isVirtual()) { 116193326Sed Out << " [ style=\"dashed\" ]"; 117193326Sed } 118193326Sed Out << ";"; 119193326Sed } 120193326Sed} 121193326Sed 122193326Sed/// WriteNodeReference - Write out a reference to the given node, 123193326Sed/// using a unique identifier for each direct base and for the 124193326Sed/// (only) virtual base. 125226633Sdimraw_ostream& 126198092SrdivackyInheritanceHierarchyWriter::WriteNodeReference(QualType Type, 127193326Sed bool FromVirtual) { 128193326Sed QualType CanonType = Context.getCanonicalType(Type); 129193326Sed 130193326Sed Out << "Class_" << CanonType.getAsOpaquePtr(); 131193326Sed if (!FromVirtual) 132193326Sed Out << "_" << DirectBaseCount[CanonType]; 133193326Sed return Out; 134193326Sed} 135193326Sed 136193326Sed/// viewInheritance - Display the inheritance hierarchy of this C++ 137193326Sed/// class using GraphViz. 138193326Sedvoid CXXRecordDecl::viewInheritance(ASTContext& Context) const { 139249423Sdim QualType Self = Context.getTypeDeclType(this); 140263508Sdim 141263508Sdim int FD; 142263508Sdim SmallString<128> Filename; 143263508Sdim error_code EC = 144263508Sdim sys::fs::createTemporaryFile(Self.getAsString(), "dot", FD, Filename); 145263508Sdim if (EC) { 146263508Sdim llvm::errs() << "Error: " << EC.message() << "\n"; 147193326Sed return; 148193326Sed } 149193326Sed 150263508Sdim llvm::errs() << "Writing '" << Filename << "'... "; 151193326Sed 152263508Sdim llvm::raw_fd_ostream O(FD, true); 153193326Sed 154263508Sdim InheritanceHierarchyWriter Writer(Context, O); 155263508Sdim Writer.WriteGraph(Self); 156263508Sdim llvm::errs() << " done. \n"; 157193326Sed 158263508Sdim O.close(); 159221345Sdim 160263508Sdim // Display the graph 161263508Sdim DisplayGraph(Filename); 162193326Sed} 163193326Sed 164193326Sed} 165