[clang-doc] Handle static members and functions (#135457)
clang-doc didn't visit VarDecl, and hence never collected info from class statics members and functions. Fixes #59813.
This commit is contained in:
@@ -274,6 +274,8 @@ llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
|
||||
return decodeRecord(R, I->Access, Blob);
|
||||
case FUNCTION_IS_METHOD:
|
||||
return decodeRecord(R, I->IsMethod, Blob);
|
||||
case FUNCTION_IS_STATIC:
|
||||
return decodeRecord(R, I->IsStatic, Blob);
|
||||
default:
|
||||
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
||||
"invalid field for FunctionInfo");
|
||||
@@ -305,6 +307,8 @@ llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
|
||||
return decodeRecord(R, I->Name, Blob);
|
||||
case MEMBER_TYPE_ACCESS:
|
||||
return decodeRecord(R, I->Access, Blob);
|
||||
case MEMBER_TYPE_IS_STATIC:
|
||||
return decodeRecord(R, I->IsStatic, Blob);
|
||||
default:
|
||||
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
||||
"invalid field for MemberTypeInfo");
|
||||
|
||||
@@ -156,6 +156,7 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
|
||||
{FIELD_DEFAULT_VALUE, {"DefaultValue", &StringAbbrev}},
|
||||
{MEMBER_TYPE_NAME, {"Name", &StringAbbrev}},
|
||||
{MEMBER_TYPE_ACCESS, {"Access", &IntAbbrev}},
|
||||
{MEMBER_TYPE_IS_STATIC, {"IsStatic", &BoolAbbrev}},
|
||||
{NAMESPACE_USR, {"USR", &SymbolIDAbbrev}},
|
||||
{NAMESPACE_NAME, {"Name", &StringAbbrev}},
|
||||
{NAMESPACE_PATH, {"Path", &StringAbbrev}},
|
||||
@@ -187,6 +188,7 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
|
||||
{FUNCTION_LOCATION, {"Location", &LocationAbbrev}},
|
||||
{FUNCTION_ACCESS, {"Access", &IntAbbrev}},
|
||||
{FUNCTION_IS_METHOD, {"IsMethod", &BoolAbbrev}},
|
||||
{FUNCTION_IS_STATIC, {"IsStatic", &BoolAbbrev}},
|
||||
{REFERENCE_USR, {"USR", &SymbolIDAbbrev}},
|
||||
{REFERENCE_NAME, {"Name", &StringAbbrev}},
|
||||
{REFERENCE_QUAL_NAME, {"QualName", &StringAbbrev}},
|
||||
@@ -222,7 +224,8 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
|
||||
// FieldType Block
|
||||
{BI_FIELD_TYPE_BLOCK_ID, {FIELD_TYPE_NAME, FIELD_DEFAULT_VALUE}},
|
||||
// MemberType Block
|
||||
{BI_MEMBER_TYPE_BLOCK_ID, {MEMBER_TYPE_NAME, MEMBER_TYPE_ACCESS}},
|
||||
{BI_MEMBER_TYPE_BLOCK_ID,
|
||||
{MEMBER_TYPE_NAME, MEMBER_TYPE_ACCESS, MEMBER_TYPE_IS_STATIC}},
|
||||
// Enum Block
|
||||
{BI_ENUM_BLOCK_ID,
|
||||
{ENUM_USR, ENUM_NAME, ENUM_DEFLOCATION, ENUM_LOCATION, ENUM_SCOPED}},
|
||||
@@ -247,7 +250,7 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
|
||||
// Function Block
|
||||
{BI_FUNCTION_BLOCK_ID,
|
||||
{FUNCTION_USR, FUNCTION_NAME, FUNCTION_DEFLOCATION, FUNCTION_LOCATION,
|
||||
FUNCTION_ACCESS, FUNCTION_IS_METHOD}},
|
||||
FUNCTION_ACCESS, FUNCTION_IS_METHOD, FUNCTION_IS_STATIC}},
|
||||
// Reference Block
|
||||
{BI_REFERENCE_BLOCK_ID,
|
||||
{REFERENCE_USR, REFERENCE_NAME, REFERENCE_QUAL_NAME, REFERENCE_TYPE,
|
||||
@@ -465,6 +468,7 @@ void ClangDocBitcodeWriter::emitBlock(const MemberTypeInfo &T) {
|
||||
emitBlock(T.Type, FieldId::F_type);
|
||||
emitRecord(T.Name, MEMBER_TYPE_NAME);
|
||||
emitRecord(T.Access, MEMBER_TYPE_ACCESS);
|
||||
emitRecord(T.IsStatic, MEMBER_TYPE_IS_STATIC);
|
||||
for (const auto &CI : T.Description)
|
||||
emitBlock(CI);
|
||||
}
|
||||
@@ -600,6 +604,7 @@ void ClangDocBitcodeWriter::emitBlock(const FunctionInfo &I) {
|
||||
emitBlock(CI);
|
||||
emitRecord(I.Access, FUNCTION_ACCESS);
|
||||
emitRecord(I.IsMethod, FUNCTION_IS_METHOD);
|
||||
emitRecord(I.IsStatic, FUNCTION_IS_STATIC);
|
||||
if (I.DefLoc)
|
||||
emitRecord(*I.DefLoc, FUNCTION_DEFLOCATION);
|
||||
for (const auto &L : I.Loc)
|
||||
|
||||
@@ -81,6 +81,7 @@ enum RecordId {
|
||||
FUNCTION_LOCATION,
|
||||
FUNCTION_ACCESS,
|
||||
FUNCTION_IS_METHOD,
|
||||
FUNCTION_IS_STATIC,
|
||||
COMMENT_KIND,
|
||||
COMMENT_TEXT,
|
||||
COMMENT_NAME,
|
||||
@@ -96,6 +97,7 @@ enum RecordId {
|
||||
FIELD_DEFAULT_VALUE,
|
||||
MEMBER_TYPE_NAME,
|
||||
MEMBER_TYPE_ACCESS,
|
||||
MEMBER_TYPE_IS_STATIC,
|
||||
NAMESPACE_USR,
|
||||
NAMESPACE_NAME,
|
||||
NAMESPACE_PATH,
|
||||
|
||||
@@ -416,12 +416,14 @@ genRecordMembersBlock(const llvm::SmallVector<MemberTypeInfo, 4> &Members,
|
||||
Out.emplace_back(std::make_unique<TagNode>(HTMLTag::TAG_UL));
|
||||
auto &ULBody = Out.back();
|
||||
for (const auto &M : Members) {
|
||||
std::string Access = getAccessSpelling(M.Access).str();
|
||||
if (Access != "")
|
||||
Access = Access + " ";
|
||||
StringRef Access = getAccessSpelling(M.Access);
|
||||
auto LIBody = std::make_unique<TagNode>(HTMLTag::TAG_LI);
|
||||
auto MemberDecl = std::make_unique<TagNode>(HTMLTag::TAG_DIV);
|
||||
MemberDecl->Children.emplace_back(std::make_unique<TextNode>(Access));
|
||||
if (!Access.empty())
|
||||
MemberDecl->Children.emplace_back(
|
||||
std::make_unique<TextNode>(Access + " "));
|
||||
if (M.IsStatic)
|
||||
MemberDecl->Children.emplace_back(std::make_unique<TextNode>("static "));
|
||||
MemberDecl->Children.emplace_back(genReference(M.Type, ParentInfoDir));
|
||||
MemberDecl->Children.emplace_back(std::make_unique<TextNode>(" " + M.Name));
|
||||
if (!M.Description.empty())
|
||||
@@ -739,6 +741,9 @@ genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
|
||||
if (Access != "")
|
||||
FunctionHeader->Children.emplace_back(
|
||||
std::make_unique<TextNode>(Access + " "));
|
||||
if (I.IsStatic)
|
||||
FunctionHeader->Children.emplace_back(
|
||||
std::make_unique<TextNode>("static "));
|
||||
if (I.ReturnType.Type.Name != "") {
|
||||
FunctionHeader->Children.emplace_back(
|
||||
genReference(I.ReturnType.Type, ParentInfoDir));
|
||||
|
||||
@@ -169,15 +169,12 @@ static void genMarkdown(const ClangDocContext &CDCtx, const FunctionInfo &I,
|
||||
First = false;
|
||||
}
|
||||
writeHeader(I.Name, 3, OS);
|
||||
std::string Access = getAccessSpelling(I.Access).str();
|
||||
if (Access != "")
|
||||
writeLine(genItalic(Access + " " + I.ReturnType.Type.QualName + " " +
|
||||
I.Name + "(" + Stream.str() + ")"),
|
||||
OS);
|
||||
else
|
||||
writeLine(genItalic(I.ReturnType.Type.QualName + " " + I.Name + "(" +
|
||||
Stream.str() + ")"),
|
||||
OS);
|
||||
StringRef Access = getAccessSpelling(I.Access);
|
||||
writeLine(genItalic(Twine(Access) + (!Access.empty() ? " " : "") +
|
||||
(I.IsStatic ? "static " : "") +
|
||||
I.ReturnType.Type.QualName.str() + " " + I.Name.str() +
|
||||
"(" + Twine(Stream.str()) + ")"),
|
||||
OS);
|
||||
|
||||
maybeWriteSourceFileRef(OS, CDCtx, I.DefLoc);
|
||||
|
||||
@@ -262,11 +259,11 @@ static void genMarkdown(const ClangDocContext &CDCtx, const RecordInfo &I,
|
||||
if (!I.Members.empty()) {
|
||||
writeHeader("Members", 2, OS);
|
||||
for (const auto &Member : I.Members) {
|
||||
std::string Access = getAccessSpelling(Member.Access).str();
|
||||
if (Access != "")
|
||||
writeLine(Access + " " + Member.Type.Name + " " + Member.Name, OS);
|
||||
else
|
||||
writeLine(Member.Type.Name + " " + Member.Name, OS);
|
||||
StringRef Access = getAccessSpelling(Member.Access);
|
||||
writeLine(Twine(Access) + (Access.empty() ? "" : " ") +
|
||||
(Member.IsStatic ? "static " : "") +
|
||||
Member.Type.Name.str() + " " + Member.Name.str(),
|
||||
OS);
|
||||
}
|
||||
writeNewLine(OS);
|
||||
}
|
||||
|
||||
@@ -220,12 +220,14 @@ struct FieldTypeInfo : public TypeInfo {
|
||||
// Info for member types.
|
||||
struct MemberTypeInfo : public FieldTypeInfo {
|
||||
MemberTypeInfo() = default;
|
||||
MemberTypeInfo(const TypeInfo &TI, StringRef Name, AccessSpecifier Access)
|
||||
: FieldTypeInfo(TI, Name), Access(Access) {}
|
||||
MemberTypeInfo(const TypeInfo &TI, StringRef Name, AccessSpecifier Access,
|
||||
bool IsStatic = false)
|
||||
: FieldTypeInfo(TI, Name), Access(Access), IsStatic(IsStatic) {}
|
||||
|
||||
bool operator==(const MemberTypeInfo &Other) const {
|
||||
return std::tie(Type, Name, Access, Description) ==
|
||||
std::tie(Other.Type, Other.Name, Other.Access, Other.Description);
|
||||
return std::tie(Type, Name, Access, IsStatic, Description) ==
|
||||
std::tie(Other.Type, Other.Name, Other.Access, Other.IsStatic,
|
||||
Other.Description);
|
||||
}
|
||||
|
||||
// Access level associated with this info (public, protected, private, none).
|
||||
@@ -235,6 +237,7 @@ struct MemberTypeInfo : public FieldTypeInfo {
|
||||
AccessSpecifier Access = AccessSpecifier::AS_public;
|
||||
|
||||
std::vector<CommentInfo> Description; // Comment description of this field.
|
||||
bool IsStatic = false;
|
||||
};
|
||||
|
||||
struct Location {
|
||||
@@ -320,9 +323,6 @@ struct SymbolInfo : public Info {
|
||||
|
||||
void merge(SymbolInfo &&I);
|
||||
|
||||
std::optional<Location> DefLoc; // Location where this decl is defined.
|
||||
llvm::SmallVector<Location, 2> Loc; // Locations where this decl is declared.
|
||||
|
||||
bool operator<(const SymbolInfo &Other) const {
|
||||
// Sort by declaration location since we want the doc to be
|
||||
// generated in the order of the source code.
|
||||
@@ -336,6 +336,10 @@ struct SymbolInfo : public Info {
|
||||
|
||||
return extractName() < Other.extractName();
|
||||
}
|
||||
|
||||
std::optional<Location> DefLoc; // Location where this decl is defined.
|
||||
llvm::SmallVector<Location, 2> Loc; // Locations where this decl is declared.
|
||||
bool IsStatic = false;
|
||||
};
|
||||
|
||||
// TODO: Expand to allow for documenting templating and default args.
|
||||
|
||||
@@ -30,7 +30,10 @@ static void
|
||||
populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
|
||||
const T *D, bool &IsAnonymousNamespace);
|
||||
|
||||
static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D);
|
||||
static void populateMemberTypeInfo(MemberTypeInfo &I, const Decl *D);
|
||||
static void populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access,
|
||||
const DeclaratorDecl *D,
|
||||
bool IsStatic = false);
|
||||
|
||||
// A function to extract the appropriate relative path for a given info's
|
||||
// documentation. The path returned is a composite of the parent namespaces.
|
||||
@@ -378,15 +381,19 @@ static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly,
|
||||
for (const FieldDecl *F : D->fields()) {
|
||||
if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F))
|
||||
continue;
|
||||
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;
|
||||
|
||||
auto &LO = F->getLangOpts();
|
||||
// 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(), LO),
|
||||
F->getNameAsString(),
|
||||
getFinalAccessSpecifier(Access, F->getAccessUnsafe()));
|
||||
populateMemberTypeInfo(NewMember, F);
|
||||
if (VD->isStaticDataMember())
|
||||
populateMemberTypeInfo(I, Access, VD, /*IsStatic=*/true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -568,7 +575,7 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
|
||||
}
|
||||
}
|
||||
|
||||
static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {
|
||||
static void populateMemberTypeInfo(MemberTypeInfo &I, const Decl *D) {
|
||||
assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");
|
||||
|
||||
ASTContext& Context = D->getASTContext();
|
||||
@@ -585,6 +592,17 @@ static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static void
|
||||
parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
|
||||
bool PublicOnly, bool IsParent,
|
||||
@@ -619,6 +637,7 @@ parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
|
||||
continue;
|
||||
FunctionInfo FI;
|
||||
FI.IsMethod = true;
|
||||
FI.IsStatic = MD->isStatic();
|
||||
// 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.
|
||||
@@ -702,7 +721,7 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
|
||||
dyn_cast<ClassTemplatePartialSpecializationDecl *>(SpecOf))
|
||||
Specialization.SpecializationOf = getUSRForDecl(CTPSD);
|
||||
|
||||
// Parameters to the specilization. For partial specializations, get the
|
||||
// Parameters to the specialization. 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
|
||||
@@ -755,6 +774,7 @@ emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
|
||||
return {};
|
||||
|
||||
Func.IsMethod = true;
|
||||
Func.IsStatic = D->isStatic();
|
||||
|
||||
const NamedDecl *Parent = nullptr;
|
||||
if (const auto *SD =
|
||||
|
||||
@@ -52,6 +52,10 @@ std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
|
||||
emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
|
||||
StringRef File, bool IsFileInRootDir, bool PublicOnly);
|
||||
|
||||
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
|
||||
emitInfo(const VarDecl *D, const FullComment *FC, int LineNumber,
|
||||
StringRef File, bool IsFileInRootDir, bool PublicOnly);
|
||||
|
||||
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
|
||||
emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
|
||||
StringRef File, bool IsFileInRootDir, bool PublicOnly);
|
||||
|
||||
@@ -134,6 +134,9 @@
|
||||
// HTML-CALC: <div>brief</div>
|
||||
// HTML-CALC: <p> Holds a public value.</p>
|
||||
// HTML-CALC: <div>public int public_val</div>
|
||||
// HTML-CALC: <div>brief</div>
|
||||
// HTML-CALC: <p> A static value.</p>
|
||||
// HTML-CALC: <div>public static const int static_val</div>
|
||||
|
||||
// HTML-CALC: <h2 id="Functions">Functions</h2>
|
||||
// HTML-CALC: <h3 id="{{([0-9A-F]{40})}}">add</h3>
|
||||
@@ -191,7 +194,7 @@
|
||||
// HTML-CALC: <div>throw</div>
|
||||
// HTML-CALC: <p>if b is zero.</p>
|
||||
|
||||
// HTML-CALC: <p>public int mod(int a, int b)</p>
|
||||
// HTML-CALC: <p>public static int mod(int a, int b)</p>
|
||||
// CALC-NO-REPOSITORY: Defined at line 54 of file .{{.}}include{{.}}Calculator.h
|
||||
// CALC-REPOSITORY: Defined at line
|
||||
// CALC-REPOSITORY-NEXT: <a href="https://repository.com/./include/Calculator.h#54">54</a>
|
||||
@@ -326,6 +329,7 @@
|
||||
// MD-CALC: Provides basic arithmetic operations.
|
||||
// MD-CALC: ## Members
|
||||
// MD-CALC: public int public_val
|
||||
// MD-CALC: public static const int static_val
|
||||
// MD-CALC: ## Functions
|
||||
// MD-CALC: ### add
|
||||
// MD-CALC: *public int add(int a, int b)*
|
||||
@@ -357,7 +361,7 @@
|
||||
// MD-CALC: **return** double The result of a / b.
|
||||
// MD-CALC: **throw**if b is zero.
|
||||
// MD-CALC: ### mod
|
||||
// MD-CALC: *public int mod(int a, int b)*
|
||||
// MD-CALC: *public static int mod(int a, int b)*
|
||||
// MD-CALC: *Defined at ./include{{[\/]}}Calculator.h#54*
|
||||
// MD-CALC: **brief** Performs the mod operation on integers.
|
||||
// MD-CALC: **a** First integer.
|
||||
|
||||
Reference in New Issue
Block a user