123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832 |
- //===-- Serialize.cpp - ClangDoc Serializer ---------------------*- C++ -*-===//
- //
- // 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 "Serialize.h"
- #include "BitcodeWriter.h"
- #include "clang/AST/Comment.h"
- #include "clang/Index/USRGeneration.h"
- #include "clang/Lex/Lexer.h"
- #include "llvm/ADT/Hashing.h"
- #include "llvm/ADT/StringExtras.h"
- #include "llvm/Support/SHA1.h"
- using clang::comments::FullComment;
- namespace clang {
- namespace doc {
- namespace serialize {
- SymbolID hashUSR(llvm::StringRef USR) {
- return llvm::SHA1::hash(arrayRefFromStringRef(USR));
- }
- template <typename T>
- static void
- populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
- const T *D, bool &IsAnonymousNamespace);
- static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D);
- // A function to extract the appropriate relative path for a given info's
- // documentation. The path returned is a composite of the parent namespaces.
- //
- // Example: Given the below, the directory path for class C info will be
- // <root>/A/B
- //
- // namespace A {
- // namespace B {
- //
- // class C {};
- //
- // }
- // }
- llvm::SmallString<128>
- getInfoRelativePath(const llvm::SmallVectorImpl<doc::Reference> &Namespaces) {
- llvm::SmallString<128> Path;
- for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R)
- llvm::sys::path::append(Path, R->Name);
- return Path;
- }
- llvm::SmallString<128> getInfoRelativePath(const Decl *D) {
- llvm::SmallVector<Reference, 4> Namespaces;
- // The third arg in populateParentNamespaces is a boolean passed by reference,
- // its value is not relevant in here so it's not used anywhere besides the
- // function call
- bool B = true;
- populateParentNamespaces(Namespaces, D, B);
- return getInfoRelativePath(Namespaces);
- }
- class ClangDocCommentVisitor
- : public ConstCommentVisitor<ClangDocCommentVisitor> {
- public:
- ClangDocCommentVisitor(CommentInfo &CI) : CurrentCI(CI) {}
- void parseComment(const comments::Comment *C);
- void visitTextComment(const TextComment *C);
- void visitInlineCommandComment(const InlineCommandComment *C);
- void visitHTMLStartTagComment(const HTMLStartTagComment *C);
- void visitHTMLEndTagComment(const HTMLEndTagComment *C);
- void visitBlockCommandComment(const BlockCommandComment *C);
- void visitParamCommandComment(const ParamCommandComment *C);
- void visitTParamCommandComment(const TParamCommandComment *C);
- void visitVerbatimBlockComment(const VerbatimBlockComment *C);
- void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
- void visitVerbatimLineComment(const VerbatimLineComment *C);
- private:
- std::string getCommandName(unsigned CommandID) const;
- bool isWhitespaceOnly(StringRef S) const;
- CommentInfo &CurrentCI;
- };
- void ClangDocCommentVisitor::parseComment(const comments::Comment *C) {
- CurrentCI.Kind = C->getCommentKindName();
- ConstCommentVisitor<ClangDocCommentVisitor>::visit(C);
- for (comments::Comment *Child :
- llvm::make_range(C->child_begin(), C->child_end())) {
- CurrentCI.Children.emplace_back(std::make_unique<CommentInfo>());
- ClangDocCommentVisitor Visitor(*CurrentCI.Children.back());
- Visitor.parseComment(Child);
- }
- }
- void ClangDocCommentVisitor::visitTextComment(const TextComment *C) {
- if (!isWhitespaceOnly(C->getText()))
- CurrentCI.Text = C->getText();
- }
- void ClangDocCommentVisitor::visitInlineCommandComment(
- const InlineCommandComment *C) {
- CurrentCI.Name = getCommandName(C->getCommandID());
- for (unsigned I = 0, E = C->getNumArgs(); I != E; ++I)
- CurrentCI.Args.push_back(C->getArgText(I));
- }
- void ClangDocCommentVisitor::visitHTMLStartTagComment(
- const HTMLStartTagComment *C) {
- CurrentCI.Name = C->getTagName();
- CurrentCI.SelfClosing = C->isSelfClosing();
- for (unsigned I = 0, E = C->getNumAttrs(); I < E; ++I) {
- const HTMLStartTagComment::Attribute &Attr = C->getAttr(I);
- CurrentCI.AttrKeys.push_back(Attr.Name);
- CurrentCI.AttrValues.push_back(Attr.Value);
- }
- }
- void ClangDocCommentVisitor::visitHTMLEndTagComment(
- const HTMLEndTagComment *C) {
- CurrentCI.Name = C->getTagName();
- CurrentCI.SelfClosing = true;
- }
- void ClangDocCommentVisitor::visitBlockCommandComment(
- const BlockCommandComment *C) {
- CurrentCI.Name = getCommandName(C->getCommandID());
- for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I)
- CurrentCI.Args.push_back(C->getArgText(I));
- }
- void ClangDocCommentVisitor::visitParamCommandComment(
- const ParamCommandComment *C) {
- CurrentCI.Direction =
- ParamCommandComment::getDirectionAsString(C->getDirection());
- CurrentCI.Explicit = C->isDirectionExplicit();
- if (C->hasParamName())
- CurrentCI.ParamName = C->getParamNameAsWritten();
- }
- void ClangDocCommentVisitor::visitTParamCommandComment(
- const TParamCommandComment *C) {
- if (C->hasParamName())
- CurrentCI.ParamName = C->getParamNameAsWritten();
- }
- void ClangDocCommentVisitor::visitVerbatimBlockComment(
- const VerbatimBlockComment *C) {
- CurrentCI.Name = getCommandName(C->getCommandID());
- CurrentCI.CloseName = C->getCloseName();
- }
- void ClangDocCommentVisitor::visitVerbatimBlockLineComment(
- const VerbatimBlockLineComment *C) {
- if (!isWhitespaceOnly(C->getText()))
- CurrentCI.Text = C->getText();
- }
- void ClangDocCommentVisitor::visitVerbatimLineComment(
- const VerbatimLineComment *C) {
- if (!isWhitespaceOnly(C->getText()))
- CurrentCI.Text = C->getText();
- }
- bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const {
- return llvm::all_of(S, isspace);
- }
- std::string ClangDocCommentVisitor::getCommandName(unsigned CommandID) const {
- const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID);
- if (Info)
- return Info->Name;
- // TODO: Add parsing for \file command.
- return "<not a builtin command>";
- }
- // Serializing functions.
- std::string getSourceCode(const Decl *D, const SourceRange &R) {
- return Lexer::getSourceText(CharSourceRange::getTokenRange(R),
- D->getASTContext().getSourceManager(),
- D->getASTContext().getLangOpts())
- .str();
- }
- template <typename T> static std::string serialize(T &I) {
- SmallString<2048> Buffer;
- llvm::BitstreamWriter Stream(Buffer);
- ClangDocBitcodeWriter Writer(Stream);
- Writer.emitBlock(I);
- return Buffer.str().str();
- }
- std::string serialize(std::unique_ptr<Info> &I) {
- switch (I->IT) {
- case InfoType::IT_namespace:
- return serialize(*static_cast<NamespaceInfo *>(I.get()));
- case InfoType::IT_record:
- return serialize(*static_cast<RecordInfo *>(I.get()));
- case InfoType::IT_enum:
- return serialize(*static_cast<EnumInfo *>(I.get()));
- case InfoType::IT_function:
- return serialize(*static_cast<FunctionInfo *>(I.get()));
- default:
- return "";
- }
- }
- static void parseFullComment(const FullComment *C, CommentInfo &CI) {
- ClangDocCommentVisitor Visitor(CI);
- Visitor.parseComment(C);
- }
- static SymbolID getUSRForDecl(const Decl *D) {
- llvm::SmallString<128> USR;
- if (index::generateUSRForDecl(D, USR))
- return SymbolID();
- return hashUSR(USR);
- }
- static TagDecl *getTagDeclForType(const QualType &T) {
- if (const TagDecl *D = T->getAsTagDecl())
- return D->getDefinition();
- return nullptr;
- }
- static RecordDecl *getRecordDeclForType(const QualType &T) {
- if (const RecordDecl *D = T->getAsRecordDecl())
- return D->getDefinition();
- return nullptr;
- }
- TypeInfo getTypeInfoForType(const QualType &T) {
- const TagDecl *TD = getTagDeclForType(T);
- if (!TD)
- return TypeInfo(Reference(SymbolID(), T.getAsString()));
- InfoType IT;
- if (dyn_cast<EnumDecl>(TD)) {
- IT = InfoType::IT_enum;
- } else if (dyn_cast<RecordDecl>(TD)) {
- IT = InfoType::IT_record;
- } else {
- IT = InfoType::IT_default;
- }
- return TypeInfo(Reference(getUSRForDecl(TD), TD->getNameAsString(), IT,
- T.getAsString(), getInfoRelativePath(TD)));
- }
- static bool isPublic(const clang::AccessSpecifier AS,
- const clang::Linkage Link) {
- if (AS == clang::AccessSpecifier::AS_private)
- return false;
- else if ((Link == clang::Linkage::ModuleLinkage) ||
- (Link == clang::Linkage::ExternalLinkage))
- return true;
- return false; // otherwise, linkage is some form of internal linkage
- }
- static bool shouldSerializeInfo(bool PublicOnly, bool IsInAnonymousNamespace,
- const NamedDecl *D) {
- bool IsAnonymousNamespace = false;
- if (const auto *N = dyn_cast<NamespaceDecl>(D))
- IsAnonymousNamespace = N->isAnonymousNamespace();
- return !PublicOnly ||
- (!IsInAnonymousNamespace && !IsAnonymousNamespace &&
- isPublic(D->getAccessUnsafe(), D->getLinkageInternal()));
- }
- // The InsertChild functions insert the given info into the given scope using
- // the method appropriate for that type. Some types are moved into the
- // appropriate vector, while other types have Reference objects generated to
- // refer to them.
- //
- // See MakeAndInsertIntoParent().
- static void InsertChild(ScopeChildren &Scope, const NamespaceInfo &Info) {
- Scope.Namespaces.emplace_back(Info.USR, Info.Name, InfoType::IT_namespace,
- Info.Name, getInfoRelativePath(Info.Namespace));
- }
- static void InsertChild(ScopeChildren &Scope, const RecordInfo &Info) {
- Scope.Records.emplace_back(Info.USR, Info.Name, InfoType::IT_record,
- Info.Name, getInfoRelativePath(Info.Namespace));
- }
- static void InsertChild(ScopeChildren &Scope, EnumInfo Info) {
- Scope.Enums.push_back(std::move(Info));
- }
- static void InsertChild(ScopeChildren &Scope, FunctionInfo Info) {
- Scope.Functions.push_back(std::move(Info));
- }
- static void InsertChild(ScopeChildren &Scope, TypedefInfo Info) {
- Scope.Typedefs.push_back(std::move(Info));
- }
- // Creates a parent of the correct type for the given child and inserts it into
- // that parent.
- //
- // This is complicated by the fact that namespaces and records are inserted by
- // reference (constructing a "Reference" object with that namespace/record's
- // info), while everything else is inserted by moving it directly into the child
- // vectors.
- //
- // For namespaces and records, explicitly specify a const& template parameter
- // when invoking this function:
- // MakeAndInsertIntoParent<const Record&>(...);
- // Otherwise, specify an rvalue reference <EnumInfo&&> and move into the
- // parameter. Since each variant is used once, it's not worth having a more
- // elaborate system to automatically deduce this information.
- template <typename ChildType>
- std::unique_ptr<Info> MakeAndInsertIntoParent(ChildType Child) {
- if (Child.Namespace.empty()) {
- // Insert into unnamed parent namespace.
- auto ParentNS = std::make_unique<NamespaceInfo>();
- InsertChild(ParentNS->Children, std::forward<ChildType>(Child));
- return ParentNS;
- }
- switch (Child.Namespace[0].RefType) {
- case InfoType::IT_namespace: {
- auto ParentNS = std::make_unique<NamespaceInfo>();
- ParentNS->USR = Child.Namespace[0].USR;
- InsertChild(ParentNS->Children, std::forward<ChildType>(Child));
- return ParentNS;
- }
- case InfoType::IT_record: {
- auto ParentRec = std::make_unique<RecordInfo>();
- ParentRec->USR = Child.Namespace[0].USR;
- InsertChild(ParentRec->Children, std::forward<ChildType>(Child));
- return ParentRec;
- }
- default:
- llvm_unreachable("Invalid reference type for parent namespace");
- }
- }
- // There are two uses for this function.
- // 1) Getting the resulting mode of inheritance of a record.
- // Example: class A {}; class B : private A {}; class C : public B {};
- // It's explicit that C is publicly inherited from C and B is privately
- // inherited from A. It's not explicit but C is also privately inherited from
- // A. This is the AS that this function calculates. FirstAS is the
- // inheritance mode of `class C : B` and SecondAS is the inheritance mode of
- // `class B : A`.
- // 2) Getting the inheritance mode of an inherited attribute / method.
- // Example : class A { public: int M; }; class B : private A {};
- // Class B is inherited from class A, which has a public attribute. This
- // attribute is now part of the derived class B but it's not public. This
- // will be private because the inheritance is private. This is the AS that
- // this function calculates. FirstAS is the inheritance mode and SecondAS is
- // the AS of the attribute / method.
- static AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS,
- AccessSpecifier SecondAS) {
- if (FirstAS == AccessSpecifier::AS_none ||
- SecondAS == AccessSpecifier::AS_none)
- return AccessSpecifier::AS_none;
- if (FirstAS == AccessSpecifier::AS_private ||
- SecondAS == AccessSpecifier::AS_private)
- return AccessSpecifier::AS_private;
- if (FirstAS == AccessSpecifier::AS_protected ||
- SecondAS == AccessSpecifier::AS_protected)
- return AccessSpecifier::AS_protected;
- return AccessSpecifier::AS_public;
- }
- // The Access parameter is only provided when parsing the field of an inherited
- // record, the access specification of the field depends on the inheritance mode
- static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly,
- AccessSpecifier Access = AccessSpecifier::AS_public) {
- for (const FieldDecl *F : D->fields()) {
- if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F))
- continue;
- // Use getAccessUnsafe so that we just get the default AS_none if it's not
- // valid, as opposed to an assert.
- MemberTypeInfo &NewMember = I.Members.emplace_back(
- getTypeInfoForType(F->getTypeSourceInfo()->getType()),
- F->getNameAsString(),
- getFinalAccessSpecifier(Access, F->getAccessUnsafe()));
- populateMemberTypeInfo(NewMember, F);
- }
- }
- static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
- for (const EnumConstantDecl *E : D->enumerators()) {
- std::string ValueExpr;
- if (const Expr *InitExpr = E->getInitExpr())
- ValueExpr = getSourceCode(D, InitExpr->getSourceRange());
- SmallString<16> ValueStr;
- E->getInitVal().toString(ValueStr);
- I.Members.emplace_back(E->getNameAsString(), ValueStr, ValueExpr);
- }
- }
- static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
- for (const ParmVarDecl *P : D->parameters()) {
- FieldTypeInfo &FieldInfo = I.Params.emplace_back(
- getTypeInfoForType(P->getOriginalType()), P->getNameAsString());
- FieldInfo.DefaultValue = getSourceCode(D, P->getDefaultArgRange());
- }
- }
- // TODO: Remove the serialization of Parents and VirtualParents, this
- // information is also extracted in the other definition of parseBases.
- static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
- // Don't parse bases if this isn't a definition.
- if (!D->isThisDeclarationADefinition())
- return;
- for (const CXXBaseSpecifier &B : D->bases()) {
- if (B.isVirtual())
- continue;
- if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
- const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
- I.Parents.emplace_back(getUSRForDecl(D), B.getType().getAsString(),
- InfoType::IT_record, B.getType().getAsString());
- } else if (const RecordDecl *P = getRecordDeclForType(B.getType()))
- I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
- InfoType::IT_record, P->getQualifiedNameAsString(),
- getInfoRelativePath(P));
- else
- I.Parents.emplace_back(SymbolID(), B.getType().getAsString());
- }
- for (const CXXBaseSpecifier &B : D->vbases()) {
- if (const RecordDecl *P = getRecordDeclForType(B.getType()))
- I.VirtualParents.emplace_back(
- getUSRForDecl(P), P->getNameAsString(), InfoType::IT_record,
- P->getQualifiedNameAsString(), getInfoRelativePath(P));
- else
- I.VirtualParents.emplace_back(SymbolID(), B.getType().getAsString());
- }
- }
- template <typename T>
- static void
- populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
- const T *D, bool &IsInAnonymousNamespace) {
- const DeclContext *DC = D->getDeclContext();
- do {
- if (const auto *N = dyn_cast<NamespaceDecl>(DC)) {
- std::string Namespace;
- if (N->isAnonymousNamespace()) {
- Namespace = "@nonymous_namespace";
- IsInAnonymousNamespace = true;
- } else
- Namespace = N->getNameAsString();
- Namespaces.emplace_back(getUSRForDecl(N), Namespace,
- InfoType::IT_namespace,
- N->getQualifiedNameAsString());
- } else if (const auto *N = dyn_cast<RecordDecl>(DC))
- Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
- InfoType::IT_record,
- N->getQualifiedNameAsString());
- else if (const auto *N = dyn_cast<FunctionDecl>(DC))
- Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
- InfoType::IT_function,
- N->getQualifiedNameAsString());
- else if (const auto *N = dyn_cast<EnumDecl>(DC))
- Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
- InfoType::IT_enum, N->getQualifiedNameAsString());
- } while ((DC = DC->getParent()));
- // The global namespace should be added to the list of namespaces if the decl
- // corresponds to a Record and if it doesn't have any namespace (because this
- // means it's in the global namespace). Also if its outermost namespace is a
- // record because that record matches the previous condition mentioned.
- if ((Namespaces.empty() && isa<RecordDecl>(D)) ||
- (!Namespaces.empty() && Namespaces.back().RefType == InfoType::IT_record))
- Namespaces.emplace_back(SymbolID(), "GlobalNamespace",
- InfoType::IT_namespace);
- }
- void PopulateTemplateParameters(std::optional<TemplateInfo> &TemplateInfo,
- const clang::Decl *D) {
- if (const TemplateParameterList *ParamList =
- D->getDescribedTemplateParams()) {
- if (!TemplateInfo) {
- TemplateInfo.emplace();
- }
- for (const NamedDecl *ND : *ParamList) {
- TemplateInfo->Params.emplace_back(
- getSourceCode(ND, ND->getSourceRange()));
- }
- }
- }
- TemplateParamInfo TemplateArgumentToInfo(const clang::Decl *D,
- const TemplateArgument &Arg) {
- // The TemplateArgument's pretty printing handles all the normal cases
- // well enough for our requirements.
- std::string Str;
- llvm::raw_string_ostream Stream(Str);
- Arg.print(PrintingPolicy(D->getLangOpts()), Stream, false);
- return TemplateParamInfo(Str);
- }
- template <typename T>
- static void populateInfo(Info &I, const T *D, const FullComment *C,
- bool &IsInAnonymousNamespace) {
- I.USR = getUSRForDecl(D);
- I.Name = D->getNameAsString();
- populateParentNamespaces(I.Namespace, D, IsInAnonymousNamespace);
- if (C) {
- I.Description.emplace_back();
- parseFullComment(C, I.Description.back());
- }
- }
- template <typename T>
- static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
- int LineNumber, StringRef Filename,
- bool IsFileInRootDir,
- bool &IsInAnonymousNamespace) {
- populateInfo(I, D, C, IsInAnonymousNamespace);
- if (D->isThisDeclarationADefinition())
- I.DefLoc.emplace(LineNumber, Filename, IsFileInRootDir);
- else
- I.Loc.emplace_back(LineNumber, Filename, IsFileInRootDir);
- }
- static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
- const FullComment *FC, int LineNumber,
- StringRef Filename, bool IsFileInRootDir,
- bool &IsInAnonymousNamespace) {
- populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir,
- IsInAnonymousNamespace);
- I.ReturnType = getTypeInfoForType(D->getReturnType());
- parseParameters(I, D);
- PopulateTemplateParameters(I.Template, D);
- // Handle function template specializations.
- if (const FunctionTemplateSpecializationInfo *FTSI =
- D->getTemplateSpecializationInfo()) {
- if (!I.Template)
- I.Template.emplace();
- I.Template->Specialization.emplace();
- auto &Specialization = *I.Template->Specialization;
- Specialization.SpecializationOf = getUSRForDecl(FTSI->getTemplate());
- // Template parameters to the specialization.
- if (FTSI->TemplateArguments) {
- for (const TemplateArgument &Arg : FTSI->TemplateArguments->asArray()) {
- Specialization.Params.push_back(TemplateArgumentToInfo(D, Arg));
- }
- }
- }
- }
- static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {
- assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");
- ASTContext& Context = D->getASTContext();
- // TODO investigate whether we can use ASTContext::getCommentForDecl instead
- // of this logic. See also similar code in Mapper.cpp.
- RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
- if (!Comment)
- return;
- Comment->setAttached();
- if (comments::FullComment* fc = Comment->parse(Context, nullptr, D)) {
- I.Description.emplace_back();
- parseFullComment(fc, I.Description.back());
- }
- }
- static void
- parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
- bool PublicOnly, bool IsParent,
- AccessSpecifier ParentAccess = AccessSpecifier::AS_public) {
- // Don't parse bases if this isn't a definition.
- if (!D->isThisDeclarationADefinition())
- return;
- for (const CXXBaseSpecifier &B : D->bases()) {
- if (const RecordType *Ty = B.getType()->getAs<RecordType>()) {
- if (const CXXRecordDecl *Base =
- cast_or_null<CXXRecordDecl>(Ty->getDecl()->getDefinition())) {
- // Initialized without USR and name, this will be set in the following
- // if-else stmt.
- BaseRecordInfo BI(
- {}, "", getInfoRelativePath(Base), B.isVirtual(),
- getFinalAccessSpecifier(ParentAccess, B.getAccessSpecifier()),
- IsParent);
- if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
- const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
- BI.USR = getUSRForDecl(D);
- BI.Name = B.getType().getAsString();
- } else {
- BI.USR = getUSRForDecl(Base);
- BI.Name = Base->getNameAsString();
- }
- parseFields(BI, Base, PublicOnly, BI.Access);
- for (const auto &Decl : Base->decls())
- if (const auto *MD = dyn_cast<CXXMethodDecl>(Decl)) {
- // Don't serialize private methods
- if (MD->getAccessUnsafe() == AccessSpecifier::AS_private ||
- !MD->isUserProvided())
- continue;
- FunctionInfo FI;
- FI.IsMethod = true;
- // The seventh arg in populateFunctionInfo is a boolean passed by
- // reference, its value is not relevant in here so it's not used
- // anywhere besides the function call.
- bool IsInAnonymousNamespace;
- populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*LineNumber=*/{},
- /*FileName=*/{}, IsFileInRootDir,
- IsInAnonymousNamespace);
- FI.Access =
- getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe());
- BI.Children.Functions.emplace_back(std::move(FI));
- }
- I.Bases.emplace_back(std::move(BI));
- // Call this function recursively to get the inherited classes of
- // this base; these new bases will also get stored in the original
- // RecordInfo: I.
- parseBases(I, Base, IsFileInRootDir, PublicOnly, false,
- I.Bases.back().Access);
- }
- }
- }
- }
- std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
- emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
- llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
- auto I = std::make_unique<NamespaceInfo>();
- bool IsInAnonymousNamespace = false;
- populateInfo(*I, D, FC, IsInAnonymousNamespace);
- if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
- return {};
- I->Name = D->isAnonymousNamespace()
- ? llvm::SmallString<16>("@nonymous_namespace")
- : I->Name;
- I->Path = getInfoRelativePath(I->Namespace);
- if (I->Namespace.empty() && I->USR == SymbolID())
- return {std::unique_ptr<Info>{std::move(I)}, nullptr};
- // Namespaces are inserted into the parent by reference, so we need to return
- // both the parent and the record itself.
- return {std::move(I), MakeAndInsertIntoParent<const NamespaceInfo &>(*I)};
- }
- std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
- emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
- llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
- auto I = std::make_unique<RecordInfo>();
- bool IsInAnonymousNamespace = false;
- populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir,
- IsInAnonymousNamespace);
- if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
- return {};
- I->TagType = D->getTagKind();
- parseFields(*I, D, PublicOnly);
- if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
- if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) {
- I->Name = TD->getNameAsString();
- I->IsTypeDef = true;
- }
- // TODO: remove first call to parseBases, that function should be deleted
- parseBases(*I, C);
- parseBases(*I, C, IsFileInRootDir, PublicOnly, true);
- }
- I->Path = getInfoRelativePath(I->Namespace);
- PopulateTemplateParameters(I->Template, D);
- // Full and partial specializations.
- if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
- if (!I->Template)
- I->Template.emplace();
- I->Template->Specialization.emplace();
- auto &Specialization = *I->Template->Specialization;
- // What this is a specialization of.
- auto SpecOf = CTSD->getSpecializedTemplateOrPartial();
- if (SpecOf.is<ClassTemplateDecl *>()) {
- Specialization.SpecializationOf =
- getUSRForDecl(SpecOf.get<ClassTemplateDecl *>());
- } else if (SpecOf.is<ClassTemplatePartialSpecializationDecl *>()) {
- Specialization.SpecializationOf =
- getUSRForDecl(SpecOf.get<ClassTemplatePartialSpecializationDecl *>());
- }
- // Parameters to the specilization. For partial specializations, get the
- // parameters "as written" from the ClassTemplatePartialSpecializationDecl
- // because the non-explicit template parameters will have generated internal
- // placeholder names rather than the names the user typed that match the
- // template parameters.
- if (const ClassTemplatePartialSpecializationDecl *CTPSD =
- dyn_cast<ClassTemplatePartialSpecializationDecl>(D)) {
- if (const ASTTemplateArgumentListInfo *AsWritten =
- CTPSD->getTemplateArgsAsWritten()) {
- for (unsigned i = 0; i < AsWritten->getNumTemplateArgs(); i++) {
- Specialization.Params.emplace_back(
- getSourceCode(D, (*AsWritten)[i].getSourceRange()));
- }
- }
- } else {
- for (const TemplateArgument &Arg : CTSD->getTemplateArgs().asArray()) {
- Specialization.Params.push_back(TemplateArgumentToInfo(D, Arg));
- }
- }
- }
- // Records are inserted into the parent by reference, so we need to return
- // both the parent and the record itself.
- auto Parent = MakeAndInsertIntoParent<const RecordInfo &>(*I);
- return {std::move(I), std::move(Parent)};
- }
- std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
- emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
- llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
- FunctionInfo Func;
- bool IsInAnonymousNamespace = false;
- populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
- IsInAnonymousNamespace);
- Func.Access = clang::AccessSpecifier::AS_none;
- if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
- return {};
- // Info is wrapped in its parent scope so is returned in the second position.
- return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
- }
- std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
- emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
- llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
- FunctionInfo Func;
- bool IsInAnonymousNamespace = false;
- populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
- IsInAnonymousNamespace);
- if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
- return {};
- Func.IsMethod = true;
- const NamedDecl *Parent = nullptr;
- if (const auto *SD =
- dyn_cast<ClassTemplateSpecializationDecl>(D->getParent()))
- Parent = SD->getSpecializedTemplate();
- else
- Parent = D->getParent();
- SymbolID ParentUSR = getUSRForDecl(Parent);
- Func.Parent =
- Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record,
- Parent->getQualifiedNameAsString()};
- Func.Access = D->getAccess();
- // Info is wrapped in its parent scope so is returned in the second position.
- return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
- }
- std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
- emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
- StringRef File, bool IsFileInRootDir, bool PublicOnly) {
- TypedefInfo Info;
- bool IsInAnonymousNamespace = false;
- populateInfo(Info, D, FC, IsInAnonymousNamespace);
- if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
- return {};
- Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
- Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
- if (Info.Underlying.Type.Name.empty()) {
- // Typedef for an unnamed type. This is like "typedef struct { } Foo;"
- // The record serializer explicitly checks for this syntax and constructs
- // a record with that name, so we don't want to emit a duplicate here.
- return {};
- }
- Info.IsUsing = false;
- // Info is wrapped in its parent scope so is returned in the second position.
- return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
- }
- // A type alias is a C++ "using" declaration for a type. It gets mapped to a
- // TypedefInfo with the IsUsing flag set.
- std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
- emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
- StringRef File, bool IsFileInRootDir, bool PublicOnly) {
- TypedefInfo Info;
- bool IsInAnonymousNamespace = false;
- populateInfo(Info, D, FC, IsInAnonymousNamespace);
- if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
- return {};
- Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
- Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
- Info.IsUsing = true;
- // Info is wrapped in its parent scope so is returned in the second position.
- return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
- }
- std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
- emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
- llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
- EnumInfo Enum;
- bool IsInAnonymousNamespace = false;
- populateSymbolInfo(Enum, D, FC, LineNumber, File, IsFileInRootDir,
- IsInAnonymousNamespace);
- if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
- return {};
- Enum.Scoped = D->isScoped();
- if (D->isFixed()) {
- auto Name = D->getIntegerType().getAsString();
- Enum.BaseType = TypeInfo(Name, Name);
- }
- parseEnumerators(Enum, D);
- // Info is wrapped in its parent scope so is returned in the second position.
- return {nullptr, MakeAndInsertIntoParent<EnumInfo &&>(std::move(Enum))};
- }
- } // namespace serialize
- } // namespace doc
- } // namespace clang
|