ASTConsumers.cpp revision 198092
1190809Sdelphij//===--- ASTConsumers.cpp - ASTConsumer implementations -------------------===// 2190809Sdelphij// 3142988Sscottl// The LLVM Compiler Infrastructure 4136849Sscottl// 5136849Sscottl// This file is distributed under the University of Illinois Open Source 6136849Sscottl// License. See LICENSE.TXT for details. 7190809Sdelphij// 8190809Sdelphij//===----------------------------------------------------------------------===// 9190809Sdelphij// 10190809Sdelphij// AST Consumer Implementations. 11190809Sdelphij// 12136849Sscottl//===----------------------------------------------------------------------===// 13190809Sdelphij 14190809Sdelphij#include "clang/Frontend/ASTConsumers.h" 15190809Sdelphij#include "clang/Frontend/DocumentXML.h" 16190809Sdelphij#include "clang/Frontend/PathDiagnosticClients.h" 17190809Sdelphij#include "clang/Basic/Diagnostic.h" 18190809Sdelphij#include "clang/Basic/SourceManager.h" 19190809Sdelphij#include "clang/Basic/FileManager.h" 20190809Sdelphij#include "clang/AST/AST.h" 21149871Sscottl#include "clang/AST/ASTConsumer.h" 22149871Sscottl#include "clang/AST/ASTContext.h" 23149871Sscottl#include "clang/AST/RecordLayout.h" 24149871Sscottl#include "clang/AST/PrettyPrinter.h" 25136849Sscottl#include "clang/CodeGen/ModuleBuilder.h" 26136849Sscottl#include "llvm/Module.h" 27136849Sscottl#include "llvm/Support/Format.h" 28136849Sscottl#include "llvm/Support/Timer.h" 29136849Sscottl#include "llvm/Support/raw_ostream.h" 30136849Sscottl#include "llvm/System/Path.h" 31136849Sscottl#include <cstdio> 32136849Sscottl 33136849Sscottlusing namespace clang; 34136849Sscottl 35136849Sscottl//===----------------------------------------------------------------------===// 36136849Sscottl/// ASTPrinter - Pretty-printer and dumper of ASTs 37190809Sdelphij 38136849Sscottlnamespace { 39136849Sscottl class ASTPrinter : public ASTConsumer { 40136849Sscottl llvm::raw_ostream &Out; 41136849Sscottl bool Dump; 42136849Sscottl 43136849Sscottl public: 44136849Sscottl ASTPrinter(llvm::raw_ostream* o = NULL, bool Dump = false) 45136849Sscottl : Out(o? *o : llvm::errs()), Dump(Dump) { } 46136849Sscottl 47136849Sscottl virtual void HandleTranslationUnit(ASTContext &Context) { 48136849Sscottl PrintingPolicy Policy = Context.PrintingPolicy; 49136849Sscottl Policy.Dump = Dump; 50136849Sscottl Context.getTranslationUnitDecl()->print(Out, Policy); 51136849Sscottl } 52136849Sscottl }; 53136849Sscottl} // end anonymous namespace 54136849Sscottl 55136849SscottlASTConsumer *clang::CreateASTPrinter(llvm::raw_ostream* out) { 56149871Sscottl return new ASTPrinter(out); 57190809Sdelphij} 58149871Sscottl 59149871Sscottl//===----------------------------------------------------------------------===// 60136849Sscottl/// ASTPrinterXML - XML-printer of ASTs 61136849Sscottl 62149871Sscottlnamespace { 63136849Sscottl class ASTPrinterXML : public ASTConsumer { 64149871Sscottl DocumentXML Doc; 65190809Sdelphij 66136849Sscottl public: 67149871Sscottl ASTPrinterXML(llvm::raw_ostream& o) : Doc("CLANG_XML", o) {} 68149871Sscottl 69149871Sscottl void Initialize(ASTContext &Context) { 70149871Sscottl Doc.initialize(Context); 71149871Sscottl } 72149871Sscottl 73149871Sscottl virtual void HandleTranslationUnit(ASTContext &Ctx) { 74149871Sscottl Doc.addSubNode("TranslationUnit"); 75149871Sscottl for (DeclContext::decl_iterator 76149871Sscottl D = Ctx.getTranslationUnitDecl()->decls_begin(), 77190809Sdelphij DEnd = Ctx.getTranslationUnitDecl()->decls_end(); 78149871Sscottl D != DEnd; 79149871Sscottl ++D) 80149871Sscottl Doc.PrintDecl(*D); 81149871Sscottl Doc.toParent(); 82149871Sscottl Doc.finalize(); 83149871Sscottl } 84149871Sscottl }; 85149871Sscottl} // end anonymous namespace 86149871Sscottl 87149871Sscottl 88149871SscottlASTConsumer *clang::CreateASTPrinterXML(llvm::raw_ostream* out) { 89149871Sscottl return new ASTPrinterXML(out ? *out : llvm::outs()); 90149871Sscottl} 91149871Sscottl 92149871SscottlASTConsumer *clang::CreateASTDumper() { 93149871Sscottl return new ASTPrinter(0, true); 94149871Sscottl} 95149871Sscottl 96149871Sscottl//===----------------------------------------------------------------------===// 97149871Sscottl/// ASTViewer - AST Visualization 98149871Sscottl 99149871Sscottlnamespace { 100149871Sscottl class ASTViewer : public ASTConsumer { 101149871Sscottl ASTContext *Context; 102149871Sscottl public: 103149871Sscottl void Initialize(ASTContext &Context) { 104149871Sscottl this->Context = &Context; 105149871Sscottl } 106149871Sscottl 107190809Sdelphij virtual void HandleTopLevelDecl(DeclGroupRef D) { 108190809Sdelphij for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) 109149871Sscottl HandleTopLevelSingleDecl(*I); 110149871Sscottl } 111149871Sscottl 112149871Sscottl void HandleTopLevelSingleDecl(Decl *D); 113190809Sdelphij }; 114136849Sscottl} 115149871Sscottl 116149871Sscottlvoid ASTViewer::HandleTopLevelSingleDecl(Decl *D) { 117149871Sscottl if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { 118149871Sscottl FD->print(llvm::errs()); 119149871Sscottl 120149871Sscottl if (Stmt *Body = FD->getBody()) { 121136849Sscottl llvm::errs() << '\n'; 122190809Sdelphij Body->viewAST(); 123149871Sscottl llvm::errs() << '\n'; 124149871Sscottl } 125149871Sscottl return; 126149871Sscottl } 127149871Sscottl 128149871Sscottl if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { 129149871Sscottl MD->print(llvm::errs()); 130149871Sscottl 131149871Sscottl if (MD->getBody()) { 132149871Sscottl llvm::errs() << '\n'; 133149871Sscottl MD->getBody()->viewAST(); 134149871Sscottl llvm::errs() << '\n'; 135149871Sscottl } 136149871Sscottl } 137149871Sscottl} 138149871Sscottl 139149871Sscottl 140149871SscottlASTConsumer *clang::CreateASTViewer() { return new ASTViewer(); } 141149871Sscottl 142149871Sscottl//===----------------------------------------------------------------------===// 143149871Sscottl/// DeclContextPrinter - Decl and DeclContext Visualization 144149871Sscottl 145149871Sscottlnamespace { 146149871Sscottl 147149871Sscottlclass DeclContextPrinter : public ASTConsumer { 148149871Sscottl llvm::raw_ostream& Out; 149149871Sscottlpublic: 150149871Sscottl DeclContextPrinter() : Out(llvm::errs()) {} 151149871Sscottl 152149871Sscottl void HandleTranslationUnit(ASTContext &C) { 153149871Sscottl PrintDeclContext(C.getTranslationUnitDecl(), 4); 154149871Sscottl } 155190809Sdelphij 156149871Sscottl void PrintDeclContext(const DeclContext* DC, unsigned Indentation); 157149871Sscottl}; 158149871Sscottl} // end anonymous namespace 159149871Sscottl 160149871Sscottlvoid DeclContextPrinter::PrintDeclContext(const DeclContext* DC, 161149871Sscottl unsigned Indentation) { 162149871Sscottl // Print DeclContext name. 163149871Sscottl switch (DC->getDeclKind()) { 164149871Sscottl case Decl::TranslationUnit: 165149871Sscottl Out << "[translation unit] " << DC; 166149871Sscottl break; 167149871Sscottl case Decl::Namespace: { 168149871Sscottl Out << "[namespace] "; 169190809Sdelphij const NamespaceDecl* ND = cast<NamespaceDecl>(DC); 170149871Sscottl Out << ND->getNameAsString(); 171149871Sscottl break; 172149871Sscottl } 173149871Sscottl case Decl::Enum: { 174149871Sscottl const EnumDecl* ED = cast<EnumDecl>(DC); 175136849Sscottl if (ED->isDefinition()) 176136849Sscottl Out << "[enum] "; 177136849Sscottl else 178136849Sscottl Out << "<enum> "; 179136849Sscottl Out << ED->getNameAsString(); 180136849Sscottl break; 181136849Sscottl } 182136849Sscottl case Decl::Record: { 183136849Sscottl const RecordDecl* RD = cast<RecordDecl>(DC); 184136849Sscottl if (RD->isDefinition()) 185136849Sscottl Out << "[struct] "; 186136849Sscottl else 187230133Suqs Out << "<struct> "; 188136849Sscottl Out << RD->getNameAsString(); 189136849Sscottl break; 190136849Sscottl } 191136849Sscottl case Decl::CXXRecord: { 192136849Sscottl const CXXRecordDecl* RD = cast<CXXRecordDecl>(DC); 193149871Sscottl if (RD->isDefinition()) 194149871Sscottl Out << "[class] "; 195190809Sdelphij else 196136849Sscottl Out << "<class> "; 197149871Sscottl Out << RD->getNameAsString() << " " << DC; 198149871Sscottl break; 199149871Sscottl } 200149871Sscottl case Decl::ObjCMethod: 201149871Sscottl Out << "[objc method]"; 202149871Sscottl break; 203149871Sscottl case Decl::ObjCInterface: 204149871Sscottl Out << "[objc interface]"; 205136849Sscottl break; 206136849Sscottl case Decl::ObjCCategory: 207136849Sscottl Out << "[objc category]"; 208136849Sscottl break; 209136849Sscottl case Decl::ObjCProtocol: 210136849Sscottl Out << "[objc protocol]"; 211136849Sscottl break; 212136849Sscottl case Decl::ObjCImplementation: 213136849Sscottl Out << "[objc implementation]"; 214136849Sscottl break; 215136849Sscottl case Decl::ObjCCategoryImpl: 216136849Sscottl Out << "[objc categoryimpl]"; 217136849Sscottl break; 218136849Sscottl case Decl::LinkageSpec: 219136849Sscottl Out << "[linkage spec]"; 220136849Sscottl break; 221136849Sscottl case Decl::Block: 222136849Sscottl Out << "[block]"; 223136849Sscottl break; 224 case Decl::Function: { 225 const FunctionDecl* FD = cast<FunctionDecl>(DC); 226 if (FD->isThisDeclarationADefinition()) 227 Out << "[function] "; 228 else 229 Out << "<function> "; 230 Out << FD->getNameAsString(); 231 // Print the parameters. 232 Out << "("; 233 bool PrintComma = false; 234 for (FunctionDecl::param_const_iterator I = FD->param_begin(), 235 E = FD->param_end(); I != E; ++I) { 236 if (PrintComma) 237 Out << ", "; 238 else 239 PrintComma = true; 240 Out << (*I)->getNameAsString(); 241 } 242 Out << ")"; 243 break; 244 } 245 case Decl::CXXMethod: { 246 const CXXMethodDecl* D = cast<CXXMethodDecl>(DC); 247 if (D->isOutOfLine()) 248 Out << "[c++ method] "; 249 else if (D->isImplicit()) 250 Out << "(c++ method) "; 251 else 252 Out << "<c++ method> "; 253 Out << D->getNameAsString(); 254 // Print the parameters. 255 Out << "("; 256 bool PrintComma = false; 257 for (FunctionDecl::param_const_iterator I = D->param_begin(), 258 E = D->param_end(); I != E; ++I) { 259 if (PrintComma) 260 Out << ", "; 261 else 262 PrintComma = true; 263 Out << (*I)->getNameAsString(); 264 } 265 Out << ")"; 266 267 // Check the semantic DeclContext. 268 const DeclContext* SemaDC = D->getDeclContext(); 269 const DeclContext* LexicalDC = D->getLexicalDeclContext(); 270 if (SemaDC != LexicalDC) 271 Out << " [[" << SemaDC << "]]"; 272 273 break; 274 } 275 case Decl::CXXConstructor: { 276 const CXXConstructorDecl* D = cast<CXXConstructorDecl>(DC); 277 if (D->isOutOfLine()) 278 Out << "[c++ ctor] "; 279 else if (D->isImplicit()) 280 Out << "(c++ ctor) "; 281 else 282 Out << "<c++ ctor> "; 283 Out << D->getNameAsString(); 284 // Print the parameters. 285 Out << "("; 286 bool PrintComma = false; 287 for (FunctionDecl::param_const_iterator I = D->param_begin(), 288 E = D->param_end(); I != E; ++I) { 289 if (PrintComma) 290 Out << ", "; 291 else 292 PrintComma = true; 293 Out << (*I)->getNameAsString(); 294 } 295 Out << ")"; 296 297 // Check the semantic DC. 298 const DeclContext* SemaDC = D->getDeclContext(); 299 const DeclContext* LexicalDC = D->getLexicalDeclContext(); 300 if (SemaDC != LexicalDC) 301 Out << " [[" << SemaDC << "]]"; 302 break; 303 } 304 case Decl::CXXDestructor: { 305 const CXXDestructorDecl* D = cast<CXXDestructorDecl>(DC); 306 if (D->isOutOfLine()) 307 Out << "[c++ dtor] "; 308 else if (D->isImplicit()) 309 Out << "(c++ dtor) "; 310 else 311 Out << "<c++ dtor> "; 312 Out << D->getNameAsString(); 313 // Check the semantic DC. 314 const DeclContext* SemaDC = D->getDeclContext(); 315 const DeclContext* LexicalDC = D->getLexicalDeclContext(); 316 if (SemaDC != LexicalDC) 317 Out << " [[" << SemaDC << "]]"; 318 break; 319 } 320 case Decl::CXXConversion: { 321 const CXXConversionDecl* D = cast<CXXConversionDecl>(DC); 322 if (D->isOutOfLine()) 323 Out << "[c++ conversion] "; 324 else if (D->isImplicit()) 325 Out << "(c++ conversion) "; 326 else 327 Out << "<c++ conversion> "; 328 Out << D->getNameAsString(); 329 // Check the semantic DC. 330 const DeclContext* SemaDC = D->getDeclContext(); 331 const DeclContext* LexicalDC = D->getLexicalDeclContext(); 332 if (SemaDC != LexicalDC) 333 Out << " [[" << SemaDC << "]]"; 334 break; 335 } 336 337 default: 338 assert(0 && "a decl that inherits DeclContext isn't handled"); 339 } 340 341 Out << "\n"; 342 343 // Print decls in the DeclContext. 344 for (DeclContext::decl_iterator I = DC->decls_begin(), E = DC->decls_end(); 345 I != E; ++I) { 346 for (unsigned i = 0; i < Indentation; ++i) 347 Out << " "; 348 349 Decl::Kind DK = I->getKind(); 350 switch (DK) { 351 case Decl::Namespace: 352 case Decl::Enum: 353 case Decl::Record: 354 case Decl::CXXRecord: 355 case Decl::ObjCMethod: 356 case Decl::ObjCInterface: 357 case Decl::ObjCCategory: 358 case Decl::ObjCProtocol: 359 case Decl::ObjCImplementation: 360 case Decl::ObjCCategoryImpl: 361 case Decl::LinkageSpec: 362 case Decl::Block: 363 case Decl::Function: 364 case Decl::CXXMethod: 365 case Decl::CXXConstructor: 366 case Decl::CXXDestructor: 367 case Decl::CXXConversion: 368 { 369 DeclContext* DC = cast<DeclContext>(*I); 370 PrintDeclContext(DC, Indentation+2); 371 break; 372 } 373 case Decl::Field: { 374 FieldDecl* FD = cast<FieldDecl>(*I); 375 Out << "<field> " << FD->getNameAsString() << "\n"; 376 break; 377 } 378 case Decl::Typedef: { 379 TypedefDecl* TD = cast<TypedefDecl>(*I); 380 Out << "<typedef> " << TD->getNameAsString() << "\n"; 381 break; 382 } 383 case Decl::EnumConstant: { 384 EnumConstantDecl* ECD = cast<EnumConstantDecl>(*I); 385 Out << "<enum constant> " << ECD->getNameAsString() << "\n"; 386 break; 387 } 388 case Decl::Var: { 389 VarDecl* VD = cast<VarDecl>(*I); 390 Out << "<var> " << VD->getNameAsString() << "\n"; 391 break; 392 } 393 case Decl::ImplicitParam: { 394 ImplicitParamDecl* IPD = cast<ImplicitParamDecl>(*I); 395 Out << "<implicit parameter> " << IPD->getNameAsString() << "\n"; 396 break; 397 } 398 case Decl::ParmVar: { 399 ParmVarDecl* PVD = cast<ParmVarDecl>(*I); 400 Out << "<parameter> " << PVD->getNameAsString() << "\n"; 401 break; 402 } 403 case Decl::OriginalParmVar: { 404 OriginalParmVarDecl* OPVD = cast<OriginalParmVarDecl>(*I); 405 Out << "<original parameter> " << OPVD->getNameAsString() << "\n"; 406 break; 407 } 408 case Decl::ObjCProperty: { 409 ObjCPropertyDecl* OPD = cast<ObjCPropertyDecl>(*I); 410 Out << "<objc property> " << OPD->getNameAsString() << "\n"; 411 break; 412 } 413 default: 414 fprintf(stderr, "DeclKind: %d \"%s\"\n", DK, I->getDeclKindName()); 415 assert(0 && "decl unhandled"); 416 } 417 } 418} 419ASTConsumer *clang::CreateDeclContextPrinter() { 420 return new DeclContextPrinter(); 421} 422 423//===----------------------------------------------------------------------===// 424/// RecordLayoutDumper - C++ Record Layout Dumping. 425namespace { 426class RecordLayoutDumper : public ASTConsumer { 427 llvm::raw_ostream& Out; 428 429 void PrintOffset(uint64_t Offset, unsigned IndentLevel) { 430 Out << llvm::format("%4d | ", Offset); 431 for (unsigned I = 0; I < IndentLevel * 2; ++I) Out << ' '; 432 } 433 434 void DumpRecordLayoutOffsets(const CXXRecordDecl *RD, ASTContext &C, 435 uint64_t Offset, 436 unsigned IndentLevel, const char* Description, 437 bool IncludeVirtualBases) { 438 const ASTRecordLayout &Info = C.getASTRecordLayout(RD); 439 440 PrintOffset(Offset, IndentLevel); 441 Out << C.getTypeDeclType((CXXRecordDecl *)RD).getAsString(); 442 if (Description) 443 Out << ' ' << Description; 444 if (RD->isEmpty()) 445 Out << " (empty)"; 446 Out << '\n'; 447 448 IndentLevel++; 449 450 const CXXRecordDecl *PrimaryBase = Info.getPrimaryBase(); 451 452 // Vtable pointer. 453 if (RD->isDynamicClass() && !PrimaryBase) { 454 PrintOffset(Offset, IndentLevel); 455 Out << '(' << RD->getNameAsString() << " vtable pointer)\n"; 456 } 457 // Dump (non-virtual) bases 458 for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(), 459 E = RD->bases_end(); I != E; ++I) { 460 if (I->isVirtual()) 461 continue; 462 463 const CXXRecordDecl *Base = 464 cast<CXXRecordDecl>(I->getType()->getAs<RecordType>()->getDecl()); 465 466 uint64_t BaseOffset = Offset + Info.getBaseClassOffset(Base) / 8; 467 468 DumpRecordLayoutOffsets(Base, C, BaseOffset, IndentLevel, 469 Base == PrimaryBase ? "(primary base)" : "(base)", 470 /*IncludeVirtualBases=*/false); 471 } 472 473 // Dump fields. 474 uint64_t FieldNo = 0; 475 for (CXXRecordDecl::field_iterator I = RD->field_begin(), 476 E = RD->field_end(); I != E; ++I, ++FieldNo) { 477 const FieldDecl *Field = *I; 478 uint64_t FieldOffset = Offset + Info.getFieldOffset(FieldNo) / 8; 479 480 if (const RecordType *RT = Field->getType()->getAs<RecordType>()) { 481 if (const CXXRecordDecl *D = dyn_cast<CXXRecordDecl>(RT->getDecl())) { 482 DumpRecordLayoutOffsets(D, C, FieldOffset, IndentLevel, 483 Field->getNameAsCString(), 484 /*IncludeVirtualBases=*/true); 485 continue; 486 } 487 } 488 489 PrintOffset(FieldOffset, IndentLevel); 490 Out << Field->getType().getAsString() << ' '; 491 Out << Field->getNameAsString() << '\n'; 492 } 493 494 if (!IncludeVirtualBases) 495 return; 496 497 // Dump virtual bases. 498 for (CXXRecordDecl::base_class_const_iterator I = RD->vbases_begin(), 499 E = RD->vbases_end(); I != E; ++I) { 500 assert(I->isVirtual() && "Found non-virtual class!"); 501 const CXXRecordDecl *VBase = 502 cast<CXXRecordDecl>(I->getType()->getAs<RecordType>()->getDecl()); 503 504 uint64_t VBaseOffset = Offset + Info.getVBaseClassOffset(VBase) / 8; 505 DumpRecordLayoutOffsets(VBase, C, VBaseOffset, IndentLevel, 506 VBase == PrimaryBase ? 507 "(primary virtual base)" : "(virtual base)", 508 /*IncludeVirtualBases=*/false); 509 } 510 } 511 512 // FIXME: Maybe this could be useful in ASTContext.cpp. 513 void DumpRecordLayout(const CXXRecordDecl *RD, ASTContext &C) { 514 const ASTRecordLayout &Info = C.getASTRecordLayout(RD); 515 516 DumpRecordLayoutOffsets(RD, C, 0, 0, 0, 517 /*IncludeVirtualBases=*/true); 518 Out << " sizeof=" << Info.getSize() / 8; 519 Out << ", dsize=" << Info.getDataSize() / 8; 520 Out << ", align=" << Info.getAlignment() / 8 << '\n'; 521 Out << " nvsize=" << Info.getNonVirtualSize() / 8; 522 Out << ", nvalign=" << Info.getNonVirtualAlign() / 8 << '\n'; 523 Out << '\n'; 524 } 525 526public: 527 RecordLayoutDumper() : Out(llvm::errs()) {} 528 529 void HandleTranslationUnit(ASTContext &C) { 530 for (ASTContext::type_iterator I = C.types_begin(), E = C.types_end(); 531 I != E; ++I) { 532 const RecordType *RT = dyn_cast<RecordType>(*I); 533 if (!RT) 534 continue; 535 536 const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(RT->getDecl()); 537 if (!RD) 538 continue; 539 540 if (RD->isImplicit()) 541 continue; 542 543 if (RD->isDependentType()) 544 continue; 545 546 if (RD->isInvalidDecl()) 547 continue; 548 549 if (!RD->getDefinition(C)) 550 continue; 551 552 // FIXME: Do we really need to hard code this? 553 if (RD->getQualifiedNameAsString() == "__va_list_tag") 554 continue; 555 556 DumpRecordLayout(RD, C); 557 } 558 } 559}; 560} // end anonymous namespace 561ASTConsumer *clang::CreateRecordLayoutDumper() { 562 return new RecordLayoutDumper(); 563} 564 565//===----------------------------------------------------------------------===// 566/// InheritanceViewer - C++ Inheritance Visualization 567 568namespace { 569class InheritanceViewer : public ASTConsumer { 570 const std::string clsname; 571public: 572 InheritanceViewer(const std::string& cname) : clsname(cname) {} 573 574 void HandleTranslationUnit(ASTContext &C) { 575 for (ASTContext::type_iterator I=C.types_begin(),E=C.types_end(); I!=E; ++I) 576 if (RecordType *T = dyn_cast<RecordType>(*I)) { 577 if (CXXRecordDecl *D = dyn_cast<CXXRecordDecl>(T->getDecl())) { 578 // FIXME: This lookup needs to be generalized to handle namespaces and 579 // (when we support them) templates. 580 if (D->getNameAsString() == clsname) { 581 D->viewInheritance(C); 582 } 583 } 584 } 585 } 586}; 587} 588 589ASTConsumer *clang::CreateInheritanceViewer(const std::string& clsname) { 590 return new InheritanceViewer(clsname); 591} 592