mirror of
https://github.com/llvm/llvm-project.git
synced 2025-05-03 05:16:07 +00:00

to reflect the new license. We understand that people may be surprised that we're moving the header entirely to discuss the new license. We checked this carefully with the Foundation's lawyer and we believe this is the correct approach. Essentially, all code in the project is now made available by the LLVM project under our new license, so you will see that the license headers include that license only. Some of our contributors have contributed code under our old license, and accordingly, we have retained a copy of our old license notice in the top-level files in each project and repository. llvm-svn: 351636
160 lines
5.1 KiB
C++
160 lines
5.1 KiB
C++
//===- InheritViz.cpp - Graphviz visualization for inheritance --*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements CXXRecordDecl::viewInheritance, which
|
|
// generates a GraphViz DOT file that depicts the class inheritance
|
|
// diagram and then calls Graphviz/dot+gv on it.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/AST/DeclCXX.h"
|
|
#include "clang/AST/TypeOrdering.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/GraphWriter.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <map>
|
|
#include <set>
|
|
using namespace clang;
|
|
|
|
namespace {
|
|
/// InheritanceHierarchyWriter - Helper class that writes out a
|
|
/// GraphViz file that diagrams the inheritance hierarchy starting at
|
|
/// a given C++ class type. Note that we do not use LLVM's
|
|
/// GraphWriter, because the interface does not permit us to properly
|
|
/// differentiate between uses of types as virtual bases
|
|
/// vs. non-virtual bases.
|
|
class InheritanceHierarchyWriter {
|
|
ASTContext& Context;
|
|
raw_ostream &Out;
|
|
std::map<QualType, int, QualTypeOrdering> DirectBaseCount;
|
|
std::set<QualType, QualTypeOrdering> KnownVirtualBases;
|
|
|
|
public:
|
|
InheritanceHierarchyWriter(ASTContext& Context, raw_ostream& Out)
|
|
: Context(Context), Out(Out) { }
|
|
|
|
void WriteGraph(QualType Type) {
|
|
Out << "digraph \"" << llvm::DOT::EscapeString(Type.getAsString())
|
|
<< "\" {\n";
|
|
WriteNode(Type, false);
|
|
Out << "}\n";
|
|
}
|
|
|
|
protected:
|
|
/// WriteNode - Write out the description of node in the inheritance
|
|
/// diagram, which may be a base class or it may be the root node.
|
|
void WriteNode(QualType Type, bool FromVirtual);
|
|
|
|
/// WriteNodeReference - Write out a reference to the given node,
|
|
/// using a unique identifier for each direct base and for the
|
|
/// (only) virtual base.
|
|
raw_ostream& WriteNodeReference(QualType Type, bool FromVirtual);
|
|
};
|
|
} // namespace
|
|
|
|
void InheritanceHierarchyWriter::WriteNode(QualType Type, bool FromVirtual) {
|
|
QualType CanonType = Context.getCanonicalType(Type);
|
|
|
|
if (FromVirtual) {
|
|
if (KnownVirtualBases.find(CanonType) != KnownVirtualBases.end())
|
|
return;
|
|
|
|
// We haven't seen this virtual base before, so display it and
|
|
// its bases.
|
|
KnownVirtualBases.insert(CanonType);
|
|
}
|
|
|
|
// Declare the node itself.
|
|
Out << " ";
|
|
WriteNodeReference(Type, FromVirtual);
|
|
|
|
// Give the node a label based on the name of the class.
|
|
std::string TypeName = Type.getAsString();
|
|
Out << " [ shape=\"box\", label=\"" << llvm::DOT::EscapeString(TypeName);
|
|
|
|
// If the name of the class was a typedef or something different
|
|
// from the "real" class name, show the real class name in
|
|
// parentheses so we don't confuse ourselves.
|
|
if (TypeName != CanonType.getAsString()) {
|
|
Out << "\\n(" << CanonType.getAsString() << ")";
|
|
}
|
|
|
|
// Finished describing the node.
|
|
Out << " \"];\n";
|
|
|
|
// Display the base classes.
|
|
const CXXRecordDecl *Decl
|
|
= static_cast<const CXXRecordDecl *>(Type->getAs<RecordType>()->getDecl());
|
|
for (const auto &Base : Decl->bases()) {
|
|
QualType CanonBaseType = Context.getCanonicalType(Base.getType());
|
|
|
|
// If this is not virtual inheritance, bump the direct base
|
|
// count for the type.
|
|
if (!Base.isVirtual())
|
|
++DirectBaseCount[CanonBaseType];
|
|
|
|
// Write out the node (if we need to).
|
|
WriteNode(Base.getType(), Base.isVirtual());
|
|
|
|
// Write out the edge.
|
|
Out << " ";
|
|
WriteNodeReference(Type, FromVirtual);
|
|
Out << " -> ";
|
|
WriteNodeReference(Base.getType(), Base.isVirtual());
|
|
|
|
// Write out edge attributes to show the kind of inheritance.
|
|
if (Base.isVirtual()) {
|
|
Out << " [ style=\"dashed\" ]";
|
|
}
|
|
Out << ";";
|
|
}
|
|
}
|
|
|
|
/// WriteNodeReference - Write out a reference to the given node,
|
|
/// using a unique identifier for each direct base and for the
|
|
/// (only) virtual base.
|
|
raw_ostream&
|
|
InheritanceHierarchyWriter::WriteNodeReference(QualType Type,
|
|
bool FromVirtual) {
|
|
QualType CanonType = Context.getCanonicalType(Type);
|
|
|
|
Out << "Class_" << CanonType.getAsOpaquePtr();
|
|
if (!FromVirtual)
|
|
Out << "_" << DirectBaseCount[CanonType];
|
|
return Out;
|
|
}
|
|
|
|
/// viewInheritance - Display the inheritance hierarchy of this C++
|
|
/// class using GraphViz.
|
|
void CXXRecordDecl::viewInheritance(ASTContext& Context) const {
|
|
QualType Self = Context.getTypeDeclType(this);
|
|
|
|
int FD;
|
|
SmallString<128> Filename;
|
|
if (std::error_code EC = llvm::sys::fs::createTemporaryFile(
|
|
Self.getAsString(), "dot", FD, Filename)) {
|
|
llvm::errs() << "Error: " << EC.message() << "\n";
|
|
return;
|
|
}
|
|
|
|
llvm::errs() << "Writing '" << Filename << "'... ";
|
|
|
|
llvm::raw_fd_ostream O(FD, true);
|
|
|
|
InheritanceHierarchyWriter Writer(Context, O);
|
|
Writer.WriteGraph(Self);
|
|
llvm::errs() << " done. \n";
|
|
|
|
O.close();
|
|
|
|
// Display the graph
|
|
DisplayGraph(Filename);
|
|
}
|