Skip to content

Commit 9fc45ca

Browse files
[clang][extract-api] Add support for typedefs
Typedef records consist of the symbol associated with the underlying TypedefDecl and a SymbolReference to the underlying type. Additionally typedefs for anonymous TagTypes use the typedef'd name as the symbol name in their respective records and USRs. As a result the declaration fragments for the anonymous TagType are those for the associated typedef. This means that when the user is defining a typedef to a typedef to a anonymous type, we use a reference the anonymous TagType itself and do not emit the typedef to the anonymous type in the generated symbol graph, including in the type destination of further typedef symbol records. Differential Revision: https://reviews.llvm.org/D123019
1 parent 482fad4 commit 9fc45ca

File tree

13 files changed

+815
-4
lines changed

13 files changed

+815
-4
lines changed

clang/include/clang/ExtractAPI/API.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ struct APIRecord {
8888
RK_ObjCInterface,
8989
RK_ObjCProtocol,
9090
RK_MacroDefinition,
91+
RK_Typedef,
9192
};
9293

9394
private:
@@ -396,6 +397,30 @@ struct MacroDefinitionRecord : APIRecord {
396397
virtual void anchor();
397398
};
398399

400+
/// This holds information associated with typedefs.
401+
///
402+
/// Note: Typedefs for anonymous enums and structs typically don't get emitted
403+
/// by the serializers but still get a TypedefRecord. Instead we use the
404+
/// typedef name as a name for the underlying anonymous struct or enum.
405+
struct TypedefRecord : APIRecord {
406+
SymbolReference UnderlyingType;
407+
408+
TypedefRecord(StringRef Name, StringRef USR, PresumedLoc Loc,
409+
const AvailabilityInfo &Availability, const DocComment &Comment,
410+
DeclarationFragments Declaration,
411+
DeclarationFragments SubHeading, SymbolReference UnderlyingType)
412+
: APIRecord(RK_Typedef, Name, USR, Loc, Availability, LinkageInfo(),
413+
Comment, Declaration, SubHeading),
414+
UnderlyingType(UnderlyingType) {}
415+
416+
static bool classof(const APIRecord *Record) {
417+
return Record->getKind() == RK_Typedef;
418+
}
419+
420+
private:
421+
virtual void anchor();
422+
};
423+
399424
/// APISet holds the set of API records collected from given inputs.
400425
class APISet {
401426
public:
@@ -564,6 +589,19 @@ class APISet {
564589
DeclarationFragments Declaration,
565590
DeclarationFragments SubHeading);
566591

592+
/// Create a typedef record into the API set.
593+
///
594+
/// Note: the caller is responsible for keeping the StringRef \p Name and
595+
/// \p USR alive. APISet::copyString provides a way to copy strings into
596+
/// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method
597+
/// to generate the USR for \c D and keep it alive in APISet.
598+
TypedefRecord *addTypedef(StringRef Name, StringRef USR, PresumedLoc Loc,
599+
const AvailabilityInfo &Availability,
600+
const DocComment &Comment,
601+
DeclarationFragments Declaration,
602+
DeclarationFragments SubHeading,
603+
SymbolReference UnderlyingType);
604+
567605
/// A mapping type to store a set of APIRecord%s with the declaration name as
568606
/// the key.
569607
template <typename RecordTy,
@@ -587,6 +625,7 @@ class APISet {
587625
return ObjCProtocols;
588626
}
589627
const RecordMap<MacroDefinitionRecord> &getMacros() const { return Macros; }
628+
const RecordMap<TypedefRecord> &getTypedefs() const { return Typedefs; }
590629

591630
/// Generate and store the USR of declaration \p D.
592631
///
@@ -626,6 +665,7 @@ class APISet {
626665
RecordMap<ObjCInterfaceRecord> ObjCInterfaces;
627666
RecordMap<ObjCProtocolRecord> ObjCProtocols;
628667
RecordMap<MacroDefinitionRecord> Macros;
668+
RecordMap<TypedefRecord> Typedefs;
629669
};
630670

631671
} // namespace extractapi

clang/include/clang/ExtractAPI/DeclarationFragments.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ class DeclarationFragmentsBuilder {
230230
static DeclarationFragments getFragmentsForMacro(StringRef Name,
231231
const MacroDirective *MD);
232232

233+
/// Build DeclarationFragments for a typedef \p TypedefNameDecl.
234+
static DeclarationFragments
235+
getFragmentsForTypedef(const TypedefNameDecl *Decl);
236+
233237
/// Build sub-heading fragments for a NamedDecl.
234238
static DeclarationFragments getSubHeading(const NamedDecl *);
235239

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@ class SymbolGraphSerializer : public APISerializer {
146146
/// Serialize a macro defintion record.
147147
void serializeMacroDefinitionRecord(const MacroDefinitionRecord &Record);
148148

149+
/// Serialize a typedef record.
150+
void serializeTypedefRecord(const TypedefRecord &Record);
151+
149152
/// Push a component to the current path components stack.
150153
///
151154
/// \param Component The component to push onto the path components stack.

clang/lib/ExtractAPI/API.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,17 @@ APISet::addMacroDefinition(StringRef Name, StringRef USR, PresumedLoc Loc,
170170
return addTopLevelRecord(Macros, Name, USR, Loc, Declaration, SubHeading);
171171
}
172172

173+
TypedefRecord *APISet::addTypedef(StringRef Name, StringRef USR,
174+
PresumedLoc Loc,
175+
const AvailabilityInfo &Availability,
176+
const DocComment &Comment,
177+
DeclarationFragments Declaration,
178+
DeclarationFragments SubHeading,
179+
SymbolReference UnderlyingType) {
180+
return addTopLevelRecord(Typedefs, Name, USR, Loc, Availability, Comment,
181+
Declaration, SubHeading, UnderlyingType);
182+
}
183+
173184
StringRef APISet::recordUSR(const Decl *D) {
174185
SmallString<128> USR;
175186
index::generateUSRForDecl(D, USR);
@@ -211,3 +222,4 @@ void ObjCMethodRecord::anchor() {}
211222
void ObjCInterfaceRecord::anchor() {}
212223
void ObjCProtocolRecord::anchor() {}
213224
void MacroDefinitionRecord::anchor() {}
225+
void TypedefRecord::anchor() {}

clang/lib/ExtractAPI/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ add_clang_library(clangExtractAPI
88
DeclarationFragments.cpp
99
Serialization/SerializerBase.cpp
1010
Serialization/SymbolGraphSerializer.cpp
11+
TypedefUnderlyingTypeResolver.cpp
1112

1213
LINK_LIBS
1314
clangAST

clang/lib/ExtractAPI/DeclarationFragments.cpp

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//===----------------------------------------------------------------------===//
1313

1414
#include "clang/ExtractAPI/DeclarationFragments.h"
15+
#include "TypedefUnderlyingTypeResolver.h"
1516
#include "clang/Index/USRGeneration.h"
1617
#include "llvm/ADT/StringSwitch.h"
1718

@@ -250,6 +251,31 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(
250251
return Fragments.append(Base.getAsString(),
251252
DeclarationFragments::FragmentKind::Keyword);
252253

254+
// If the type is a typedefed type, get the underlying TypedefNameDecl for a
255+
// direct reference to the typedef instead of the wrapped type.
256+
if (const TypedefType *TypedefTy = dyn_cast<TypedefType>(T)) {
257+
const TypedefNameDecl *Decl = TypedefTy->getDecl();
258+
std::string USR =
259+
TypedefUnderlyingTypeResolver(Context).getUSRForType(QualType(T, 0));
260+
return Fragments.append(Decl->getName(),
261+
DeclarationFragments::FragmentKind::TypeIdentifier,
262+
USR);
263+
}
264+
265+
// If the base type is a TagType (struct/interface/union/class/enum), let's
266+
// get the underlying Decl for better names and USRs.
267+
if (const TagType *TagTy = dyn_cast<TagType>(Base)) {
268+
const TagDecl *Decl = TagTy->getDecl();
269+
// Anonymous decl, skip this fragment.
270+
if (Decl->getName().empty())
271+
return Fragments;
272+
SmallString<128> TagUSR;
273+
clang::index::generateUSRForDecl(Decl, TagUSR);
274+
return Fragments.append(Decl->getName(),
275+
DeclarationFragments::FragmentKind::TypeIdentifier,
276+
TagUSR);
277+
}
278+
253279
// If the base type is an ObjCInterfaceType, use the underlying
254280
// ObjCInterfaceDecl for the true USR.
255281
if (const auto *ObjCIT = dyn_cast<ObjCInterfaceType>(Base)) {
@@ -426,8 +452,8 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForEnumConstant(
426452

427453
DeclarationFragments
428454
DeclarationFragmentsBuilder::getFragmentsForEnum(const EnumDecl *EnumDecl) {
429-
// TODO: After we support typedef records, if there's a typedef for this enum
430-
// just use the declaration fragments of the typedef decl.
455+
if (const auto *TypedefNameDecl = EnumDecl->getTypedefNameForAnonDecl())
456+
return getFragmentsForTypedef(TypedefNameDecl);
431457

432458
DeclarationFragments Fragments, After;
433459
Fragments.append("enum", DeclarationFragments::FragmentKind::Keyword);
@@ -457,8 +483,8 @@ DeclarationFragmentsBuilder::getFragmentsForField(const FieldDecl *Field) {
457483

458484
DeclarationFragments
459485
DeclarationFragmentsBuilder::getFragmentsForStruct(const RecordDecl *Record) {
460-
// TODO: After we support typedef records, if there's a typedef for this
461-
// struct just use the declaration fragments of the typedef decl.
486+
if (const auto *TypedefNameDecl = Record->getTypedefNameForAnonDecl())
487+
return getFragmentsForTypedef(TypedefNameDecl);
462488

463489
DeclarationFragments Fragments;
464490
Fragments.append("struct", DeclarationFragments::FragmentKind::Keyword);
@@ -680,6 +706,20 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(
680706
return Fragments;
681707
}
682708

709+
DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForTypedef(
710+
const TypedefNameDecl *Decl) {
711+
DeclarationFragments Fragments, After;
712+
Fragments.append("typedef", DeclarationFragments::FragmentKind::Keyword)
713+
.appendSpace()
714+
.append(getFragmentsForType(Decl->getUnderlyingType(),
715+
Decl->getASTContext(), After))
716+
.append(std::move(After))
717+
.appendSpace()
718+
.append(Decl->getName(), DeclarationFragments::FragmentKind::Identifier);
719+
720+
return Fragments;
721+
}
722+
683723
template <typename FunctionT>
684724
FunctionSignature
685725
DeclarationFragmentsBuilder::getFunctionSignature(const FunctionT *Function) {

clang/lib/ExtractAPI/ExtractAPIConsumer.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
///
1313
//===----------------------------------------------------------------------===//
1414

15+
#include "TypedefUnderlyingTypeResolver.h"
1516
#include "clang/AST/ASTConsumer.h"
1617
#include "clang/AST/ASTContext.h"
1718
#include "clang/AST/Decl.h"
@@ -41,6 +42,13 @@ using namespace extractapi;
4142

4243
namespace {
4344

45+
StringRef getTypedefName(const TagDecl *Decl) {
46+
if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl())
47+
return TypedefDecl->getName();
48+
49+
return {};
50+
}
51+
4452
/// The RecursiveASTVisitor to traverse symbol declarations and collect API
4553
/// information.
4654
class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
@@ -161,6 +169,8 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
161169

162170
// Collect symbol information.
163171
StringRef Name = Decl->getName();
172+
if (Name.empty())
173+
Name = getTypedefName(Decl);
164174
StringRef USR = API.recordUSR(Decl);
165175
PresumedLoc Loc =
166176
Context.getSourceManager().getPresumedLoc(Decl->getLocation());
@@ -196,6 +206,8 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
196206

197207
// Collect symbol information.
198208
StringRef Name = Decl->getName();
209+
if (Name.empty())
210+
Name = getTypedefName(Decl);
199211
StringRef USR = API.recordUSR(Decl);
200212
PresumedLoc Loc =
201213
Context.getSourceManager().getPresumedLoc(Decl->getLocation());
@@ -296,6 +308,36 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
296308
return true;
297309
}
298310

311+
bool VisitTypedefNameDecl(const TypedefNameDecl *Decl) {
312+
// Skip ObjC Type Parameter for now.
313+
if (isa<ObjCTypeParamDecl>(Decl))
314+
return true;
315+
316+
if (!Decl->isDefinedOutsideFunctionOrMethod())
317+
return true;
318+
319+
PresumedLoc Loc =
320+
Context.getSourceManager().getPresumedLoc(Decl->getLocation());
321+
StringRef Name = Decl->getName();
322+
AvailabilityInfo Availability = getAvailability(Decl);
323+
StringRef USR = API.recordUSR(Decl);
324+
DocComment Comment;
325+
if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
326+
Comment = RawComment->getFormattedLines(Context.getSourceManager(),
327+
Context.getDiagnostics());
328+
329+
QualType Type = Decl->getUnderlyingType();
330+
SymbolReference SymRef =
331+
TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type,
332+
API);
333+
334+
API.addTypedef(Name, USR, Loc, Availability, Comment,
335+
DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl),
336+
DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef);
337+
338+
return true;
339+
}
340+
299341
private:
300342
/// Get availability information of the declaration \p D.
301343
AvailabilityInfo getAvailability(const Decl *D) const {

clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,11 @@ Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
408408
case APIRecord::RK_MacroDefinition:
409409
Kind["identifier"] = AddLangPrefix("macro");
410410
Kind["displayName"] = "Macro";
411+
break;
412+
case APIRecord::RK_Typedef:
413+
Kind["identifier"] = AddLangPrefix("typealias");
414+
Kind["displayName"] = "Type Alias";
415+
break;
411416
}
412417

413418
return Kind;
@@ -618,6 +623,27 @@ void SymbolGraphSerializer::serializeMacroDefinitionRecord(
618623
Symbols.emplace_back(std::move(*Macro));
619624
}
620625

626+
void SymbolGraphSerializer::serializeTypedefRecord(
627+
const TypedefRecord &Record) {
628+
// Typedefs of anonymous types have their entries unified with the underlying
629+
// type.
630+
bool ShouldDrop = Record.UnderlyingType.Name.empty();
631+
// enums declared with `NS_OPTION` have a named enum and a named typedef, with
632+
// the same name
633+
ShouldDrop |= (Record.UnderlyingType.Name == Record.Name);
634+
if (ShouldDrop)
635+
return;
636+
637+
auto TypedefPathComponentGuard = makePathComponentGuard(Record.Name);
638+
auto Typedef = serializeAPIRecord(Record);
639+
if (!Typedef)
640+
return;
641+
642+
(*Typedef)["type"] = Record.UnderlyingType.USR;
643+
644+
Symbols.emplace_back(std::move(*Typedef));
645+
}
646+
621647
SymbolGraphSerializer::PathComponentGuard
622648
SymbolGraphSerializer::makePathComponentGuard(StringRef Component) {
623649
return PathComponentGuard(PathComponents, Component);
@@ -651,6 +677,9 @@ Object SymbolGraphSerializer::serialize() {
651677
for (const auto &Macro : API.getMacros())
652678
serializeMacroDefinitionRecord(*Macro.second);
653679

680+
for (const auto &Typedef : API.getTypedefs())
681+
serializeTypedefRecord(*Typedef.second);
682+
654683
Root["symbols"] = std::move(Symbols);
655684
Root["relationships"] = std::move(Relationships);
656685

0 commit comments

Comments
 (0)