397 lines · cpp
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-exception6//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 18namespace clang {19namespace comments {20 21// Check that no comment class has a non-trival destructor. They are allocated22// 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 COMMENT29#undef ABSTRACT_COMMENT30 31// DeclInfo is also allocated with a BumpPtrAllocator.32static_assert(std::is_trivially_destructible_v<DeclInfo>,33 "DeclInfo should be trivially destructible!");34 35const 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 COMMENT45#undef ABSTRACT_COMMENT46 }47 llvm_unreachable("Unknown comment kind!");48}49 50namespace {51struct good {};52struct bad {};53 54template <typename T>55good implements_child_begin_end(Comment::child_iterator (T::*)() const) {56 return good();57}58 59[[maybe_unused]]60static inline bad61implements_child_begin_end(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[[maybe_unused]]69static 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 COMMENT76#undef ABSTRACT_COMMENT77}78 79#undef ASSERT_IMPLEMENTS_child_begin80 81} // end unnamed namespace82 83Comment::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 COMMENT93#undef ABSTRACT_COMMENT94 }95 llvm_unreachable("Unknown comment kind!");96}97 98Comment::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 COMMENT108#undef ABSTRACT_COMMENT109 }110 llvm_unreachable("Unknown comment kind!");111}112 113bool TextComment::isWhitespaceNoCache() const {114 return llvm::all_of(Text, clang::isWhitespace);115}116 117bool 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 } else123 return false;124 }125 return true;126}127 128static 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 151 return TL;152}153 154static bool getFunctionTypeLoc(TypeLoc TL, FunctionTypeLoc &ResFTL) {155 TypeLoc PrevTL;156 while (PrevTL != TL) {157 PrevTL = TL;158 TL = lookThroughTypedefOrTypeAliasLocs(TL);159 }160 161 if (FunctionTypeLoc FTL = TL.getAs<FunctionTypeLoc>()) {162 ResFTL = FTL;163 return true;164 }165 166 if (TemplateSpecializationTypeLoc STL =167 TL.getAs<TemplateSpecializationTypeLoc>()) {168 // If we have a typedef to a template specialization with exactly one169 // template argument of a function type, this looks like std::function,170 // boost::function, or other function wrapper. Treat these typedefs as171 // functions.172 if (STL.getNumArgs() != 1)173 return false;174 TemplateArgumentLoc MaybeFunction = STL.getArgLoc(0);175 if (MaybeFunction.getArgument().getKind() != TemplateArgument::Type)176 return false;177 TypeSourceInfo *MaybeFunctionTSI = MaybeFunction.getTypeSourceInfo();178 TypeLoc TL = MaybeFunctionTSI->getTypeLoc().getUnqualifiedLoc();179 if (FunctionTypeLoc FTL = TL.getAs<FunctionTypeLoc>()) {180 ResFTL = FTL;181 return true;182 }183 }184 185 return false;186}187 188const char *189ParamCommandComment::getDirectionAsString(ParamCommandPassDirection D) {190 switch (D) {191 case ParamCommandPassDirection::In:192 return "[in]";193 case ParamCommandPassDirection::Out:194 return "[out]";195 case ParamCommandPassDirection::InOut:196 return "[in,out]";197 }198 llvm_unreachable("unknown PassDirection");199}200 201void DeclInfo::fill() {202 assert(!IsFilled);203 204 // Set defaults.205 Kind = OtherKind;206 TemplateKind = NotTemplate;207 IsObjCMethod = false;208 IsInstanceMethod = false;209 IsClassMethod = false;210 IsVariadic = false;211 ParamVars = {};212 TemplateParameters = nullptr;213 214 if (!CommentDecl) {215 // If there is no declaration, the defaults is our only guess.216 IsFilled = true;217 return;218 }219 CurrentDecl = CommentDecl;220 221 Decl::Kind K = CommentDecl->getKind();222 const TypeSourceInfo *TSI = nullptr;223 switch (K) {224 default:225 // Defaults are should be good for declarations we don't handle explicitly.226 break;227 case Decl::Function:228 case Decl::CXXMethod:229 case Decl::CXXConstructor:230 case Decl::CXXDestructor:231 case Decl::CXXConversion: {232 const FunctionDecl *FD = cast<FunctionDecl>(CommentDecl);233 Kind = FunctionKind;234 ParamVars = FD->parameters();235 ReturnType = FD->getReturnType();236 unsigned NumLists = FD->getNumTemplateParameterLists();237 if (NumLists != 0) {238 TemplateKind = TemplateSpecialization;239 TemplateParameters =240 FD->getTemplateParameterList(NumLists - 1);241 }242 243 if (K == Decl::CXXMethod || K == Decl::CXXConstructor ||244 K == Decl::CXXDestructor || K == Decl::CXXConversion) {245 const CXXMethodDecl *MD = cast<CXXMethodDecl>(CommentDecl);246 IsInstanceMethod = MD->isInstance();247 IsClassMethod = !IsInstanceMethod;248 }249 IsVariadic = FD->isVariadic();250 assert(involvesFunctionType());251 break;252 }253 case Decl::ObjCMethod: {254 const ObjCMethodDecl *MD = cast<ObjCMethodDecl>(CommentDecl);255 Kind = FunctionKind;256 ParamVars = MD->parameters();257 ReturnType = MD->getReturnType();258 IsObjCMethod = true;259 IsInstanceMethod = MD->isInstanceMethod();260 IsClassMethod = !IsInstanceMethod;261 IsVariadic = MD->isVariadic();262 assert(involvesFunctionType());263 break;264 }265 case Decl::FunctionTemplate: {266 const FunctionTemplateDecl *FTD = cast<FunctionTemplateDecl>(CommentDecl);267 Kind = FunctionKind;268 TemplateKind = Template;269 const FunctionDecl *FD = FTD->getTemplatedDecl();270 ParamVars = FD->parameters();271 ReturnType = FD->getReturnType();272 TemplateParameters = FTD->getTemplateParameters();273 IsVariadic = FD->isVariadic();274 assert(involvesFunctionType());275 break;276 }277 case Decl::ClassTemplate: {278 const ClassTemplateDecl *CTD = cast<ClassTemplateDecl>(CommentDecl);279 Kind = ClassKind;280 TemplateKind = Template;281 TemplateParameters = CTD->getTemplateParameters();282 break;283 }284 case Decl::ClassTemplatePartialSpecialization: {285 const ClassTemplatePartialSpecializationDecl *CTPSD =286 cast<ClassTemplatePartialSpecializationDecl>(CommentDecl);287 Kind = ClassKind;288 TemplateKind = TemplatePartialSpecialization;289 TemplateParameters = CTPSD->getTemplateParameters();290 break;291 }292 case Decl::VarTemplatePartialSpecialization: {293 const auto *VTPSD = cast<VarTemplatePartialSpecializationDecl>(CommentDecl);294 Kind = VariableKind;295 TemplateKind = TemplatePartialSpecialization;296 TemplateParameters = VTPSD->getTemplateParameters();297 break;298 }299 case Decl::ClassTemplateSpecialization:300 Kind = ClassKind;301 TemplateKind = TemplateSpecialization;302 break;303 case Decl::Record:304 case Decl::CXXRecord:305 Kind = ClassKind;306 break;307 case Decl::Var:308 if (const VarTemplateDecl *VTD =309 cast<VarDecl>(CommentDecl)->getDescribedVarTemplate()) {310 TemplateKind = TemplateSpecialization;311 TemplateParameters = VTD->getTemplateParameters();312 }313 [[fallthrough]];314 case Decl::Field:315 case Decl::EnumConstant:316 case Decl::ObjCIvar:317 case Decl::ObjCAtDefsField:318 case Decl::ObjCProperty:319 if (const auto *VD = dyn_cast<DeclaratorDecl>(CommentDecl))320 TSI = VD->getTypeSourceInfo();321 else if (const auto *PD = dyn_cast<ObjCPropertyDecl>(CommentDecl))322 TSI = PD->getTypeSourceInfo();323 Kind = VariableKind;324 break;325 case Decl::VarTemplate: {326 const VarTemplateDecl *VTD = cast<VarTemplateDecl>(CommentDecl);327 Kind = VariableKind;328 TemplateKind = Template;329 TemplateParameters = VTD->getTemplateParameters();330 if (const VarDecl *VD = VTD->getTemplatedDecl())331 TSI = VD->getTypeSourceInfo();332 break;333 }334 case Decl::Namespace:335 Kind = NamespaceKind;336 break;337 case Decl::TypeAlias:338 case Decl::Typedef:339 Kind = TypedefKind;340 TSI = cast<TypedefNameDecl>(CommentDecl)->getTypeSourceInfo();341 break;342 case Decl::TypeAliasTemplate: {343 const TypeAliasTemplateDecl *TAT = cast<TypeAliasTemplateDecl>(CommentDecl);344 Kind = TypedefKind;345 TemplateKind = Template;346 TemplateParameters = TAT->getTemplateParameters();347 if (TypeAliasDecl *TAD = TAT->getTemplatedDecl())348 TSI = TAD->getTypeSourceInfo();349 break;350 }351 case Decl::Enum:352 Kind = EnumKind;353 break;354 }355 356 // If the type is a typedef / using to something we consider a function,357 // extract arguments and return type.358 if (TSI) {359 TypeLoc TL = TSI->getTypeLoc().getUnqualifiedLoc();360 FunctionTypeLoc FTL;361 if (getFunctionTypeLoc(TL, FTL)) {362 ParamVars = FTL.getParams();363 ReturnType = FTL.getReturnLoc().getType();364 if (const auto *FPT = dyn_cast<FunctionProtoType>(FTL.getTypePtr()))365 IsVariadic = FPT->isVariadic();366 assert(involvesFunctionType());367 }368 }369 370 IsFilled = true;371}372 373StringRef ParamCommandComment::getParamName(const FullComment *FC) const {374 assert(isParamIndexValid());375 if (isVarArgParam())376 return "...";377 return FC->getDeclInfo()->ParamVars[getParamIndex()]->getName();378}379 380StringRef TParamCommandComment::getParamName(const FullComment *FC) const {381 assert(isPositionValid());382 const TemplateParameterList *TPL = FC->getDeclInfo()->TemplateParameters;383 for (unsigned i = 0, e = getDepth(); i != e; ++i) {384 assert(TPL && "Unknown TemplateParameterList");385 if (i == e - 1)386 return TPL->getParam(getIndex(i))->getName();387 const NamedDecl *Param = TPL->getParam(getIndex(i));388 if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(Param))389 TPL = TTP->getTemplateParameters();390 }391 return "";392}393 394} // end namespace comments395} // end namespace clang396 397