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