2019-03-01 09:52:53 +00:00
|
|
|
//===-- Serialize.cpp - ClangDoc Serializer ---------------------*- C++ -*-===//
|
2018-03-22 23:34:46 +00:00
|
|
|
//
|
2019-01-19 08:50:56 +00:00
|
|
|
// 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
|
2018-03-22 23:34:46 +00:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "Serialize.h"
|
|
|
|
#include "BitcodeWriter.h"
|
|
|
|
#include "clang/AST/Comment.h"
|
|
|
|
#include "clang/Index/USRGeneration.h"
|
2022-09-16 17:24:51 +00:00
|
|
|
#include "clang/Lex/Lexer.h"
|
2018-03-22 23:34:46 +00:00
|
|
|
#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));
|
|
|
|
}
|
|
|
|
|
2019-07-12 18:32:00 +00:00
|
|
|
template <typename T>
|
|
|
|
static void
|
|
|
|
populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
|
|
|
|
const T *D, bool &IsAnonymousNamespace);
|
|
|
|
|
2025-04-18 18:08:06 -07:00
|
|
|
static void populateMemberTypeInfo(MemberTypeInfo &I, const Decl *D);
|
|
|
|
static void populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access,
|
|
|
|
const DeclaratorDecl *D,
|
|
|
|
bool IsStatic = false);
|
2022-08-11 17:11:23 +00:00
|
|
|
|
2019-07-12 18:32:00 +00:00
|
|
|
// A function to extract the appropriate relative path for a given info's
|
|
|
|
// documentation. The path returned is a composite of the parent namespaces.
|
|
|
|
//
|
2020-01-04 10:28:41 -05:00
|
|
|
// Example: Given the below, the directory path for class C info will be
|
2019-07-12 18:32:00 +00:00
|
|
|
// <root>/A/B
|
|
|
|
//
|
|
|
|
// namespace A {
|
2020-04-05 15:28:11 +09:00
|
|
|
// namespace B {
|
2019-07-12 18:32:00 +00:00
|
|
|
//
|
|
|
|
// class C {};
|
|
|
|
//
|
|
|
|
// }
|
|
|
|
// }
|
2025-04-18 23:34:57 -07:00
|
|
|
static llvm::SmallString<128>
|
2019-07-12 18:32:00 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2025-04-18 23:34:57 -07:00
|
|
|
static llvm::SmallString<128> getInfoRelativePath(const Decl *D) {
|
2019-07-12 18:32:00 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-03-22 23:34:46 +00:00
|
|
|
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())) {
|
2019-08-14 23:52:23 +00:00
|
|
|
CurrentCI.Children.emplace_back(std::make_unique<CommentInfo>());
|
2018-03-22 23:34:46 +00:00
|
|
|
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 {
|
2022-08-14 16:25:36 -07:00
|
|
|
return llvm::all_of(S, isspace);
|
2018-03-22 23:34:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
2025-04-18 23:34:57 -07:00
|
|
|
static std::string getSourceCode(const Decl *D, const SourceRange &R) {
|
2022-09-19 21:52:24 +00:00
|
|
|
return Lexer::getSourceText(CharSourceRange::getTokenRange(R),
|
|
|
|
D->getASTContext().getSourceManager(),
|
|
|
|
D->getASTContext().getLangOpts())
|
|
|
|
.str();
|
|
|
|
}
|
|
|
|
|
2018-03-22 23:34:46 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2018-08-02 20:10:17 +00:00
|
|
|
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 "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-22 23:34:46 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-09-20 17:54:28 +00:00
|
|
|
static TagDecl *getTagDeclForType(const QualType &T) {
|
|
|
|
if (const TagDecl *D = T->getAsTagDecl())
|
|
|
|
return D->getDefinition();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static RecordDecl *getRecordDeclForType(const QualType &T) {
|
2019-06-24 19:31:02 +00:00
|
|
|
if (const RecordDecl *D = T->getAsRecordDecl())
|
|
|
|
return D->getDefinition();
|
|
|
|
return nullptr;
|
2018-03-22 23:34:46 +00:00
|
|
|
}
|
|
|
|
|
2025-04-18 23:34:57 -07:00
|
|
|
static TypeInfo getTypeInfoForType(const QualType &T,
|
|
|
|
const PrintingPolicy &Policy) {
|
2022-09-20 17:54:28 +00:00
|
|
|
const TagDecl *TD = getTagDeclForType(T);
|
|
|
|
if (!TD)
|
2024-12-18 08:39:19 -08:00
|
|
|
return TypeInfo(Reference(SymbolID(), T.getAsString(Policy)));
|
2022-09-20 17:54:28 +00:00
|
|
|
|
|
|
|
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,
|
2024-12-18 08:39:19 -08:00
|
|
|
T.getAsString(Policy), getInfoRelativePath(TD)));
|
2022-09-20 17:54:28 +00:00
|
|
|
}
|
|
|
|
|
2018-07-20 18:49:55 +00:00
|
|
|
static bool isPublic(const clang::AccessSpecifier AS,
|
|
|
|
const clang::Linkage Link) {
|
|
|
|
if (AS == clang::AccessSpecifier::AS_private)
|
|
|
|
return false;
|
2023-11-02 20:57:29 +04:00
|
|
|
else if ((Link == clang::Linkage::Module) ||
|
|
|
|
(Link == clang::Linkage::External))
|
2018-07-20 18:49:55 +00:00
|
|
|
return true;
|
|
|
|
return false; // otherwise, linkage is some form of internal linkage
|
|
|
|
}
|
|
|
|
|
2019-08-16 00:10:49 +00:00
|
|
|
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()));
|
|
|
|
}
|
|
|
|
|
2022-10-14 14:28:43 -07:00
|
|
|
// 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,
|
2022-12-07 10:26:04 -08:00
|
|
|
Info.Name, getInfoRelativePath(Info.Namespace));
|
2022-10-14 14:28:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void InsertChild(ScopeChildren &Scope, const RecordInfo &Info) {
|
|
|
|
Scope.Records.emplace_back(Info.USR, Info.Name, InfoType::IT_record,
|
2022-12-07 10:26:04 -08:00
|
|
|
Info.Name, getInfoRelativePath(Info.Namespace));
|
2022-10-14 14:28:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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>
|
2025-04-18 23:34:57 -07:00
|
|
|
static std::unique_ptr<Info> MakeAndInsertIntoParent(ChildType Child) {
|
2022-10-14 14:28:43 -07:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-16 00:10:49 +00:00
|
|
|
// 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) {
|
2018-03-22 23:34:46 +00:00
|
|
|
for (const FieldDecl *F : D->fields()) {
|
2019-08-16 00:10:49 +00:00
|
|
|
if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F))
|
2018-07-20 18:49:55 +00:00
|
|
|
continue;
|
2025-04-18 18:08:06 -07:00
|
|
|
populateMemberTypeInfo(I, Access, F);
|
|
|
|
}
|
|
|
|
const auto *CxxRD = dyn_cast<CXXRecordDecl>(D);
|
|
|
|
if (!CxxRD)
|
|
|
|
return;
|
|
|
|
for (Decl *CxxDecl : CxxRD->decls()) {
|
|
|
|
auto *VD = dyn_cast<VarDecl>(CxxDecl);
|
|
|
|
if (!VD ||
|
|
|
|
!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, VD))
|
|
|
|
continue;
|
2022-08-11 17:11:23 +00:00
|
|
|
|
2025-04-18 18:08:06 -07:00
|
|
|
if (VD->isStaticDataMember())
|
|
|
|
populateMemberTypeInfo(I, Access, VD, /*IsStatic=*/true);
|
2018-03-22 23:34:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
|
2022-09-19 21:52:24 +00:00
|
|
|
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);
|
2024-08-16 18:37:17 -04:00
|
|
|
I.Members.emplace_back(E->getNameAsString(), ValueStr.str(), ValueExpr);
|
|
|
|
ASTContext &Context = E->getASTContext();
|
|
|
|
if (RawComment *Comment =
|
|
|
|
E->getASTContext().getRawCommentForDeclNoCache(E)) {
|
|
|
|
CommentInfo CInfo;
|
|
|
|
Comment->setAttached();
|
|
|
|
if (comments::FullComment *Fc = Comment->parse(Context, nullptr, E)) {
|
|
|
|
EnumValueInfo &Member = I.Members.back();
|
|
|
|
Member.Description.emplace_back();
|
|
|
|
parseFullComment(Fc, Member.Description.back());
|
|
|
|
}
|
|
|
|
}
|
2022-09-19 21:52:24 +00:00
|
|
|
}
|
2018-03-22 23:34:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
|
2024-12-18 08:39:19 -08:00
|
|
|
auto &LO = D->getLangOpts();
|
2018-03-22 23:34:46 +00:00
|
|
|
for (const ParmVarDecl *P : D->parameters()) {
|
2022-09-20 17:54:28 +00:00
|
|
|
FieldTypeInfo &FieldInfo = I.Params.emplace_back(
|
2024-12-18 08:39:19 -08:00
|
|
|
getTypeInfoForType(P->getOriginalType(), LO), P->getNameAsString());
|
2022-12-07 10:26:04 -08:00
|
|
|
FieldInfo.DefaultValue = getSourceCode(D, P->getDefaultArgRange());
|
2018-03-22 23:34:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-16 00:10:49 +00:00
|
|
|
// TODO: Remove the serialization of Parents and VirtualParents, this
|
|
|
|
// information is also extracted in the other definition of parseBases.
|
2018-03-22 23:34:46 +00:00
|
|
|
static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
|
2018-10-03 18:25:27 +00:00
|
|
|
// Don't parse bases if this isn't a definition.
|
|
|
|
if (!D->isThisDeclarationADefinition())
|
|
|
|
return;
|
2018-03-22 23:34:46 +00:00
|
|
|
for (const CXXBaseSpecifier &B : D->bases()) {
|
|
|
|
if (B.isVirtual())
|
|
|
|
continue;
|
2019-06-24 19:31:02 +00:00
|
|
|
if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
|
|
|
|
const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
|
|
|
|
I.Parents.emplace_back(getUSRForDecl(D), B.getType().getAsString(),
|
2022-12-07 10:26:04 -08:00
|
|
|
InfoType::IT_record, B.getType().getAsString());
|
2022-09-20 17:54:28 +00:00
|
|
|
} else if (const RecordDecl *P = getRecordDeclForType(B.getType()))
|
2018-05-04 17:02:13 +00:00
|
|
|
I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
|
2022-12-07 10:26:04 -08:00
|
|
|
InfoType::IT_record, P->getQualifiedNameAsString(),
|
|
|
|
getInfoRelativePath(P));
|
2018-03-22 23:34:46 +00:00
|
|
|
else
|
2022-09-27 23:29:04 +00:00
|
|
|
I.Parents.emplace_back(SymbolID(), B.getType().getAsString());
|
2018-03-22 23:34:46 +00:00
|
|
|
}
|
|
|
|
for (const CXXBaseSpecifier &B : D->vbases()) {
|
2022-09-20 17:54:28 +00:00
|
|
|
if (const RecordDecl *P = getRecordDeclForType(B.getType()))
|
2022-12-07 10:26:04 -08:00
|
|
|
I.VirtualParents.emplace_back(
|
|
|
|
getUSRForDecl(P), P->getNameAsString(), InfoType::IT_record,
|
|
|
|
P->getQualifiedNameAsString(), getInfoRelativePath(P));
|
2018-03-22 23:34:46 +00:00
|
|
|
else
|
2022-09-27 23:29:04 +00:00
|
|
|
I.VirtualParents.emplace_back(SymbolID(), B.getType().getAsString());
|
2018-03-22 23:34:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
static void
|
|
|
|
populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
|
2019-07-12 18:32:00 +00:00
|
|
|
const T *D, bool &IsInAnonymousNamespace) {
|
2022-10-14 14:28:43 -07:00
|
|
|
const DeclContext *DC = D->getDeclContext();
|
|
|
|
do {
|
2019-06-28 19:07:56 +00:00
|
|
|
if (const auto *N = dyn_cast<NamespaceDecl>(DC)) {
|
|
|
|
std::string Namespace;
|
|
|
|
if (N->isAnonymousNamespace()) {
|
|
|
|
Namespace = "@nonymous_namespace";
|
2019-07-12 18:32:00 +00:00
|
|
|
IsInAnonymousNamespace = true;
|
2019-06-28 19:07:56 +00:00
|
|
|
} else
|
|
|
|
Namespace = N->getNameAsString();
|
|
|
|
Namespaces.emplace_back(getUSRForDecl(N), Namespace,
|
2022-12-07 10:26:04 -08:00
|
|
|
InfoType::IT_namespace,
|
|
|
|
N->getQualifiedNameAsString());
|
2019-06-28 19:07:56 +00:00
|
|
|
} else if (const auto *N = dyn_cast<RecordDecl>(DC))
|
2018-05-04 17:02:13 +00:00
|
|
|
Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
|
2022-12-07 10:26:04 -08:00
|
|
|
InfoType::IT_record,
|
|
|
|
N->getQualifiedNameAsString());
|
2018-03-22 23:34:46 +00:00
|
|
|
else if (const auto *N = dyn_cast<FunctionDecl>(DC))
|
2018-05-04 17:02:13 +00:00
|
|
|
Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
|
2022-12-07 10:26:04 -08:00
|
|
|
InfoType::IT_function,
|
|
|
|
N->getQualifiedNameAsString());
|
2018-03-22 23:34:46 +00:00
|
|
|
else if (const auto *N = dyn_cast<EnumDecl>(DC))
|
2018-05-04 17:02:13 +00:00
|
|
|
Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
|
2022-12-07 10:26:04 -08:00
|
|
|
InfoType::IT_enum, N->getQualifiedNameAsString());
|
2022-10-14 14:28:43 -07:00
|
|
|
} while ((DC = DC->getParent()));
|
2019-08-16 16:10:32 +00:00
|
|
|
// 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.
|
2021-12-24 21:22:27 -08:00
|
|
|
if ((Namespaces.empty() && isa<RecordDecl>(D)) ||
|
2019-08-16 16:10:32 +00:00
|
|
|
(!Namespaces.empty() && Namespaces.back().RefType == InfoType::IT_record))
|
|
|
|
Namespaces.emplace_back(SymbolID(), "GlobalNamespace",
|
|
|
|
InfoType::IT_namespace);
|
2018-03-22 23:34:46 +00:00
|
|
|
}
|
|
|
|
|
2025-04-18 23:34:57 -07:00
|
|
|
static void
|
|
|
|
PopulateTemplateParameters(std::optional<TemplateInfo> &TemplateInfo,
|
|
|
|
const clang::Decl *D) {
|
2022-12-07 10:26:04 -08:00
|
|
|
if (const TemplateParameterList *ParamList =
|
|
|
|
D->getDescribedTemplateParams()) {
|
|
|
|
if (!TemplateInfo) {
|
|
|
|
TemplateInfo.emplace();
|
|
|
|
}
|
|
|
|
for (const NamedDecl *ND : *ParamList) {
|
|
|
|
TemplateInfo->Params.emplace_back(
|
|
|
|
getSourceCode(ND, ND->getSourceRange()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-04-18 23:34:57 -07:00
|
|
|
static TemplateParamInfo TemplateArgumentToInfo(const clang::Decl *D,
|
|
|
|
const TemplateArgument &Arg) {
|
2022-12-07 10:26:04 -08:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2018-03-22 23:34:46 +00:00
|
|
|
template <typename T>
|
2019-06-28 19:07:56 +00:00
|
|
|
static void populateInfo(Info &I, const T *D, const FullComment *C,
|
|
|
|
bool &IsInAnonymousNamespace) {
|
2018-03-22 23:34:46 +00:00
|
|
|
I.USR = getUSRForDecl(D);
|
|
|
|
I.Name = D->getNameAsString();
|
2019-06-28 19:07:56 +00:00
|
|
|
populateParentNamespaces(I.Namespace, D, IsInAnonymousNamespace);
|
2018-03-22 23:34:46 +00:00
|
|
|
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,
|
2019-06-28 19:07:56 +00:00
|
|
|
int LineNumber, StringRef Filename,
|
2019-08-09 17:49:41 +00:00
|
|
|
bool IsFileInRootDir,
|
2019-06-28 19:07:56 +00:00
|
|
|
bool &IsInAnonymousNamespace) {
|
|
|
|
populateInfo(I, D, C, IsInAnonymousNamespace);
|
2018-03-22 23:34:46 +00:00
|
|
|
if (D->isThisDeclarationADefinition())
|
2019-08-09 17:49:41 +00:00
|
|
|
I.DefLoc.emplace(LineNumber, Filename, IsFileInRootDir);
|
2018-03-22 23:34:46 +00:00
|
|
|
else
|
2019-08-09 17:49:41 +00:00
|
|
|
I.Loc.emplace_back(LineNumber, Filename, IsFileInRootDir);
|
2018-03-22 23:34:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
|
|
|
|
const FullComment *FC, int LineNumber,
|
2019-08-09 17:49:41 +00:00
|
|
|
StringRef Filename, bool IsFileInRootDir,
|
2019-06-28 19:07:56 +00:00
|
|
|
bool &IsInAnonymousNamespace) {
|
2019-08-09 17:49:41 +00:00
|
|
|
populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir,
|
|
|
|
IsInAnonymousNamespace);
|
2024-12-18 08:39:19 -08:00
|
|
|
auto &LO = D->getLangOpts();
|
|
|
|
I.ReturnType = getTypeInfoForType(D->getReturnType(), LO);
|
2018-03-22 23:34:46 +00:00
|
|
|
parseParameters(I, D);
|
2022-12-07 10:26:04 -08:00
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-03-22 23:34:46 +00:00
|
|
|
}
|
|
|
|
|
2025-04-18 18:08:06 -07:00
|
|
|
static void populateMemberTypeInfo(MemberTypeInfo &I, const Decl *D) {
|
2022-08-11 17:11:23 +00:00
|
|
|
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();
|
2024-08-16 18:37:17 -04:00
|
|
|
if (comments::FullComment *fc = Comment->parse(Context, nullptr, D)) {
|
2022-08-11 17:11:23 +00:00
|
|
|
I.Description.emplace_back();
|
|
|
|
parseFullComment(fc, I.Description.back());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-04-18 18:08:06 -07:00
|
|
|
static void populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access,
|
|
|
|
const DeclaratorDecl *D, bool IsStatic) {
|
|
|
|
// 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(D->getTypeSourceInfo()->getType(), D->getLangOpts()),
|
|
|
|
D->getNameAsString(),
|
|
|
|
getFinalAccessSpecifier(Access, D->getAccessUnsafe()), IsStatic);
|
|
|
|
populateMemberTypeInfo(NewMember, D);
|
|
|
|
}
|
|
|
|
|
2019-08-16 00:10:49 +00:00
|
|
|
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;
|
2025-04-18 18:08:06 -07:00
|
|
|
FI.IsStatic = MD->isStatic();
|
2019-08-16 00:10:49 +00:00
|
|
|
// 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());
|
2022-10-14 14:28:43 -07:00
|
|
|
BI.Children.Functions.emplace_back(std::move(FI));
|
2019-08-16 00:10:49 +00:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-02 19:59:56 +00:00
|
|
|
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
|
|
|
|
emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
|
2019-08-09 17:49:41 +00:00
|
|
|
llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
|
2019-08-14 23:52:23 +00:00
|
|
|
auto I = std::make_unique<NamespaceInfo>();
|
2019-06-28 19:07:56 +00:00
|
|
|
bool IsInAnonymousNamespace = false;
|
|
|
|
populateInfo(*I, D, FC, IsInAnonymousNamespace);
|
2019-08-16 00:10:49 +00:00
|
|
|
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
|
2019-07-02 19:59:56 +00:00
|
|
|
return {};
|
2019-08-16 00:10:49 +00:00
|
|
|
|
2019-06-28 19:07:56 +00:00
|
|
|
I->Name = D->isAnonymousNamespace()
|
|
|
|
? llvm::SmallString<16>("@nonymous_namespace")
|
|
|
|
: I->Name;
|
2019-07-12 18:32:00 +00:00
|
|
|
I->Path = getInfoRelativePath(I->Namespace);
|
2019-07-02 19:59:56 +00:00
|
|
|
if (I->Namespace.empty() && I->USR == SymbolID())
|
|
|
|
return {std::unique_ptr<Info>{std::move(I)}, nullptr};
|
|
|
|
|
2022-10-14 14:28:43 -07:00
|
|
|
// 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)};
|
2018-03-22 23:34:46 +00:00
|
|
|
}
|
|
|
|
|
2019-07-02 19:59:56 +00:00
|
|
|
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
|
|
|
|
emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
|
2019-08-09 17:49:41 +00:00
|
|
|
llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
|
2019-08-14 23:52:23 +00:00
|
|
|
auto I = std::make_unique<RecordInfo>();
|
2019-06-28 19:07:56 +00:00
|
|
|
bool IsInAnonymousNamespace = false;
|
2019-08-09 17:49:41 +00:00
|
|
|
populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir,
|
|
|
|
IsInAnonymousNamespace);
|
2019-08-16 00:10:49 +00:00
|
|
|
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
|
2019-07-02 19:59:56 +00:00
|
|
|
return {};
|
2019-06-28 19:07:56 +00:00
|
|
|
|
2018-08-02 20:10:17 +00:00
|
|
|
I->TagType = D->getTagKind();
|
|
|
|
parseFields(*I, D, PublicOnly);
|
2019-06-24 19:31:02 +00:00
|
|
|
if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
|
|
|
|
if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) {
|
|
|
|
I->Name = TD->getNameAsString();
|
|
|
|
I->IsTypeDef = true;
|
|
|
|
}
|
2019-08-16 00:10:49 +00:00
|
|
|
// TODO: remove first call to parseBases, that function should be deleted
|
2018-08-02 20:10:17 +00:00
|
|
|
parseBases(*I, C);
|
2019-08-16 00:10:49 +00:00
|
|
|
parseBases(*I, C, IsFileInRootDir, PublicOnly, true);
|
2019-06-24 19:31:02 +00:00
|
|
|
}
|
2019-07-12 18:32:00 +00:00
|
|
|
I->Path = getInfoRelativePath(I->Namespace);
|
2019-07-02 19:59:56 +00:00
|
|
|
|
2022-12-07 10:26:04 -08:00
|
|
|
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();
|
2024-12-22 01:30:08 -08:00
|
|
|
if (auto *CTD = dyn_cast<ClassTemplateDecl *>(SpecOf))
|
|
|
|
Specialization.SpecializationOf = getUSRForDecl(CTD);
|
|
|
|
else if (auto *CTPSD =
|
|
|
|
dyn_cast<ClassTemplatePartialSpecializationDecl *>(SpecOf))
|
|
|
|
Specialization.SpecializationOf = getUSRForDecl(CTPSD);
|
2022-12-07 10:26:04 -08:00
|
|
|
|
2025-04-18 18:08:06 -07:00
|
|
|
// Parameters to the specialization. For partial specializations, get the
|
2022-12-07 10:26:04 -08:00
|
|
|
// 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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-14 14:28:43 -07:00
|
|
|
// 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)};
|
2018-03-22 23:34:46 +00:00
|
|
|
}
|
|
|
|
|
2019-07-02 19:59:56 +00:00
|
|
|
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
|
|
|
|
emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
|
2019-08-09 17:49:41 +00:00
|
|
|
llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
|
2018-08-02 20:10:17 +00:00
|
|
|
FunctionInfo Func;
|
2019-06-28 19:07:56 +00:00
|
|
|
bool IsInAnonymousNamespace = false;
|
2019-08-09 17:49:41 +00:00
|
|
|
populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
|
|
|
|
IsInAnonymousNamespace);
|
2019-08-15 23:04:27 +00:00
|
|
|
Func.Access = clang::AccessSpecifier::AS_none;
|
2019-08-16 00:10:49 +00:00
|
|
|
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
|
2019-07-02 19:59:56 +00:00
|
|
|
return {};
|
2019-06-28 19:07:56 +00:00
|
|
|
|
2022-10-14 14:28:43 -07:00
|
|
|
// Info is wrapped in its parent scope so is returned in the second position.
|
|
|
|
return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
|
2018-03-22 23:34:46 +00:00
|
|
|
}
|
|
|
|
|
2019-07-02 19:59:56 +00:00
|
|
|
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
|
|
|
|
emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
|
2019-08-09 17:49:41 +00:00
|
|
|
llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
|
2018-08-02 20:10:17 +00:00
|
|
|
FunctionInfo Func;
|
2019-06-28 19:07:56 +00:00
|
|
|
bool IsInAnonymousNamespace = false;
|
2019-08-09 17:49:41 +00:00
|
|
|
populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
|
|
|
|
IsInAnonymousNamespace);
|
2019-08-16 00:10:49 +00:00
|
|
|
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
|
2019-07-02 19:59:56 +00:00
|
|
|
return {};
|
2019-06-28 19:07:56 +00:00
|
|
|
|
2018-08-02 20:10:17 +00:00
|
|
|
Func.IsMethod = true;
|
2025-04-18 18:08:06 -07:00
|
|
|
Func.IsStatic = D->isStatic();
|
2018-08-02 20:10:17 +00:00
|
|
|
|
2019-06-24 19:31:02 +00:00
|
|
|
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 =
|
2022-12-07 10:26:04 -08:00
|
|
|
Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record,
|
|
|
|
Parent->getQualifiedNameAsString()};
|
2018-08-02 20:10:17 +00:00
|
|
|
Func.Access = D->getAccess();
|
|
|
|
|
2022-10-14 14:28:43 -07:00
|
|
|
// 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);
|
2024-12-18 08:39:19 -08:00
|
|
|
auto &LO = D->getLangOpts();
|
|
|
|
Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);
|
2022-10-14 14:28:43 -07:00
|
|
|
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);
|
2024-12-18 08:39:19 -08:00
|
|
|
auto &LO = D->getLangOpts();
|
|
|
|
Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);
|
2022-10-14 14:28:43 -07:00
|
|
|
Info.IsUsing = true;
|
|
|
|
|
|
|
|
// Info is wrapped in its parent scope so is returned in the second position.
|
|
|
|
return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
|
2018-03-22 23:34:46 +00:00
|
|
|
}
|
|
|
|
|
2019-07-02 19:59:56 +00:00
|
|
|
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
|
|
|
|
emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
|
2019-08-09 17:49:41 +00:00
|
|
|
llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
|
2018-08-02 20:10:17 +00:00
|
|
|
EnumInfo Enum;
|
2019-06-28 19:07:56 +00:00
|
|
|
bool IsInAnonymousNamespace = false;
|
2019-08-09 17:49:41 +00:00
|
|
|
populateSymbolInfo(Enum, D, FC, LineNumber, File, IsFileInRootDir,
|
|
|
|
IsInAnonymousNamespace);
|
2019-08-16 00:10:49 +00:00
|
|
|
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
|
2019-07-02 19:59:56 +00:00
|
|
|
return {};
|
2019-06-28 19:07:56 +00:00
|
|
|
|
2018-08-02 20:10:17 +00:00
|
|
|
Enum.Scoped = D->isScoped();
|
2022-12-07 10:26:04 -08:00
|
|
|
if (D->isFixed()) {
|
|
|
|
auto Name = D->getIntegerType().getAsString();
|
|
|
|
Enum.BaseType = TypeInfo(Name, Name);
|
|
|
|
}
|
2018-08-02 20:10:17 +00:00
|
|
|
parseEnumerators(Enum, D);
|
|
|
|
|
2022-10-14 14:28:43 -07:00
|
|
|
// Info is wrapped in its parent scope so is returned in the second position.
|
|
|
|
return {nullptr, MakeAndInsertIntoParent<EnumInfo &&>(std::move(Enum))};
|
2018-03-22 23:34:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace serialize
|
|
|
|
} // namespace doc
|
|
|
|
} // namespace clang
|