1 //===--- TextNodeDumper.cpp - Printing of AST nodes -----------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file implements AST dumping of components of individual AST nodes.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "clang/AST/TextNodeDumper.h"
15 #include "clang/AST/DeclFriend.h"
16 #include "clang/AST/DeclOpenMP.h"
17 #include "clang/AST/DeclTemplate.h"
18 #include "clang/AST/LocInfoType.h"
19 
20 using namespace clang;
21 
dumpPreviousDeclImpl(raw_ostream & OS,...)22 static void dumpPreviousDeclImpl(raw_ostream &OS, ...) {}
23 
24 template <typename T>
dumpPreviousDeclImpl(raw_ostream & OS,const Mergeable<T> * D)25 static void dumpPreviousDeclImpl(raw_ostream &OS, const Mergeable<T> *D) {
26   const T *First = D->getFirstDecl();
27   if (First != D)
28     OS << " first " << First;
29 }
30 
31 template <typename T>
dumpPreviousDeclImpl(raw_ostream & OS,const Redeclarable<T> * D)32 static void dumpPreviousDeclImpl(raw_ostream &OS, const Redeclarable<T> *D) {
33   const T *Prev = D->getPreviousDecl();
34   if (Prev)
35     OS << " prev " << Prev;
36 }
37 
38 /// Dump the previous declaration in the redeclaration chain for a declaration,
39 /// if any.
dumpPreviousDecl(raw_ostream & OS,const Decl * D)40 static void dumpPreviousDecl(raw_ostream &OS, const Decl *D) {
41   switch (D->getKind()) {
42 #define DECL(DERIVED, BASE)                                                    \
43   case Decl::DERIVED:                                                          \
44     return dumpPreviousDeclImpl(OS, cast<DERIVED##Decl>(D));
45 #define ABSTRACT_DECL(DECL)
46 #include "clang/AST/DeclNodes.inc"
47   }
48   llvm_unreachable("Decl that isn't part of DeclNodes.inc!");
49 }
50 
TextNodeDumper(raw_ostream & OS,bool ShowColors,const SourceManager * SM,const PrintingPolicy & PrintPolicy,const comments::CommandTraits * Traits)51 TextNodeDumper::TextNodeDumper(raw_ostream &OS, bool ShowColors,
52                                const SourceManager *SM,
53                                const PrintingPolicy &PrintPolicy,
54                                const comments::CommandTraits *Traits)
55     : TextTreeStructure(OS, ShowColors), OS(OS), ShowColors(ShowColors), SM(SM),
56       PrintPolicy(PrintPolicy), Traits(Traits) {}
57 
Visit(const comments::Comment * C,const comments::FullComment * FC)58 void TextNodeDumper::Visit(const comments::Comment *C,
59                            const comments::FullComment *FC) {
60   if (!C) {
61     ColorScope Color(OS, ShowColors, NullColor);
62     OS << "<<<NULL>>>";
63     return;
64   }
65 
66   {
67     ColorScope Color(OS, ShowColors, CommentColor);
68     OS << C->getCommentKindName();
69   }
70   dumpPointer(C);
71   dumpSourceRange(C->getSourceRange());
72 
73   ConstCommentVisitor<TextNodeDumper, void,
74                       const comments::FullComment *>::visit(C, FC);
75 }
76 
Visit(const Attr * A)77 void TextNodeDumper::Visit(const Attr *A) {
78   {
79     ColorScope Color(OS, ShowColors, AttrColor);
80 
81     switch (A->getKind()) {
82 #define ATTR(X)                                                                \
83   case attr::X:                                                                \
84     OS << #X;                                                                  \
85     break;
86 #include "clang/Basic/AttrList.inc"
87     }
88     OS << "Attr";
89   }
90   dumpPointer(A);
91   dumpSourceRange(A->getRange());
92   if (A->isInherited())
93     OS << " Inherited";
94   if (A->isImplicit())
95     OS << " Implicit";
96 
97   ConstAttrVisitor<TextNodeDumper>::Visit(A);
98 }
99 
Visit(const TemplateArgument & TA,SourceRange R,const Decl * From,StringRef Label)100 void TextNodeDumper::Visit(const TemplateArgument &TA, SourceRange R,
101                            const Decl *From, StringRef Label) {
102   OS << "TemplateArgument";
103   if (R.isValid())
104     dumpSourceRange(R);
105 
106   if (From)
107     dumpDeclRef(From, Label);
108 
109   ConstTemplateArgumentVisitor<TextNodeDumper>::Visit(TA);
110 }
111 
Visit(const Stmt * Node)112 void TextNodeDumper::Visit(const Stmt *Node) {
113   if (!Node) {
114     ColorScope Color(OS, ShowColors, NullColor);
115     OS << "<<<NULL>>>";
116     return;
117   }
118   {
119     ColorScope Color(OS, ShowColors, StmtColor);
120     OS << Node->getStmtClassName();
121   }
122   dumpPointer(Node);
123   dumpSourceRange(Node->getSourceRange());
124 
125   if (const auto *E = dyn_cast<Expr>(Node)) {
126     dumpType(E->getType());
127 
128     {
129       ColorScope Color(OS, ShowColors, ValueKindColor);
130       switch (E->getValueKind()) {
131       case VK_RValue:
132         break;
133       case VK_LValue:
134         OS << " lvalue";
135         break;
136       case VK_XValue:
137         OS << " xvalue";
138         break;
139       }
140     }
141 
142     {
143       ColorScope Color(OS, ShowColors, ObjectKindColor);
144       switch (E->getObjectKind()) {
145       case OK_Ordinary:
146         break;
147       case OK_BitField:
148         OS << " bitfield";
149         break;
150       case OK_ObjCProperty:
151         OS << " objcproperty";
152         break;
153       case OK_ObjCSubscript:
154         OS << " objcsubscript";
155         break;
156       case OK_VectorComponent:
157         OS << " vectorcomponent";
158         break;
159       }
160     }
161   }
162 
163   ConstStmtVisitor<TextNodeDumper>::Visit(Node);
164 }
165 
Visit(const Type * T)166 void TextNodeDumper::Visit(const Type *T) {
167   if (!T) {
168     ColorScope Color(OS, ShowColors, NullColor);
169     OS << "<<<NULL>>>";
170     return;
171   }
172   if (isa<LocInfoType>(T)) {
173     {
174       ColorScope Color(OS, ShowColors, TypeColor);
175       OS << "LocInfo Type";
176     }
177     dumpPointer(T);
178     return;
179   }
180 
181   {
182     ColorScope Color(OS, ShowColors, TypeColor);
183     OS << T->getTypeClassName() << "Type";
184   }
185   dumpPointer(T);
186   OS << " ";
187   dumpBareType(QualType(T, 0), false);
188 
189   QualType SingleStepDesugar =
190       T->getLocallyUnqualifiedSingleStepDesugaredType();
191   if (SingleStepDesugar != QualType(T, 0))
192     OS << " sugar";
193 
194   if (T->isDependentType())
195     OS << " dependent";
196   else if (T->isInstantiationDependentType())
197     OS << " instantiation_dependent";
198 
199   if (T->isVariablyModifiedType())
200     OS << " variably_modified";
201   if (T->containsUnexpandedParameterPack())
202     OS << " contains_unexpanded_pack";
203   if (T->isFromAST())
204     OS << " imported";
205 
206   TypeVisitor<TextNodeDumper>::Visit(T);
207 }
208 
Visit(QualType T)209 void TextNodeDumper::Visit(QualType T) {
210   OS << "QualType";
211   dumpPointer(T.getAsOpaquePtr());
212   OS << " ";
213   dumpBareType(T, false);
214   OS << " " << T.split().Quals.getAsString();
215 }
216 
Visit(const Decl * D)217 void TextNodeDumper::Visit(const Decl *D) {
218   if (!D) {
219     ColorScope Color(OS, ShowColors, NullColor);
220     OS << "<<<NULL>>>";
221     return;
222   }
223 
224   {
225     ColorScope Color(OS, ShowColors, DeclKindNameColor);
226     OS << D->getDeclKindName() << "Decl";
227   }
228   dumpPointer(D);
229   if (D->getLexicalDeclContext() != D->getDeclContext())
230     OS << " parent " << cast<Decl>(D->getDeclContext());
231   dumpPreviousDecl(OS, D);
232   dumpSourceRange(D->getSourceRange());
233   OS << ' ';
234   dumpLocation(D->getLocation());
235   if (D->isFromASTFile())
236     OS << " imported";
237   if (Module *M = D->getOwningModule())
238     OS << " in " << M->getFullModuleName();
239   if (auto *ND = dyn_cast<NamedDecl>(D))
240     for (Module *M : D->getASTContext().getModulesWithMergedDefinition(
241              const_cast<NamedDecl *>(ND)))
242       AddChild([=] { OS << "also in " << M->getFullModuleName(); });
243   if (const NamedDecl *ND = dyn_cast<NamedDecl>(D))
244     if (ND->isHidden())
245       OS << " hidden";
246   if (D->isImplicit())
247     OS << " implicit";
248 
249   if (D->isUsed())
250     OS << " used";
251   else if (D->isThisDeclarationReferenced())
252     OS << " referenced";
253 
254   if (D->isInvalidDecl())
255     OS << " invalid";
256   if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
257     if (FD->isConstexpr())
258       OS << " constexpr";
259 }
260 
Visit(const CXXCtorInitializer * Init)261 void TextNodeDumper::Visit(const CXXCtorInitializer *Init) {
262   OS << "CXXCtorInitializer";
263   if (Init->isAnyMemberInitializer()) {
264     OS << ' ';
265     dumpBareDeclRef(Init->getAnyMember());
266   } else if (Init->isBaseInitializer()) {
267     dumpType(QualType(Init->getBaseClass(), 0));
268   } else if (Init->isDelegatingInitializer()) {
269     dumpType(Init->getTypeSourceInfo()->getType());
270   } else {
271     llvm_unreachable("Unknown initializer type");
272   }
273 }
274 
Visit(const BlockDecl::Capture & C)275 void TextNodeDumper::Visit(const BlockDecl::Capture &C) {
276   OS << "capture";
277   if (C.isByRef())
278     OS << " byref";
279   if (C.isNested())
280     OS << " nested";
281   if (C.getVariable()) {
282     OS << ' ';
283     dumpBareDeclRef(C.getVariable());
284   }
285 }
286 
Visit(const OMPClause * C)287 void TextNodeDumper::Visit(const OMPClause *C) {
288   if (!C) {
289     ColorScope Color(OS, ShowColors, NullColor);
290     OS << "<<<NULL>>> OMPClause";
291     return;
292   }
293   {
294     ColorScope Color(OS, ShowColors, AttrColor);
295     StringRef ClauseName(getOpenMPClauseName(C->getClauseKind()));
296     OS << "OMP" << ClauseName.substr(/*Start=*/0, /*N=*/1).upper()
297        << ClauseName.drop_front() << "Clause";
298   }
299   dumpPointer(C);
300   dumpSourceRange(SourceRange(C->getBeginLoc(), C->getEndLoc()));
301   if (C->isImplicit())
302     OS << " <implicit>";
303 }
304 
dumpPointer(const void * Ptr)305 void TextNodeDumper::dumpPointer(const void *Ptr) {
306   ColorScope Color(OS, ShowColors, AddressColor);
307   OS << ' ' << Ptr;
308 }
309 
dumpLocation(SourceLocation Loc)310 void TextNodeDumper::dumpLocation(SourceLocation Loc) {
311   if (!SM)
312     return;
313 
314   ColorScope Color(OS, ShowColors, LocationColor);
315   SourceLocation SpellingLoc = SM->getSpellingLoc(Loc);
316 
317   // The general format we print out is filename:line:col, but we drop pieces
318   // that haven't changed since the last loc printed.
319   PresumedLoc PLoc = SM->getPresumedLoc(SpellingLoc);
320 
321   if (PLoc.isInvalid()) {
322     OS << "<invalid sloc>";
323     return;
324   }
325 
326   if (strcmp(PLoc.getFilename(), LastLocFilename) != 0) {
327     OS << PLoc.getFilename() << ':' << PLoc.getLine() << ':'
328        << PLoc.getColumn();
329     LastLocFilename = PLoc.getFilename();
330     LastLocLine = PLoc.getLine();
331   } else if (PLoc.getLine() != LastLocLine) {
332     OS << "line" << ':' << PLoc.getLine() << ':' << PLoc.getColumn();
333     LastLocLine = PLoc.getLine();
334   } else {
335     OS << "col" << ':' << PLoc.getColumn();
336   }
337 }
338 
dumpSourceRange(SourceRange R)339 void TextNodeDumper::dumpSourceRange(SourceRange R) {
340   // Can't translate locations if a SourceManager isn't available.
341   if (!SM)
342     return;
343 
344   OS << " <";
345   dumpLocation(R.getBegin());
346   if (R.getBegin() != R.getEnd()) {
347     OS << ", ";
348     dumpLocation(R.getEnd());
349   }
350   OS << ">";
351 
352   // <t2.c:123:421[blah], t2.c:412:321>
353 }
354 
dumpBareType(QualType T,bool Desugar)355 void TextNodeDumper::dumpBareType(QualType T, bool Desugar) {
356   ColorScope Color(OS, ShowColors, TypeColor);
357 
358   SplitQualType T_split = T.split();
359   OS << "'" << QualType::getAsString(T_split, PrintPolicy) << "'";
360 
361   if (Desugar && !T.isNull()) {
362     // If the type is sugared, also dump a (shallow) desugared type.
363     SplitQualType D_split = T.getSplitDesugaredType();
364     if (T_split != D_split)
365       OS << ":'" << QualType::getAsString(D_split, PrintPolicy) << "'";
366   }
367 }
368 
dumpType(QualType T)369 void TextNodeDumper::dumpType(QualType T) {
370   OS << ' ';
371   dumpBareType(T);
372 }
373 
dumpBareDeclRef(const Decl * D)374 void TextNodeDumper::dumpBareDeclRef(const Decl *D) {
375   if (!D) {
376     ColorScope Color(OS, ShowColors, NullColor);
377     OS << "<<<NULL>>>";
378     return;
379   }
380 
381   {
382     ColorScope Color(OS, ShowColors, DeclKindNameColor);
383     OS << D->getDeclKindName();
384   }
385   dumpPointer(D);
386 
387   if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) {
388     ColorScope Color(OS, ShowColors, DeclNameColor);
389     OS << " '" << ND->getDeclName() << '\'';
390   }
391 
392   if (const ValueDecl *VD = dyn_cast<ValueDecl>(D))
393     dumpType(VD->getType());
394 }
395 
dumpName(const NamedDecl * ND)396 void TextNodeDumper::dumpName(const NamedDecl *ND) {
397   if (ND->getDeclName()) {
398     ColorScope Color(OS, ShowColors, DeclNameColor);
399     OS << ' ' << ND->getNameAsString();
400   }
401 }
402 
dumpAccessSpecifier(AccessSpecifier AS)403 void TextNodeDumper::dumpAccessSpecifier(AccessSpecifier AS) {
404   switch (AS) {
405   case AS_none:
406     break;
407   case AS_public:
408     OS << "public";
409     break;
410   case AS_protected:
411     OS << "protected";
412     break;
413   case AS_private:
414     OS << "private";
415     break;
416   }
417 }
418 
dumpCXXTemporary(const CXXTemporary * Temporary)419 void TextNodeDumper::dumpCXXTemporary(const CXXTemporary *Temporary) {
420   OS << "(CXXTemporary";
421   dumpPointer(Temporary);
422   OS << ")";
423 }
424 
dumpDeclRef(const Decl * D,StringRef Label)425 void TextNodeDumper::dumpDeclRef(const Decl *D, StringRef Label) {
426   if (!D)
427     return;
428 
429   AddChild([=] {
430     if (!Label.empty())
431       OS << Label << ' ';
432     dumpBareDeclRef(D);
433   });
434 }
435 
getCommandName(unsigned CommandID)436 const char *TextNodeDumper::getCommandName(unsigned CommandID) {
437   if (Traits)
438     return Traits->getCommandInfo(CommandID)->Name;
439   const comments::CommandInfo *Info =
440       comments::CommandTraits::getBuiltinCommandInfo(CommandID);
441   if (Info)
442     return Info->Name;
443   return "<not a builtin command>";
444 }
445 
visitTextComment(const comments::TextComment * C,const comments::FullComment *)446 void TextNodeDumper::visitTextComment(const comments::TextComment *C,
447                                       const comments::FullComment *) {
448   OS << " Text=\"" << C->getText() << "\"";
449 }
450 
visitInlineCommandComment(const comments::InlineCommandComment * C,const comments::FullComment *)451 void TextNodeDumper::visitInlineCommandComment(
452     const comments::InlineCommandComment *C, const comments::FullComment *) {
453   OS << " Name=\"" << getCommandName(C->getCommandID()) << "\"";
454   switch (C->getRenderKind()) {
455   case comments::InlineCommandComment::RenderNormal:
456     OS << " RenderNormal";
457     break;
458   case comments::InlineCommandComment::RenderBold:
459     OS << " RenderBold";
460     break;
461   case comments::InlineCommandComment::RenderMonospaced:
462     OS << " RenderMonospaced";
463     break;
464   case comments::InlineCommandComment::RenderEmphasized:
465     OS << " RenderEmphasized";
466     break;
467   }
468 
469   for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i)
470     OS << " Arg[" << i << "]=\"" << C->getArgText(i) << "\"";
471 }
472 
visitHTMLStartTagComment(const comments::HTMLStartTagComment * C,const comments::FullComment *)473 void TextNodeDumper::visitHTMLStartTagComment(
474     const comments::HTMLStartTagComment *C, const comments::FullComment *) {
475   OS << " Name=\"" << C->getTagName() << "\"";
476   if (C->getNumAttrs() != 0) {
477     OS << " Attrs: ";
478     for (unsigned i = 0, e = C->getNumAttrs(); i != e; ++i) {
479       const comments::HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
480       OS << " \"" << Attr.Name << "=\"" << Attr.Value << "\"";
481     }
482   }
483   if (C->isSelfClosing())
484     OS << " SelfClosing";
485 }
486 
visitHTMLEndTagComment(const comments::HTMLEndTagComment * C,const comments::FullComment *)487 void TextNodeDumper::visitHTMLEndTagComment(
488     const comments::HTMLEndTagComment *C, const comments::FullComment *) {
489   OS << " Name=\"" << C->getTagName() << "\"";
490 }
491 
visitBlockCommandComment(const comments::BlockCommandComment * C,const comments::FullComment *)492 void TextNodeDumper::visitBlockCommandComment(
493     const comments::BlockCommandComment *C, const comments::FullComment *) {
494   OS << " Name=\"" << getCommandName(C->getCommandID()) << "\"";
495   for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i)
496     OS << " Arg[" << i << "]=\"" << C->getArgText(i) << "\"";
497 }
498 
visitParamCommandComment(const comments::ParamCommandComment * C,const comments::FullComment * FC)499 void TextNodeDumper::visitParamCommandComment(
500     const comments::ParamCommandComment *C, const comments::FullComment *FC) {
501   OS << " "
502      << comments::ParamCommandComment::getDirectionAsString(C->getDirection());
503 
504   if (C->isDirectionExplicit())
505     OS << " explicitly";
506   else
507     OS << " implicitly";
508 
509   if (C->hasParamName()) {
510     if (C->isParamIndexValid())
511       OS << " Param=\"" << C->getParamName(FC) << "\"";
512     else
513       OS << " Param=\"" << C->getParamNameAsWritten() << "\"";
514   }
515 
516   if (C->isParamIndexValid() && !C->isVarArgParam())
517     OS << " ParamIndex=" << C->getParamIndex();
518 }
519 
visitTParamCommandComment(const comments::TParamCommandComment * C,const comments::FullComment * FC)520 void TextNodeDumper::visitTParamCommandComment(
521     const comments::TParamCommandComment *C, const comments::FullComment *FC) {
522   if (C->hasParamName()) {
523     if (C->isPositionValid())
524       OS << " Param=\"" << C->getParamName(FC) << "\"";
525     else
526       OS << " Param=\"" << C->getParamNameAsWritten() << "\"";
527   }
528 
529   if (C->isPositionValid()) {
530     OS << " Position=<";
531     for (unsigned i = 0, e = C->getDepth(); i != e; ++i) {
532       OS << C->getIndex(i);
533       if (i != e - 1)
534         OS << ", ";
535     }
536     OS << ">";
537   }
538 }
539 
visitVerbatimBlockComment(const comments::VerbatimBlockComment * C,const comments::FullComment *)540 void TextNodeDumper::visitVerbatimBlockComment(
541     const comments::VerbatimBlockComment *C, const comments::FullComment *) {
542   OS << " Name=\"" << getCommandName(C->getCommandID())
543      << "\""
544         " CloseName=\""
545      << C->getCloseName() << "\"";
546 }
547 
visitVerbatimBlockLineComment(const comments::VerbatimBlockLineComment * C,const comments::FullComment *)548 void TextNodeDumper::visitVerbatimBlockLineComment(
549     const comments::VerbatimBlockLineComment *C,
550     const comments::FullComment *) {
551   OS << " Text=\"" << C->getText() << "\"";
552 }
553 
visitVerbatimLineComment(const comments::VerbatimLineComment * C,const comments::FullComment *)554 void TextNodeDumper::visitVerbatimLineComment(
555     const comments::VerbatimLineComment *C, const comments::FullComment *) {
556   OS << " Text=\"" << C->getText() << "\"";
557 }
558 
VisitNullTemplateArgument(const TemplateArgument &)559 void TextNodeDumper::VisitNullTemplateArgument(const TemplateArgument &) {
560   OS << " null";
561 }
562 
VisitTypeTemplateArgument(const TemplateArgument & TA)563 void TextNodeDumper::VisitTypeTemplateArgument(const TemplateArgument &TA) {
564   OS << " type";
565   dumpType(TA.getAsType());
566 }
567 
VisitDeclarationTemplateArgument(const TemplateArgument & TA)568 void TextNodeDumper::VisitDeclarationTemplateArgument(
569     const TemplateArgument &TA) {
570   OS << " decl";
571   dumpDeclRef(TA.getAsDecl());
572 }
573 
VisitNullPtrTemplateArgument(const TemplateArgument &)574 void TextNodeDumper::VisitNullPtrTemplateArgument(const TemplateArgument &) {
575   OS << " nullptr";
576 }
577 
VisitIntegralTemplateArgument(const TemplateArgument & TA)578 void TextNodeDumper::VisitIntegralTemplateArgument(const TemplateArgument &TA) {
579   OS << " integral " << TA.getAsIntegral();
580 }
581 
VisitTemplateTemplateArgument(const TemplateArgument & TA)582 void TextNodeDumper::VisitTemplateTemplateArgument(const TemplateArgument &TA) {
583   OS << " template ";
584   TA.getAsTemplate().dump(OS);
585 }
586 
VisitTemplateExpansionTemplateArgument(const TemplateArgument & TA)587 void TextNodeDumper::VisitTemplateExpansionTemplateArgument(
588     const TemplateArgument &TA) {
589   OS << " template expansion ";
590   TA.getAsTemplateOrTemplatePattern().dump(OS);
591 }
592 
VisitExpressionTemplateArgument(const TemplateArgument &)593 void TextNodeDumper::VisitExpressionTemplateArgument(const TemplateArgument &) {
594   OS << " expr";
595 }
596 
VisitPackTemplateArgument(const TemplateArgument &)597 void TextNodeDumper::VisitPackTemplateArgument(const TemplateArgument &) {
598   OS << " pack";
599 }
600 
dumpBasePath(raw_ostream & OS,const CastExpr * Node)601 static void dumpBasePath(raw_ostream &OS, const CastExpr *Node) {
602   if (Node->path_empty())
603     return;
604 
605   OS << " (";
606   bool First = true;
607   for (CastExpr::path_const_iterator I = Node->path_begin(),
608                                      E = Node->path_end();
609        I != E; ++I) {
610     const CXXBaseSpecifier *Base = *I;
611     if (!First)
612       OS << " -> ";
613 
614     const CXXRecordDecl *RD =
615         cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl());
616 
617     if (Base->isVirtual())
618       OS << "virtual ";
619     OS << RD->getName();
620     First = false;
621   }
622 
623   OS << ')';
624 }
625 
VisitIfStmt(const IfStmt * Node)626 void TextNodeDumper::VisitIfStmt(const IfStmt *Node) {
627   if (Node->hasInitStorage())
628     OS << " has_init";
629   if (Node->hasVarStorage())
630     OS << " has_var";
631   if (Node->hasElseStorage())
632     OS << " has_else";
633 }
634 
VisitSwitchStmt(const SwitchStmt * Node)635 void TextNodeDumper::VisitSwitchStmt(const SwitchStmt *Node) {
636   if (Node->hasInitStorage())
637     OS << " has_init";
638   if (Node->hasVarStorage())
639     OS << " has_var";
640 }
641 
VisitWhileStmt(const WhileStmt * Node)642 void TextNodeDumper::VisitWhileStmt(const WhileStmt *Node) {
643   if (Node->hasVarStorage())
644     OS << " has_var";
645 }
646 
VisitLabelStmt(const LabelStmt * Node)647 void TextNodeDumper::VisitLabelStmt(const LabelStmt *Node) {
648   OS << " '" << Node->getName() << "'";
649 }
650 
VisitGotoStmt(const GotoStmt * Node)651 void TextNodeDumper::VisitGotoStmt(const GotoStmt *Node) {
652   OS << " '" << Node->getLabel()->getName() << "'";
653   dumpPointer(Node->getLabel());
654 }
655 
VisitCaseStmt(const CaseStmt * Node)656 void TextNodeDumper::VisitCaseStmt(const CaseStmt *Node) {
657   if (Node->caseStmtIsGNURange())
658     OS << " gnu_range";
659 }
660 
VisitCallExpr(const CallExpr * Node)661 void TextNodeDumper::VisitCallExpr(const CallExpr *Node) {
662   if (Node->usesADL())
663     OS << " adl";
664 }
665 
VisitCastExpr(const CastExpr * Node)666 void TextNodeDumper::VisitCastExpr(const CastExpr *Node) {
667   OS << " <";
668   {
669     ColorScope Color(OS, ShowColors, CastColor);
670     OS << Node->getCastKindName();
671   }
672   dumpBasePath(OS, Node);
673   OS << ">";
674 }
675 
VisitImplicitCastExpr(const ImplicitCastExpr * Node)676 void TextNodeDumper::VisitImplicitCastExpr(const ImplicitCastExpr *Node) {
677   VisitCastExpr(Node);
678   if (Node->isPartOfExplicitCast())
679     OS << " part_of_explicit_cast";
680 }
681 
VisitDeclRefExpr(const DeclRefExpr * Node)682 void TextNodeDumper::VisitDeclRefExpr(const DeclRefExpr *Node) {
683   OS << " ";
684   dumpBareDeclRef(Node->getDecl());
685   if (Node->getDecl() != Node->getFoundDecl()) {
686     OS << " (";
687     dumpBareDeclRef(Node->getFoundDecl());
688     OS << ")";
689   }
690 }
691 
VisitUnresolvedLookupExpr(const UnresolvedLookupExpr * Node)692 void TextNodeDumper::VisitUnresolvedLookupExpr(
693     const UnresolvedLookupExpr *Node) {
694   OS << " (";
695   if (!Node->requiresADL())
696     OS << "no ";
697   OS << "ADL) = '" << Node->getName() << '\'';
698 
699   UnresolvedLookupExpr::decls_iterator I = Node->decls_begin(),
700                                        E = Node->decls_end();
701   if (I == E)
702     OS << " empty";
703   for (; I != E; ++I)
704     dumpPointer(*I);
705 }
706 
VisitObjCIvarRefExpr(const ObjCIvarRefExpr * Node)707 void TextNodeDumper::VisitObjCIvarRefExpr(const ObjCIvarRefExpr *Node) {
708   {
709     ColorScope Color(OS, ShowColors, DeclKindNameColor);
710     OS << " " << Node->getDecl()->getDeclKindName() << "Decl";
711   }
712   OS << "='" << *Node->getDecl() << "'";
713   dumpPointer(Node->getDecl());
714   if (Node->isFreeIvar())
715     OS << " isFreeIvar";
716 }
717 
VisitPredefinedExpr(const PredefinedExpr * Node)718 void TextNodeDumper::VisitPredefinedExpr(const PredefinedExpr *Node) {
719   OS << " " << PredefinedExpr::getIdentKindName(Node->getIdentKind());
720 }
721 
VisitCharacterLiteral(const CharacterLiteral * Node)722 void TextNodeDumper::VisitCharacterLiteral(const CharacterLiteral *Node) {
723   ColorScope Color(OS, ShowColors, ValueColor);
724   OS << " " << Node->getValue();
725 }
726 
VisitIntegerLiteral(const IntegerLiteral * Node)727 void TextNodeDumper::VisitIntegerLiteral(const IntegerLiteral *Node) {
728   bool isSigned = Node->getType()->isSignedIntegerType();
729   ColorScope Color(OS, ShowColors, ValueColor);
730   OS << " " << Node->getValue().toString(10, isSigned);
731 }
732 
VisitFixedPointLiteral(const FixedPointLiteral * Node)733 void TextNodeDumper::VisitFixedPointLiteral(const FixedPointLiteral *Node) {
734   ColorScope Color(OS, ShowColors, ValueColor);
735   OS << " " << Node->getValueAsString(/*Radix=*/10);
736 }
737 
VisitFloatingLiteral(const FloatingLiteral * Node)738 void TextNodeDumper::VisitFloatingLiteral(const FloatingLiteral *Node) {
739   ColorScope Color(OS, ShowColors, ValueColor);
740   OS << " " << Node->getValueAsApproximateDouble();
741 }
742 
VisitStringLiteral(const StringLiteral * Str)743 void TextNodeDumper::VisitStringLiteral(const StringLiteral *Str) {
744   ColorScope Color(OS, ShowColors, ValueColor);
745   OS << " ";
746   Str->outputString(OS);
747 }
748 
VisitInitListExpr(const InitListExpr * ILE)749 void TextNodeDumper::VisitInitListExpr(const InitListExpr *ILE) {
750   if (auto *Field = ILE->getInitializedFieldInUnion()) {
751     OS << " field ";
752     dumpBareDeclRef(Field);
753   }
754 }
755 
VisitUnaryOperator(const UnaryOperator * Node)756 void TextNodeDumper::VisitUnaryOperator(const UnaryOperator *Node) {
757   OS << " " << (Node->isPostfix() ? "postfix" : "prefix") << " '"
758      << UnaryOperator::getOpcodeStr(Node->getOpcode()) << "'";
759   if (!Node->canOverflow())
760     OS << " cannot overflow";
761 }
762 
VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr * Node)763 void TextNodeDumper::VisitUnaryExprOrTypeTraitExpr(
764     const UnaryExprOrTypeTraitExpr *Node) {
765   switch (Node->getKind()) {
766   case UETT_SizeOf:
767     OS << " sizeof";
768     break;
769   case UETT_AlignOf:
770     OS << " alignof";
771     break;
772   case UETT_VecStep:
773     OS << " vec_step";
774     break;
775   case UETT_OpenMPRequiredSimdAlign:
776     OS << " __builtin_omp_required_simd_align";
777     break;
778   case UETT_PreferredAlignOf:
779     OS << " __alignof";
780     break;
781   }
782   if (Node->isArgumentType())
783     dumpType(Node->getArgumentType());
784 }
785 
VisitMemberExpr(const MemberExpr * Node)786 void TextNodeDumper::VisitMemberExpr(const MemberExpr *Node) {
787   OS << " " << (Node->isArrow() ? "->" : ".") << *Node->getMemberDecl();
788   dumpPointer(Node->getMemberDecl());
789 }
790 
VisitExtVectorElementExpr(const ExtVectorElementExpr * Node)791 void TextNodeDumper::VisitExtVectorElementExpr(
792     const ExtVectorElementExpr *Node) {
793   OS << " " << Node->getAccessor().getNameStart();
794 }
795 
VisitBinaryOperator(const BinaryOperator * Node)796 void TextNodeDumper::VisitBinaryOperator(const BinaryOperator *Node) {
797   OS << " '" << BinaryOperator::getOpcodeStr(Node->getOpcode()) << "'";
798 }
799 
VisitCompoundAssignOperator(const CompoundAssignOperator * Node)800 void TextNodeDumper::VisitCompoundAssignOperator(
801     const CompoundAssignOperator *Node) {
802   OS << " '" << BinaryOperator::getOpcodeStr(Node->getOpcode())
803      << "' ComputeLHSTy=";
804   dumpBareType(Node->getComputationLHSType());
805   OS << " ComputeResultTy=";
806   dumpBareType(Node->getComputationResultType());
807 }
808 
VisitAddrLabelExpr(const AddrLabelExpr * Node)809 void TextNodeDumper::VisitAddrLabelExpr(const AddrLabelExpr *Node) {
810   OS << " " << Node->getLabel()->getName();
811   dumpPointer(Node->getLabel());
812 }
813 
VisitCXXNamedCastExpr(const CXXNamedCastExpr * Node)814 void TextNodeDumper::VisitCXXNamedCastExpr(const CXXNamedCastExpr *Node) {
815   OS << " " << Node->getCastName() << "<"
816      << Node->getTypeAsWritten().getAsString() << ">"
817      << " <" << Node->getCastKindName();
818   dumpBasePath(OS, Node);
819   OS << ">";
820 }
821 
VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr * Node)822 void TextNodeDumper::VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *Node) {
823   OS << " " << (Node->getValue() ? "true" : "false");
824 }
825 
VisitCXXThisExpr(const CXXThisExpr * Node)826 void TextNodeDumper::VisitCXXThisExpr(const CXXThisExpr *Node) {
827   OS << " this";
828 }
829 
VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr * Node)830 void TextNodeDumper::VisitCXXFunctionalCastExpr(
831     const CXXFunctionalCastExpr *Node) {
832   OS << " functional cast to " << Node->getTypeAsWritten().getAsString() << " <"
833      << Node->getCastKindName() << ">";
834 }
835 
VisitCXXUnresolvedConstructExpr(const CXXUnresolvedConstructExpr * Node)836 void TextNodeDumper::VisitCXXUnresolvedConstructExpr(
837     const CXXUnresolvedConstructExpr *Node) {
838   dumpType(Node->getTypeAsWritten());
839   if (Node->isListInitialization())
840     OS << " list";
841 }
842 
VisitCXXConstructExpr(const CXXConstructExpr * Node)843 void TextNodeDumper::VisitCXXConstructExpr(const CXXConstructExpr *Node) {
844   CXXConstructorDecl *Ctor = Node->getConstructor();
845   dumpType(Ctor->getType());
846   if (Node->isElidable())
847     OS << " elidable";
848   if (Node->isListInitialization())
849     OS << " list";
850   if (Node->isStdInitListInitialization())
851     OS << " std::initializer_list";
852   if (Node->requiresZeroInitialization())
853     OS << " zeroing";
854 }
855 
VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr * Node)856 void TextNodeDumper::VisitCXXBindTemporaryExpr(
857     const CXXBindTemporaryExpr *Node) {
858   OS << " ";
859   dumpCXXTemporary(Node->getTemporary());
860 }
861 
VisitCXXNewExpr(const CXXNewExpr * Node)862 void TextNodeDumper::VisitCXXNewExpr(const CXXNewExpr *Node) {
863   if (Node->isGlobalNew())
864     OS << " global";
865   if (Node->isArray())
866     OS << " array";
867   if (Node->getOperatorNew()) {
868     OS << ' ';
869     dumpBareDeclRef(Node->getOperatorNew());
870   }
871   // We could dump the deallocation function used in case of error, but it's
872   // usually not that interesting.
873 }
874 
VisitCXXDeleteExpr(const CXXDeleteExpr * Node)875 void TextNodeDumper::VisitCXXDeleteExpr(const CXXDeleteExpr *Node) {
876   if (Node->isGlobalDelete())
877     OS << " global";
878   if (Node->isArrayForm())
879     OS << " array";
880   if (Node->getOperatorDelete()) {
881     OS << ' ';
882     dumpBareDeclRef(Node->getOperatorDelete());
883   }
884 }
885 
VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr * Node)886 void TextNodeDumper::VisitMaterializeTemporaryExpr(
887     const MaterializeTemporaryExpr *Node) {
888   if (const ValueDecl *VD = Node->getExtendingDecl()) {
889     OS << " extended by ";
890     dumpBareDeclRef(VD);
891   }
892 }
893 
VisitExprWithCleanups(const ExprWithCleanups * Node)894 void TextNodeDumper::VisitExprWithCleanups(const ExprWithCleanups *Node) {
895   for (unsigned i = 0, e = Node->getNumObjects(); i != e; ++i)
896     dumpDeclRef(Node->getObject(i), "cleanup");
897 }
898 
VisitSizeOfPackExpr(const SizeOfPackExpr * Node)899 void TextNodeDumper::VisitSizeOfPackExpr(const SizeOfPackExpr *Node) {
900   dumpPointer(Node->getPack());
901   dumpName(Node->getPack());
902 }
903 
VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr * Node)904 void TextNodeDumper::VisitCXXDependentScopeMemberExpr(
905     const CXXDependentScopeMemberExpr *Node) {
906   OS << " " << (Node->isArrow() ? "->" : ".") << Node->getMember();
907 }
908 
VisitObjCMessageExpr(const ObjCMessageExpr * Node)909 void TextNodeDumper::VisitObjCMessageExpr(const ObjCMessageExpr *Node) {
910   OS << " selector=";
911   Node->getSelector().print(OS);
912   switch (Node->getReceiverKind()) {
913   case ObjCMessageExpr::Instance:
914     break;
915 
916   case ObjCMessageExpr::Class:
917     OS << " class=";
918     dumpBareType(Node->getClassReceiver());
919     break;
920 
921   case ObjCMessageExpr::SuperInstance:
922     OS << " super (instance)";
923     break;
924 
925   case ObjCMessageExpr::SuperClass:
926     OS << " super (class)";
927     break;
928   }
929 }
930 
VisitObjCBoxedExpr(const ObjCBoxedExpr * Node)931 void TextNodeDumper::VisitObjCBoxedExpr(const ObjCBoxedExpr *Node) {
932   if (auto *BoxingMethod = Node->getBoxingMethod()) {
933     OS << " selector=";
934     BoxingMethod->getSelector().print(OS);
935   }
936 }
937 
VisitObjCAtCatchStmt(const ObjCAtCatchStmt * Node)938 void TextNodeDumper::VisitObjCAtCatchStmt(const ObjCAtCatchStmt *Node) {
939   if (!Node->getCatchParamDecl())
940     OS << " catch all";
941 }
942 
VisitObjCEncodeExpr(const ObjCEncodeExpr * Node)943 void TextNodeDumper::VisitObjCEncodeExpr(const ObjCEncodeExpr *Node) {
944   dumpType(Node->getEncodedType());
945 }
946 
VisitObjCSelectorExpr(const ObjCSelectorExpr * Node)947 void TextNodeDumper::VisitObjCSelectorExpr(const ObjCSelectorExpr *Node) {
948   OS << " ";
949   Node->getSelector().print(OS);
950 }
951 
VisitObjCProtocolExpr(const ObjCProtocolExpr * Node)952 void TextNodeDumper::VisitObjCProtocolExpr(const ObjCProtocolExpr *Node) {
953   OS << ' ' << *Node->getProtocol();
954 }
955 
VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr * Node)956 void TextNodeDumper::VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *Node) {
957   if (Node->isImplicitProperty()) {
958     OS << " Kind=MethodRef Getter=\"";
959     if (Node->getImplicitPropertyGetter())
960       Node->getImplicitPropertyGetter()->getSelector().print(OS);
961     else
962       OS << "(null)";
963 
964     OS << "\" Setter=\"";
965     if (ObjCMethodDecl *Setter = Node->getImplicitPropertySetter())
966       Setter->getSelector().print(OS);
967     else
968       OS << "(null)";
969     OS << "\"";
970   } else {
971     OS << " Kind=PropertyRef Property=\"" << *Node->getExplicitProperty()
972        << '"';
973   }
974 
975   if (Node->isSuperReceiver())
976     OS << " super";
977 
978   OS << " Messaging=";
979   if (Node->isMessagingGetter() && Node->isMessagingSetter())
980     OS << "Getter&Setter";
981   else if (Node->isMessagingGetter())
982     OS << "Getter";
983   else if (Node->isMessagingSetter())
984     OS << "Setter";
985 }
986 
VisitObjCSubscriptRefExpr(const ObjCSubscriptRefExpr * Node)987 void TextNodeDumper::VisitObjCSubscriptRefExpr(
988     const ObjCSubscriptRefExpr *Node) {
989   if (Node->isArraySubscriptRefExpr())
990     OS << " Kind=ArraySubscript GetterForArray=\"";
991   else
992     OS << " Kind=DictionarySubscript GetterForDictionary=\"";
993   if (Node->getAtIndexMethodDecl())
994     Node->getAtIndexMethodDecl()->getSelector().print(OS);
995   else
996     OS << "(null)";
997 
998   if (Node->isArraySubscriptRefExpr())
999     OS << "\" SetterForArray=\"";
1000   else
1001     OS << "\" SetterForDictionary=\"";
1002   if (Node->setAtIndexMethodDecl())
1003     Node->setAtIndexMethodDecl()->getSelector().print(OS);
1004   else
1005     OS << "(null)";
1006 }
1007 
VisitObjCBoolLiteralExpr(const ObjCBoolLiteralExpr * Node)1008 void TextNodeDumper::VisitObjCBoolLiteralExpr(const ObjCBoolLiteralExpr *Node) {
1009   OS << " " << (Node->getValue() ? "__objc_yes" : "__objc_no");
1010 }
1011 
VisitRValueReferenceType(const ReferenceType * T)1012 void TextNodeDumper::VisitRValueReferenceType(const ReferenceType *T) {
1013   if (T->isSpelledAsLValue())
1014     OS << " written as lvalue reference";
1015 }
1016 
VisitArrayType(const ArrayType * T)1017 void TextNodeDumper::VisitArrayType(const ArrayType *T) {
1018   switch (T->getSizeModifier()) {
1019   case ArrayType::Normal:
1020     break;
1021   case ArrayType::Static:
1022     OS << " static";
1023     break;
1024   case ArrayType::Star:
1025     OS << " *";
1026     break;
1027   }
1028   OS << " " << T->getIndexTypeQualifiers().getAsString();
1029 }
1030 
VisitConstantArrayType(const ConstantArrayType * T)1031 void TextNodeDumper::VisitConstantArrayType(const ConstantArrayType *T) {
1032   OS << " " << T->getSize();
1033   VisitArrayType(T);
1034 }
1035 
VisitVariableArrayType(const VariableArrayType * T)1036 void TextNodeDumper::VisitVariableArrayType(const VariableArrayType *T) {
1037   OS << " ";
1038   dumpSourceRange(T->getBracketsRange());
1039   VisitArrayType(T);
1040 }
1041 
VisitDependentSizedArrayType(const DependentSizedArrayType * T)1042 void TextNodeDumper::VisitDependentSizedArrayType(
1043     const DependentSizedArrayType *T) {
1044   VisitArrayType(T);
1045   OS << " ";
1046   dumpSourceRange(T->getBracketsRange());
1047 }
1048 
VisitDependentSizedExtVectorType(const DependentSizedExtVectorType * T)1049 void TextNodeDumper::VisitDependentSizedExtVectorType(
1050     const DependentSizedExtVectorType *T) {
1051   OS << " ";
1052   dumpLocation(T->getAttributeLoc());
1053 }
1054 
VisitVectorType(const VectorType * T)1055 void TextNodeDumper::VisitVectorType(const VectorType *T) {
1056   switch (T->getVectorKind()) {
1057   case VectorType::GenericVector:
1058     break;
1059   case VectorType::AltiVecVector:
1060     OS << " altivec";
1061     break;
1062   case VectorType::AltiVecPixel:
1063     OS << " altivec pixel";
1064     break;
1065   case VectorType::AltiVecBool:
1066     OS << " altivec bool";
1067     break;
1068   case VectorType::NeonVector:
1069     OS << " neon";
1070     break;
1071   case VectorType::NeonPolyVector:
1072     OS << " neon poly";
1073     break;
1074   }
1075   OS << " " << T->getNumElements();
1076 }
1077 
VisitFunctionType(const FunctionType * T)1078 void TextNodeDumper::VisitFunctionType(const FunctionType *T) {
1079   auto EI = T->getExtInfo();
1080   if (EI.getNoReturn())
1081     OS << " noreturn";
1082   if (EI.getProducesResult())
1083     OS << " produces_result";
1084   if (EI.getHasRegParm())
1085     OS << " regparm " << EI.getRegParm();
1086   OS << " " << FunctionType::getNameForCallConv(EI.getCC());
1087 }
1088 
VisitFunctionProtoType(const FunctionProtoType * T)1089 void TextNodeDumper::VisitFunctionProtoType(const FunctionProtoType *T) {
1090   auto EPI = T->getExtProtoInfo();
1091   if (EPI.HasTrailingReturn)
1092     OS << " trailing_return";
1093   if (T->isConst())
1094     OS << " const";
1095   if (T->isVolatile())
1096     OS << " volatile";
1097   if (T->isRestrict())
1098     OS << " restrict";
1099   switch (EPI.RefQualifier) {
1100   case RQ_None:
1101     break;
1102   case RQ_LValue:
1103     OS << " &";
1104     break;
1105   case RQ_RValue:
1106     OS << " &&";
1107     break;
1108   }
1109   // FIXME: Exception specification.
1110   // FIXME: Consumed parameters.
1111   VisitFunctionType(T);
1112 }
1113 
VisitUnresolvedUsingType(const UnresolvedUsingType * T)1114 void TextNodeDumper::VisitUnresolvedUsingType(const UnresolvedUsingType *T) {
1115   dumpDeclRef(T->getDecl());
1116 }
1117 
VisitTypedefType(const TypedefType * T)1118 void TextNodeDumper::VisitTypedefType(const TypedefType *T) {
1119   dumpDeclRef(T->getDecl());
1120 }
1121 
VisitUnaryTransformType(const UnaryTransformType * T)1122 void TextNodeDumper::VisitUnaryTransformType(const UnaryTransformType *T) {
1123   switch (T->getUTTKind()) {
1124   case UnaryTransformType::EnumUnderlyingType:
1125     OS << " underlying_type";
1126     break;
1127   }
1128 }
1129 
VisitTagType(const TagType * T)1130 void TextNodeDumper::VisitTagType(const TagType *T) {
1131   dumpDeclRef(T->getDecl());
1132 }
1133 
VisitTemplateTypeParmType(const TemplateTypeParmType * T)1134 void TextNodeDumper::VisitTemplateTypeParmType(const TemplateTypeParmType *T) {
1135   OS << " depth " << T->getDepth() << " index " << T->getIndex();
1136   if (T->isParameterPack())
1137     OS << " pack";
1138   dumpDeclRef(T->getDecl());
1139 }
1140 
VisitAutoType(const AutoType * T)1141 void TextNodeDumper::VisitAutoType(const AutoType *T) {
1142   if (T->isDecltypeAuto())
1143     OS << " decltype(auto)";
1144   if (!T->isDeduced())
1145     OS << " undeduced";
1146 }
1147 
VisitTemplateSpecializationType(const TemplateSpecializationType * T)1148 void TextNodeDumper::VisitTemplateSpecializationType(
1149     const TemplateSpecializationType *T) {
1150   if (T->isTypeAlias())
1151     OS << " alias";
1152   OS << " ";
1153   T->getTemplateName().dump(OS);
1154 }
1155 
VisitInjectedClassNameType(const InjectedClassNameType * T)1156 void TextNodeDumper::VisitInjectedClassNameType(
1157     const InjectedClassNameType *T) {
1158   dumpDeclRef(T->getDecl());
1159 }
1160 
VisitObjCInterfaceType(const ObjCInterfaceType * T)1161 void TextNodeDumper::VisitObjCInterfaceType(const ObjCInterfaceType *T) {
1162   dumpDeclRef(T->getDecl());
1163 }
1164 
VisitPackExpansionType(const PackExpansionType * T)1165 void TextNodeDumper::VisitPackExpansionType(const PackExpansionType *T) {
1166   if (auto N = T->getNumExpansions())
1167     OS << " expansions " << *N;
1168 }
1169