clang API Documentation

InheritViz.cpp
Go to the documentation of this file.
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 }