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