1f4a2713aSLionel Sambuc //===- InheritViz.cpp - Graphviz visualization for inheritance --*- C++ -*-===//
2f4a2713aSLionel Sambuc //
3f4a2713aSLionel Sambuc // The LLVM Compiler Infrastructure
4f4a2713aSLionel Sambuc //
5f4a2713aSLionel Sambuc // This file is distributed under the University of Illinois Open Source
6f4a2713aSLionel Sambuc // License. See LICENSE.TXT for details.
7f4a2713aSLionel Sambuc //
8f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
9f4a2713aSLionel Sambuc //
10f4a2713aSLionel Sambuc // This file implements CXXRecordDecl::viewInheritance, which
11f4a2713aSLionel Sambuc // generates a GraphViz DOT file that depicts the class inheritance
12f4a2713aSLionel Sambuc // diagram and then calls Graphviz/dot+gv on it.
13f4a2713aSLionel Sambuc //
14f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
15f4a2713aSLionel Sambuc
16f4a2713aSLionel Sambuc #include "clang/AST/ASTContext.h"
17f4a2713aSLionel Sambuc #include "clang/AST/Decl.h"
18f4a2713aSLionel Sambuc #include "clang/AST/DeclCXX.h"
19f4a2713aSLionel Sambuc #include "clang/AST/TypeOrdering.h"
20f4a2713aSLionel Sambuc #include "llvm/Support/FileSystem.h"
21f4a2713aSLionel Sambuc #include "llvm/Support/GraphWriter.h"
22f4a2713aSLionel Sambuc #include "llvm/Support/raw_ostream.h"
23f4a2713aSLionel Sambuc #include <map>
24f4a2713aSLionel Sambuc #include <set>
25f4a2713aSLionel Sambuc
26f4a2713aSLionel Sambuc using namespace llvm;
27f4a2713aSLionel Sambuc
28f4a2713aSLionel Sambuc namespace clang {
29f4a2713aSLionel Sambuc
30f4a2713aSLionel Sambuc /// InheritanceHierarchyWriter - Helper class that writes out a
31f4a2713aSLionel Sambuc /// GraphViz file that diagrams the inheritance hierarchy starting at
32f4a2713aSLionel Sambuc /// a given C++ class type. Note that we do not use LLVM's
33f4a2713aSLionel Sambuc /// GraphWriter, because the interface does not permit us to properly
34f4a2713aSLionel Sambuc /// differentiate between uses of types as virtual bases
35f4a2713aSLionel Sambuc /// vs. non-virtual bases.
36f4a2713aSLionel Sambuc class InheritanceHierarchyWriter {
37f4a2713aSLionel Sambuc ASTContext& Context;
38f4a2713aSLionel Sambuc raw_ostream &Out;
39f4a2713aSLionel Sambuc std::map<QualType, int, QualTypeOrdering> DirectBaseCount;
40f4a2713aSLionel Sambuc std::set<QualType, QualTypeOrdering> KnownVirtualBases;
41f4a2713aSLionel Sambuc
42f4a2713aSLionel Sambuc public:
InheritanceHierarchyWriter(ASTContext & Context,raw_ostream & Out)43f4a2713aSLionel Sambuc InheritanceHierarchyWriter(ASTContext& Context, raw_ostream& Out)
44f4a2713aSLionel Sambuc : Context(Context), Out(Out) { }
45f4a2713aSLionel Sambuc
WriteGraph(QualType Type)46f4a2713aSLionel Sambuc void WriteGraph(QualType Type) {
47f4a2713aSLionel Sambuc Out << "digraph \"" << DOT::EscapeString(Type.getAsString()) << "\" {\n";
48f4a2713aSLionel Sambuc WriteNode(Type, false);
49f4a2713aSLionel Sambuc Out << "}\n";
50f4a2713aSLionel Sambuc }
51f4a2713aSLionel Sambuc
52f4a2713aSLionel Sambuc protected:
53f4a2713aSLionel Sambuc /// WriteNode - Write out the description of node in the inheritance
54f4a2713aSLionel Sambuc /// diagram, which may be a base class or it may be the root node.
55f4a2713aSLionel Sambuc void WriteNode(QualType Type, bool FromVirtual);
56f4a2713aSLionel Sambuc
57f4a2713aSLionel Sambuc /// WriteNodeReference - Write out a reference to the given node,
58f4a2713aSLionel Sambuc /// using a unique identifier for each direct base and for the
59f4a2713aSLionel Sambuc /// (only) virtual base.
60f4a2713aSLionel Sambuc raw_ostream& WriteNodeReference(QualType Type, bool FromVirtual);
61f4a2713aSLionel Sambuc };
62f4a2713aSLionel Sambuc
WriteNode(QualType Type,bool FromVirtual)63f4a2713aSLionel Sambuc void InheritanceHierarchyWriter::WriteNode(QualType Type, bool FromVirtual) {
64f4a2713aSLionel Sambuc QualType CanonType = Context.getCanonicalType(Type);
65f4a2713aSLionel Sambuc
66f4a2713aSLionel Sambuc if (FromVirtual) {
67f4a2713aSLionel Sambuc if (KnownVirtualBases.find(CanonType) != KnownVirtualBases.end())
68f4a2713aSLionel Sambuc return;
69f4a2713aSLionel Sambuc
70f4a2713aSLionel Sambuc // We haven't seen this virtual base before, so display it and
71f4a2713aSLionel Sambuc // its bases.
72f4a2713aSLionel Sambuc KnownVirtualBases.insert(CanonType);
73f4a2713aSLionel Sambuc }
74f4a2713aSLionel Sambuc
75f4a2713aSLionel Sambuc // Declare the node itself.
76f4a2713aSLionel Sambuc Out << " ";
77f4a2713aSLionel Sambuc WriteNodeReference(Type, FromVirtual);
78f4a2713aSLionel Sambuc
79f4a2713aSLionel Sambuc // Give the node a label based on the name of the class.
80f4a2713aSLionel Sambuc std::string TypeName = Type.getAsString();
81f4a2713aSLionel Sambuc Out << " [ shape=\"box\", label=\"" << DOT::EscapeString(TypeName);
82f4a2713aSLionel Sambuc
83f4a2713aSLionel Sambuc // If the name of the class was a typedef or something different
84f4a2713aSLionel Sambuc // from the "real" class name, show the real class name in
85f4a2713aSLionel Sambuc // parentheses so we don't confuse ourselves.
86f4a2713aSLionel Sambuc if (TypeName != CanonType.getAsString()) {
87f4a2713aSLionel Sambuc Out << "\\n(" << CanonType.getAsString() << ")";
88f4a2713aSLionel Sambuc }
89f4a2713aSLionel Sambuc
90f4a2713aSLionel Sambuc // Finished describing the node.
91f4a2713aSLionel Sambuc Out << " \"];\n";
92f4a2713aSLionel Sambuc
93f4a2713aSLionel Sambuc // Display the base classes.
94f4a2713aSLionel Sambuc const CXXRecordDecl *Decl
95f4a2713aSLionel Sambuc = static_cast<const CXXRecordDecl *>(Type->getAs<RecordType>()->getDecl());
96*0a6a1f1dSLionel Sambuc for (const auto &Base : Decl->bases()) {
97*0a6a1f1dSLionel Sambuc QualType CanonBaseType = Context.getCanonicalType(Base.getType());
98f4a2713aSLionel Sambuc
99f4a2713aSLionel Sambuc // If this is not virtual inheritance, bump the direct base
100f4a2713aSLionel Sambuc // count for the type.
101*0a6a1f1dSLionel Sambuc if (!Base.isVirtual())
102f4a2713aSLionel Sambuc ++DirectBaseCount[CanonBaseType];
103f4a2713aSLionel Sambuc
104f4a2713aSLionel Sambuc // Write out the node (if we need to).
105*0a6a1f1dSLionel Sambuc WriteNode(Base.getType(), Base.isVirtual());
106f4a2713aSLionel Sambuc
107f4a2713aSLionel Sambuc // Write out the edge.
108f4a2713aSLionel Sambuc Out << " ";
109f4a2713aSLionel Sambuc WriteNodeReference(Type, FromVirtual);
110f4a2713aSLionel Sambuc Out << " -> ";
111*0a6a1f1dSLionel Sambuc WriteNodeReference(Base.getType(), Base.isVirtual());
112f4a2713aSLionel Sambuc
113f4a2713aSLionel Sambuc // Write out edge attributes to show the kind of inheritance.
114*0a6a1f1dSLionel Sambuc if (Base.isVirtual()) {
115f4a2713aSLionel Sambuc Out << " [ style=\"dashed\" ]";
116f4a2713aSLionel Sambuc }
117f4a2713aSLionel Sambuc Out << ";";
118f4a2713aSLionel Sambuc }
119f4a2713aSLionel Sambuc }
120f4a2713aSLionel Sambuc
121f4a2713aSLionel Sambuc /// WriteNodeReference - Write out a reference to the given node,
122f4a2713aSLionel Sambuc /// using a unique identifier for each direct base and for the
123f4a2713aSLionel Sambuc /// (only) virtual base.
124f4a2713aSLionel Sambuc raw_ostream&
WriteNodeReference(QualType Type,bool FromVirtual)125f4a2713aSLionel Sambuc InheritanceHierarchyWriter::WriteNodeReference(QualType Type,
126f4a2713aSLionel Sambuc bool FromVirtual) {
127f4a2713aSLionel Sambuc QualType CanonType = Context.getCanonicalType(Type);
128f4a2713aSLionel Sambuc
129f4a2713aSLionel Sambuc Out << "Class_" << CanonType.getAsOpaquePtr();
130f4a2713aSLionel Sambuc if (!FromVirtual)
131f4a2713aSLionel Sambuc Out << "_" << DirectBaseCount[CanonType];
132f4a2713aSLionel Sambuc return Out;
133f4a2713aSLionel Sambuc }
134f4a2713aSLionel Sambuc
135f4a2713aSLionel Sambuc /// viewInheritance - Display the inheritance hierarchy of this C++
136f4a2713aSLionel Sambuc /// class using GraphViz.
viewInheritance(ASTContext & Context) const137f4a2713aSLionel Sambuc void CXXRecordDecl::viewInheritance(ASTContext& Context) const {
138f4a2713aSLionel Sambuc QualType Self = Context.getTypeDeclType(this);
139f4a2713aSLionel Sambuc
140f4a2713aSLionel Sambuc int FD;
141f4a2713aSLionel Sambuc SmallString<128> Filename;
142*0a6a1f1dSLionel Sambuc std::error_code EC =
143f4a2713aSLionel Sambuc sys::fs::createTemporaryFile(Self.getAsString(), "dot", FD, Filename);
144f4a2713aSLionel Sambuc if (EC) {
145f4a2713aSLionel Sambuc llvm::errs() << "Error: " << EC.message() << "\n";
146f4a2713aSLionel Sambuc return;
147f4a2713aSLionel Sambuc }
148f4a2713aSLionel Sambuc
149f4a2713aSLionel Sambuc llvm::errs() << "Writing '" << Filename << "'... ";
150f4a2713aSLionel Sambuc
151f4a2713aSLionel Sambuc llvm::raw_fd_ostream O(FD, true);
152f4a2713aSLionel Sambuc
153f4a2713aSLionel Sambuc InheritanceHierarchyWriter Writer(Context, O);
154f4a2713aSLionel Sambuc Writer.WriteGraph(Self);
155f4a2713aSLionel Sambuc llvm::errs() << " done. \n";
156f4a2713aSLionel Sambuc
157f4a2713aSLionel Sambuc O.close();
158f4a2713aSLionel Sambuc
159f4a2713aSLionel Sambuc // Display the graph
160f4a2713aSLionel Sambuc DisplayGraph(Filename);
161f4a2713aSLionel Sambuc }
162f4a2713aSLionel Sambuc
163f4a2713aSLionel Sambuc }
164