Skip to content

Commit 5bb5704

Browse files
committed
[clang][extract-api] Add struct support
- Add `StructFieldRecord` and `StructRecord` to store API information for structs - Implement `VisitRecordDecl` in `ExtractAPIVisitor` - Implement Symbol Graph serialization for struct records. - Add test case for struct records. Depends on D121873 Differential Revision: https://reviews.llvm.org/D122202
1 parent 311bdbc commit 5bb5704

File tree

8 files changed

+518
-0
lines changed

8 files changed

+518
-0
lines changed

clang/include/clang/ExtractAPI/API.h

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ struct APIRecord {
9494
RK_Global,
9595
RK_EnumConstant,
9696
RK_Enum,
97+
RK_StructField,
98+
RK_Struct,
9799
};
98100

99101
private:
@@ -176,6 +178,36 @@ struct EnumRecord : APIRecord {
176178
}
177179
};
178180

181+
/// This holds information associated with struct fields.
182+
struct StructFieldRecord : APIRecord {
183+
StructFieldRecord(StringRef Name, StringRef USR, PresumedLoc Loc,
184+
const AvailabilityInfo &Availability,
185+
const DocComment &Comment, DeclarationFragments Declaration,
186+
DeclarationFragments SubHeading)
187+
: APIRecord(RK_StructField, Name, USR, Loc, Availability,
188+
LinkageInfo::none(), Comment, Declaration, SubHeading) {}
189+
190+
static bool classof(const APIRecord *Record) {
191+
return Record->getKind() == RK_StructField;
192+
}
193+
};
194+
195+
/// This holds information associated with structs.
196+
struct StructRecord : APIRecord {
197+
SmallVector<APIRecordUniquePtr<StructFieldRecord>> Fields;
198+
199+
StructRecord(StringRef Name, StringRef USR, PresumedLoc Loc,
200+
const AvailabilityInfo &Availability, const DocComment &Comment,
201+
DeclarationFragments Declaration,
202+
DeclarationFragments SubHeading)
203+
: APIRecord(RK_Struct, Name, USR, Loc, Availability, LinkageInfo::none(),
204+
Comment, Declaration, SubHeading) {}
205+
206+
static bool classof(const APIRecord *Record) {
207+
return Record->getKind() == RK_Struct;
208+
}
209+
};
210+
179211
/// APISet holds the set of API records collected from given inputs.
180212
class APISet {
181213
public:
@@ -242,6 +274,31 @@ class APISet {
242274
DeclarationFragments Declaration,
243275
DeclarationFragments SubHeading);
244276

277+
/// Create and add a struct field record into the API set.
278+
///
279+
/// Note: the caller is responsible for keeping the StringRef \p Name and
280+
/// \p USR alive. APISet::copyString provides a way to copy strings into
281+
/// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
282+
/// to generate the USR for \c D and keep it alive in APISet.
283+
StructFieldRecord *addStructField(StructRecord *Struct, StringRef Name,
284+
StringRef USR, PresumedLoc Loc,
285+
const AvailabilityInfo &Availability,
286+
const DocComment &Comment,
287+
DeclarationFragments Declaration,
288+
DeclarationFragments SubHeading);
289+
290+
/// Create and add a struct record into the API set.
291+
///
292+
/// Note: the caller is responsible for keeping the StringRef \p Name and
293+
/// \p USR alive. APISet::copyString provides a way to copy strings into
294+
/// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
295+
/// to generate the USR for \c D and keep it alive in APISet.
296+
StructRecord *addStruct(StringRef Name, StringRef USR, PresumedLoc Loc,
297+
const AvailabilityInfo &Availability,
298+
const DocComment &Comment,
299+
DeclarationFragments Declaration,
300+
DeclarationFragments SubHeading);
301+
245302
/// A map to store the set of GlobalRecord%s with the declaration name as the
246303
/// key.
247304
using GlobalRecordMap =
@@ -252,6 +309,11 @@ class APISet {
252309
using EnumRecordMap =
253310
llvm::MapVector<StringRef, APIRecordUniquePtr<EnumRecord>>;
254311

312+
/// A map to store the set of StructRecord%s with the declaration name as the
313+
/// key.
314+
using StructRecordMap =
315+
llvm::MapVector<StringRef, APIRecordUniquePtr<StructRecord>>;
316+
255317
/// Get the target triple for the ExtractAPI invocation.
256318
const llvm::Triple &getTarget() const { return Target; }
257319

@@ -260,6 +322,7 @@ class APISet {
260322

261323
const GlobalRecordMap &getGlobals() const { return Globals; }
262324
const EnumRecordMap &getEnums() const { return Enums; }
325+
const StructRecordMap &getStructs() const { return Structs; }
263326

264327
/// Generate and store the USR of declaration \p D.
265328
///
@@ -285,6 +348,7 @@ class APISet {
285348

286349
GlobalRecordMap Globals;
287350
EnumRecordMap Enums;
351+
StructRecordMap Structs;
288352
};
289353

290354
} // namespace extractapi

clang/include/clang/ExtractAPI/DeclarationFragments.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,12 @@ class DeclarationFragmentsBuilder {
196196
/// Build DeclarationFragments for an enum declaration EnumDecl.
197197
static DeclarationFragments getFragmentsForEnum(const EnumDecl *);
198198

199+
/// Build DeclarationFragments for a field declaration FieldDecl.
200+
static DeclarationFragments getFragmentsForField(const FieldDecl *);
201+
202+
/// Build DeclarationFragments for a struct record declaration RecordDecl.
203+
static DeclarationFragments getFragmentsForStruct(const RecordDecl *);
204+
199205
/// Build sub-heading fragments for a NamedDecl.
200206
static DeclarationFragments getSubHeading(const NamedDecl *);
201207

clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ class SymbolGraphSerializer : public APISerializer {
110110
/// Serialize an enum record.
111111
void serializeEnumRecord(const EnumRecord &Record);
112112

113+
/// Serialize a struct record.
114+
void serializeStructRecord(const StructRecord &Record);
115+
113116
public:
114117
SymbolGraphSerializer(const APISet &API, StringRef ProductName,
115118
APISerializerOption Options = {})

clang/lib/ExtractAPI/API.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,33 @@ EnumRecord *APISet::addEnum(StringRef Name, StringRef USR, PresumedLoc Loc,
8484
return Result.first->second.get();
8585
}
8686

87+
StructFieldRecord *APISet::addStructField(StructRecord *Struct, StringRef Name,
88+
StringRef USR, PresumedLoc Loc,
89+
const AvailabilityInfo &Availability,
90+
const DocComment &Comment,
91+
DeclarationFragments Declaration,
92+
DeclarationFragments SubHeading) {
93+
auto Record =
94+
APIRecordUniquePtr<StructFieldRecord>(new (Allocator) StructFieldRecord{
95+
Name, USR, Loc, Availability, Comment, Declaration, SubHeading});
96+
return Struct->Fields.emplace_back(std::move(Record)).get();
97+
}
98+
99+
StructRecord *APISet::addStruct(StringRef Name, StringRef USR, PresumedLoc Loc,
100+
const AvailabilityInfo &Availability,
101+
const DocComment &Comment,
102+
DeclarationFragments Declaration,
103+
DeclarationFragments SubHeading) {
104+
auto Result = Structs.insert({Name, nullptr});
105+
if (Result.second) {
106+
// Create the record if it does not already exist.
107+
auto Record = APIRecordUniquePtr<StructRecord>(new (Allocator) StructRecord{
108+
Name, USR, Loc, Availability, Comment, Declaration, SubHeading});
109+
Result.first->second = std::move(Record);
110+
}
111+
return Result.first->second.get();
112+
}
113+
87114
StringRef APISet::recordUSR(const Decl *D) {
88115
SmallString<128> USR;
89116
index::generateUSRForDecl(D, USR);

clang/lib/ExtractAPI/DeclarationFragments.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,30 @@ DeclarationFragmentsBuilder::getFragmentsForEnum(const EnumDecl *EnumDecl) {
430430
return Fragments;
431431
}
432432

433+
DeclarationFragments
434+
DeclarationFragmentsBuilder::getFragmentsForField(const FieldDecl *Field) {
435+
DeclarationFragments After;
436+
return getFragmentsForType(Field->getType(), Field->getASTContext(), After)
437+
.appendSpace()
438+
.append(Field->getName(), DeclarationFragments::FragmentKind::Identifier)
439+
.append(std::move(After));
440+
}
441+
442+
DeclarationFragments
443+
DeclarationFragmentsBuilder::getFragmentsForStruct(const RecordDecl *Record) {
444+
// TODO: After we support typedef records, if there's a typedef for this
445+
// struct just use the declaration fragments of the typedef decl.
446+
447+
DeclarationFragments Fragments;
448+
Fragments.append("struct", DeclarationFragments::FragmentKind::Keyword);
449+
450+
if (!Record->getName().empty())
451+
Fragments.appendSpace().append(
452+
Record->getName(), DeclarationFragments::FragmentKind::Identifier);
453+
454+
return Fragments;
455+
}
456+
433457
FunctionSignature
434458
DeclarationFragmentsBuilder::getFunctionSignature(const FunctionDecl *Func) {
435459
FunctionSignature Signature;

clang/lib/ExtractAPI/ExtractAPIConsumer.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,41 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
179179
return true;
180180
}
181181

182+
bool VisitRecordDecl(const RecordDecl *Decl) {
183+
if (!Decl->isCompleteDefinition())
184+
return true;
185+
186+
// Skip C++ structs/classes/unions
187+
// TODO: support C++ records
188+
if (isa<CXXRecordDecl>(Decl))
189+
return true;
190+
191+
// Collect symbol information.
192+
StringRef Name = Decl->getName();
193+
StringRef USR = API.recordUSR(Decl);
194+
PresumedLoc Loc =
195+
Context.getSourceManager().getPresumedLoc(Decl->getLocation());
196+
AvailabilityInfo Availability = getAvailability(Decl);
197+
DocComment Comment;
198+
if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
199+
Comment = RawComment->getFormattedLines(Context.getSourceManager(),
200+
Context.getDiagnostics());
201+
202+
// Build declaration fragments and sub-heading for the struct.
203+
DeclarationFragments Declaration =
204+
DeclarationFragmentsBuilder::getFragmentsForStruct(Decl);
205+
DeclarationFragments SubHeading =
206+
DeclarationFragmentsBuilder::getSubHeading(Decl);
207+
208+
StructRecord *StructRecord = API.addStruct(
209+
Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
210+
211+
// Now collect information about the fields in this struct.
212+
recordStructFields(StructRecord, Decl->fields());
213+
214+
return true;
215+
}
216+
182217
private:
183218
/// Get availability information of the declaration \p D.
184219
AvailabilityInfo getAvailability(const Decl *D) const {
@@ -238,6 +273,33 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
238273
}
239274
}
240275

276+
/// Collect API information for the struct fields and associate with the
277+
/// parent struct.
278+
void recordStructFields(StructRecord *StructRecord,
279+
const RecordDecl::field_range Fields) {
280+
for (const auto *Field : Fields) {
281+
// Collect symbol information.
282+
StringRef Name = Field->getName();
283+
StringRef USR = API.recordUSR(Field);
284+
PresumedLoc Loc =
285+
Context.getSourceManager().getPresumedLoc(Field->getLocation());
286+
AvailabilityInfo Availability = getAvailability(Field);
287+
DocComment Comment;
288+
if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field))
289+
Comment = RawComment->getFormattedLines(Context.getSourceManager(),
290+
Context.getDiagnostics());
291+
292+
// Build declaration fragments and sub-heading for the struct field.
293+
DeclarationFragments Declaration =
294+
DeclarationFragmentsBuilder::getFragmentsForField(Field);
295+
DeclarationFragments SubHeading =
296+
DeclarationFragmentsBuilder::getSubHeading(Field);
297+
298+
API.addStructField(StructRecord, Name, USR, Loc, Availability, Comment,
299+
Declaration, SubHeading);
300+
}
301+
}
302+
241303
ASTContext &Context;
242304
APISet API;
243305
};

clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,14 @@ Object serializeSymbolKind(const APIRecord &Record,
367367
Kind["identifier"] = AddLangPrefix("enum");
368368
Kind["displayName"] = "Enumeration";
369369
break;
370+
case APIRecord::RK_StructField:
371+
Kind["identifier"] = AddLangPrefix("property");
372+
Kind["displayName"] = "Instance Property";
373+
break;
374+
case APIRecord::RK_Struct:
375+
Kind["identifier"] = AddLangPrefix("struct");
376+
Kind["displayName"] = "Structure";
377+
break;
370378
}
371379

372380
return Kind;
@@ -474,6 +482,23 @@ void SymbolGraphSerializer::serializeEnumRecord(const EnumRecord &Record) {
474482
}
475483
}
476484

485+
void SymbolGraphSerializer::serializeStructRecord(const StructRecord &Record) {
486+
auto Struct = serializeAPIRecord(Record);
487+
if (!Struct)
488+
return;
489+
490+
Symbols.emplace_back(std::move(*Struct));
491+
492+
for (const auto &Field : Record.Fields) {
493+
auto StructField = serializeAPIRecord(*Field);
494+
if (!StructField)
495+
continue;
496+
497+
Symbols.emplace_back(std::move(*StructField));
498+
serializeRelationship(RelationshipKind::MemberOf, *Field, Record);
499+
}
500+
}
501+
477502
Object SymbolGraphSerializer::serialize() {
478503
Object Root;
479504
serializeObject(Root, "metadata", serializeMetadata());
@@ -487,6 +512,10 @@ Object SymbolGraphSerializer::serialize() {
487512
for (const auto &Enum : API.getEnums())
488513
serializeEnumRecord(*Enum.second);
489514

515+
// Serialize struct records in the API set.
516+
for (const auto &Struct : API.getStructs())
517+
serializeStructRecord(*Struct.second);
518+
490519
Root["symbols"] = std::move(Symbols);
491520
Root["relationhips"] = std::move(Relationships);
492521

0 commit comments

Comments
 (0)