xref: /openbsd-src/gnu/llvm/clang/lib/AST/Comment.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
1e5dd7070Spatrick //===--- Comment.cpp - Comment AST node implementation --------------------===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick 
9e5dd7070Spatrick #include "clang/AST/Comment.h"
10e5dd7070Spatrick #include "clang/AST/ASTContext.h"
11e5dd7070Spatrick #include "clang/AST/Decl.h"
12e5dd7070Spatrick #include "clang/AST/DeclObjC.h"
13e5dd7070Spatrick #include "clang/AST/DeclTemplate.h"
14e5dd7070Spatrick #include "clang/Basic/CharInfo.h"
15e5dd7070Spatrick #include "llvm/Support/ErrorHandling.h"
16e5dd7070Spatrick #include <type_traits>
17e5dd7070Spatrick 
18e5dd7070Spatrick namespace clang {
19e5dd7070Spatrick namespace comments {
20e5dd7070Spatrick 
21e5dd7070Spatrick // Check that no comment class has a non-trival destructor. They are allocated
22e5dd7070Spatrick // with a BumpPtrAllocator and therefore their destructor is not executed.
23e5dd7070Spatrick #define ABSTRACT_COMMENT(COMMENT)
24e5dd7070Spatrick #define COMMENT(CLASS, PARENT)                                                 \
25e5dd7070Spatrick   static_assert(std::is_trivially_destructible<CLASS>::value,                  \
26e5dd7070Spatrick                 #CLASS " should be trivially destructible!");
27e5dd7070Spatrick #include "clang/AST/CommentNodes.inc"
28e5dd7070Spatrick #undef COMMENT
29e5dd7070Spatrick #undef ABSTRACT_COMMENT
30e5dd7070Spatrick 
31e5dd7070Spatrick // DeclInfo is also allocated with a BumpPtrAllocator.
32*12c85518Srobert static_assert(std::is_trivially_destructible_v<DeclInfo>,
33e5dd7070Spatrick               "DeclInfo should be trivially destructible!");
34e5dd7070Spatrick 
getCommentKindName() const35e5dd7070Spatrick const char *Comment::getCommentKindName() const {
36e5dd7070Spatrick   switch (getCommentKind()) {
37e5dd7070Spatrick   case NoCommentKind: return "NoCommentKind";
38e5dd7070Spatrick #define ABSTRACT_COMMENT(COMMENT)
39e5dd7070Spatrick #define COMMENT(CLASS, PARENT) \
40e5dd7070Spatrick   case CLASS##Kind: \
41e5dd7070Spatrick     return #CLASS;
42e5dd7070Spatrick #include "clang/AST/CommentNodes.inc"
43e5dd7070Spatrick #undef COMMENT
44e5dd7070Spatrick #undef ABSTRACT_COMMENT
45e5dd7070Spatrick   }
46e5dd7070Spatrick   llvm_unreachable("Unknown comment kind!");
47e5dd7070Spatrick }
48e5dd7070Spatrick 
49e5dd7070Spatrick namespace {
50e5dd7070Spatrick struct good {};
51e5dd7070Spatrick struct bad {};
52e5dd7070Spatrick 
53e5dd7070Spatrick template <typename T>
implements_child_begin_end(Comment::child_iterator (T::*)()const)54e5dd7070Spatrick good implements_child_begin_end(Comment::child_iterator (T::*)() const) {
55e5dd7070Spatrick   return good();
56e5dd7070Spatrick }
57e5dd7070Spatrick 
58e5dd7070Spatrick LLVM_ATTRIBUTE_UNUSED
implements_child_begin_end(Comment::child_iterator (Comment::*)()const)59e5dd7070Spatrick static inline bad implements_child_begin_end(
60e5dd7070Spatrick                       Comment::child_iterator (Comment::*)() const) {
61e5dd7070Spatrick   return bad();
62e5dd7070Spatrick }
63e5dd7070Spatrick 
64e5dd7070Spatrick #define ASSERT_IMPLEMENTS_child_begin(function) \
65e5dd7070Spatrick   (void) good(implements_child_begin_end(function))
66e5dd7070Spatrick 
67e5dd7070Spatrick LLVM_ATTRIBUTE_UNUSED
CheckCommentASTNodes()68e5dd7070Spatrick static inline void CheckCommentASTNodes() {
69e5dd7070Spatrick #define ABSTRACT_COMMENT(COMMENT)
70e5dd7070Spatrick #define COMMENT(CLASS, PARENT) \
71e5dd7070Spatrick   ASSERT_IMPLEMENTS_child_begin(&CLASS::child_begin); \
72e5dd7070Spatrick   ASSERT_IMPLEMENTS_child_begin(&CLASS::child_end);
73e5dd7070Spatrick #include "clang/AST/CommentNodes.inc"
74e5dd7070Spatrick #undef COMMENT
75e5dd7070Spatrick #undef ABSTRACT_COMMENT
76e5dd7070Spatrick }
77e5dd7070Spatrick 
78e5dd7070Spatrick #undef ASSERT_IMPLEMENTS_child_begin
79e5dd7070Spatrick 
80e5dd7070Spatrick } // end unnamed namespace
81e5dd7070Spatrick 
child_begin() const82e5dd7070Spatrick Comment::child_iterator Comment::child_begin() const {
83e5dd7070Spatrick   switch (getCommentKind()) {
84e5dd7070Spatrick   case NoCommentKind: llvm_unreachable("comment without a kind");
85e5dd7070Spatrick #define ABSTRACT_COMMENT(COMMENT)
86e5dd7070Spatrick #define COMMENT(CLASS, PARENT) \
87e5dd7070Spatrick   case CLASS##Kind: \
88e5dd7070Spatrick     return static_cast<const CLASS *>(this)->child_begin();
89e5dd7070Spatrick #include "clang/AST/CommentNodes.inc"
90e5dd7070Spatrick #undef COMMENT
91e5dd7070Spatrick #undef ABSTRACT_COMMENT
92e5dd7070Spatrick   }
93e5dd7070Spatrick   llvm_unreachable("Unknown comment kind!");
94e5dd7070Spatrick }
95e5dd7070Spatrick 
child_end() const96e5dd7070Spatrick Comment::child_iterator Comment::child_end() const {
97e5dd7070Spatrick   switch (getCommentKind()) {
98e5dd7070Spatrick   case NoCommentKind: llvm_unreachable("comment without a kind");
99e5dd7070Spatrick #define ABSTRACT_COMMENT(COMMENT)
100e5dd7070Spatrick #define COMMENT(CLASS, PARENT) \
101e5dd7070Spatrick   case CLASS##Kind: \
102e5dd7070Spatrick     return static_cast<const CLASS *>(this)->child_end();
103e5dd7070Spatrick #include "clang/AST/CommentNodes.inc"
104e5dd7070Spatrick #undef COMMENT
105e5dd7070Spatrick #undef ABSTRACT_COMMENT
106e5dd7070Spatrick   }
107e5dd7070Spatrick   llvm_unreachable("Unknown comment kind!");
108e5dd7070Spatrick }
109e5dd7070Spatrick 
isWhitespaceNoCache() const110e5dd7070Spatrick bool TextComment::isWhitespaceNoCache() const {
111*12c85518Srobert   return llvm::all_of(Text, clang::isWhitespace);
112e5dd7070Spatrick }
113e5dd7070Spatrick 
isWhitespaceNoCache() const114e5dd7070Spatrick bool ParagraphComment::isWhitespaceNoCache() const {
115e5dd7070Spatrick   for (child_iterator I = child_begin(), E = child_end(); I != E; ++I) {
116e5dd7070Spatrick     if (const TextComment *TC = dyn_cast<TextComment>(*I)) {
117e5dd7070Spatrick       if (!TC->isWhitespace())
118e5dd7070Spatrick         return false;
119e5dd7070Spatrick     } else
120e5dd7070Spatrick       return false;
121e5dd7070Spatrick   }
122e5dd7070Spatrick   return true;
123e5dd7070Spatrick }
124e5dd7070Spatrick 
lookThroughTypedefOrTypeAliasLocs(TypeLoc & SrcTL)125e5dd7070Spatrick static TypeLoc lookThroughTypedefOrTypeAliasLocs(TypeLoc &SrcTL) {
126e5dd7070Spatrick   TypeLoc TL = SrcTL.IgnoreParens();
127e5dd7070Spatrick 
128e5dd7070Spatrick   // Look through attribute types.
129e5dd7070Spatrick   if (AttributedTypeLoc AttributeTL = TL.getAs<AttributedTypeLoc>())
130e5dd7070Spatrick     return AttributeTL.getModifiedLoc();
131e5dd7070Spatrick   // Look through qualified types.
132e5dd7070Spatrick   if (QualifiedTypeLoc QualifiedTL = TL.getAs<QualifiedTypeLoc>())
133e5dd7070Spatrick     return QualifiedTL.getUnqualifiedLoc();
134e5dd7070Spatrick   // Look through pointer types.
135e5dd7070Spatrick   if (PointerTypeLoc PointerTL = TL.getAs<PointerTypeLoc>())
136e5dd7070Spatrick     return PointerTL.getPointeeLoc().getUnqualifiedLoc();
137e5dd7070Spatrick   // Look through reference types.
138e5dd7070Spatrick   if (ReferenceTypeLoc ReferenceTL = TL.getAs<ReferenceTypeLoc>())
139e5dd7070Spatrick     return ReferenceTL.getPointeeLoc().getUnqualifiedLoc();
140e5dd7070Spatrick   // Look through adjusted types.
141e5dd7070Spatrick   if (AdjustedTypeLoc ATL = TL.getAs<AdjustedTypeLoc>())
142e5dd7070Spatrick     return ATL.getOriginalLoc();
143e5dd7070Spatrick   if (BlockPointerTypeLoc BlockPointerTL = TL.getAs<BlockPointerTypeLoc>())
144e5dd7070Spatrick     return BlockPointerTL.getPointeeLoc().getUnqualifiedLoc();
145e5dd7070Spatrick   if (MemberPointerTypeLoc MemberPointerTL = TL.getAs<MemberPointerTypeLoc>())
146e5dd7070Spatrick     return MemberPointerTL.getPointeeLoc().getUnqualifiedLoc();
147e5dd7070Spatrick   if (ElaboratedTypeLoc ETL = TL.getAs<ElaboratedTypeLoc>())
148e5dd7070Spatrick     return ETL.getNamedTypeLoc();
149e5dd7070Spatrick 
150e5dd7070Spatrick   return TL;
151e5dd7070Spatrick }
152e5dd7070Spatrick 
getFunctionTypeLoc(TypeLoc TL,FunctionTypeLoc & ResFTL)153e5dd7070Spatrick static bool getFunctionTypeLoc(TypeLoc TL, FunctionTypeLoc &ResFTL) {
154e5dd7070Spatrick   TypeLoc PrevTL;
155e5dd7070Spatrick   while (PrevTL != TL) {
156e5dd7070Spatrick     PrevTL = TL;
157e5dd7070Spatrick     TL = lookThroughTypedefOrTypeAliasLocs(TL);
158e5dd7070Spatrick   }
159e5dd7070Spatrick 
160e5dd7070Spatrick   if (FunctionTypeLoc FTL = TL.getAs<FunctionTypeLoc>()) {
161e5dd7070Spatrick     ResFTL = FTL;
162e5dd7070Spatrick     return true;
163e5dd7070Spatrick   }
164e5dd7070Spatrick 
165e5dd7070Spatrick   if (TemplateSpecializationTypeLoc STL =
166e5dd7070Spatrick           TL.getAs<TemplateSpecializationTypeLoc>()) {
167e5dd7070Spatrick     // If we have a typedef to a template specialization with exactly one
168e5dd7070Spatrick     // template argument of a function type, this looks like std::function,
169e5dd7070Spatrick     // boost::function, or other function wrapper.  Treat these typedefs as
170e5dd7070Spatrick     // functions.
171e5dd7070Spatrick     if (STL.getNumArgs() != 1)
172e5dd7070Spatrick       return false;
173e5dd7070Spatrick     TemplateArgumentLoc MaybeFunction = STL.getArgLoc(0);
174e5dd7070Spatrick     if (MaybeFunction.getArgument().getKind() != TemplateArgument::Type)
175e5dd7070Spatrick       return false;
176e5dd7070Spatrick     TypeSourceInfo *MaybeFunctionTSI = MaybeFunction.getTypeSourceInfo();
177e5dd7070Spatrick     TypeLoc TL = MaybeFunctionTSI->getTypeLoc().getUnqualifiedLoc();
178e5dd7070Spatrick     if (FunctionTypeLoc FTL = TL.getAs<FunctionTypeLoc>()) {
179e5dd7070Spatrick       ResFTL = FTL;
180e5dd7070Spatrick       return true;
181e5dd7070Spatrick     }
182e5dd7070Spatrick   }
183e5dd7070Spatrick 
184e5dd7070Spatrick   return false;
185e5dd7070Spatrick }
186e5dd7070Spatrick 
getDirectionAsString(PassDirection D)187e5dd7070Spatrick const char *ParamCommandComment::getDirectionAsString(PassDirection D) {
188e5dd7070Spatrick   switch (D) {
189e5dd7070Spatrick   case ParamCommandComment::In:
190e5dd7070Spatrick     return "[in]";
191e5dd7070Spatrick   case ParamCommandComment::Out:
192e5dd7070Spatrick     return "[out]";
193e5dd7070Spatrick   case ParamCommandComment::InOut:
194e5dd7070Spatrick     return "[in,out]";
195e5dd7070Spatrick   }
196e5dd7070Spatrick   llvm_unreachable("unknown PassDirection");
197e5dd7070Spatrick }
198e5dd7070Spatrick 
fill()199e5dd7070Spatrick void DeclInfo::fill() {
200e5dd7070Spatrick   assert(!IsFilled);
201e5dd7070Spatrick 
202e5dd7070Spatrick   // Set defaults.
203e5dd7070Spatrick   Kind = OtherKind;
204e5dd7070Spatrick   TemplateKind = NotTemplate;
205e5dd7070Spatrick   IsObjCMethod = false;
206e5dd7070Spatrick   IsInstanceMethod = false;
207e5dd7070Spatrick   IsClassMethod = false;
208*12c85518Srobert   IsVariadic = false;
209*12c85518Srobert   ParamVars = std::nullopt;
210e5dd7070Spatrick   TemplateParameters = nullptr;
211e5dd7070Spatrick 
212e5dd7070Spatrick   if (!CommentDecl) {
213e5dd7070Spatrick     // If there is no declaration, the defaults is our only guess.
214e5dd7070Spatrick     IsFilled = true;
215e5dd7070Spatrick     return;
216e5dd7070Spatrick   }
217e5dd7070Spatrick   CurrentDecl = CommentDecl;
218e5dd7070Spatrick 
219e5dd7070Spatrick   Decl::Kind K = CommentDecl->getKind();
220*12c85518Srobert   const TypeSourceInfo *TSI = nullptr;
221e5dd7070Spatrick   switch (K) {
222e5dd7070Spatrick   default:
223e5dd7070Spatrick     // Defaults are should be good for declarations we don't handle explicitly.
224e5dd7070Spatrick     break;
225e5dd7070Spatrick   case Decl::Function:
226e5dd7070Spatrick   case Decl::CXXMethod:
227e5dd7070Spatrick   case Decl::CXXConstructor:
228e5dd7070Spatrick   case Decl::CXXDestructor:
229e5dd7070Spatrick   case Decl::CXXConversion: {
230e5dd7070Spatrick     const FunctionDecl *FD = cast<FunctionDecl>(CommentDecl);
231e5dd7070Spatrick     Kind = FunctionKind;
232e5dd7070Spatrick     ParamVars = FD->parameters();
233e5dd7070Spatrick     ReturnType = FD->getReturnType();
234e5dd7070Spatrick     unsigned NumLists = FD->getNumTemplateParameterLists();
235e5dd7070Spatrick     if (NumLists != 0) {
236e5dd7070Spatrick       TemplateKind = TemplateSpecialization;
237e5dd7070Spatrick       TemplateParameters =
238e5dd7070Spatrick           FD->getTemplateParameterList(NumLists - 1);
239e5dd7070Spatrick     }
240e5dd7070Spatrick 
241e5dd7070Spatrick     if (K == Decl::CXXMethod || K == Decl::CXXConstructor ||
242e5dd7070Spatrick         K == Decl::CXXDestructor || K == Decl::CXXConversion) {
243e5dd7070Spatrick       const CXXMethodDecl *MD = cast<CXXMethodDecl>(CommentDecl);
244e5dd7070Spatrick       IsInstanceMethod = MD->isInstance();
245e5dd7070Spatrick       IsClassMethod = !IsInstanceMethod;
246e5dd7070Spatrick     }
247*12c85518Srobert     IsVariadic = FD->isVariadic();
248*12c85518Srobert     assert(involvesFunctionType());
249e5dd7070Spatrick     break;
250e5dd7070Spatrick   }
251e5dd7070Spatrick   case Decl::ObjCMethod: {
252e5dd7070Spatrick     const ObjCMethodDecl *MD = cast<ObjCMethodDecl>(CommentDecl);
253e5dd7070Spatrick     Kind = FunctionKind;
254e5dd7070Spatrick     ParamVars = MD->parameters();
255e5dd7070Spatrick     ReturnType = MD->getReturnType();
256e5dd7070Spatrick     IsObjCMethod = true;
257e5dd7070Spatrick     IsInstanceMethod = MD->isInstanceMethod();
258e5dd7070Spatrick     IsClassMethod = !IsInstanceMethod;
259*12c85518Srobert     IsVariadic = MD->isVariadic();
260*12c85518Srobert     assert(involvesFunctionType());
261e5dd7070Spatrick     break;
262e5dd7070Spatrick   }
263e5dd7070Spatrick   case Decl::FunctionTemplate: {
264e5dd7070Spatrick     const FunctionTemplateDecl *FTD = cast<FunctionTemplateDecl>(CommentDecl);
265e5dd7070Spatrick     Kind = FunctionKind;
266e5dd7070Spatrick     TemplateKind = Template;
267e5dd7070Spatrick     const FunctionDecl *FD = FTD->getTemplatedDecl();
268e5dd7070Spatrick     ParamVars = FD->parameters();
269e5dd7070Spatrick     ReturnType = FD->getReturnType();
270e5dd7070Spatrick     TemplateParameters = FTD->getTemplateParameters();
271*12c85518Srobert     IsVariadic = FD->isVariadic();
272*12c85518Srobert     assert(involvesFunctionType());
273e5dd7070Spatrick     break;
274e5dd7070Spatrick   }
275e5dd7070Spatrick   case Decl::ClassTemplate: {
276e5dd7070Spatrick     const ClassTemplateDecl *CTD = cast<ClassTemplateDecl>(CommentDecl);
277e5dd7070Spatrick     Kind = ClassKind;
278e5dd7070Spatrick     TemplateKind = Template;
279e5dd7070Spatrick     TemplateParameters = CTD->getTemplateParameters();
280e5dd7070Spatrick     break;
281e5dd7070Spatrick   }
282e5dd7070Spatrick   case Decl::ClassTemplatePartialSpecialization: {
283e5dd7070Spatrick     const ClassTemplatePartialSpecializationDecl *CTPSD =
284e5dd7070Spatrick         cast<ClassTemplatePartialSpecializationDecl>(CommentDecl);
285e5dd7070Spatrick     Kind = ClassKind;
286e5dd7070Spatrick     TemplateKind = TemplatePartialSpecialization;
287e5dd7070Spatrick     TemplateParameters = CTPSD->getTemplateParameters();
288e5dd7070Spatrick     break;
289e5dd7070Spatrick   }
290e5dd7070Spatrick   case Decl::ClassTemplateSpecialization:
291e5dd7070Spatrick     Kind = ClassKind;
292e5dd7070Spatrick     TemplateKind = TemplateSpecialization;
293e5dd7070Spatrick     break;
294e5dd7070Spatrick   case Decl::Record:
295e5dd7070Spatrick   case Decl::CXXRecord:
296e5dd7070Spatrick     Kind = ClassKind;
297e5dd7070Spatrick     break;
298e5dd7070Spatrick   case Decl::Var:
299*12c85518Srobert     if (const VarTemplateDecl *VTD =
300*12c85518Srobert             cast<VarDecl>(CommentDecl)->getDescribedVarTemplate()) {
301*12c85518Srobert       TemplateKind = TemplateSpecialization;
302*12c85518Srobert       TemplateParameters = VTD->getTemplateParameters();
303*12c85518Srobert     }
304*12c85518Srobert     [[fallthrough]];
305e5dd7070Spatrick   case Decl::Field:
306e5dd7070Spatrick   case Decl::EnumConstant:
307e5dd7070Spatrick   case Decl::ObjCIvar:
308e5dd7070Spatrick   case Decl::ObjCAtDefsField:
309*12c85518Srobert   case Decl::ObjCProperty:
310e5dd7070Spatrick     if (const auto *VD = dyn_cast<DeclaratorDecl>(CommentDecl))
311e5dd7070Spatrick       TSI = VD->getTypeSourceInfo();
312e5dd7070Spatrick     else if (const auto *PD = dyn_cast<ObjCPropertyDecl>(CommentDecl))
313e5dd7070Spatrick       TSI = PD->getTypeSourceInfo();
314e5dd7070Spatrick     Kind = VariableKind;
315e5dd7070Spatrick     break;
316*12c85518Srobert   case Decl::VarTemplate: {
317*12c85518Srobert     const VarTemplateDecl *VTD = cast<VarTemplateDecl>(CommentDecl);
318*12c85518Srobert     Kind = VariableKind;
319*12c85518Srobert     TemplateKind = Template;
320*12c85518Srobert     TemplateParameters = VTD->getTemplateParameters();
321*12c85518Srobert     if (const VarDecl *VD = VTD->getTemplatedDecl())
322*12c85518Srobert       TSI = VD->getTypeSourceInfo();
323*12c85518Srobert     break;
324e5dd7070Spatrick   }
325e5dd7070Spatrick   case Decl::Namespace:
326e5dd7070Spatrick     Kind = NamespaceKind;
327e5dd7070Spatrick     break;
328e5dd7070Spatrick   case Decl::TypeAlias:
329*12c85518Srobert   case Decl::Typedef:
330e5dd7070Spatrick     Kind = TypedefKind;
331*12c85518Srobert     TSI = cast<TypedefNameDecl>(CommentDecl)->getTypeSourceInfo();
332e5dd7070Spatrick     break;
333e5dd7070Spatrick   case Decl::TypeAliasTemplate: {
334e5dd7070Spatrick     const TypeAliasTemplateDecl *TAT = cast<TypeAliasTemplateDecl>(CommentDecl);
335e5dd7070Spatrick     Kind = TypedefKind;
336e5dd7070Spatrick     TemplateKind = Template;
337e5dd7070Spatrick     TemplateParameters = TAT->getTemplateParameters();
338*12c85518Srobert     if (TypeAliasDecl *TAD = TAT->getTemplatedDecl())
339*12c85518Srobert       TSI = TAD->getTypeSourceInfo();
340e5dd7070Spatrick     break;
341e5dd7070Spatrick   }
342e5dd7070Spatrick   case Decl::Enum:
343e5dd7070Spatrick     Kind = EnumKind;
344e5dd7070Spatrick     break;
345e5dd7070Spatrick   }
346e5dd7070Spatrick 
347*12c85518Srobert   // If the type is a typedef / using to something we consider a function,
348*12c85518Srobert   // extract arguments and return type.
349*12c85518Srobert   if (TSI) {
350*12c85518Srobert     TypeLoc TL = TSI->getTypeLoc().getUnqualifiedLoc();
351*12c85518Srobert     FunctionTypeLoc FTL;
352*12c85518Srobert     if (getFunctionTypeLoc(TL, FTL)) {
353*12c85518Srobert       ParamVars = FTL.getParams();
354*12c85518Srobert       ReturnType = FTL.getReturnLoc().getType();
355*12c85518Srobert       if (const auto *FPT = dyn_cast<FunctionProtoType>(FTL.getTypePtr()))
356*12c85518Srobert         IsVariadic = FPT->isVariadic();
357*12c85518Srobert       assert(involvesFunctionType());
358*12c85518Srobert     }
359*12c85518Srobert   }
360*12c85518Srobert 
361e5dd7070Spatrick   IsFilled = true;
362e5dd7070Spatrick }
363e5dd7070Spatrick 
getParamName(const FullComment * FC) const364e5dd7070Spatrick StringRef ParamCommandComment::getParamName(const FullComment *FC) const {
365e5dd7070Spatrick   assert(isParamIndexValid());
366e5dd7070Spatrick   if (isVarArgParam())
367e5dd7070Spatrick     return "...";
368e5dd7070Spatrick   return FC->getDeclInfo()->ParamVars[getParamIndex()]->getName();
369e5dd7070Spatrick }
370e5dd7070Spatrick 
getParamName(const FullComment * FC) const371e5dd7070Spatrick StringRef TParamCommandComment::getParamName(const FullComment *FC) const {
372e5dd7070Spatrick   assert(isPositionValid());
373e5dd7070Spatrick   const TemplateParameterList *TPL = FC->getDeclInfo()->TemplateParameters;
374e5dd7070Spatrick   for (unsigned i = 0, e = getDepth(); i != e; ++i) {
375e5dd7070Spatrick     assert(TPL && "Unknown TemplateParameterList");
376e5dd7070Spatrick     if (i == e - 1)
377e5dd7070Spatrick       return TPL->getParam(getIndex(i))->getName();
378e5dd7070Spatrick     const NamedDecl *Param = TPL->getParam(getIndex(i));
379e5dd7070Spatrick     if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(Param))
380e5dd7070Spatrick       TPL = TTP->getTemplateParameters();
381e5dd7070Spatrick   }
382e5dd7070Spatrick   return "";
383e5dd7070Spatrick }
384e5dd7070Spatrick 
385e5dd7070Spatrick } // end namespace comments
386e5dd7070Spatrick } // end namespace clang
387e5dd7070Spatrick 
388