https://github.com/daniel-grumberg created https://github.com/llvm/llvm-project/pull/87772
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. >From f2755070ead054c56f7e9f9a2084ec40c10968bf Mon Sep 17 00:00:00 2001 From: Daniel Grumberg <dgrumb...@apple.com> Date: Thu, 4 Apr 2024 18:33:25 +0100 Subject: [PATCH] [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 57f966c8b2be35..ca47f02218bf8d 100644 --- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp +++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp @@ -665,6 +665,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@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@anonymous_record_no_typedef.c@{{[0-9]+}}@FI@prefix $ c:@global" +// PREFIX-LABEL: "!testLabel": "c:@S@anonymous_record_no_typedef.c@{{[0-9]+}}@FI@prefix" +// PREFIX: "title": "prefix" +// PREFIX: "pathComponents": [ +// PREFIX-NEXT: "global", +// PREFIX-NEXT: "prefix" +// PREFIX-NEXT: ] + +// CONTENT: "!testRelLabel": "memberOf $ c:@S@anonymous_record_no_typedef.c@{{[0-9]+}}@FI@content $ c:@global" +// CONTENT-LABEL: "!testLabel": "c:@S@anonymous_record_no_typedef.c@{{[0-9]+}}@FI@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@Vehicle@FI@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@Vehicle@E@anonymous_record_no_typedef.c@{{[0-9]+}}@Bicycle $ c:@S@Vehicle@FI@type" + // BICYCLE-LABEL: "!testLabel": "c:@S@Vehicle@E@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@Vehicle@E@anonymous_record_no_typedef.c@{{[0-9]+}}@Car $ c:@S@Vehicle@FI@type" + // CAR-LABEL: "!testLabel": "c:@S@Vehicle@E@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@Vehicle@FI@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@Vehicle@U@anonymous_record_no_typedef.c@{{[0-9]+}}@FI@wheels $ c:@S@Vehicle@FI@information" + // WHEELS-LABEL: "!testLabel": "c:@S@Vehicle@U@anonymous_record_no_typedef.c@{{[0-9]+}}@FI@wheels" + // WHEELS: "title": "wheels" + // WHEELS: "pathComponents": [ + // WHEELS-NEXT: "Vehicle", + // WHEELS-NEXT: "information", + // WHEELS-NEXT: "wheels" + // WHEELS-NEXT: ] + + // NAME: "!testRelLabel": "memberOf $ c:@S@Vehicle@U@anonymous_record_no_typedef.c@{{[0-9]+}}@FI@name $ c:@S@Vehicle@FI@information" + // NAME-LABEL: "!testLabel": "c:@S@Vehicle@U@anonymous_record_no_typedef.c@{{[0-9]+}}@FI@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@Vehicle@E@input.h@64@Bicycle", - "target": "c:@S@Vehicle@E@input.h@64", - "targetFallback": "Vehicle::enum (unnamed)" - }, - { - "kind": "memberOf", - "source": "c:@S@Vehicle@E@input.h@64@Car", - "target": "c:@S@Vehicle@E@input.h@64", - "targetFallback": "Vehicle::enum (unnamed)" - }, - { - "kind": "memberOf", - "source": "c:@S@Vehicle@FI@type", - "target": "c:@S@Vehicle", - "targetFallback": "Vehicle" - }, - { - "kind": "memberOf", - "source": "c:@S@Vehicle@FI@information", - "target": "c:@S@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@Vehicle@E@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@Vehicle@E@input.h@64@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@Vehicle@E@input.h@64@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@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@Vehicle@FI@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@Vehicle@FI@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@GlobalCase@GlobalCase $ c:@Ea@GlobalCase" +// GLOBALENUM-DAG: "!testRelLabel": "memberOf $ c:@Ea@GlobalCase@GlobalOtherCase $ c:@Ea@GlobalCase" +// GLOBALENUM-LABEL: "!testLabel": "c:@Ea@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", _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits