1 | //===--- JSONNodeDumper.h - Printing of AST nodes to JSON -----------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file implements AST dumping of components of individual AST nodes to |
10 | // a JSON. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #ifndef LLVM_CLANG_AST_JSONNODEDUMPER_H |
15 | #define LLVM_CLANG_AST_JSONNODEDUMPER_H |
16 | |
17 | #include "clang/AST/ASTContext.h" |
18 | #include "clang/AST/ASTDumperUtils.h" |
19 | #include "clang/AST/ASTNodeTraverser.h" |
20 | #include "clang/AST/AttrVisitor.h" |
21 | #include "clang/AST/CommentCommandTraits.h" |
22 | #include "clang/AST/CommentVisitor.h" |
23 | #include "clang/AST/ExprConcepts.h" |
24 | #include "clang/AST/ExprCXX.h" |
25 | #include "clang/AST/Mangle.h" |
26 | #include "clang/AST/Type.h" |
27 | #include "llvm/Support/JSON.h" |
28 | |
29 | namespace clang { |
30 | |
31 | class APValue; |
32 | |
33 | class NodeStreamer { |
34 | bool FirstChild = true; |
35 | bool TopLevel = true; |
36 | llvm::SmallVector<std::function<void(bool IsLastChild)>, 32> Pending; |
37 | |
38 | protected: |
39 | llvm::json::OStream JOS; |
40 | |
41 | public: |
42 | /// Add a child of the current node. Calls DoAddChild without arguments |
43 | template <typename Fn> void AddChild(Fn DoAddChild) { |
44 | return AddChild("" , DoAddChild); |
45 | } |
46 | |
47 | /// Add a child of the current node with an optional label. |
48 | /// Calls DoAddChild without arguments. |
49 | template <typename Fn> void AddChild(StringRef Label, Fn DoAddChild) { |
50 | // If we're at the top level, there's nothing interesting to do; just |
51 | // run the dumper. |
52 | if (TopLevel) { |
53 | TopLevel = false; |
54 | JOS.objectBegin(); |
55 | |
56 | DoAddChild(); |
57 | |
58 | while (!Pending.empty()) { |
59 | Pending.back()(true); |
60 | Pending.pop_back(); |
61 | } |
62 | |
63 | JOS.objectEnd(); |
64 | TopLevel = true; |
65 | return; |
66 | } |
67 | |
68 | // We need to capture an owning-string in the lambda because the lambda |
69 | // is invoked in a deferred manner. |
70 | std::string LabelStr(!Label.empty() ? Label : "inner" ); |
71 | bool WasFirstChild = FirstChild; |
72 | auto DumpWithIndent = [=](bool IsLastChild) { |
73 | if (WasFirstChild) { |
74 | JOS.attributeBegin(Key: LabelStr); |
75 | JOS.arrayBegin(); |
76 | } |
77 | |
78 | FirstChild = true; |
79 | unsigned Depth = Pending.size(); |
80 | JOS.objectBegin(); |
81 | |
82 | DoAddChild(); |
83 | |
84 | // If any children are left, they're the last at their nesting level. |
85 | // Dump those ones out now. |
86 | while (Depth < Pending.size()) { |
87 | Pending.back()(true); |
88 | this->Pending.pop_back(); |
89 | } |
90 | |
91 | JOS.objectEnd(); |
92 | |
93 | if (IsLastChild) { |
94 | JOS.arrayEnd(); |
95 | JOS.attributeEnd(); |
96 | } |
97 | }; |
98 | |
99 | if (FirstChild) { |
100 | Pending.push_back(std::move(DumpWithIndent)); |
101 | } else { |
102 | Pending.back()(false); |
103 | Pending.back() = std::move(DumpWithIndent); |
104 | } |
105 | FirstChild = false; |
106 | } |
107 | |
108 | NodeStreamer(raw_ostream &OS) : JOS(OS, 2) {} |
109 | }; |
110 | |
111 | // Dumps AST nodes in JSON format. There is no implied stability for the |
112 | // content or format of the dump between major releases of Clang, other than it |
113 | // being valid JSON output. Further, there is no requirement that the |
114 | // information dumped is a complete representation of the AST, only that the |
115 | // information presented is correct. |
116 | class JSONNodeDumper |
117 | : public ConstAttrVisitor<JSONNodeDumper>, |
118 | public comments::ConstCommentVisitor<JSONNodeDumper, void, |
119 | const comments::FullComment *>, |
120 | public ConstTemplateArgumentVisitor<JSONNodeDumper>, |
121 | public ConstStmtVisitor<JSONNodeDumper>, |
122 | public TypeVisitor<JSONNodeDumper>, |
123 | public ConstDeclVisitor<JSONNodeDumper>, |
124 | public NodeStreamer { |
125 | friend class JSONDumper; |
126 | |
127 | const SourceManager &SM; |
128 | ASTContext& Ctx; |
129 | ASTNameGenerator ASTNameGen; |
130 | PrintingPolicy PrintPolicy; |
131 | const comments::CommandTraits *Traits; |
132 | StringRef LastLocFilename, LastLocPresumedFilename; |
133 | unsigned LastLocLine, LastLocPresumedLine; |
134 | |
135 | using InnerAttrVisitor = ConstAttrVisitor<JSONNodeDumper>; |
136 | using = |
137 | comments::ConstCommentVisitor<JSONNodeDumper, void, |
138 | const comments::FullComment *>; |
139 | using InnerTemplateArgVisitor = ConstTemplateArgumentVisitor<JSONNodeDumper>; |
140 | using InnerStmtVisitor = ConstStmtVisitor<JSONNodeDumper>; |
141 | using InnerTypeVisitor = TypeVisitor<JSONNodeDumper>; |
142 | using InnerDeclVisitor = ConstDeclVisitor<JSONNodeDumper>; |
143 | |
144 | void attributeOnlyIfTrue(StringRef Key, bool Value) { |
145 | if (Value) |
146 | JOS.attribute(Key, Contents: Value); |
147 | } |
148 | |
149 | void writeIncludeStack(PresumedLoc Loc, bool JustFirst = false); |
150 | |
151 | // Writes the attributes of a SourceLocation object without. |
152 | void writeBareSourceLocation(SourceLocation Loc, bool IsSpelling); |
153 | |
154 | // Writes the attributes of a SourceLocation to JSON based on its presumed |
155 | // spelling location. If the given location represents a macro invocation, |
156 | // this outputs two sub-objects: one for the spelling and one for the |
157 | // expansion location. |
158 | void writeSourceLocation(SourceLocation Loc); |
159 | void writeSourceRange(SourceRange R); |
160 | std::string createPointerRepresentation(const void *Ptr); |
161 | llvm::json::Object createQualType(QualType QT, bool Desugar = true); |
162 | llvm::json::Object createBareDeclRef(const Decl *D); |
163 | llvm::json::Object createFPOptions(FPOptionsOverride FPO); |
164 | void writeBareDeclRef(const Decl *D); |
165 | llvm::json::Object createCXXRecordDefinitionData(const CXXRecordDecl *RD); |
166 | llvm::json::Object createCXXBaseSpecifier(const CXXBaseSpecifier &BS); |
167 | std::string createAccessSpecifier(AccessSpecifier AS); |
168 | llvm::json::Array createCastPath(const CastExpr *C); |
169 | |
170 | void writePreviousDeclImpl(...) {} |
171 | |
172 | template <typename T> void writePreviousDeclImpl(const Mergeable<T> *D) { |
173 | const T *First = D->getFirstDecl(); |
174 | if (First != D) |
175 | JOS.attribute(Key: "firstRedecl" , Contents: createPointerRepresentation(Ptr: First)); |
176 | } |
177 | |
178 | template <typename T> void writePreviousDeclImpl(const Redeclarable<T> *D) { |
179 | const T *Prev = D->getPreviousDecl(); |
180 | if (Prev) |
181 | JOS.attribute(Key: "previousDecl" , Contents: createPointerRepresentation(Ptr: Prev)); |
182 | } |
183 | void addPreviousDeclaration(const Decl *D); |
184 | |
185 | StringRef getCommentCommandName(unsigned CommandID) const; |
186 | |
187 | public: |
188 | JSONNodeDumper(raw_ostream &OS, const SourceManager &SrcMgr, ASTContext &Ctx, |
189 | const PrintingPolicy &PrintPolicy, |
190 | const comments::CommandTraits *Traits) |
191 | : NodeStreamer(OS), SM(SrcMgr), Ctx(Ctx), ASTNameGen(Ctx), |
192 | PrintPolicy(PrintPolicy), Traits(Traits), LastLocLine(0), |
193 | LastLocPresumedLine(0) {} |
194 | |
195 | void Visit(const Attr *A); |
196 | void Visit(const Stmt *Node); |
197 | void Visit(const Type *T); |
198 | void Visit(QualType T); |
199 | void Visit(const Decl *D); |
200 | void Visit(TypeLoc TL); |
201 | |
202 | void (const comments::Comment *C, const comments::FullComment *FC); |
203 | void Visit(const TemplateArgument &TA, SourceRange R = {}, |
204 | const Decl *From = nullptr, StringRef Label = {}); |
205 | void Visit(const CXXCtorInitializer *Init); |
206 | void Visit(const OpenACCClause *C); |
207 | void Visit(const OMPClause *C); |
208 | void Visit(const BlockDecl::Capture &C); |
209 | void Visit(const GenericSelectionExpr::ConstAssociation &A); |
210 | void Visit(const concepts::Requirement *R); |
211 | void Visit(const APValue &Value, QualType Ty); |
212 | void Visit(const ConceptReference *); |
213 | |
214 | void VisitAliasAttr(const AliasAttr *AA); |
215 | void VisitCleanupAttr(const CleanupAttr *CA); |
216 | void VisitDeprecatedAttr(const DeprecatedAttr *DA); |
217 | void VisitUnavailableAttr(const UnavailableAttr *UA); |
218 | void VisitSectionAttr(const SectionAttr *SA); |
219 | void VisitVisibilityAttr(const VisibilityAttr *VA); |
220 | void VisitTLSModelAttr(const TLSModelAttr *TA); |
221 | |
222 | void VisitTypedefType(const TypedefType *TT); |
223 | void VisitUsingType(const UsingType *TT); |
224 | void VisitFunctionType(const FunctionType *T); |
225 | void VisitFunctionProtoType(const FunctionProtoType *T); |
226 | void VisitRValueReferenceType(const ReferenceType *RT); |
227 | void VisitArrayType(const ArrayType *AT); |
228 | void VisitConstantArrayType(const ConstantArrayType *CAT); |
229 | void VisitDependentSizedExtVectorType(const DependentSizedExtVectorType *VT); |
230 | void VisitVectorType(const VectorType *VT); |
231 | void VisitUnresolvedUsingType(const UnresolvedUsingType *UUT); |
232 | void VisitUnaryTransformType(const UnaryTransformType *UTT); |
233 | void VisitTagType(const TagType *TT); |
234 | void VisitTemplateTypeParmType(const TemplateTypeParmType *TTPT); |
235 | void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *STTPT); |
236 | void |
237 | VisitSubstTemplateTypeParmPackType(const SubstTemplateTypeParmPackType *T); |
238 | void VisitAutoType(const AutoType *AT); |
239 | void VisitTemplateSpecializationType(const TemplateSpecializationType *TST); |
240 | void VisitInjectedClassNameType(const InjectedClassNameType *ICNT); |
241 | void VisitObjCInterfaceType(const ObjCInterfaceType *OIT); |
242 | void VisitPackExpansionType(const PackExpansionType *PET); |
243 | void VisitElaboratedType(const ElaboratedType *ET); |
244 | void VisitMacroQualifiedType(const MacroQualifiedType *MQT); |
245 | void VisitMemberPointerType(const MemberPointerType *MPT); |
246 | |
247 | void VisitNamedDecl(const NamedDecl *ND); |
248 | void VisitTypedefDecl(const TypedefDecl *TD); |
249 | void VisitTypeAliasDecl(const TypeAliasDecl *TAD); |
250 | void VisitNamespaceDecl(const NamespaceDecl *ND); |
251 | void VisitUsingDirectiveDecl(const UsingDirectiveDecl *UDD); |
252 | void VisitNamespaceAliasDecl(const NamespaceAliasDecl *NAD); |
253 | void VisitUsingDecl(const UsingDecl *UD); |
254 | void VisitUsingEnumDecl(const UsingEnumDecl *UED); |
255 | void VisitUsingShadowDecl(const UsingShadowDecl *USD); |
256 | void VisitVarDecl(const VarDecl *VD); |
257 | void VisitFieldDecl(const FieldDecl *FD); |
258 | void VisitFunctionDecl(const FunctionDecl *FD); |
259 | void VisitEnumDecl(const EnumDecl *ED); |
260 | void VisitEnumConstantDecl(const EnumConstantDecl *ECD); |
261 | void VisitRecordDecl(const RecordDecl *RD); |
262 | void VisitCXXRecordDecl(const CXXRecordDecl *RD); |
263 | void VisitHLSLBufferDecl(const HLSLBufferDecl *D); |
264 | void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D); |
265 | void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D); |
266 | void VisitTemplateTemplateParmDecl(const TemplateTemplateParmDecl *D); |
267 | void VisitLinkageSpecDecl(const LinkageSpecDecl *LSD); |
268 | void VisitAccessSpecDecl(const AccessSpecDecl *ASD); |
269 | void VisitFriendDecl(const FriendDecl *FD); |
270 | |
271 | void VisitObjCIvarDecl(const ObjCIvarDecl *D); |
272 | void VisitObjCMethodDecl(const ObjCMethodDecl *D); |
273 | void VisitObjCTypeParamDecl(const ObjCTypeParamDecl *D); |
274 | void VisitObjCCategoryDecl(const ObjCCategoryDecl *D); |
275 | void VisitObjCCategoryImplDecl(const ObjCCategoryImplDecl *D); |
276 | void VisitObjCProtocolDecl(const ObjCProtocolDecl *D); |
277 | void VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D); |
278 | void VisitObjCImplementationDecl(const ObjCImplementationDecl *D); |
279 | void VisitObjCCompatibleAliasDecl(const ObjCCompatibleAliasDecl *D); |
280 | void VisitObjCPropertyDecl(const ObjCPropertyDecl *D); |
281 | void VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D); |
282 | void VisitBlockDecl(const BlockDecl *D); |
283 | |
284 | void VisitDeclRefExpr(const DeclRefExpr *DRE); |
285 | void VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *E); |
286 | void VisitPredefinedExpr(const PredefinedExpr *PE); |
287 | void VisitUnaryOperator(const UnaryOperator *UO); |
288 | void VisitBinaryOperator(const BinaryOperator *BO); |
289 | void VisitCompoundAssignOperator(const CompoundAssignOperator *CAO); |
290 | void VisitMemberExpr(const MemberExpr *ME); |
291 | void VisitAtomicExpr(const AtomicExpr *AE); |
292 | void VisitCXXNewExpr(const CXXNewExpr *NE); |
293 | void VisitCXXDeleteExpr(const CXXDeleteExpr *DE); |
294 | void VisitCXXThisExpr(const CXXThisExpr *TE); |
295 | void VisitCastExpr(const CastExpr *CE); |
296 | void VisitImplicitCastExpr(const ImplicitCastExpr *ICE); |
297 | void VisitCallExpr(const CallExpr *CE); |
298 | void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *TTE); |
299 | void VisitSizeOfPackExpr(const SizeOfPackExpr *SOPE); |
300 | void VisitUnresolvedLookupExpr(const UnresolvedLookupExpr *ULE); |
301 | void VisitAddrLabelExpr(const AddrLabelExpr *ALE); |
302 | void VisitCXXTypeidExpr(const CXXTypeidExpr *CTE); |
303 | void VisitConstantExpr(const ConstantExpr *CE); |
304 | void VisitInitListExpr(const InitListExpr *ILE); |
305 | void VisitGenericSelectionExpr(const GenericSelectionExpr *GSE); |
306 | void VisitCXXUnresolvedConstructExpr(const CXXUnresolvedConstructExpr *UCE); |
307 | void VisitCXXConstructExpr(const CXXConstructExpr *CE); |
308 | void VisitExprWithCleanups(const ExprWithCleanups *EWC); |
309 | void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE); |
310 | void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE); |
311 | void VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr *ME); |
312 | void VisitRequiresExpr(const RequiresExpr *RE); |
313 | void VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *Node); |
314 | void VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *Node); |
315 | |
316 | void VisitObjCEncodeExpr(const ObjCEncodeExpr *OEE); |
317 | void VisitObjCMessageExpr(const ObjCMessageExpr *OME); |
318 | void VisitObjCBoxedExpr(const ObjCBoxedExpr *OBE); |
319 | void VisitObjCSelectorExpr(const ObjCSelectorExpr *OSE); |
320 | void VisitObjCProtocolExpr(const ObjCProtocolExpr *OPE); |
321 | void VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *OPRE); |
322 | void VisitObjCSubscriptRefExpr(const ObjCSubscriptRefExpr *OSRE); |
323 | void VisitObjCIvarRefExpr(const ObjCIvarRefExpr *OIRE); |
324 | void VisitObjCBoolLiteralExpr(const ObjCBoolLiteralExpr *OBLE); |
325 | |
326 | void VisitIntegerLiteral(const IntegerLiteral *IL); |
327 | void VisitCharacterLiteral(const CharacterLiteral *CL); |
328 | void VisitFixedPointLiteral(const FixedPointLiteral *FPL); |
329 | void VisitFloatingLiteral(const FloatingLiteral *FL); |
330 | void VisitStringLiteral(const StringLiteral *SL); |
331 | void VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *BLE); |
332 | |
333 | void VisitIfStmt(const IfStmt *IS); |
334 | void VisitSwitchStmt(const SwitchStmt *SS); |
335 | void VisitCaseStmt(const CaseStmt *CS); |
336 | void VisitLabelStmt(const LabelStmt *LS); |
337 | void VisitGotoStmt(const GotoStmt *GS); |
338 | void VisitWhileStmt(const WhileStmt *WS); |
339 | void VisitObjCAtCatchStmt(const ObjCAtCatchStmt *OACS); |
340 | void VisitCompoundStmt(const CompoundStmt *IS); |
341 | |
342 | void VisitNullTemplateArgument(const TemplateArgument &TA); |
343 | void VisitTypeTemplateArgument(const TemplateArgument &TA); |
344 | void VisitDeclarationTemplateArgument(const TemplateArgument &TA); |
345 | void VisitNullPtrTemplateArgument(const TemplateArgument &TA); |
346 | void VisitIntegralTemplateArgument(const TemplateArgument &TA); |
347 | void VisitTemplateTemplateArgument(const TemplateArgument &TA); |
348 | void VisitTemplateExpansionTemplateArgument(const TemplateArgument &TA); |
349 | void VisitExpressionTemplateArgument(const TemplateArgument &TA); |
350 | void VisitPackTemplateArgument(const TemplateArgument &TA); |
351 | |
352 | void (const comments::TextComment *C, |
353 | const comments::FullComment *); |
354 | void visitInlineCommandComment(const comments::InlineCommandComment *C, |
355 | const comments::FullComment *); |
356 | void (const comments::HTMLStartTagComment *C, |
357 | const comments::FullComment *); |
358 | void (const comments::HTMLEndTagComment *C, |
359 | const comments::FullComment *); |
360 | void visitBlockCommandComment(const comments::BlockCommandComment *C, |
361 | const comments::FullComment *); |
362 | void visitParamCommandComment(const comments::ParamCommandComment *C, |
363 | const comments::FullComment *FC); |
364 | void visitTParamCommandComment(const comments::TParamCommandComment *C, |
365 | const comments::FullComment *FC); |
366 | void (const comments::VerbatimBlockComment *C, |
367 | const comments::FullComment *); |
368 | void |
369 | (const comments::VerbatimBlockLineComment *C, |
370 | const comments::FullComment *); |
371 | void (const comments::VerbatimLineComment *C, |
372 | const comments::FullComment *); |
373 | }; |
374 | |
375 | class JSONDumper : public ASTNodeTraverser<JSONDumper, JSONNodeDumper> { |
376 | JSONNodeDumper NodeDumper; |
377 | |
378 | template <typename SpecializationDecl> |
379 | void writeTemplateDeclSpecialization(const SpecializationDecl *SD, |
380 | bool DumpExplicitInst, |
381 | bool DumpRefOnly) { |
382 | bool DumpedAny = false; |
383 | for (const auto *RedeclWithBadType : SD->redecls()) { |
384 | // FIXME: The redecls() range sometimes has elements of a less-specific |
385 | // type. (In particular, ClassTemplateSpecializationDecl::redecls() gives |
386 | // us TagDecls, and should give CXXRecordDecls). |
387 | const auto *Redecl = dyn_cast<SpecializationDecl>(RedeclWithBadType); |
388 | if (!Redecl) { |
389 | // Found the injected-class-name for a class template. This will be |
390 | // dumped as part of its surrounding class so we don't need to dump it |
391 | // here. |
392 | assert(isa<CXXRecordDecl>(RedeclWithBadType) && |
393 | "expected an injected-class-name" ); |
394 | continue; |
395 | } |
396 | |
397 | switch (Redecl->getTemplateSpecializationKind()) { |
398 | case TSK_ExplicitInstantiationDeclaration: |
399 | case TSK_ExplicitInstantiationDefinition: |
400 | if (!DumpExplicitInst) |
401 | break; |
402 | [[fallthrough]]; |
403 | case TSK_Undeclared: |
404 | case TSK_ImplicitInstantiation: |
405 | if (DumpRefOnly) |
406 | NodeDumper.AddChild([=] { NodeDumper.writeBareDeclRef(D: Redecl); }); |
407 | else |
408 | Visit(Redecl); |
409 | DumpedAny = true; |
410 | break; |
411 | case TSK_ExplicitSpecialization: |
412 | break; |
413 | } |
414 | } |
415 | |
416 | // Ensure we dump at least one decl for each specialization. |
417 | if (!DumpedAny) |
418 | NodeDumper.AddChild([=] { NodeDumper.writeBareDeclRef(D: SD); }); |
419 | } |
420 | |
421 | template <typename TemplateDecl> |
422 | void writeTemplateDecl(const TemplateDecl *TD, bool DumpExplicitInst) { |
423 | // FIXME: it would be nice to dump template parameters and specializations |
424 | // to their own named arrays rather than shoving them into the "inner" |
425 | // array. However, template declarations are currently being handled at the |
426 | // wrong "level" of the traversal hierarchy and so it is difficult to |
427 | // achieve without losing information elsewhere. |
428 | |
429 | dumpTemplateParameters(TPL: TD->getTemplateParameters()); |
430 | |
431 | Visit(TD->getTemplatedDecl()); |
432 | |
433 | for (const auto *Child : TD->specializations()) |
434 | writeTemplateDeclSpecialization(Child, DumpExplicitInst, |
435 | !TD->isCanonicalDecl()); |
436 | } |
437 | |
438 | public: |
439 | JSONDumper(raw_ostream &OS, const SourceManager &SrcMgr, ASTContext &Ctx, |
440 | const PrintingPolicy &PrintPolicy, |
441 | const comments::CommandTraits *Traits) |
442 | : NodeDumper(OS, SrcMgr, Ctx, PrintPolicy, Traits) {} |
443 | |
444 | JSONNodeDumper &doGetNodeDelegate() { return NodeDumper; } |
445 | |
446 | void VisitFunctionTemplateDecl(const FunctionTemplateDecl *FTD) { |
447 | writeTemplateDecl(TD: FTD, DumpExplicitInst: true); |
448 | } |
449 | void VisitClassTemplateDecl(const ClassTemplateDecl *CTD) { |
450 | writeTemplateDecl(TD: CTD, DumpExplicitInst: false); |
451 | } |
452 | void VisitVarTemplateDecl(const VarTemplateDecl *VTD) { |
453 | writeTemplateDecl(TD: VTD, DumpExplicitInst: false); |
454 | } |
455 | }; |
456 | |
457 | } // namespace clang |
458 | |
459 | #endif // LLVM_CLANG_AST_JSONNODEDUMPER_H |
460 | |