//===--- 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/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 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(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(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(*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()) return AttributeTL.getModifiedLoc(); // Look through qualified types. if (QualifiedTypeLoc QualifiedTL = TL.getAs()) return QualifiedTL.getUnqualifiedLoc(); // Look through pointer types. if (PointerTypeLoc PointerTL = TL.getAs()) return PointerTL.getPointeeLoc().getUnqualifiedLoc(); // Look through reference types. if (ReferenceTypeLoc ReferenceTL = TL.getAs()) return ReferenceTL.getPointeeLoc().getUnqualifiedLoc(); // Look through adjusted types. if (AdjustedTypeLoc ATL = TL.getAs()) return ATL.getOriginalLoc(); if (BlockPointerTypeLoc BlockPointerTL = TL.getAs()) return BlockPointerTL.getPointeeLoc().getUnqualifiedLoc(); if (MemberPointerTypeLoc MemberPointerTL = TL.getAs()) return MemberPointerTL.getPointeeLoc().getUnqualifiedLoc(); if (ElaboratedTypeLoc ETL = TL.getAs()) 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()) { ResFTL = FTL; return true; } if (TemplateSpecializationTypeLoc STL = TL.getAs()) { // 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()) { 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(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(CommentDecl); IsInstanceMethod = MD->isInstance(); IsClassMethod = !IsInstanceMethod; } break; } case Decl::ObjCMethod: { const ObjCMethodDecl *MD = cast(CommentDecl); Kind = FunctionKind; ParamVars = MD->parameters(); ReturnType = MD->getReturnType(); IsObjCMethod = true; IsInstanceMethod = MD->isInstanceMethod(); IsClassMethod = !IsInstanceMethod; break; } case Decl::FunctionTemplate: { const FunctionTemplateDecl *FTD = cast(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(CommentDecl); Kind = ClassKind; TemplateKind = Template; TemplateParameters = CTD->getTemplateParameters(); break; } case Decl::ClassTemplatePartialSpecialization: { const ClassTemplatePartialSpecializationDecl *CTPSD = cast(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(CommentDecl)) TSI = VD->getTypeSourceInfo(); else if (const auto *PD = dyn_cast(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(CommentDecl)->getTypeSourceInfo() : cast(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(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(Param)) TPL = TTP->getTemplateParameters(); } return ""; } } // end namespace comments } // end namespace clang