-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[clang][ExtractAPI] Fix handling of anonymous TagDecls #87772
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[clang][ExtractAPI] Fix handling of anonymous TagDecls #87772
Conversation
@llvm/pr-subscribers-clang Author: Daniel Grumberg (daniel-grumberg) ChangesThis changes the handling of anonymous TagDecls to the following rules:
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. Patch is 56.69 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/87772.diff 14 Files Affected:
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 : publ...
[truncated]
|
clang/lib/ExtractAPI/API.cpp
Outdated
void RecordContext::stealRecordChain(RecordContext &Other) { | ||
First = Other.First; | ||
Last = Other.Last; | ||
Other.First = nullptr; | ||
Other.Last = nullptr; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on how this is used it seems like it's fine, but we might want to make a note about how this overwrites this context's record chain, or otherwise rewrite this to append the other context's record chain onto this one if it's already been loaded.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a better name for this would be transferRecordChain
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name stealRecordChain
is fine, it makes it plain that Other
's record chain will be set to null and moved to this context. I'm just worried with the implementation; if this stands as it is, with no comment and no guard that the current context has no record chain already, then i'm worried about a future use of this function that leads to broken behavior.
If this were a more verbose codebase i would call it overwriteRecordChainByStealingFrom
but that's a bit too far 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no harm in "stealing" an empty record chain, and it's not necessarily even semantically wrong, all it indicates is that the current RecordContext won't have any children during any traversals. Semantic problems would still be caught by having a test.
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.
f275507
to
0d76b37
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! One last nitpick but you can take it or leave it.
if (First) | ||
Last->NextInContext = Other.First; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the tiniest of nitpicks, but the fact that this only checks First
but then accesses Last
(which semantically makes sense but is syntactically suspect) feels wrong to me. 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documented the invariant that First
being non-null implies that Last
is also non-null in an assert above.
Specifically that the First and Last pointer are always both null or both non-null.
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. rdar://121436298
This changes the handling of anonymous TagDecls to the following rules:
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.