You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
347 lines
10 KiB
347 lines
10 KiB
//===--- Comment.cpp - Comment AST node implementation --------------------===// |
|
// |
|
// The LLVM Compiler Infrastructure |
|
// |
|
// This file is distributed under the University of Illinois Open Source |
|
// License. See LICENSE.TXT for details. |
|
// |
|
//===----------------------------------------------------------------------===// |
|
|
|
#include "clang/AST/ASTContext.h" |
|
#include "clang/AST/Comment.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" |
|
#include "llvm/Support/raw_ostream.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; |
|
} |
|
|
|
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: |
|
Kind = VariableKind; |
|
break; |
|
case Decl::Namespace: |
|
Kind = NamespaceKind; |
|
break; |
|
case Decl::Typedef: { |
|
Kind = TypedefKind; |
|
// If this is a typedef to something we consider a function, extract |
|
// arguments and return type. |
|
const TypedefDecl *TD = cast<TypedefDecl>(CommentDecl); |
|
const TypeSourceInfo *TSI = TD->getTypeSourceInfo(); |
|
if (!TSI) |
|
break; |
|
TypeLoc TL = TSI->getTypeLoc().getUnqualifiedLoc(); |
|
while (true) { |
|
TL = TL.IgnoreParens(); |
|
// Look through qualified types. |
|
if (QualifiedTypeLoc QualifiedTL = TL.getAs<QualifiedTypeLoc>()) { |
|
TL = QualifiedTL.getUnqualifiedLoc(); |
|
continue; |
|
} |
|
// Look through pointer types. |
|
if (PointerTypeLoc PointerTL = TL.getAs<PointerTypeLoc>()) { |
|
TL = PointerTL.getPointeeLoc().getUnqualifiedLoc(); |
|
continue; |
|
} |
|
// Look through reference types. |
|
if (ReferenceTypeLoc ReferenceTL = TL.getAs<ReferenceTypeLoc>()) { |
|
TL = ReferenceTL.getPointeeLoc().getUnqualifiedLoc(); |
|
continue; |
|
} |
|
// Look through adjusted types. |
|
if (AdjustedTypeLoc ATL = TL.getAs<AdjustedTypeLoc>()) { |
|
TL = ATL.getOriginalLoc(); |
|
continue; |
|
} |
|
if (BlockPointerTypeLoc BlockPointerTL = |
|
TL.getAs<BlockPointerTypeLoc>()) { |
|
TL = BlockPointerTL.getPointeeLoc().getUnqualifiedLoc(); |
|
continue; |
|
} |
|
if (MemberPointerTypeLoc MemberPointerTL = |
|
TL.getAs<MemberPointerTypeLoc>()) { |
|
TL = MemberPointerTL.getPointeeLoc().getUnqualifiedLoc(); |
|
continue; |
|
} |
|
if (ElaboratedTypeLoc ETL = TL.getAs<ElaboratedTypeLoc>()) { |
|
TL = ETL.getNamedTypeLoc(); |
|
continue; |
|
} |
|
// Is this a typedef for a function type? |
|
if (FunctionTypeLoc FTL = TL.getAs<FunctionTypeLoc>()) { |
|
Kind = FunctionKind; |
|
ParamVars = FTL.getParams(); |
|
ReturnType = FTL.getReturnLoc().getType(); |
|
break; |
|
} |
|
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) |
|
break; |
|
TemplateArgumentLoc MaybeFunction = STL.getArgLoc(0); |
|
if (MaybeFunction.getArgument().getKind() != TemplateArgument::Type) |
|
break; |
|
TypeSourceInfo *MaybeFunctionTSI = MaybeFunction.getTypeSourceInfo(); |
|
TypeLoc TL = MaybeFunctionTSI->getTypeLoc().getUnqualifiedLoc(); |
|
if (FunctionTypeLoc FTL = TL.getAs<FunctionTypeLoc>()) { |
|
Kind = FunctionKind; |
|
ParamVars = FTL.getParams(); |
|
ReturnType = FTL.getReturnLoc().getType(); |
|
} |
|
break; |
|
} |
|
break; |
|
} |
|
break; |
|
} |
|
case Decl::TypeAlias: |
|
Kind = TypedefKind; |
|
break; |
|
case Decl::TypeAliasTemplate: { |
|
const TypeAliasTemplateDecl *TAT = cast<TypeAliasTemplateDecl>(CommentDecl); |
|
Kind = TypedefKind; |
|
TemplateKind = Template; |
|
TemplateParameters = TAT->getTemplateParameters(); |
|
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 |
|
|
|
|