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