Skip to content

[clang-doc] Handle static members and functions #135457

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Apr 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clang-tools-extra/clang-doc/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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");
Expand Down
9 changes: 7 additions & 2 deletions clang-tools-extra/clang-doc/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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}},
Expand Down Expand Up @@ -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}},
Expand Down Expand Up @@ -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}},
Expand All @@ -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,
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions clang-tools-extra/clang-doc/BitcodeWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ enum RecordId {
FUNCTION_LOCATION,
FUNCTION_ACCESS,
FUNCTION_IS_METHOD,
FUNCTION_IS_STATIC,
COMMENT_KIND,
COMMENT_TEXT,
COMMENT_NAME,
Expand All @@ -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,
Expand Down
13 changes: 9 additions & 4 deletions clang-tools-extra/clang-doc/HTMLGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -740,6 +742,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));
Expand Down
25 changes: 11 additions & 14 deletions clang-tools-extra/clang-doc/MDGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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);
}
Expand Down
18 changes: 11 additions & 7 deletions clang-tools-extra/clang-doc/Representation.h
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand All @@ -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 {
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand Down
42 changes: 31 additions & 11 deletions clang-tools-extra/clang-doc/Serialize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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();
Expand All @@ -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,
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 =
Expand Down
4 changes: 4 additions & 0 deletions clang-tools-extra/clang-doc/Serialize.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 6 additions & 2 deletions clang-tools-extra/test/clang-doc/basic-project.test
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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)*
Expand Down Expand Up @@ -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.
Expand Down
Loading