mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-27 15:06:05 +00:00

to reflect the new license. We understand that people may be surprised that we're moving the header entirely to discuss the new license. We checked this carefully with the Foundation's lawyer and we believe this is the correct approach. Essentially, all code in the project is now made available by the LLVM project under our new license, so you will see that the license headers include that license only. Some of our contributors have contributed code under our old license, and accordingly, we have retained a copy of our old license notice in the top-level files in each project and repository. llvm-svn: 351636
380 lines
11 KiB
C++
380 lines
11 KiB
C++
//===--- Comment.cpp - Comment AST node implementation --------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/AST/Comment.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/AST/DeclObjC.h"
|
|
#include "clang/AST/DeclTemplate.h"
|
|
#include "clang/Basic/CharInfo.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
|
|
namespace clang {
|
|
namespace comments {
|
|
|
|
const char *Comment::getCommentKindName() const {
|
|
switch (getCommentKind()) {
|
|
case NoCommentKind: return "NoCommentKind";
|
|
#define ABSTRACT_COMMENT(COMMENT)
|
|
#define COMMENT(CLASS, PARENT) \
|
|
case CLASS##Kind: \
|
|
return #CLASS;
|
|
#include "clang/AST/CommentNodes.inc"
|
|
#undef COMMENT
|
|
#undef ABSTRACT_COMMENT
|
|
}
|
|
llvm_unreachable("Unknown comment kind!");
|
|
}
|
|
|
|
namespace {
|
|
struct good {};
|
|
struct bad {};
|
|
|
|
template <typename T>
|
|
good implements_child_begin_end(Comment::child_iterator (T::*)() const) {
|
|
return good();
|
|
}
|
|
|
|
LLVM_ATTRIBUTE_UNUSED
|
|
static inline bad implements_child_begin_end(
|
|
Comment::child_iterator (Comment::*)() const) {
|
|
return bad();
|
|
}
|
|
|
|
#define ASSERT_IMPLEMENTS_child_begin(function) \
|
|
(void) good(implements_child_begin_end(function))
|
|
|
|
LLVM_ATTRIBUTE_UNUSED
|
|
static inline void CheckCommentASTNodes() {
|
|
#define ABSTRACT_COMMENT(COMMENT)
|
|
#define COMMENT(CLASS, PARENT) \
|
|
ASSERT_IMPLEMENTS_child_begin(&CLASS::child_begin); \
|
|
ASSERT_IMPLEMENTS_child_begin(&CLASS::child_end);
|
|
#include "clang/AST/CommentNodes.inc"
|
|
#undef COMMENT
|
|
#undef ABSTRACT_COMMENT
|
|
}
|
|
|
|
#undef ASSERT_IMPLEMENTS_child_begin
|
|
|
|
} // end unnamed namespace
|
|
|
|
Comment::child_iterator Comment::child_begin() const {
|
|
switch (getCommentKind()) {
|
|
case NoCommentKind: llvm_unreachable("comment without a kind");
|
|
#define ABSTRACT_COMMENT(COMMENT)
|
|
#define COMMENT(CLASS, PARENT) \
|
|
case CLASS##Kind: \
|
|
return static_cast<const CLASS *>(this)->child_begin();
|
|
#include "clang/AST/CommentNodes.inc"
|
|
#undef COMMENT
|
|
#undef ABSTRACT_COMMENT
|
|
}
|
|
llvm_unreachable("Unknown comment kind!");
|
|
}
|
|
|
|
Comment::child_iterator Comment::child_end() const {
|
|
switch (getCommentKind()) {
|
|
case NoCommentKind: llvm_unreachable("comment without a kind");
|
|
#define ABSTRACT_COMMENT(COMMENT)
|
|
#define COMMENT(CLASS, PARENT) \
|
|
case CLASS##Kind: \
|
|
return static_cast<const CLASS *>(this)->child_end();
|
|
#include "clang/AST/CommentNodes.inc"
|
|
#undef COMMENT
|
|
#undef ABSTRACT_COMMENT
|
|
}
|
|
llvm_unreachable("Unknown comment kind!");
|
|
}
|
|
|
|
bool TextComment::isWhitespaceNoCache() const {
|
|
for (StringRef::const_iterator I = Text.begin(), E = Text.end();
|
|
I != E; ++I) {
|
|
if (!clang::isWhitespace(*I))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ParagraphComment::isWhitespaceNoCache() const {
|
|
for (child_iterator I = child_begin(), E = child_end(); I != E; ++I) {
|
|
if (const TextComment *TC = dyn_cast<TextComment>(*I)) {
|
|
if (!TC->isWhitespace())
|
|
return false;
|
|
} else
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static TypeLoc lookThroughTypedefOrTypeAliasLocs(TypeLoc &SrcTL) {
|
|
TypeLoc TL = SrcTL.IgnoreParens();
|
|
|
|
// Look through attribute types.
|
|
if (AttributedTypeLoc AttributeTL = TL.getAs<AttributedTypeLoc>())
|
|
return AttributeTL.getModifiedLoc();
|
|
// Look through qualified types.
|
|
if (QualifiedTypeLoc QualifiedTL = TL.getAs<QualifiedTypeLoc>())
|
|
return QualifiedTL.getUnqualifiedLoc();
|
|
// Look through pointer types.
|
|
if (PointerTypeLoc PointerTL = TL.getAs<PointerTypeLoc>())
|
|
return PointerTL.getPointeeLoc().getUnqualifiedLoc();
|
|
// Look through reference types.
|
|
if (ReferenceTypeLoc ReferenceTL = TL.getAs<ReferenceTypeLoc>())
|
|
return ReferenceTL.getPointeeLoc().getUnqualifiedLoc();
|
|
// Look through adjusted types.
|
|
if (AdjustedTypeLoc ATL = TL.getAs<AdjustedTypeLoc>())
|
|
return ATL.getOriginalLoc();
|
|
if (BlockPointerTypeLoc BlockPointerTL = TL.getAs<BlockPointerTypeLoc>())
|
|
return BlockPointerTL.getPointeeLoc().getUnqualifiedLoc();
|
|
if (MemberPointerTypeLoc MemberPointerTL = TL.getAs<MemberPointerTypeLoc>())
|
|
return MemberPointerTL.getPointeeLoc().getUnqualifiedLoc();
|
|
if (ElaboratedTypeLoc ETL = TL.getAs<ElaboratedTypeLoc>())
|
|
return ETL.getNamedTypeLoc();
|
|
|
|
return TL;
|
|
}
|
|
|
|
static bool getFunctionTypeLoc(TypeLoc TL, FunctionTypeLoc &ResFTL) {
|
|
TypeLoc PrevTL;
|
|
while (PrevTL != TL) {
|
|
PrevTL = TL;
|
|
TL = lookThroughTypedefOrTypeAliasLocs(TL);
|
|
}
|
|
|
|
if (FunctionTypeLoc FTL = TL.getAs<FunctionTypeLoc>()) {
|
|
ResFTL = FTL;
|
|
return true;
|
|
}
|
|
|
|
if (TemplateSpecializationTypeLoc STL =
|
|
TL.getAs<TemplateSpecializationTypeLoc>()) {
|
|
// If we have a typedef to a template specialization with exactly one
|
|
// template argument of a function type, this looks like std::function,
|
|
// boost::function, or other function wrapper. Treat these typedefs as
|
|
// functions.
|
|
if (STL.getNumArgs() != 1)
|
|
return false;
|
|
TemplateArgumentLoc MaybeFunction = STL.getArgLoc(0);
|
|
if (MaybeFunction.getArgument().getKind() != TemplateArgument::Type)
|
|
return false;
|
|
TypeSourceInfo *MaybeFunctionTSI = MaybeFunction.getTypeSourceInfo();
|
|
TypeLoc TL = MaybeFunctionTSI->getTypeLoc().getUnqualifiedLoc();
|
|
if (FunctionTypeLoc FTL = TL.getAs<FunctionTypeLoc>()) {
|
|
ResFTL = FTL;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const char *ParamCommandComment::getDirectionAsString(PassDirection D) {
|
|
switch (D) {
|
|
case ParamCommandComment::In:
|
|
return "[in]";
|
|
case ParamCommandComment::Out:
|
|
return "[out]";
|
|
case ParamCommandComment::InOut:
|
|
return "[in,out]";
|
|
}
|
|
llvm_unreachable("unknown PassDirection");
|
|
}
|
|
|
|
void DeclInfo::fill() {
|
|
assert(!IsFilled);
|
|
|
|
// Set defaults.
|
|
Kind = OtherKind;
|
|
TemplateKind = NotTemplate;
|
|
IsObjCMethod = false;
|
|
IsInstanceMethod = false;
|
|
IsClassMethod = false;
|
|
ParamVars = None;
|
|
TemplateParameters = nullptr;
|
|
|
|
if (!CommentDecl) {
|
|
// If there is no declaration, the defaults is our only guess.
|
|
IsFilled = true;
|
|
return;
|
|
}
|
|
CurrentDecl = CommentDecl;
|
|
|
|
Decl::Kind K = CommentDecl->getKind();
|
|
switch (K) {
|
|
default:
|
|
// Defaults are should be good for declarations we don't handle explicitly.
|
|
break;
|
|
case Decl::Function:
|
|
case Decl::CXXMethod:
|
|
case Decl::CXXConstructor:
|
|
case Decl::CXXDestructor:
|
|
case Decl::CXXConversion: {
|
|
const FunctionDecl *FD = cast<FunctionDecl>(CommentDecl);
|
|
Kind = FunctionKind;
|
|
ParamVars = FD->parameters();
|
|
ReturnType = FD->getReturnType();
|
|
unsigned NumLists = FD->getNumTemplateParameterLists();
|
|
if (NumLists != 0) {
|
|
TemplateKind = TemplateSpecialization;
|
|
TemplateParameters =
|
|
FD->getTemplateParameterList(NumLists - 1);
|
|
}
|
|
|
|
if (K == Decl::CXXMethod || K == Decl::CXXConstructor ||
|
|
K == Decl::CXXDestructor || K == Decl::CXXConversion) {
|
|
const CXXMethodDecl *MD = cast<CXXMethodDecl>(CommentDecl);
|
|
IsInstanceMethod = MD->isInstance();
|
|
IsClassMethod = !IsInstanceMethod;
|
|
}
|
|
break;
|
|
}
|
|
case Decl::ObjCMethod: {
|
|
const ObjCMethodDecl *MD = cast<ObjCMethodDecl>(CommentDecl);
|
|
Kind = FunctionKind;
|
|
ParamVars = MD->parameters();
|
|
ReturnType = MD->getReturnType();
|
|
IsObjCMethod = true;
|
|
IsInstanceMethod = MD->isInstanceMethod();
|
|
IsClassMethod = !IsInstanceMethod;
|
|
break;
|
|
}
|
|
case Decl::FunctionTemplate: {
|
|
const FunctionTemplateDecl *FTD = cast<FunctionTemplateDecl>(CommentDecl);
|
|
Kind = FunctionKind;
|
|
TemplateKind = Template;
|
|
const FunctionDecl *FD = FTD->getTemplatedDecl();
|
|
ParamVars = FD->parameters();
|
|
ReturnType = FD->getReturnType();
|
|
TemplateParameters = FTD->getTemplateParameters();
|
|
break;
|
|
}
|
|
case Decl::ClassTemplate: {
|
|
const ClassTemplateDecl *CTD = cast<ClassTemplateDecl>(CommentDecl);
|
|
Kind = ClassKind;
|
|
TemplateKind = Template;
|
|
TemplateParameters = CTD->getTemplateParameters();
|
|
break;
|
|
}
|
|
case Decl::ClassTemplatePartialSpecialization: {
|
|
const ClassTemplatePartialSpecializationDecl *CTPSD =
|
|
cast<ClassTemplatePartialSpecializationDecl>(CommentDecl);
|
|
Kind = ClassKind;
|
|
TemplateKind = TemplatePartialSpecialization;
|
|
TemplateParameters = CTPSD->getTemplateParameters();
|
|
break;
|
|
}
|
|
case Decl::ClassTemplateSpecialization:
|
|
Kind = ClassKind;
|
|
TemplateKind = TemplateSpecialization;
|
|
break;
|
|
case Decl::Record:
|
|
case Decl::CXXRecord:
|
|
Kind = ClassKind;
|
|
break;
|
|
case Decl::Var:
|
|
case Decl::Field:
|
|
case Decl::EnumConstant:
|
|
case Decl::ObjCIvar:
|
|
case Decl::ObjCAtDefsField:
|
|
case Decl::ObjCProperty: {
|
|
const TypeSourceInfo *TSI;
|
|
if (const auto *VD = dyn_cast<DeclaratorDecl>(CommentDecl))
|
|
TSI = VD->getTypeSourceInfo();
|
|
else if (const auto *PD = dyn_cast<ObjCPropertyDecl>(CommentDecl))
|
|
TSI = PD->getTypeSourceInfo();
|
|
else
|
|
TSI = nullptr;
|
|
if (TSI) {
|
|
TypeLoc TL = TSI->getTypeLoc().getUnqualifiedLoc();
|
|
FunctionTypeLoc FTL;
|
|
if (getFunctionTypeLoc(TL, FTL)) {
|
|
ParamVars = FTL.getParams();
|
|
ReturnType = FTL.getReturnLoc().getType();
|
|
}
|
|
}
|
|
Kind = VariableKind;
|
|
break;
|
|
}
|
|
case Decl::Namespace:
|
|
Kind = NamespaceKind;
|
|
break;
|
|
case Decl::TypeAlias:
|
|
case Decl::Typedef: {
|
|
Kind = TypedefKind;
|
|
// If this is a typedef / using to something we consider a function, extract
|
|
// arguments and return type.
|
|
const TypeSourceInfo *TSI =
|
|
K == Decl::Typedef
|
|
? cast<TypedefDecl>(CommentDecl)->getTypeSourceInfo()
|
|
: cast<TypeAliasDecl>(CommentDecl)->getTypeSourceInfo();
|
|
if (!TSI)
|
|
break;
|
|
TypeLoc TL = TSI->getTypeLoc().getUnqualifiedLoc();
|
|
FunctionTypeLoc FTL;
|
|
if (getFunctionTypeLoc(TL, FTL)) {
|
|
Kind = FunctionKind;
|
|
ParamVars = FTL.getParams();
|
|
ReturnType = FTL.getReturnLoc().getType();
|
|
}
|
|
break;
|
|
}
|
|
case Decl::TypeAliasTemplate: {
|
|
const TypeAliasTemplateDecl *TAT = cast<TypeAliasTemplateDecl>(CommentDecl);
|
|
Kind = TypedefKind;
|
|
TemplateKind = Template;
|
|
TemplateParameters = TAT->getTemplateParameters();
|
|
TypeAliasDecl *TAD = TAT->getTemplatedDecl();
|
|
if (!TAD)
|
|
break;
|
|
|
|
const TypeSourceInfo *TSI = TAD->getTypeSourceInfo();
|
|
if (!TSI)
|
|
break;
|
|
TypeLoc TL = TSI->getTypeLoc().getUnqualifiedLoc();
|
|
FunctionTypeLoc FTL;
|
|
if (getFunctionTypeLoc(TL, FTL)) {
|
|
Kind = FunctionKind;
|
|
ParamVars = FTL.getParams();
|
|
ReturnType = FTL.getReturnLoc().getType();
|
|
}
|
|
break;
|
|
}
|
|
case Decl::Enum:
|
|
Kind = EnumKind;
|
|
break;
|
|
}
|
|
|
|
IsFilled = true;
|
|
}
|
|
|
|
StringRef ParamCommandComment::getParamName(const FullComment *FC) const {
|
|
assert(isParamIndexValid());
|
|
if (isVarArgParam())
|
|
return "...";
|
|
return FC->getDeclInfo()->ParamVars[getParamIndex()]->getName();
|
|
}
|
|
|
|
StringRef TParamCommandComment::getParamName(const FullComment *FC) const {
|
|
assert(isPositionValid());
|
|
const TemplateParameterList *TPL = FC->getDeclInfo()->TemplateParameters;
|
|
for (unsigned i = 0, e = getDepth(); i != e; ++i) {
|
|
if (i == e-1)
|
|
return TPL->getParam(getIndex(i))->getName();
|
|
const NamedDecl *Param = TPL->getParam(getIndex(i));
|
|
if (const TemplateTemplateParmDecl *TTP =
|
|
dyn_cast<TemplateTemplateParmDecl>(Param))
|
|
TPL = TTP->getTemplateParameters();
|
|
}
|
|
return "";
|
|
}
|
|
|
|
} // end namespace comments
|
|
} // end namespace clang
|
|
|