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