CallGraph.cpp revision a50a5cd9db67f061c94557d188b992be34ccad2f
1196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks//== CallGraph.cpp - AST-based Call graph ----------------------*- C++ -*--==// 2196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks// 3196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks// The LLVM Compiler Infrastructure 4196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks// 5196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks// This file is distributed under the University of Illinois Open Source 6196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks// License. See LICENSE.TXT for details. 7196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks// 8196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks//===----------------------------------------------------------------------===// 9196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks// 10196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks// This file defines the AST-based CallGraph. 11196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks// 12196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks//===----------------------------------------------------------------------===// 13196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks#include "clang/Analysis/CallGraph.h" 14196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 15196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks#include "clang/AST/ASTContext.h" 16196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks#include "clang/AST/Decl.h" 17196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks#include "clang/AST/StmtVisitor.h" 18196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 19196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks#include "llvm/Support/GraphWriter.h" 20196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 21196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaksusing namespace clang; 22196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 23196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks/// Determine if a declaration should be included in the graph. 24196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaksstatic bool includeInGraph(const Decl *D) { 25196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { 26196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks // We skip function template definitions, as their semantics is 27196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks // only determined when they are instantiated. 28196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks if (!FD->isThisDeclarationADefinition() || 29196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks FD->isDependentContext()) 30196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks return false; 31196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 32196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks IdentifierInfo *II = FD->getIdentifier(); 33196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks if (II && II->getName().startswith("__inline")) 34196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks return false; 35196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks } 36196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 37196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks if (const ObjCMethodDecl *ID = dyn_cast<ObjCMethodDecl>(D)) { 38196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks if (!ID->isThisDeclarationADefinition()) 39196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks return false; 40196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks } 41196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 42196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks return true; 43196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks} 44196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 45196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaksnamespace { 46196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks/// A helper class, which walks the AST and locates all the call sites in the 47196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks/// given function body. 48196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaksclass CGBuilder : public StmtVisitor<CGBuilder> { 49196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks CallGraph *G; 50196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks const Decl *FD; 51196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks CallGraphNode *CallerNode; 52196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 53196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zakspublic: 54196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks CGBuilder(CallGraph *g, const Decl *D, CallGraphNode *N) 55196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks : G(g), FD(D), CallerNode(N) {} 56196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 57196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks void VisitStmt(Stmt *S) { VisitChildren(S); } 58196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 59196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks void VisitCallExpr(CallExpr *CE) { 60196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks // TODO: We need to handle ObjC method calls as well. 61196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks if (FunctionDecl *CalleeDecl = CE->getDirectCallee()) 62196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks if (includeInGraph(CalleeDecl)) { 63196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks CallGraphNode *CalleeNode = G->getOrInsertFunction(CalleeDecl); 64196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks CallerNode->addCallee(CalleeNode, G); 65196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks } 66a50a5cd9db67f061c94557d188b992be34ccad2fDaniel Dunbar } 67196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 68196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks void VisitChildren(Stmt *S) { 69196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks for (Stmt::child_range I = S->children(); I; ++I) 70196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks if (*I) 71196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks static_cast<CGBuilder*>(this)->Visit(*I); 72196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks } 73196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks}; 74196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks} // end anonymous namespace 75196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 76196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna ZaksCallGraph::CallGraph() { 77196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks Root = getOrInsertFunction(0); 78196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks} 79196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 80196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna ZaksCallGraph::~CallGraph() { 81196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks if (!FunctionMap.empty()) { 82196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks for (FunctionMapTy::iterator I = FunctionMap.begin(), E = FunctionMap.end(); 83196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks I != E; ++I) 84196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks delete I->second; 85196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks FunctionMap.clear(); 86196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks } 87196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks} 88196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 89196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaksvoid CallGraph::addToCallGraph(Decl* D, bool IsGlobal) { 90196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks CallGraphNode *Node = getOrInsertFunction(D); 91196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 92196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks if (IsGlobal) 93196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks Root->addCallee(Node, this); 94196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 95196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks // Process all the calls by this function as well. 96196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks CGBuilder builder(this, D, Node); 97196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks builder.Visit(D->getBody()); 98196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks} 99196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 100196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaksvoid CallGraph::addToCallGraph(DeclContext *DC) { 101196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks for (DeclContext::decl_iterator I = DC->decls_begin(), E = DC->decls_end(); 102196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks I != E; ++I) { 103196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks Decl *D = *I; 104196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks switch (D->getKind()) { 105196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks case Decl::CXXConstructor: 106196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks case Decl::CXXDestructor: 107196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks case Decl::CXXConversion: 108196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks case Decl::CXXMethod: 109196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks case Decl::Function: { 110196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks FunctionDecl *FD = cast<FunctionDecl>(D); 111196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks // We skip function template definitions, as their semantics is 112196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks // only determined when they are instantiated. 113196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks if (includeInGraph(FD)) 114196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks // If this function has external linkage, anything could call it. 115196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks // Note, we are not precise here. For example, the function could have 116196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks // its address taken. 117196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks addToCallGraph(FD, FD->isGlobal()); 118196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks break; 119196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks } 120196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 121196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks case Decl::ObjCCategoryImpl: 122196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks case Decl::ObjCImplementation: { 123196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks ObjCImplDecl *ID = cast<ObjCImplDecl>(D); 124196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks for (ObjCContainerDecl::method_iterator MI = ID->meth_begin(), 125196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks ME = ID->meth_end(); MI != ME; ++MI) { 126196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks if (includeInGraph(*MI)) 127196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks addToCallGraph(*MI, true); 128196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks } 129196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks break; 130196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks } 131196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 132196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks default: 133196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks break; 134196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks } 135196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks } 136196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks} 137196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 138196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna ZaksCallGraphNode *CallGraph::getOrInsertFunction(Decl *F) { 139196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks CallGraphNode *&Node = FunctionMap[F]; 140196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks if (Node) 141196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks return Node; 142196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 143196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks Node = new CallGraphNode(F); 144196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks ParentlessNodes.insert(Node); 145196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks return Node; 146196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks} 147196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 148196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaksvoid CallGraph::print(raw_ostream &OS) const { 149196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks OS << " --- Call graph Dump --- \n"; 150196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks for (const_iterator I = begin(), E = end(); I != E; ++I) { 151196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks OS << " Function: "; 152196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks if (I->second == Root) 153196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks OS << "< root >"; 154196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks else 155196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks I->second->print(OS); 156196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks OS << " calls: "; 157196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks for (CallGraphNode::iterator CI = I->second->begin(), 158196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks CE = I->second->end(); CI != CE; ++CI) { 159196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks assert(*CI != Root && "No one can call the root node."); 160196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks (*CI)->print(OS); 161196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks OS << " "; 162196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks } 163196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks OS << '\n'; 164196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks } 165196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks OS.flush(); 166196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks} 167196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 168196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaksvoid CallGraph::dump() const { 169196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks print(llvm::errs()); 170196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks} 171196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 172196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaksvoid CallGraph::viewGraph() const { 173196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks llvm::ViewGraph(this, "CallGraph"); 174196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks} 175196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 176196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna ZaksStringRef CallGraphNode::getName() const { 177196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks if (const FunctionDecl *D = dyn_cast_or_null<FunctionDecl>(FD)) 178196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks if (const IdentifierInfo *II = D->getIdentifier()) 179196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks return II->getName(); 180196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks return "< >"; 181196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks} 182196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 183196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaksvoid CallGraphNode::print(raw_ostream &os) const { 184196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks os << getName(); 185196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks} 186196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 187196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaksvoid CallGraphNode::dump() const { 188196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks print(llvm::errs()); 189196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks} 190196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 191196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaksnamespace llvm { 192196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 193196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zakstemplate <> 194196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaksstruct DOTGraphTraits<const CallGraph*> : public DefaultDOTGraphTraits { 195196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 196196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {} 197196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 198196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks static std::string getNodeLabel(const CallGraphNode *Node, 199196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks const CallGraph *CG) { 200196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks if (CG->getRoot() == Node) { 201196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks return "< root >"; 202196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks } 203196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks return Node->getName(); 204196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks } 205196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks 206196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks}; 207196b8cfe9cfcc452eb2f83aa4ad330c2324f8c7dAnna Zaks} 208