[clang] [clang][ExtractAPI] Fix handling of anonymous TagDecls (PR #87772)
Daniel Grumberg via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 23 06:56:19 PDT 2024
https://github.com/daniel-grumberg updated https://github.com/llvm/llvm-project/pull/87772
>From 18912352db31406c7c5b530d6e22f77e775fbf38 Mon Sep 17 00:00:00 2001
From: Daniel Grumberg <dgrumberg at apple.com>
Date: Thu, 4 Apr 2024 18:33:25 +0100
Subject: [PATCH 1/2] [clang][ExtractAPI] Fix handling of anonymous TagDecls
This changes the handling of anonymous TagDecls to the following rules:
- If the TagDecl is embedded in the declaration for some VarDecl (this
is the only possibility for RecordDecls), then pretend the child decls
belong to the VarDecl
- If it's an EnumDecl proceed as we did previously, i.e., embed it in
the enclosing DeclContext.
Additionally this fixes a few issues with declaration fragments not
consistently including "{ ... }" for anonymous TagDecls. To make testing
these additions easier this patch fixes some text declaration fragments
merging issues and updates tests accordingly.
---
clang/include/clang/ExtractAPI/API.h | 132 ++--
clang/include/clang/ExtractAPI/APIRecords.inc | 16 +-
.../clang/ExtractAPI/DeclarationFragments.h | 84 ++-
.../clang/ExtractAPI/ExtractAPIVisitor.h | 76 ++-
clang/lib/ExtractAPI/API.cpp | 8 +
clang/lib/ExtractAPI/DeclarationFragments.cpp | 17 +-
.../Serialization/SymbolGraphSerializer.cpp | 8 +
.../ExtractAPI/anonymous_record_no_typedef.c | 565 +++++-------------
clang/test/ExtractAPI/enum.c | 12 +-
clang/test/ExtractAPI/function_noexcepts.cpp | 18 +-
clang/test/ExtractAPI/methods.cpp | 6 +-
clang/test/ExtractAPI/objc_block.m | 48 +-
.../ExtractAPI/typedef_anonymous_record.c | 4 +-
clang/test/ExtractAPI/typedef_struct_enum.c | 2 +-
14 files changed, 427 insertions(+), 569 deletions(-)
diff --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h
index 92cacf65c7d64e..05cfabd072a560 100644
--- a/clang/include/clang/ExtractAPI/API.h
+++ b/clang/include/clang/ExtractAPI/API.h
@@ -208,20 +208,20 @@ struct APIRecord {
RK_ClassTemplate,
RK_ClassTemplateSpecialization,
RK_ClassTemplatePartialSpecialization,
- RK_LastRecordContext,
- RK_GlobalFunction,
- RK_GlobalFunctionTemplate,
- RK_GlobalFunctionTemplateSpecialization,
+ RK_StructField,
+ RK_UnionField,
+ RK_CXXField,
+ RK_StaticField,
+ RK_CXXFieldTemplate,
RK_GlobalVariable,
RK_GlobalVariableTemplate,
RK_GlobalVariableTemplateSpecialization,
RK_GlobalVariableTemplatePartialSpecialization,
+ RK_LastRecordContext,
+ RK_GlobalFunction,
+ RK_GlobalFunctionTemplate,
+ RK_GlobalFunctionTemplateSpecialization,
RK_EnumConstant,
- RK_StructField,
- RK_UnionField,
- RK_StaticField,
- RK_CXXField,
- RK_CXXFieldTemplate,
RK_Concept,
RK_CXXStaticMethod,
RK_CXXInstanceMethod,
@@ -321,6 +321,8 @@ class RecordContext {
RecordContext(APIRecord::RecordKind Kind) : Kind(Kind) {}
+ void stealRecordChain(RecordContext &Other);
+
APIRecord::RecordKind getKind() const { return Kind; }
struct record_iterator {
@@ -475,7 +477,7 @@ struct GlobalFunctionTemplateSpecializationRecord : GlobalFunctionRecord {
};
/// This holds information associated with global functions.
-struct GlobalVariableRecord : APIRecord {
+struct GlobalVariableRecord : APIRecord, RecordContext {
GlobalVariableRecord(StringRef USR, StringRef Name, SymbolReference Parent,
PresumedLoc Loc, AvailabilityInfo Availability,
LinkageInfo Linkage, const DocComment &Comment,
@@ -483,23 +485,28 @@ struct GlobalVariableRecord : APIRecord {
DeclarationFragments SubHeading, bool IsFromSystemHeader)
: APIRecord(RK_GlobalVariable, USR, Name, Parent, Loc,
std::move(Availability), Linkage, Comment, Declaration,
- SubHeading, IsFromSystemHeader) {}
+ SubHeading, IsFromSystemHeader),
+ RecordContext(RK_GlobalVariable) {}
GlobalVariableRecord(RecordKind Kind, StringRef USR, StringRef Name,
- SymbolReference Parent,
-
- PresumedLoc Loc, AvailabilityInfo Availability,
- LinkageInfo Linkage, const DocComment &Comment,
+ SymbolReference Parent, PresumedLoc Loc,
+ AvailabilityInfo Availability, LinkageInfo Linkage,
+ const DocComment &Comment,
DeclarationFragments Declaration,
DeclarationFragments SubHeading, bool IsFromSystemHeader)
: APIRecord(Kind, USR, Name, Parent, Loc, std::move(Availability),
Linkage, Comment, Declaration, SubHeading,
- IsFromSystemHeader) {}
+ IsFromSystemHeader),
+ RecordContext(Kind) {}
static bool classof(const APIRecord *Record) {
return classofKind(Record->getKind());
}
- static bool classofKind(RecordKind K) { return K == RK_GlobalVariable; }
+ static bool classofKind(RecordKind K) {
+ return K == RK_GlobalVariable || K == RK_GlobalVariableTemplate ||
+ K == RK_GlobalVariableTemplateSpecialization ||
+ K == RK_GlobalVariableTemplatePartialSpecialization;
+ }
private:
virtual void anchor();
@@ -591,20 +598,47 @@ struct EnumConstantRecord : APIRecord {
virtual void anchor();
};
+struct TagRecord : APIRecord, RecordContext {
+ TagRecord(RecordKind Kind, StringRef USR, StringRef Name,
+ SymbolReference Parent, PresumedLoc Loc,
+ AvailabilityInfo Availability, const DocComment &Comment,
+ DeclarationFragments Declaration, DeclarationFragments SubHeading,
+ bool IsFromSystemHeader, bool IsEmbeddedInVarDeclarator,
+ AccessControl Access = AccessControl())
+ : APIRecord(Kind, USR, Name, Parent, Loc, std::move(Availability),
+ LinkageInfo::none(), Comment, Declaration, SubHeading,
+ IsFromSystemHeader, std::move(Access)),
+ RecordContext(Kind),
+ IsEmbeddedInVarDeclarator(IsEmbeddedInVarDeclarator){};
+
+ static bool classof(const APIRecord *Record) {
+ return classofKind(Record->getKind());
+ }
+ static bool classofKind(RecordKind K) {
+ return K == RK_Struct || K == RK_Union || K == RK_Enum;
+ }
+
+ bool IsEmbeddedInVarDeclarator;
+
+ virtual ~TagRecord() = 0;
+};
+
/// This holds information associated with enums.
-struct EnumRecord : APIRecord, RecordContext {
+struct EnumRecord : TagRecord {
EnumRecord(StringRef USR, StringRef Name, SymbolReference Parent,
PresumedLoc Loc, AvailabilityInfo Availability,
const DocComment &Comment, DeclarationFragments Declaration,
- DeclarationFragments SubHeading, bool IsFromSystemHeader)
- : APIRecord(RK_Enum, USR, Name, Parent, Loc, std::move(Availability),
- LinkageInfo::none(), Comment, Declaration, SubHeading,
- IsFromSystemHeader),
- RecordContext(RK_Enum) {}
+ DeclarationFragments SubHeading, bool IsFromSystemHeader,
+ bool IsEmbeddedInVarDeclarator,
+ AccessControl Access = AccessControl())
+ : TagRecord(RK_Enum, USR, Name, Parent, Loc, std::move(Availability),
+ Comment, Declaration, SubHeading, IsFromSystemHeader,
+ IsEmbeddedInVarDeclarator, std::move(Access)) {}
static bool classof(const APIRecord *Record) {
return classofKind(Record->getKind());
}
+
static bool classofKind(RecordKind K) { return K == RK_Enum; }
private:
@@ -612,7 +646,7 @@ struct EnumRecord : APIRecord, RecordContext {
};
/// This holds information associated with struct or union fields fields.
-struct RecordFieldRecord : APIRecord {
+struct RecordFieldRecord : APIRecord, RecordContext {
RecordFieldRecord(RecordKind Kind, StringRef USR, StringRef Name,
SymbolReference Parent, PresumedLoc Loc,
AvailabilityInfo Availability, const DocComment &Comment,
@@ -620,7 +654,8 @@ struct RecordFieldRecord : APIRecord {
DeclarationFragments SubHeading, bool IsFromSystemHeader)
: APIRecord(Kind, USR, Name, Parent, Loc, std::move(Availability),
LinkageInfo::none(), Comment, Declaration, SubHeading,
- IsFromSystemHeader) {}
+ IsFromSystemHeader),
+ RecordContext(Kind) {}
static bool classof(const APIRecord *Record) {
return classofKind(Record->getKind());
@@ -633,16 +668,17 @@ struct RecordFieldRecord : APIRecord {
};
/// This holds information associated with structs and unions.
-struct RecordRecord : APIRecord, RecordContext {
+struct RecordRecord : TagRecord {
RecordRecord(RecordKind Kind, StringRef USR, StringRef Name,
SymbolReference Parent, PresumedLoc Loc,
AvailabilityInfo Availability, const DocComment &Comment,
DeclarationFragments Declaration,
- DeclarationFragments SubHeading, bool IsFromSystemHeader)
- : APIRecord(Kind, USR, Name, Parent, Loc, std::move(Availability),
- LinkageInfo::none(), Comment, Declaration, SubHeading,
- IsFromSystemHeader),
- RecordContext(Kind) {}
+ DeclarationFragments SubHeading, bool IsFromSystemHeader,
+ bool IsEmbeddedInVarDeclarator,
+ AccessControl Access = AccessControl())
+ : TagRecord(Kind, USR, Name, Parent, Loc, std::move(Availability),
+ Comment, Declaration, SubHeading, IsFromSystemHeader,
+ IsEmbeddedInVarDeclarator, std::move(Access)) {}
static bool classof(const APIRecord *Record) {
return classofKind(Record->getKind());
@@ -651,6 +687,8 @@ struct RecordRecord : APIRecord, RecordContext {
return K == RK_Struct || K == RK_Union;
}
+ bool isAnonymousWithNoTypedef() { return Name.empty(); }
+
virtual ~RecordRecord() = 0;
};
@@ -676,9 +714,11 @@ struct StructRecord : RecordRecord {
StructRecord(StringRef USR, StringRef Name, SymbolReference Parent,
PresumedLoc Loc, AvailabilityInfo Availability,
const DocComment &Comment, DeclarationFragments Declaration,
- DeclarationFragments SubHeading, bool IsFromSystemHeader)
+ DeclarationFragments SubHeading, bool IsFromSystemHeader,
+ bool IsEmbeddedInVarDeclarator)
: RecordRecord(RK_Struct, USR, Name, Parent, Loc, std::move(Availability),
- Comment, Declaration, SubHeading, IsFromSystemHeader) {}
+ Comment, Declaration, SubHeading, IsFromSystemHeader,
+ IsEmbeddedInVarDeclarator) {}
static bool classof(const APIRecord *Record) {
return classofKind(Record->getKind());
@@ -711,9 +751,11 @@ struct UnionRecord : RecordRecord {
UnionRecord(StringRef USR, StringRef Name, SymbolReference Parent,
PresumedLoc Loc, AvailabilityInfo Availability,
const DocComment &Comment, DeclarationFragments Declaration,
- DeclarationFragments SubHeading, bool IsFromSystemHeader)
+ DeclarationFragments SubHeading, bool IsFromSystemHeader,
+ bool IsEmbeddedInVarDeclarator)
: RecordRecord(RK_Union, USR, Name, Parent, Loc, std::move(Availability),
- Comment, Declaration, SubHeading, IsFromSystemHeader) {}
+ Comment, Declaration, SubHeading, IsFromSystemHeader,
+ IsEmbeddedInVarDeclarator) {}
static bool classof(const APIRecord *Record) {
return classofKind(Record->getKind());
@@ -724,7 +766,7 @@ struct UnionRecord : RecordRecord {
virtual void anchor();
};
-struct CXXFieldRecord : APIRecord {
+struct CXXFieldRecord : APIRecord, RecordContext {
CXXFieldRecord(StringRef USR, StringRef Name, SymbolReference Parent,
PresumedLoc Loc, AvailabilityInfo Availability,
const DocComment &Comment, DeclarationFragments Declaration,
@@ -732,7 +774,8 @@ struct CXXFieldRecord : APIRecord {
bool IsFromSystemHeader)
: APIRecord(RK_CXXField, USR, Name, Parent, Loc, std::move(Availability),
LinkageInfo::none(), Comment, Declaration, SubHeading,
- IsFromSystemHeader, std::move(Access)) {}
+ IsFromSystemHeader, std::move(Access)),
+ RecordContext(RK_CXXField) {}
CXXFieldRecord(RecordKind Kind, StringRef USR, StringRef Name,
SymbolReference Parent, PresumedLoc Loc,
@@ -742,7 +785,8 @@ struct CXXFieldRecord : APIRecord {
bool IsFromSystemHeader)
: APIRecord(Kind, USR, Name, Parent, Loc, std::move(Availability),
LinkageInfo::none(), Comment, Declaration, SubHeading,
- IsFromSystemHeader, std::move(Access)) {}
+ IsFromSystemHeader, std::move(Access)),
+ RecordContext(Kind) {}
static bool classof(const APIRecord *Record) {
return classofKind(Record->getKind());
@@ -1118,18 +1162,18 @@ struct ObjCContainerRecord : APIRecord, RecordContext {
virtual ~ObjCContainerRecord() = 0;
};
-struct CXXClassRecord : APIRecord, RecordContext {
+struct CXXClassRecord : RecordRecord {
SmallVector<SymbolReference> Bases;
CXXClassRecord(StringRef USR, StringRef Name, SymbolReference Parent,
PresumedLoc Loc, AvailabilityInfo Availability,
const DocComment &Comment, DeclarationFragments Declaration,
DeclarationFragments SubHeading, RecordKind Kind,
- AccessControl Access, bool IsFromSystemHeader)
- : APIRecord(Kind, USR, Name, Parent, Loc, std::move(Availability),
- LinkageInfo::none(), Comment, Declaration, SubHeading,
- IsFromSystemHeader, std::move(Access)),
- RecordContext(Kind) {}
+ AccessControl Access, bool IsFromSystemHeader,
+ bool IsEmbeddedInVarDeclarator = false)
+ : RecordRecord(Kind, USR, Name, Parent, Loc, std::move(Availability),
+ Comment, Declaration, SubHeading, IsFromSystemHeader,
+ IsEmbeddedInVarDeclarator, std::move(Access)) {}
static bool classof(const APIRecord *Record) {
return classofKind(Record->getKind());
diff --git a/clang/include/clang/ExtractAPI/APIRecords.inc b/clang/include/clang/ExtractAPI/APIRecords.inc
index 15fee809656d9a..4cda4ef2f9be63 100644
--- a/clang/include/clang/ExtractAPI/APIRecords.inc
+++ b/clang/include/clang/ExtractAPI/APIRecords.inc
@@ -35,10 +35,11 @@ CONCRETE_RECORD(GlobalVariableTemplateSpecializationRecord,
CONCRETE_RECORD(GlobalVariableTemplatePartialSpecializationRecord,
GlobalVariableRecord,
RK_GlobalVariableTemplatePartialSpecialization)
+ABSTRACT_RECORD(TagRecord, APIRecord)
CONCRETE_RECORD(EnumConstantRecord, APIRecord, RK_EnumConstant)
-CONCRETE_RECORD(EnumRecord, APIRecord, RK_Enum)
+CONCRETE_RECORD(EnumRecord, TagRecord, RK_Enum)
ABSTRACT_RECORD(RecordFieldRecord, APIRecord)
-ABSTRACT_RECORD(RecordRecord, APIRecord)
+ABSTRACT_RECORD(RecordRecord, TagRecord)
CONCRETE_RECORD(StructFieldRecord, RecordFieldRecord, RK_StructField)
CONCRETE_RECORD(StructRecord, APIRecord, RK_Struct)
CONCRETE_RECORD(UnionFieldRecord, RecordFieldRecord, RK_UnionField)
@@ -99,5 +100,16 @@ RECORD_CONTEXT(ClassTemplateSpecializationRecord,
RK_ClassTemplateSpecialization)
RECORD_CONTEXT(ClassTemplatePartialSpecializationRecord,
RK_ClassTemplatePartialSpecialization)
+RECORD_CONTEXT(StructFieldRecord, RK_StructField)
+RECORD_CONTEXT(UnionFieldRecord, RK_UnionField)
+RECORD_CONTEXT(CXXFieldRecord, RK_CXXField)
+RECORD_CONTEXT(StaticFieldRecord, RK_StaticField)
+RECORD_CONTEXT(CXXFieldTemplateRecord, RK_CXXFieldTemplate)
+RECORD_CONTEXT(GlobalVariableRecord, RK_GlobalVariable)
+RECORD_CONTEXT(GlobalVariableTemplateRecord, RK_GlobalVariableTemplate)
+RECORD_CONTEXT(GlobalVariableTemplateSpecializationRecord,
+ RK_GlobalVariableTemplateSpecialization)
+RECORD_CONTEXT(GlobalVariableTemplatePartialSpecializationRecord,
+ RK_GlobalVariableTemplatePartialSpecialization)
#undef RECORD_CONTEXT
diff --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h
index 94392c18516595..535da90b98284b 100644
--- a/clang/include/clang/ExtractAPI/DeclarationFragments.h
+++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h
@@ -27,6 +27,8 @@
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Lex/MacroInfo.h"
+#include <iterator>
+#include <utility>
#include <vector>
namespace clang {
@@ -113,28 +115,26 @@ class DeclarationFragments {
ConstFragmentIterator cend() const { return Fragments.cend(); }
- // Add a new Fragment at an arbitrary offset.
- DeclarationFragments &insert(FragmentIterator It, StringRef Spelling,
- FragmentKind Kind,
- StringRef PreciseIdentifier = "",
- const Decl *Declaration = nullptr) {
- Fragments.insert(It,
- Fragment(Spelling, Kind, PreciseIdentifier, Declaration));
- return *this;
+ /// Prepend another DeclarationFragments to the beginning.
+ ///
+ /// \returns a reference to the DeclarationFragments object itself after
+ /// appending to chain up consecutive operations.
+ DeclarationFragments &prepend(DeclarationFragments Other) {
+ return insert(begin(), std::move(Other));
}
- DeclarationFragments &insert(FragmentIterator It,
- DeclarationFragments &&Other) {
- Fragments.insert(It, std::make_move_iterator(Other.Fragments.begin()),
- std::make_move_iterator(Other.Fragments.end()));
- Other.Fragments.clear();
- return *this;
+ /// Append another DeclarationFragments to the end.
+ ///
+ /// \returns a reference to the DeclarationFragments object itself after
+ /// appending to chain up consecutive operations.
+ DeclarationFragments &append(DeclarationFragments Other) {
+ return insert(end(), std::move(Other));
}
/// Append a new Fragment to the end of the Fragments.
///
/// \returns a reference to the DeclarationFragments object itself after
- /// appending to chain up consecutive appends.
+ /// appending to chain up consecutive operations.
DeclarationFragments &append(StringRef Spelling, FragmentKind Kind,
StringRef PreciseIdentifier = "",
const Decl *Declaration = nullptr) {
@@ -149,18 +149,48 @@ class DeclarationFragments {
return *this;
}
- /// Append another DeclarationFragments to the end.
- ///
- /// Note: \p Other is moved from and cannot be used after a call to this
- /// method.
+ /// Inserts another DeclarationFragments at \p It.
///
/// \returns a reference to the DeclarationFragments object itself after
- /// appending to chain up consecutive appends.
- DeclarationFragments &append(DeclarationFragments &&Other) {
- Fragments.insert(Fragments.end(),
- std::make_move_iterator(Other.Fragments.begin()),
- std::make_move_iterator(Other.Fragments.end()));
- Other.Fragments.clear();
+ /// appending to chain up consecutive operations.
+ DeclarationFragments &insert(FragmentIterator It,
+ DeclarationFragments Other) {
+ if (Other.Fragments.empty())
+ return *this;
+
+ if (Fragments.empty()) {
+ Fragments = std::move(Other.Fragments);
+ return *this;
+ }
+
+ const auto &OtherFrags = Other.Fragments;
+ auto ToInsertBegin = std::make_move_iterator(Other.begin());
+ auto ToInsertEnd = std::make_move_iterator(Other.end());
+
+ // If we aren't inserting at the end let's make sure that we merge their
+ // last fragment with It if both are text fragments.
+ if (It != end() && It->Kind == FragmentKind::Text &&
+ OtherFrags.back().Kind == FragmentKind::Text) {
+ auto &TheirBackSpelling = OtherFrags.back().Spelling;
+ It->Spelling.reserve(It->Spelling.size() + TheirBackSpelling.size());
+ It->Spelling.insert(It->Spelling.begin(), TheirBackSpelling.begin(),
+ TheirBackSpelling.end());
+ --ToInsertEnd;
+ }
+
+ // If we aren't inserting at the beginning we want to merge their first
+ // fragment with the fragment before It if both are text fragments.
+ if (It != begin() && std::prev(It)->Kind == FragmentKind::Text &&
+ OtherFrags.front().Kind == FragmentKind::Text) {
+ auto PrevIt = std::prev(It);
+ auto &TheirFrontSpelling = OtherFrags.front().Spelling;
+ PrevIt->Spelling.reserve(PrevIt->Spelling.size() +
+ TheirFrontSpelling.size());
+ PrevIt->Spelling.append(TheirFrontSpelling);
+ ++ToInsertBegin;
+ }
+
+ Fragments.insert(It, ToInsertBegin, ToInsertEnd);
return *this;
}
@@ -177,13 +207,13 @@ class DeclarationFragments {
/// Append a text Fragment of a space character.
///
/// \returns a reference to the DeclarationFragments object itself after
- /// appending to chain up consecutive appends.
+ /// appending to chain up consecutive operations.
DeclarationFragments &appendSpace();
/// Append a text Fragment of a semicolon character.
///
/// \returns a reference to the DeclarationFragments object itself after
- /// appending to chain up consecutive appends.
+ /// appending to chain up consecutive operations.
DeclarationFragments &appendSemicolon();
/// Removes a trailing semicolon character if present.
diff --git a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
index 4cb866892b5d00..97cc457ea2a926 100644
--- a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
+++ b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
@@ -224,6 +224,29 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
return API.createSymbolReference(Name, USR, getOwningModuleName(D));
}
+
+ bool isEmbeddedInVarDeclarator(const TagDecl &D) {
+ return D.getName().empty() && getTypedefName(&D).empty() &&
+ D.isEmbeddedInDeclarator();
+ }
+
+ void maybeMergeWithAnonymousTag(const DeclaratorDecl &D,
+ RecordContext *NewRecordContext) {
+ if (!NewRecordContext)
+ return;
+ auto *Tag = D.getType()->getAsTagDecl();
+ SmallString<128> TagUSR;
+ clang::index::generateUSRForDecl(Tag, TagUSR);
+ if (auto *Record = llvm::dyn_cast_if_present<TagRecord>(
+ API.findRecordForUSR(TagUSR))) {
+ if (Record->IsEmbeddedInVarDeclarator) {
+ NewRecordContext->stealRecordChain(*Record);
+ auto *NewRecord = cast<APIRecord>(NewRecordContext);
+ if (NewRecord->Comment.empty())
+ NewRecord->Comment = Record->Comment;
+ }
+ }
+ }
};
template <typename Derived>
@@ -273,12 +296,18 @@ bool ExtractAPIVisitorBase<Derived>::VisitVarDecl(const VarDecl *Decl) {
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Linkage, Comment, Declaration,
SubHeading, Access, isInSystemHeader(Decl));
- } else
+ } else {
// Add the global variable record to the API set.
- API.createRecord<GlobalVariableRecord>(
+ auto *NewRecord = API.createRecord<GlobalVariableRecord>(
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Linkage, Comment, Declaration,
SubHeading, isInSystemHeader(Decl));
+
+ // If this global variable has a non typedef'd anonymous tag type let's
+ // pretend the type's child records are under us in the hierarchy.
+ maybeMergeWithAnonymousTag(*Decl, NewRecord);
+ }
+
return true;
}
@@ -364,7 +393,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitEnumDecl(const EnumDecl *Decl) {
if (Name.empty()) {
llvm::raw_svector_ostream OS(QualifiedNameBuffer);
Decl->printQualifiedName(OS);
- Name = QualifiedNameBuffer.str();
+ Name = QualifiedNameBuffer;
}
SmallString<128> USR;
@@ -385,7 +414,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitEnumDecl(const EnumDecl *Decl) {
auto *ER = API.createRecord<EnumRecord>(
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Comment, Declaration, SubHeading,
- isInSystemHeader(Decl));
+ isInSystemHeader(Decl), isEmbeddedInVarDeclarator(*Decl));
// Now collect information about the enumerators in this enum.
getDerivedExtractAPIVisitor().recordEnumConstants(ER, Decl->enumerators());
@@ -510,16 +539,10 @@ bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) {
if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
return true;
- SmallString<128> QualifiedNameBuffer;
// Collect symbol information.
StringRef Name = Decl->getName();
if (Name.empty())
Name = getTypedefName(Decl);
- if (Name.empty()) {
- llvm::raw_svector_ostream OS(QualifiedNameBuffer);
- Decl->printQualifiedName(OS);
- Name = QualifiedNameBuffer.str();
- }
SmallString<128> USR;
index::generateUSRForDecl(Decl, USR);
@@ -541,12 +564,12 @@ bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) {
API.createRecord<UnionRecord>(
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
- SubHeading, isInSystemHeader(Decl));
+ SubHeading, isInSystemHeader(Decl), isEmbeddedInVarDeclarator(*Decl));
else
API.createRecord<StructRecord>(
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
- SubHeading, isInSystemHeader(Decl));
+ SubHeading, isInSystemHeader(Decl), isEmbeddedInVarDeclarator(*Decl));
return true;
}
@@ -559,6 +582,9 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl(
return true;
StringRef Name = Decl->getName();
+ if (Name.empty())
+ Name = getTypedefName(Decl);
+
SmallString<128> USR;
index::generateUSRForDecl(Decl, USR);
PresumedLoc Loc =
@@ -585,8 +611,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl(
CXXClassRecord *Record;
if (Decl->getDescribedClassTemplate()) {
// Inject template fragments before class fragments.
- Declaration.insert(
- Declaration.begin(),
+ Declaration.prepend(
DeclarationFragmentsBuilder::getFragmentsForRedeclarableTemplate(
Decl->getDescribedClassTemplate()));
Record = API.createRecord<ClassTemplateRecord>(
@@ -598,7 +623,8 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl(
Record = API.createRecord<CXXClassRecord>(
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
- SubHeading, Kind, Access, isInSystemHeader(Decl));
+ SubHeading, Kind, Access, isInSystemHeader(Decl),
+ isEmbeddedInVarDeclarator(*Decl));
Record->Bases = getBases(Decl);
@@ -1075,18 +1101,17 @@ bool ExtractAPIVisitorBase<Derived>::VisitTypedefNameDecl(
// If the underlying type was defined as part of the typedef modify it's
// fragments directly and pretend the typedef doesn't exist.
if (auto *TagDecl = Decl->getUnderlyingType()->getAsTagDecl()) {
- if (TagDecl->getName() == Decl->getName() &&
- TagDecl->isEmbeddedInDeclarator() && TagDecl->isCompleteDefinition()) {
+ if (TagDecl->isEmbeddedInDeclarator() && TagDecl->isCompleteDefinition() &&
+ Decl->getName() == TagDecl->getName()) {
SmallString<128> TagUSR;
index::generateUSRForDecl(TagDecl, TagUSR);
if (auto *Record = API.findRecordForUSR(TagUSR)) {
DeclarationFragments LeadingFragments;
LeadingFragments.append("typedef",
- DeclarationFragments::FragmentKind::Keyword, "",
- nullptr);
+ DeclarationFragments::FragmentKind::Keyword);
LeadingFragments.appendSpace();
Record->Declaration.removeTrailingSemicolon()
- .insert(Record->Declaration.begin(), std::move(LeadingFragments))
+ .prepend(std::move(LeadingFragments))
.append(" { ... } ", DeclarationFragments::FragmentKind::Text)
.append(Name, DeclarationFragments::FragmentKind::Identifier)
.appendSemicolon();
@@ -1221,26 +1246,31 @@ bool ExtractAPIVisitorBase<Derived>::VisitFieldDecl(const FieldDecl *Decl) {
DeclarationFragments SubHeading =
DeclarationFragmentsBuilder::getSubHeading(Decl);
+ RecordContext *NewRecord = nullptr;
if (isa<CXXRecordDecl>(Decl->getDeclContext())) {
AccessControl Access = DeclarationFragmentsBuilder::getAccessControl(Decl);
- API.createRecord<CXXFieldRecord>(
+ NewRecord = API.createRecord<CXXFieldRecord>(
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
SubHeading, Access, isInSystemHeader(Decl));
} else if (auto *RD = dyn_cast<RecordDecl>(Decl->getDeclContext())) {
if (RD->isUnion())
- API.createRecord<UnionFieldRecord>(
+ NewRecord = API.createRecord<UnionFieldRecord>(
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
SubHeading, isInSystemHeader(Decl));
else
- API.createRecord<StructFieldRecord>(
+ NewRecord = API.createRecord<StructFieldRecord>(
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
SubHeading, isInSystemHeader(Decl));
}
+ // If this field has a non typedef'd anonymous tag type let's pretend the
+ // type's child records are under us in the hierarchy.
+ maybeMergeWithAnonymousTag(*Decl, NewRecord);
+
return true;
}
diff --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp
index 5a62c5deb24083..58c3c4c98c39ab 100644
--- a/clang/lib/ExtractAPI/API.cpp
+++ b/clang/lib/ExtractAPI/API.cpp
@@ -54,6 +54,13 @@ RecordContext *APIRecord::castToRecordContext(const APIRecord *Record) {
}
}
+void RecordContext::stealRecordChain(RecordContext &Other) {
+ First = Other.First;
+ Last = Other.Last;
+ Other.First = nullptr;
+ Other.Last = nullptr;
+}
+
void RecordContext::addToRecordChain(APIRecord *Record) const {
if (!First) {
First = Record;
@@ -95,6 +102,7 @@ SymbolReference APISet::createSymbolReference(StringRef Name, StringRef USR,
}
APIRecord::~APIRecord() {}
+TagRecord::~TagRecord() {}
RecordRecord::~RecordRecord() {}
RecordFieldRecord::~RecordFieldRecord() {}
ObjCContainerRecord::~ObjCContainerRecord() {}
diff --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp
index 0a243120b7c0e3..9bf7950888dbb2 100644
--- a/clang/lib/ExtractAPI/DeclarationFragments.cpp
+++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp
@@ -396,7 +396,8 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(
const TagDecl *Decl = TagTy->getDecl();
// Anonymous decl, skip this fragment.
if (Decl->getName().empty())
- return Fragments;
+ return Fragments.append("{ ... }",
+ DeclarationFragments::FragmentKind::Text);
SmallString<128> TagUSR;
clang::index::generateUSRForDecl(Decl, TagUSR);
return Fragments.append(Decl->getName(),
@@ -743,11 +744,16 @@ DeclarationFragmentsBuilder::getFragmentsForEnum(const EnumDecl *EnumDecl) {
QualType IntegerType = EnumDecl->getIntegerType();
if (!IntegerType.isNull())
- Fragments.append(": ", DeclarationFragments::FragmentKind::Text)
+ Fragments.appendSpace()
+ .append(": ", DeclarationFragments::FragmentKind::Text)
.append(
getFragmentsForType(IntegerType, EnumDecl->getASTContext(), After))
.append(std::move(After));
+ if (EnumDecl->getName().empty())
+ Fragments.appendSpace().append("{ ... }",
+ DeclarationFragments::FragmentKind::Text);
+
return Fragments.appendSemicolon();
}
@@ -778,9 +784,12 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForRecordDecl(
else
Fragments.append("struct", DeclarationFragments::FragmentKind::Keyword);
+ Fragments.appendSpace();
if (!Record->getName().empty())
- Fragments.appendSpace().append(
- Record->getName(), DeclarationFragments::FragmentKind::Identifier);
+ Fragments.append(Record->getName(),
+ DeclarationFragments::FragmentKind::Identifier);
+ else
+ Fragments.append("{ ... }", DeclarationFragments::FragmentKind::Text);
return Fragments.appendSemicolon();
}
diff --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
index 8b1dcb4a4144f4..34278b5d40c422 100644
--- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -667,6 +667,14 @@ bool SymbolGraphSerializer::shouldSkip(const APIRecord *Record) const {
if (Record->Availability.isUnconditionallyUnavailable())
return true;
+ // Filter out symbols without a name as we can generate correct symbol graphs
+ // for them. In practice these are anonymous record types that aren't attached
+ // to a declaration.
+ if (auto *Tag = dyn_cast<TagRecord>(Record)) {
+ if (Tag->IsEmbeddedInVarDeclarator)
+ return true;
+ }
+
// Filter out symbols prefixed with an underscored as they are understood to
// be symbols clients should not use.
if (Record->Name.starts_with("_"))
diff --git a/clang/test/ExtractAPI/anonymous_record_no_typedef.c b/clang/test/ExtractAPI/anonymous_record_no_typedef.c
index 049e8b1f85bb96..71e460afb12833 100644
--- a/clang/test/ExtractAPI/anonymous_record_no_typedef.c
+++ b/clang/test/ExtractAPI/anonymous_record_no_typedef.c
@@ -1,417 +1,182 @@
-// XFAIL: *
// RUN: rm -rf %t
-// RUN: split-file %s %t
-// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
-// RUN: %t/reference.output.json.in >> %t/reference.output.json
-// RUN: %clang_cc1 -extract-api --pretty-sgf -triple arm64-apple-macosx \
-// RUN: -x c-header %t/input.h -o %t/output.json -verify
+// RUN: %clang_cc1 -extract-api --pretty-sgf --emit-sgf-symbol-labels-for-testing \
+// RUN: -triple arm64-apple-macosx -isystem %S -fretain-comments-from-system-headers \
+// RUN: -x c-header %s -o %t/output.symbols.json -verify
-// Generator version is not consistent across test runs, normalize it.
-// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
-// RUN: %t/output.json >> %t/output-normalized.json
-// RUN: diff %t/reference.output.json %t/output-normalized.json
+// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix GLOBAL
+// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix PREFIX
+// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix CONTENT
+/// A global variable with an anonymous struct type.
+struct { char *prefix; char *content; } global;
+// GLOBAL-LABEL: "!testLabel": "c:@global"
+// GLOBAL: "declarationFragments": [
+// GLOBAL-NEXT: {
+// GLOBAL-NEXT: "kind": "keyword",
+// GLOBAL-NEXT: "spelling": "struct"
+// GLOBAL-NEXT: },
+// GLOBAL-NEXT: {
+// GLOBAL-NEXT: "kind": "text",
+// GLOBAL-NEXT: "spelling": " { ... } "
+// GLOBAL-NEXT: },
+// GLOBAL-NEXT: {
+// GLOBAL-NEXT: "kind": "identifier",
+// GLOBAL-NEXT: "spelling": "global"
+// GLOBAL-NEXT: },
+// GLOBAL-NEXT: {
+// GLOBAL-NEXT: "kind": "text",
+// GLOBAL-NEXT: "spelling": ";"
+// GLOBAL-NEXT: }
+// GLOBAL-NEXT: ],
+// GLOBAL: "text": "A global variable with an anonymous struct type."
+// GLOBAL: "kind": {
+// GLOBAL-NEXT: "displayName": "Global Variable",
+// GLOBAL-NEXT: "identifier": "c.var"
+// GLOBAL: "title": "global"
+// GLOBAL: "pathComponents": [
+// GLOBAL-NEXT: "global"
+// GLOBAL-NEXT:]
+
+// PREFIX: "!testRelLabel": "memberOf $ c:@S at anonymous_record_no_typedef.c@{{[0-9]+}}@FI at prefix $ c:@global"
+// PREFIX-LABEL: "!testLabel": "c:@S at anonymous_record_no_typedef.c@{{[0-9]+}}@FI at prefix"
+// PREFIX: "title": "prefix"
+// PREFIX: "pathComponents": [
+// PREFIX-NEXT: "global",
+// PREFIX-NEXT: "prefix"
+// PREFIX-NEXT: ]
+
+// CONTENT: "!testRelLabel": "memberOf $ c:@S at anonymous_record_no_typedef.c@{{[0-9]+}}@FI at content $ c:@global"
+// CONTENT-LABEL: "!testLabel": "c:@S at anonymous_record_no_typedef.c@{{[0-9]+}}@FI at content"
+// CONTENT: "title": "content"
+// CONTENT: "pathComponents": [
+// CONTENT-NEXT: "global",
+// CONTENT-NEXT: "content"
+// CONTENT-NEXT: ]
-//--- input.h
/// A Vehicle
struct Vehicle {
+ // RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix TYPE
+ // RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix BICYCLE
+ // RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix CAR
/// The type of vehicle.
enum {
Bicycle,
Car
} type;
+ // TYPE-LABEL: "!testLabel": "c:@S at Vehicle@FI at type"
+ // TYPE: "declarationFragments": [
+ // TYPE-NEXT: {
+ // TYPE-NEXT: "kind": "keyword",
+ // TYPE-NEXT: "spelling": "enum"
+ // TYPE-NEXT: },
+ // TYPE-NEXT: {
+ // TYPE-NEXT: "kind": "text",
+ // TYPE-NEXT: "spelling": " { ... } "
+ // TYPE-NEXT: },
+ // TYPE-NEXT: {
+ // TYPE-NEXT: "kind": "identifier",
+ // TYPE-NEXT: "spelling": "type"
+ // TYPE-NEXT: },
+ // TYPE-NEXT: {
+ // TYPE-NEXT: "kind": "text",
+ // TYPE-NEXT: "spelling": ";"
+ // TYPE-NEXT: }
+ // TYPE-NEXT: ],
+ // TYPE: "text": "The type of vehicle."
+ // TYPE: "title": "type"
+
+ // BICYCLE: "!testRelLabel": "memberOf $ c:@S at Vehicle@E at anonymous_record_no_typedef.c@{{[0-9]+}}@Bicycle $ c:@S at Vehicle@FI at type"
+ // BICYCLE-LABEL: "!testLabel": "c:@S at Vehicle@E at anonymous_record_no_typedef.c@{{[0-9]+}}@Bicycle"
+ // BICYCLE: "title": "Bicycle"
+ // BICYCLE: "pathComponents": [
+ // BICYCLE-NEXT: "Vehicle",
+ // BICYCLE-NEXT: "type",
+ // BICYCLE-NEXT: "Bicycle"
+ // BICYCLE-NEXT: ]
+ // CAR: "!testRelLabel": "memberOf $ c:@S at Vehicle@E at anonymous_record_no_typedef.c@{{[0-9]+}}@Car $ c:@S at Vehicle@FI at type"
+ // CAR-LABEL: "!testLabel": "c:@S at Vehicle@E at anonymous_record_no_typedef.c@{{[0-9]+}}@Car"
+ // CAR: "title": "Car"
+ // CAR: "pathComponents": [
+ // CAR-NEXT: "Vehicle",
+ // CAR-NEXT: "type",
+ // CAR-NEXT: "Car"
+ // CAR-NEXT: ]
+
+ // RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix INFORMATION
+ // RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix WHEELS
+ // RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix NAME
/// The information about the vehicle.
- struct {
+ union {
int wheels;
char *name;
} information;
+ // INFORMATION-LABEL: "!testLabel": "c:@S at Vehicle@FI at information"
+ // INFORMATION: "declarationFragments": [
+ // INFORMATION-NEXT: {
+ // INFORMATION-NEXT: "kind": "keyword",
+ // INFORMATION-NEXT: "spelling": "union"
+ // INFORMATION-NEXT: },
+ // INFORMATION-NEXT: {
+ // INFORMATION-NEXT: "kind": "text",
+ // INFORMATION-NEXT: "spelling": " { ... } "
+ // INFORMATION-NEXT: },
+ // INFORMATION-NEXT: {
+ // INFORMATION-NEXT: "kind": "identifier",
+ // INFORMATION-NEXT: "spelling": "information"
+ // INFORMATION-NEXT: },
+ // INFORMATION-NEXT: {
+ // INFORMATION-NEXT: "kind": "text",
+ // INFORMATION-NEXT: "spelling": ";"
+ // INFORMATION-NEXT: }
+ // INFORMATION-NEXT: ],
+ // INFORMATION: "text": "The information about the vehicle."
+ // INFORMATION: "title": "information"
+
+ // WHEELS: "!testRelLabel": "memberOf $ c:@S at Vehicle@U at anonymous_record_no_typedef.c@{{[0-9]+}}@FI at wheels $ c:@S at Vehicle@FI at information"
+ // WHEELS-LABEL: "!testLabel": "c:@S at Vehicle@U at anonymous_record_no_typedef.c@{{[0-9]+}}@FI at wheels"
+ // WHEELS: "title": "wheels"
+ // WHEELS: "pathComponents": [
+ // WHEELS-NEXT: "Vehicle",
+ // WHEELS-NEXT: "information",
+ // WHEELS-NEXT: "wheels"
+ // WHEELS-NEXT: ]
+
+ // NAME: "!testRelLabel": "memberOf $ c:@S at Vehicle@U at anonymous_record_no_typedef.c@{{[0-9]+}}@FI at name $ c:@S at Vehicle@FI at information"
+ // NAME-LABEL: "!testLabel": "c:@S at Vehicle@U at anonymous_record_no_typedef.c@{{[0-9]+}}@FI at name"
+ // NAME: "title": "name"
+ // NAME: "pathComponents": [
+ // NAME-NEXT: "Vehicle",
+ // NAME-NEXT: "information",
+ // NAME-NEXT: "name"
+ // NAME-NEXT: ]
};
-// expected-no-diagnostics
-//--- reference.output.json.in
-{
- "metadata": {
- "formatVersion": {
- "major": 0,
- "minor": 5,
- "patch": 3
- },
- "generator": "?"
- },
- "module": {
- "name": "",
- "platform": {
- "architecture": "arm64",
- "operatingSystem": {
- "minimumVersion": {
- "major": 11,
- "minor": 0,
- "patch": 0
- },
- "name": "macosx"
- },
- "vendor": "apple"
- }
- },
- "relationships": [
- {
- "kind": "memberOf",
- "source": "c:@S at Vehicle@E at input.h@64 at Bicycle",
- "target": "c:@S at Vehicle@E at input.h@64",
- "targetFallback": "Vehicle::enum (unnamed)"
- },
- {
- "kind": "memberOf",
- "source": "c:@S at Vehicle@E at input.h@64 at Car",
- "target": "c:@S at Vehicle@E at input.h@64",
- "targetFallback": "Vehicle::enum (unnamed)"
- },
- {
- "kind": "memberOf",
- "source": "c:@S at Vehicle@FI at type",
- "target": "c:@S at Vehicle",
- "targetFallback": "Vehicle"
- },
- {
- "kind": "memberOf",
- "source": "c:@S at Vehicle@FI at information",
- "target": "c:@S at Vehicle",
- "targetFallback": "Vehicle"
- }
- ],
- "symbols": [
- {
- "accessLevel": "public",
- "declarationFragments": [
- {
- "kind": "keyword",
- "spelling": "enum"
- },
- {
- "kind": "text",
- "spelling": ": "
- },
- {
- "kind": "typeIdentifier",
- "preciseIdentifier": "c:i",
- "spelling": "unsigned int"
- },
- {
- "kind": "text",
- "spelling": ";"
- }
- ],
- "docComment": {
- "lines": [
- {
- "range": {
- "end": {
- "character": 28,
- "line": 2
- },
- "start": {
- "character": 8,
- "line": 2
- }
- },
- "text": "The type of vehicle."
- }
- ]
- },
- "identifier": {
- "interfaceLanguage": "c",
- "precise": "c:@S at Vehicle@E at input.h@64"
- },
- "kind": {
- "displayName": "Enumeration",
- "identifier": "c.enum"
- },
- "location": {
- "position": {
- "character": 4,
- "line": 3
- },
- "uri": "file://INPUT_DIR/input.h"
- },
- "names": {
- "navigator": [
- {
- "kind": "identifier",
- "spelling": "Vehicle::enum (unnamed)"
- }
- ],
- "title": "Vehicle::enum (unnamed)"
- },
- "pathComponents": [
- "Vehicle::enum (unnamed)"
- ]
- },
- {
- "accessLevel": "public",
- "declarationFragments": [
- {
- "kind": "identifier",
- "spelling": "Bicycle"
- }
- ],
- "identifier": {
- "interfaceLanguage": "c",
- "precise": "c:@S at Vehicle@E at input.h@64 at Bicycle"
- },
- "kind": {
- "displayName": "Enumeration Case",
- "identifier": "c.enum.case"
- },
- "location": {
- "position": {
- "character": 8,
- "line": 4
- },
- "uri": "file://INPUT_DIR/input.h"
- },
- "names": {
- "navigator": [
- {
- "kind": "identifier",
- "spelling": "Bicycle"
- }
- ],
- "subHeading": [
- {
- "kind": "identifier",
- "spelling": "Bicycle"
- }
- ],
- "title": "Bicycle"
- },
- "pathComponents": [
- "Vehicle::enum (unnamed)",
- "Bicycle"
- ]
- },
- {
- "accessLevel": "public",
- "declarationFragments": [
- {
- "kind": "identifier",
- "spelling": "Car"
- }
- ],
- "identifier": {
- "interfaceLanguage": "c",
- "precise": "c:@S at Vehicle@E at input.h@64 at Car"
- },
- "kind": {
- "displayName": "Enumeration Case",
- "identifier": "c.enum.case"
- },
- "location": {
- "position": {
- "character": 8,
- "line": 5
- },
- "uri": "file://INPUT_DIR/input.h"
- },
- "names": {
- "navigator": [
- {
- "kind": "identifier",
- "spelling": "Car"
- }
- ],
- "subHeading": [
- {
- "kind": "identifier",
- "spelling": "Car"
- }
- ],
- "title": "Car"
- },
- "pathComponents": [
- "Vehicle::enum (unnamed)",
- "Car"
- ]
- },
- {
- "accessLevel": "public",
- "declarationFragments": [
- {
- "kind": "keyword",
- "spelling": "struct"
- },
- {
- "kind": "text",
- "spelling": " "
- },
- {
- "kind": "identifier",
- "spelling": "Vehicle"
- },
- {
- "kind": "text",
- "spelling": ";"
- }
- ],
- "docComment": {
- "lines": [
- {
- "range": {
- "end": {
- "character": 13,
- "line": 0
- },
- "start": {
- "character": 4,
- "line": 0
- }
- },
- "text": "A Vehicle"
- }
- ]
- },
- "identifier": {
- "interfaceLanguage": "c",
- "precise": "c:@S at Vehicle"
- },
- "kind": {
- "displayName": "Structure",
- "identifier": "c.struct"
- },
- "location": {
- "position": {
- "character": 7,
- "line": 1
- },
- "uri": "file://INPUT_DIR/input.h"
- },
- "names": {
- "navigator": [
- {
- "kind": "identifier",
- "spelling": "Vehicle"
- }
- ],
- "subHeading": [
- {
- "kind": "identifier",
- "spelling": "Vehicle"
- }
- ],
- "title": "Vehicle"
- },
- "pathComponents": [
- "Vehicle"
- ]
- },
- {
- "accessLevel": "public",
- "declarationFragments": [
- {
- "kind": "keyword",
- "spelling": "enum"
- },
- {
- "kind": "text",
- "spelling": " "
- },
- {
- "kind": "identifier",
- "spelling": "type"
- },
- {
- "kind": "text",
- "spelling": ";"
- }
- ],
- "identifier": {
- "interfaceLanguage": "c",
- "precise": "c:@S at Vehicle@FI at type"
- },
- "kind": {
- "displayName": "Instance Property",
- "identifier": "c.property"
- },
- "location": {
- "position": {
- "character": 6,
- "line": 6
- },
- "uri": "file://INPUT_DIR/input.h"
- },
- "names": {
- "navigator": [
- {
- "kind": "identifier",
- "spelling": "type"
- }
- ],
- "subHeading": [
- {
- "kind": "identifier",
- "spelling": "type"
- }
- ],
- "title": "type"
- },
- "pathComponents": [
- "Vehicle",
- "type"
- ]
- },
- {
- "accessLevel": "public",
- "declarationFragments": [
- {
- "kind": "keyword",
- "spelling": "struct"
- },
- {
- "kind": "text",
- "spelling": " "
- },
- {
- "kind": "identifier",
- "spelling": "information"
- },
- {
- "kind": "text",
- "spelling": ";"
- }
- ],
- "identifier": {
- "interfaceLanguage": "c",
- "precise": "c:@S at Vehicle@FI at information"
- },
- "kind": {
- "displayName": "Instance Property",
- "identifier": "c.property"
- },
- "location": {
- "position": {
- "character": 6,
- "line": 12
- },
- "uri": "file://INPUT_DIR/input.h"
- },
- "names": {
- "navigator": [
- {
- "kind": "identifier",
- "spelling": "information"
- }
- ],
- "subHeading": [
- {
- "kind": "identifier",
- "spelling": "information"
- }
- ],
- "title": "information"
- },
- "pathComponents": [
- "Vehicle",
- "information"
- ]
- }
- ]
-}
+// RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix GLOBALENUM
+enum {
+ GlobalCase,
+ GlobalOtherCase
+};
+// GLOBALENUM-DAG: "!testRelLabel": "memberOf $ c:@Ea at GlobalCase@GlobalCase $ c:@Ea at GlobalCase"
+// GLOBALENUM-DAG: "!testRelLabel": "memberOf $ c:@Ea at GlobalCase@GlobalOtherCase $ c:@Ea at GlobalCase"
+// GLOBALENUM-LABEL: "!testLabel": "c:@Ea at GlobalCase"
+// GLOBALENUM: "declarationFragments": [
+// GLOBALENUM-NEXT: {
+// GLOBALENUM-NEXT: "kind": "keyword",
+// GLOBALENUM-NEXT: "spelling": "enum"
+// GLOBALENUM-NEXT: },
+// GLOBALENUM-NEXT: {
+// GLOBALENUM-NEXT: "kind": "text",
+// GLOBALENUM-NEXT: "spelling": " : "
+// GLOBALENUM-NEXT: },
+// GLOBALENUM-NEXT: {
+// GLOBALENUM-NEXT: "kind": "typeIdentifier",
+// GLOBALENUM-NEXT: "preciseIdentifier": "c:i",
+// GLOBALENUM-NEXT: "spelling": "unsigned int"
+// GLOBALENUM-NEXT: },
+// GLOBALENUM-NEXT: {
+// GLOBALENUM-NEXT: "kind": "text",
+// GLOBALENUM-NEXT: "spelling": " { ... };"
+// GLOBALENUM-NEXT: }
+// GLOBALENUM-NEXT: ]
+
+// expected-no-diagnostics
diff --git a/clang/test/ExtractAPI/enum.c b/clang/test/ExtractAPI/enum.c
index 1cdf45ca3cdf4b..67e003834a7d58 100644
--- a/clang/test/ExtractAPI/enum.c
+++ b/clang/test/ExtractAPI/enum.c
@@ -147,7 +147,7 @@ enum {
},
{
"kind": "text",
- "spelling": ": "
+ "spelling": " : "
},
{
"kind": "typeIdentifier",
@@ -459,7 +459,7 @@ enum {
},
{
"kind": "text",
- "spelling": ": "
+ "spelling": " : "
},
{
"kind": "typeIdentifier",
@@ -686,7 +686,7 @@ enum {
},
{
"kind": "text",
- "spelling": ": "
+ "spelling": " : "
},
{
"kind": "typeIdentifier",
@@ -695,7 +695,7 @@ enum {
},
{
"kind": "text",
- "spelling": ";"
+ "spelling": " { ... };"
}
],
"identifier": {
@@ -778,7 +778,7 @@ enum {
},
{
"kind": "text",
- "spelling": ": "
+ "spelling": " : "
},
{
"kind": "typeIdentifier",
@@ -787,7 +787,7 @@ enum {
},
{
"kind": "text",
- "spelling": ";"
+ "spelling": " { ... };"
}
],
"identifier": {
diff --git a/clang/test/ExtractAPI/function_noexcepts.cpp b/clang/test/ExtractAPI/function_noexcepts.cpp
index d95eaaa7e769a5..fc18ecb04fefd0 100644
--- a/clang/test/ExtractAPI/function_noexcepts.cpp
+++ b/clang/test/ExtractAPI/function_noexcepts.cpp
@@ -63,11 +63,7 @@ void getFooBar() noexcept(false);
},
{
"kind": "text",
- "spelling": "()"
- },
- {
- "kind": "text",
- "spelling": " "
+ "spelling": "() "
},
{
"kind": "keyword",
@@ -139,11 +135,7 @@ void getFooBar() noexcept(false);
},
{
"kind": "text",
- "spelling": "()"
- },
- {
- "kind": "text",
- "spelling": " "
+ "spelling": "() "
},
{
"kind": "keyword",
@@ -223,11 +215,7 @@ void getFooBar() noexcept(false);
},
{
"kind": "text",
- "spelling": "()"
- },
- {
- "kind": "text",
- "spelling": " "
+ "spelling": "() "
},
{
"kind": "keyword",
diff --git a/clang/test/ExtractAPI/methods.cpp b/clang/test/ExtractAPI/methods.cpp
index 412c0bb3f903c3..67f04b4d33db83 100644
--- a/clang/test/ExtractAPI/methods.cpp
+++ b/clang/test/ExtractAPI/methods.cpp
@@ -81,11 +81,7 @@ class Foo {
// SETL-NEXT: },
// SETL-NEXT: {
// SETL-NEXT: "kind": "text",
- // SETL-NEXT: "spelling": ")"
- // SETL-NEXT: },
- // SETL-NEXT: {
- // SETL-NEXT: "kind": "text",
- // SETL-NEXT: "spelling": " "
+ // SETL-NEXT: "spelling": ") "
// SETL-NEXT: },
// SETL-NEXT: {
// SETL-NEXT: "kind": "keyword",
diff --git a/clang/test/ExtractAPI/objc_block.m b/clang/test/ExtractAPI/objc_block.m
index 4a4335ec09832d..4761a864f5349f 100644
--- a/clang/test/ExtractAPI/objc_block.m
+++ b/clang/test/ExtractAPI/objc_block.m
@@ -35,11 +35,7 @@ -(void)methodBlockNoParam:(void (^)())block;
// NOPARAM-NEXT: },
// NOPARAM-NEXT: {
// NOPARAM-NEXT: "kind": "text",
-// NOPARAM-NEXT: "spelling": " (^"
-// NOPARAM-NEXT: },
-// NOPARAM-NEXT: {
-// NOPARAM-NEXT: "kind": "text",
-// NOPARAM-NEXT: "spelling": ")()) "
+// NOPARAM-NEXT: "spelling": " (^)()) "
// NOPARAM-NEXT: },
// NOPARAM-NEXT: {
// NOPARAM-NEXT: "kind": "internalParam",
@@ -65,11 +61,7 @@ -(void)methodBlockNoParam:(void (^)())block;
// NOPARAM-NEXT: },
// NOPARAM-NEXT: {
// NOPARAM-NEXT: "kind": "text",
-// NOPARAM-NEXT: "spelling": " (^"
-// NOPARAM-NEXT: },
-// NOPARAM-NEXT: {
-// NOPARAM-NEXT: "kind": "text",
-// NOPARAM-NEXT: "spelling": ")()) "
+// NOPARAM-NEXT: "spelling": " (^)()) "
// NOPARAM-NEXT: },
// NOPARAM-NEXT: {
// NOPARAM-NEXT: "kind": "internalParam",
@@ -120,11 +112,7 @@ -(void)methodBlockWithParam:(int (^)(int foo))block;
// PARAM-NEXT: },
// PARAM-NEXT: {
// PARAM-NEXT: "kind": "text",
-// PARAM-NEXT: "spelling": " (^"
-// PARAM-NEXT: },
-// PARAM-NEXT: {
-// PARAM-NEXT: "kind": "text",
-// PARAM-NEXT: "spelling": ")("
+// PARAM-NEXT: "spelling": " (^)("
// PARAM-NEXT: },
// PARAM-NEXT: {
// PARAM-NEXT: "kind": "typeIdentifier",
@@ -167,11 +155,7 @@ -(void)methodBlockWithParam:(int (^)(int foo))block;
// PARAM-NEXT: },
// PARAM-NEXT: {
// PARAM-NEXT: "kind": "text",
-// PARAM-NEXT: "spelling": " (^"
-// PARAM-NEXT: },
-// PARAM-NEXT: {
-// PARAM-NEXT: "kind": "text",
-// PARAM-NEXT: "spelling": ")("
+// PARAM-NEXT: "spelling": " (^)("
// PARAM-NEXT: },
// PARAM-NEXT: {
// PARAM-NEXT: "kind": "typeIdentifier",
@@ -239,11 +223,7 @@ -(void)methodBlockWithMultipleParam:(int (^)(int foo, unsigned baz))block;
// MULTIPARAM-NEXT: },
// MULTIPARAM-NEXT: {
// MULTIPARAM-NEXT: "kind": "text",
-// MULTIPARAM-NEXT: "spelling": " (^"
-// MULTIPARAM-NEXT: },
-// MULTIPARAM-NEXT: {
-// MULTIPARAM-NEXT: "kind": "text",
-// MULTIPARAM-NEXT: "spelling": ")("
+// MULTIPARAM-NEXT: "spelling": " (^)("
// MULTIPARAM-NEXT: },
// MULTIPARAM-NEXT: {
// MULTIPARAM-NEXT: "kind": "typeIdentifier",
@@ -303,11 +283,7 @@ -(void)methodBlockWithMultipleParam:(int (^)(int foo, unsigned baz))block;
// MULTIPARAM-NEXT: },
// MULTIPARAM-NEXT: {
// MULTIPARAM-NEXT: "kind": "text",
-// MULTIPARAM-NEXT: "spelling": " (^"
-// MULTIPARAM-NEXT: },
-// MULTIPARAM-NEXT: {
-// MULTIPARAM-NEXT: "kind": "text",
-// MULTIPARAM-NEXT: "spelling": ")("
+// MULTIPARAM-NEXT: "spelling": " (^)("
// MULTIPARAM-NEXT: },
// MULTIPARAM-NEXT: {
// MULTIPARAM-NEXT: "kind": "typeIdentifier",
@@ -392,11 +368,7 @@ -(void)methodBlockVariadic:(int (^)(int foo, ...))block;
// VARIADIC-NEXT: },
// VARIADIC-NEXT: {
// VARIADIC-NEXT: "kind": "text",
-// VARIADIC-NEXT: "spelling": " (^"
-// VARIADIC-NEXT: },
-// VARIADIC-NEXT: {
-// VARIADIC-NEXT: "kind": "text",
-// VARIADIC-NEXT: "spelling": ")("
+// VARIADIC-NEXT: "spelling": " (^)("
// VARIADIC-NEXT: },
// VARIADIC-NEXT: {
// VARIADIC-NEXT: "kind": "typeIdentifier",
@@ -439,11 +411,7 @@ -(void)methodBlockVariadic:(int (^)(int foo, ...))block;
// VARIADIC-NEXT: },
// VARIADIC-NEXT: {
// VARIADIC-NEXT: "kind": "text",
-// VARIADIC-NEXT: "spelling": " (^"
-// VARIADIC-NEXT: },
-// VARIADIC-NEXT: {
-// VARIADIC-NEXT: "kind": "text",
-// VARIADIC-NEXT: "spelling": ")("
+// VARIADIC-NEXT: "spelling": " (^)("
// VARIADIC-NEXT: },
// VARIADIC-NEXT: {
// VARIADIC-NEXT: "kind": "typeIdentifier",
diff --git a/clang/test/ExtractAPI/typedef_anonymous_record.c b/clang/test/ExtractAPI/typedef_anonymous_record.c
index 9e00ff75254654..9c03e9e190ed6b 100644
--- a/clang/test/ExtractAPI/typedef_anonymous_record.c
+++ b/clang/test/ExtractAPI/typedef_anonymous_record.c
@@ -21,7 +21,7 @@ typedef struct { } MyStruct;
// MYSTRUCT-NEXT: },
// MYSTRUCT-NEXT: {
// MYSTRUCT-NEXT: "kind": "text",
-// MYSTRUCT-NEXT: "spelling": " "
+// MYSTRUCT-NEXT: "spelling": " { ... } "
// MYSTRUCT-NEXT: },
// MYSTRUCT-NEXT: {
// MYSTRUCT-NEXT: "kind": "identifier",
@@ -97,7 +97,7 @@ typedef enum { Case } MyEnum;
// MYENUM-NEXT: },
// MYENUM-NEXT: {
// MYENUM-NEXT: "kind": "text",
-// MYENUM-NEXT: "spelling": " "
+// MYENUM-NEXT: "spelling": " { ... } "
// MYENUM-NEXT: },
// MYENUM-NEXT: {
// MYENUM-NEXT: "kind": "identifier",
diff --git a/clang/test/ExtractAPI/typedef_struct_enum.c b/clang/test/ExtractAPI/typedef_struct_enum.c
index fb6fbe987624f8..64b7186756660f 100644
--- a/clang/test/ExtractAPI/typedef_struct_enum.c
+++ b/clang/test/ExtractAPI/typedef_struct_enum.c
@@ -72,7 +72,7 @@ typedef enum Test2 {
// TEST2-NEXT: },
// TEST2-NEXT: {
// TEST2-NEXT: "kind": "text",
-// TEST2-NEXT: "spelling": ": "
+// TEST2-NEXT: "spelling": " : "
// TEST2-NEXT: },
// TEST2-NEXT: {
// TEST2-NEXT: "kind": "typeIdentifier",
>From 0d76b370efa863be21751e45ed37bebe8b5acf66 Mon Sep 17 00:00:00 2001
From: Daniel Grumberg <dgrumberg at apple.com>
Date: Tue, 23 Apr 2024 14:52:35 +0100
Subject: [PATCH 2/2] Change semantic of `RecordContext::stealRecordChain` to
concatenate the chains
---
clang/include/clang/ExtractAPI/API.h | 2 ++
clang/lib/ExtractAPI/API.cpp | 9 ++++++++-
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h
index 05cfabd072a560..ce43a151524da4 100644
--- a/clang/include/clang/ExtractAPI/API.h
+++ b/clang/include/clang/ExtractAPI/API.h
@@ -321,6 +321,8 @@ class RecordContext {
RecordContext(APIRecord::RecordKind Kind) : Kind(Kind) {}
+ /// Append \p Other children chain into ours and empty out Other's record
+ /// chain.
void stealRecordChain(RecordContext &Other);
APIRecord::RecordKind getKind() const { return Kind; }
diff --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp
index 58c3c4c98c39ab..77c783312f6e70 100644
--- a/clang/lib/ExtractAPI/API.cpp
+++ b/clang/lib/ExtractAPI/API.cpp
@@ -55,8 +55,15 @@ RecordContext *APIRecord::castToRecordContext(const APIRecord *Record) {
}
void RecordContext::stealRecordChain(RecordContext &Other) {
- First = Other.First;
+ // If we don't have an empty chain append Other's chain into ours.
+ if (First)
+ Last->NextInContext = Other.First;
+ else
+ First = Other.First;
+
Last = Other.Last;
+
+ // Delete Other's chain to ensure we don't accidentally traverse it.
Other.First = nullptr;
Other.Last = nullptr;
}
More information about the cfe-commits
mailing list