Skip to content

Commit c60da1a

Browse files
[clang][ExtractAPI] Stop dropping fields of nested anonymous record types when they aren't attached to variable declaration (llvm#104600)
- Introduce primitives for removing records from `APISet` and managing the record chain of `RecordContext` - Detect nested anonymous record types and remove them from the `APISet` after they have been fully traversed and transfer ownership of child records to the parent context (if any)
1 parent 79f6ae0 commit c60da1a

File tree

5 files changed

+110
-27
lines changed

5 files changed

+110
-27
lines changed

clang/include/clang/ExtractAPI/API.h

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,13 @@
1919
#define LLVM_CLANG_EXTRACTAPI_API_H
2020

2121
#include "clang/AST/Availability.h"
22-
#include "clang/AST/Decl.h"
2322
#include "clang/AST/DeclBase.h"
24-
#include "clang/AST/DeclObjC.h"
2523
#include "clang/AST/RawCommentList.h"
2624
#include "clang/Basic/SourceLocation.h"
27-
#include "clang/Basic/Specifiers.h"
2825
#include "clang/ExtractAPI/DeclarationFragments.h"
29-
#include "llvm/ADT/ArrayRef.h"
30-
#include "llvm/ADT/MapVector.h"
31-
#include "llvm/ADT/StringRef.h"
26+
#include "llvm/ADT/SmallPtrSet.h"
3227
#include "llvm/Support/Allocator.h"
3328
#include "llvm/Support/Casting.h"
34-
#include "llvm/Support/Compiler.h"
35-
#include "llvm/Support/ErrorHandling.h"
36-
#include "llvm/Support/raw_ostream.h"
3729
#include "llvm/TargetParser/Triple.h"
3830
#include <cstddef>
3931
#include <iterator>
@@ -328,6 +320,8 @@ class RecordContext {
328320
/// chain.
329321
void stealRecordChain(RecordContext &Other);
330322

323+
void removeFromRecordChain(APIRecord *Record);
324+
331325
APIRecord::RecordKind getKind() const { return Kind; }
332326

333327
struct record_iterator {
@@ -1426,10 +1420,15 @@ class APISet {
14261420
typename std::enable_if_t<std::is_base_of_v<APIRecord, RecordTy>, RecordTy> *
14271421
createRecord(StringRef USR, StringRef Name, CtorArgsContTy &&...CtorArgs);
14281422

1429-
ArrayRef<const APIRecord *> getTopLevelRecords() const {
1430-
return TopLevelRecords;
1423+
auto getTopLevelRecords() const {
1424+
return llvm::iterator_range<decltype(TopLevelRecords)::iterator>(
1425+
TopLevelRecords);
14311426
}
14321427

1428+
void removeRecord(StringRef USR);
1429+
1430+
void removeRecord(APIRecord *Record);
1431+
14331432
APISet(const llvm::Triple &Target, Language Lang,
14341433
const std::string &ProductName)
14351434
: Target(Target), Lang(Lang), ProductName(ProductName) {}
@@ -1456,7 +1455,7 @@ class APISet {
14561455
// lives in the BumpPtrAllocator.
14571456
using APIRecordStoredPtr = std::unique_ptr<APIRecord, APIRecordDeleter>;
14581457
llvm::DenseMap<StringRef, APIRecordStoredPtr> USRBasedLookupTable;
1459-
std::vector<const APIRecord *> TopLevelRecords;
1458+
llvm::SmallPtrSet<const APIRecord *, 32> TopLevelRecords;
14601459

14611460
public:
14621461
const std::string ProductName;
@@ -1482,7 +1481,7 @@ APISet::createRecord(StringRef USR, StringRef Name,
14821481
dyn_cast_if_present<RecordContext>(Record->Parent.Record))
14831482
ParentContext->addToRecordChain(Record);
14841483
else
1485-
TopLevelRecords.push_back(Record);
1484+
TopLevelRecords.insert(Record);
14861485
} else {
14871486
Record = dyn_cast<RecordTy>(Result.first->second.get());
14881487
}

clang/include/clang/ExtractAPI/ExtractAPIVisitor.h

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,11 @@
2323
#include "clang/AST/RecursiveASTVisitor.h"
2424
#include "clang/Basic/LLVM.h"
2525
#include "clang/Basic/Module.h"
26-
#include "clang/Basic/SourceManager.h"
2726
#include "clang/Basic/Specifiers.h"
2827
#include "clang/ExtractAPI/API.h"
2928
#include "clang/ExtractAPI/DeclarationFragments.h"
3029
#include "clang/ExtractAPI/TypedefUnderlyingTypeResolver.h"
3130
#include "clang/Index/USRGeneration.h"
32-
#include "llvm/ADT/SmallString.h"
3331
#include "llvm/ADT/StringRef.h"
3432
#include "llvm/Support/Casting.h"
3533
#include <type_traits>
@@ -40,6 +38,8 @@ namespace impl {
4038

4139
template <typename Derived>
4240
class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
41+
using Base = RecursiveASTVisitor<Derived>;
42+
4343
protected:
4444
ExtractAPIVisitorBase(ASTContext &Context, APISet &API)
4545
: Context(Context), API(API) {}
@@ -81,8 +81,10 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
8181

8282
bool VisitNamespaceDecl(const NamespaceDecl *Decl);
8383

84+
bool TraverseRecordDecl(RecordDecl *Decl);
8485
bool VisitRecordDecl(const RecordDecl *Decl);
8586

87+
bool TraverseCXXRecordDecl(CXXRecordDecl *Decl);
8688
bool VisitCXXRecordDecl(const CXXRecordDecl *Decl);
8789

8890
bool VisitCXXMethodDecl(const CXXMethodDecl *Decl);
@@ -240,7 +242,7 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
240242

241243
bool isEmbeddedInVarDeclarator(const TagDecl &D) {
242244
return D.getName().empty() && getTypedefName(&D).empty() &&
243-
D.isEmbeddedInDeclarator();
245+
D.isEmbeddedInDeclarator() && !D.isFreeStanding();
244246
}
245247

246248
void maybeMergeWithAnonymousTag(const DeclaratorDecl &D,
@@ -252,8 +254,10 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
252254
clang::index::generateUSRForDecl(Tag, TagUSR);
253255
if (auto *Record = llvm::dyn_cast_if_present<TagRecord>(
254256
API.findRecordForUSR(TagUSR))) {
255-
if (Record->IsEmbeddedInVarDeclarator)
257+
if (Record->IsEmbeddedInVarDeclarator) {
256258
NewRecordContext->stealRecordChain(*Record);
259+
API.removeRecord(Record);
260+
}
257261
}
258262
}
259263
};
@@ -548,6 +552,19 @@ bool ExtractAPIVisitorBase<Derived>::VisitNamespaceDecl(
548552
return true;
549553
}
550554

555+
template <typename Derived>
556+
bool ExtractAPIVisitorBase<Derived>::TraverseRecordDecl(RecordDecl *Decl) {
557+
bool Ret = Base::TraverseRecordDecl(Decl);
558+
559+
if (!isEmbeddedInVarDeclarator(*Decl) && Decl->isAnonymousStructOrUnion()) {
560+
SmallString<128> USR;
561+
index::generateUSRForDecl(Decl, USR);
562+
API.removeRecord(USR);
563+
}
564+
565+
return Ret;
566+
}
567+
551568
template <typename Derived>
552569
bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) {
553570
if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
@@ -588,6 +605,20 @@ bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) {
588605
return true;
589606
}
590607

608+
template <typename Derived>
609+
bool ExtractAPIVisitorBase<Derived>::TraverseCXXRecordDecl(
610+
CXXRecordDecl *Decl) {
611+
bool Ret = Base::TraverseCXXRecordDecl(Decl);
612+
613+
if (!isEmbeddedInVarDeclarator(*Decl) && Decl->isAnonymousStructOrUnion()) {
614+
SmallString<128> USR;
615+
index::generateUSRForDecl(Decl, USR);
616+
API.removeRecord(USR);
617+
}
618+
619+
return Ret;
620+
}
621+
591622
template <typename Derived>
592623
bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl(
593624
const CXXRecordDecl *Decl) {

clang/lib/ExtractAPI/API.cpp

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
#include "clang/ExtractAPI/API.h"
16-
#include "clang/AST/RawCommentList.h"
17-
#include "clang/Index/USRGeneration.h"
1816
#include "llvm/ADT/StringRef.h"
1917
#include "llvm/Support/ErrorHandling.h"
2018
#include <memory>
@@ -60,6 +58,10 @@ bool RecordContext::IsWellFormed() const {
6058

6159
void RecordContext::stealRecordChain(RecordContext &Other) {
6260
assert(IsWellFormed());
61+
// Other's record chain is empty, nothing to do
62+
if (Other.First == nullptr && Other.Last == nullptr)
63+
return;
64+
6365
// If we don't have an empty chain append Other's chain into ours.
6466
if (First)
6567
Last->NextInContext = Other.First;
@@ -68,6 +70,10 @@ void RecordContext::stealRecordChain(RecordContext &Other) {
6870

6971
Last = Other.Last;
7072

73+
for (auto *StolenRecord = Other.First; StolenRecord != nullptr;
74+
StolenRecord = StolenRecord->getNextInContext())
75+
StolenRecord->Parent = SymbolReference(cast<APIRecord>(this));
76+
7177
// Delete Other's chain to ensure we don't accidentally traverse it.
7278
Other.First = nullptr;
7379
Other.Last = nullptr;
@@ -85,6 +91,22 @@ void RecordContext::addToRecordChain(APIRecord *Record) const {
8591
Last = Record;
8692
}
8793

94+
void RecordContext::removeFromRecordChain(APIRecord *Record) {
95+
APIRecord *Prev = nullptr;
96+
for (APIRecord *Curr = First; Curr != Record; Curr = Curr->NextInContext)
97+
Prev = Curr;
98+
99+
if (Prev)
100+
Prev->NextInContext = Record->NextInContext;
101+
else
102+
First = Record->NextInContext;
103+
104+
if (Last == Record)
105+
Last = Prev;
106+
107+
Record->NextInContext = nullptr;
108+
}
109+
88110
APIRecord *APISet::findRecordForUSR(StringRef USR) const {
89111
if (USR.empty())
90112
return nullptr;
@@ -114,6 +136,33 @@ SymbolReference APISet::createSymbolReference(StringRef Name, StringRef USR,
114136
return SymbolReference(copyString(Name), copyString(USR), copyString(Source));
115137
}
116138

139+
void APISet::removeRecord(StringRef USR) {
140+
auto Result = USRBasedLookupTable.find(USR);
141+
if (Result != USRBasedLookupTable.end()) {
142+
auto *Record = Result->getSecond().get();
143+
auto &ParentReference = Record->Parent;
144+
auto *ParentRecord = const_cast<APIRecord *>(ParentReference.Record);
145+
if (!ParentRecord)
146+
ParentRecord = findRecordForUSR(ParentReference.USR);
147+
148+
if (auto *ParentCtx = llvm::cast_if_present<RecordContext>(ParentRecord)) {
149+
ParentCtx->removeFromRecordChain(Record);
150+
if (auto *RecordAsCtx = llvm::dyn_cast<RecordContext>(Record))
151+
ParentCtx->stealRecordChain(*RecordAsCtx);
152+
} else {
153+
TopLevelRecords.erase(Record);
154+
if (auto *RecordAsCtx = llvm::dyn_cast<RecordContext>(Record)) {
155+
for (const auto *Child = RecordAsCtx->First; Child != nullptr;
156+
Child = Child->getNextInContext())
157+
TopLevelRecords.insert(Child);
158+
}
159+
}
160+
USRBasedLookupTable.erase(Result);
161+
}
162+
}
163+
164+
void APISet::removeRecord(APIRecord *Record) { removeRecord(Record->USR); }
165+
117166
APIRecord::~APIRecord() {}
118167
TagRecord::~TagRecord() {}
119168
RecordRecord::~RecordRecord() {}

clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -673,14 +673,6 @@ bool SymbolGraphSerializer::shouldSkip(const APIRecord *Record) const {
673673
if (Record->Availability.isUnconditionallyUnavailable())
674674
return true;
675675

676-
// Filter out symbols without a name as we can generate correct symbol graphs
677-
// for them. In practice these are anonymous record types that aren't attached
678-
// to a declaration.
679-
if (auto *Tag = dyn_cast<TagRecord>(Record)) {
680-
if (Tag->IsEmbeddedInVarDeclarator)
681-
return true;
682-
}
683-
684676
// Filter out symbols prefixed with an underscored as they are understood to
685677
// be symbols clients should not use.
686678
if (Record->Name.starts_with("_"))

clang/test/ExtractAPI/anonymous_record_no_typedef.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,16 @@ enum {
163163
// GLOBALOTHERCASE-NEXT: "GlobalOtherCase"
164164
// GLOBALOTHERCASE-NEXT: ]
165165

166+
// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix VEC
167+
union Vector {
168+
struct {
169+
float X;
170+
float Y;
171+
};
172+
float Data[2];
173+
};
174+
// VEC-DAG: "!testRelLabel": "memberOf $ c:@U@Vector@FI@Data $ c:@U@Vector"
175+
// VEC-DAG: "!testRelLabel": "memberOf $ c:@U@Vector@Sa@FI@X $ c:@U@Vector"
176+
// VEC-DAG: "!testRelLabel": "memberOf $ c:@U@Vector@Sa@FI@Y $ c:@U@Vector"
177+
166178
// expected-no-diagnostics

0 commit comments

Comments
 (0)