clang API Documentation
00001 //===- InheritViz.cpp - Graphviz visualization for inheritance --*- C++ -*-===// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // This file implements CXXRecordDecl::viewInheritance, which 00011 // generates a GraphViz DOT file that depicts the class inheritance 00012 // diagram and then calls Graphviz/dot+gv on it. 00013 // 00014 //===----------------------------------------------------------------------===// 00015 00016 #include "clang/AST/ASTContext.h" 00017 #include "clang/AST/Decl.h" 00018 #include "clang/AST/DeclCXX.h" 00019 #include "clang/AST/TypeOrdering.h" 00020 #include "llvm/Support/GraphWriter.h" 00021 #include "llvm/Support/raw_ostream.h" 00022 #include <map> 00023 00024 using namespace llvm; 00025 00026 namespace clang { 00027 00028 /// InheritanceHierarchyWriter - Helper class that writes out a 00029 /// GraphViz file that diagrams the inheritance hierarchy starting at 00030 /// a given C++ class type. Note that we do not use LLVM's 00031 /// GraphWriter, because the interface does not permit us to properly 00032 /// differentiate between uses of types as virtual bases 00033 /// vs. non-virtual bases. 00034 class InheritanceHierarchyWriter { 00035 ASTContext& Context; 00036 raw_ostream &Out; 00037 std::map<QualType, int, QualTypeOrdering> DirectBaseCount; 00038 std::set<QualType, QualTypeOrdering> KnownVirtualBases; 00039 00040 public: 00041 InheritanceHierarchyWriter(ASTContext& Context, raw_ostream& Out) 00042 : Context(Context), Out(Out) { } 00043 00044 void WriteGraph(QualType Type) { 00045 Out << "digraph \"" << DOT::EscapeString(Type.getAsString()) << "\" {\n"; 00046 WriteNode(Type, false); 00047 Out << "}\n"; 00048 } 00049 00050 protected: 00051 /// WriteNode - Write out the description of node in the inheritance 00052 /// diagram, which may be a base class or it may be the root node. 00053 void WriteNode(QualType Type, bool FromVirtual); 00054 00055 /// WriteNodeReference - Write out a reference to the given node, 00056 /// using a unique identifier for each direct base and for the 00057 /// (only) virtual base. 00058 raw_ostream& WriteNodeReference(QualType Type, bool FromVirtual); 00059 }; 00060 00061 void InheritanceHierarchyWriter::WriteNode(QualType Type, bool FromVirtual) { 00062 QualType CanonType = Context.getCanonicalType(Type); 00063 00064 if (FromVirtual) { 00065 if (KnownVirtualBases.find(CanonType) != KnownVirtualBases.end()) 00066 return; 00067 00068 // We haven't seen this virtual base before, so display it and 00069 // its bases. 00070 KnownVirtualBases.insert(CanonType); 00071 } 00072 00073 // Declare the node itself. 00074 Out << " "; 00075 WriteNodeReference(Type, FromVirtual); 00076 00077 // Give the node a label based on the name of the class. 00078 std::string TypeName = Type.getAsString(); 00079 Out << " [ shape=\"box\", label=\"" << DOT::EscapeString(TypeName); 00080 00081 // If the name of the class was a typedef or something different 00082 // from the "real" class name, show the real class name in 00083 // parentheses so we don't confuse ourselves. 00084 if (TypeName != CanonType.getAsString()) { 00085 Out << "\\n(" << CanonType.getAsString() << ")"; 00086 } 00087 00088 // Finished describing the node. 00089 Out << " \"];\n"; 00090 00091 // Display the base classes. 00092 const CXXRecordDecl *Decl 00093 = static_cast<const CXXRecordDecl *>(Type->getAs<RecordType>()->getDecl()); 00094 for (CXXRecordDecl::base_class_const_iterator Base = Decl->bases_begin(); 00095 Base != Decl->bases_end(); ++Base) { 00096 QualType CanonBaseType = Context.getCanonicalType(Base->getType()); 00097 00098 // If this is not virtual inheritance, bump the direct base 00099 // count for the type. 00100 if (!Base->isVirtual()) 00101 ++DirectBaseCount[CanonBaseType]; 00102 00103 // Write out the node (if we need to). 00104 WriteNode(Base->getType(), Base->isVirtual()); 00105 00106 // Write out the edge. 00107 Out << " "; 00108 WriteNodeReference(Type, FromVirtual); 00109 Out << " -> "; 00110 WriteNodeReference(Base->getType(), Base->isVirtual()); 00111 00112 // Write out edge attributes to show the kind of inheritance. 00113 if (Base->isVirtual()) { 00114 Out << " [ style=\"dashed\" ]"; 00115 } 00116 Out << ";"; 00117 } 00118 } 00119 00120 /// WriteNodeReference - Write out a reference to the given node, 00121 /// using a unique identifier for each direct base and for the 00122 /// (only) virtual base. 00123 raw_ostream& 00124 InheritanceHierarchyWriter::WriteNodeReference(QualType Type, 00125 bool FromVirtual) { 00126 QualType CanonType = Context.getCanonicalType(Type); 00127 00128 Out << "Class_" << CanonType.getAsOpaquePtr(); 00129 if (!FromVirtual) 00130 Out << "_" << DirectBaseCount[CanonType]; 00131 return Out; 00132 } 00133 00134 /// viewInheritance - Display the inheritance hierarchy of this C++ 00135 /// class using GraphViz. 00136 void CXXRecordDecl::viewInheritance(ASTContext& Context) const { 00137 QualType Self = Context.getTypeDeclType(const_cast<CXXRecordDecl *>(this)); 00138 std::string ErrMsg; 00139 sys::Path Filename = sys::Path::GetTemporaryDirectory(&ErrMsg); 00140 if (Filename.isEmpty()) { 00141 llvm::errs() << "Error: " << ErrMsg << "\n"; 00142 return; 00143 } 00144 Filename.appendComponent(Self.getAsString() + ".dot"); 00145 if (Filename.makeUnique(true,&ErrMsg)) { 00146 llvm::errs() << "Error: " << ErrMsg << "\n"; 00147 return; 00148 } 00149 00150 llvm::errs() << "Writing '" << Filename.c_str() << "'... "; 00151 00152 llvm::raw_fd_ostream O(Filename.c_str(), ErrMsg); 00153 00154 if (ErrMsg.empty()) { 00155 InheritanceHierarchyWriter Writer(Context, O); 00156 Writer.WriteGraph(Self); 00157 llvm::errs() << " done. \n"; 00158 00159 O.close(); 00160 00161 // Display the graph 00162 DisplayGraph(Filename); 00163 } else { 00164 llvm::errs() << "error opening file for writing!\n"; 00165 } 00166 } 00167 00168 }