From 8cf917cee9be14451db612919d64a356b8396bd7 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Tue, 21 Mar 2017 16:56:02 +0000 Subject: [PATCH] [index/AST] Determine if a typedef shares a name and spelling location with its underlying tag type In such a case, as when using the NS_ENUM macro, for indexing purposes treat the typedef as 'transparent', meaning we treat its references as symbols of the underlying tag symbol. Also provide a libclang API to check for such typedefs. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@298392 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang-c/Index.h | 10 +++++++++ include/clang/AST/Decl.h | 18 +++++++++++++++- lib/AST/Decl.cpp | 24 +++++++++++++++++++++ lib/Index/IndexDecl.cpp | 5 +++-- lib/Index/IndexTypeSourceInfo.cpp | 12 ++++++++--- test/Index/Core/index-source.m | 36 +++++++++++++++++++++++++++++++ test/Index/get-cursor.m | 33 ++++++++++++++++++++++++++++ tools/c-index-test/c-index-test.c | 9 ++++++++ tools/libclang/CXType.cpp | 9 ++++++++ tools/libclang/libclang.exports | 1 + 10 files changed, 151 insertions(+), 6 deletions(-) diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index d466fce851..208da7d0f4 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -3436,6 +3436,16 @@ CINDEX_LINKAGE long long clang_getArraySize(CXType T); */ CINDEX_LINKAGE CXType clang_Type_getNamedType(CXType T); +/** + * \brief Determine if a typedef is 'transparent' tag. + * + * A typedef is considered 'transparent' if it shares a name and spelling + * location with its underlying tag type, as is the case with the NS_ENUM macro. + * + * \returns non-zero if transparent and zero otherwise. + */ +CINDEX_LINKAGE unsigned clang_Type_isTransparentTagTypedef(CXType T); + /** * \brief List the possible error codes for \c clang_Type_getSizeOf, * \c clang_Type_getAlignOf, \c clang_Type_getOffsetOf and diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 623d9c4792..f31d750160 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -2641,12 +2641,17 @@ class TypedefNameDecl : public TypeDecl, public Redeclarable { typedef std::pair ModedTInfo; llvm::PointerUnion MaybeModedTInfo; + // FIXME: This can be packed into the bitfields in Decl. + /// If 0, we have not computed IsTransparentTag. + /// Otherwise, IsTransparentTag is (CacheIsTransparentTag >> 1). + mutable unsigned CacheIsTransparentTag : 2; + protected: TypedefNameDecl(Kind DK, ASTContext &C, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, IdentifierInfo *Id, TypeSourceInfo *TInfo) : TypeDecl(DK, DC, IdLoc, Id, StartLoc), redeclarable_base(C), - MaybeModedTInfo(TInfo) {} + MaybeModedTInfo(TInfo), CacheIsTransparentTag(0) {} typedef Redeclarable redeclarable_base; TypedefNameDecl *getNextRedeclarationImpl() override { @@ -2699,11 +2704,22 @@ public: /// this typedef declaration. TagDecl *getAnonDeclWithTypedefName(bool AnyRedecl = false) const; + /// Determines if this typedef shares a name and spelling location with its + /// underlying tag type, as is the case with the NS_ENUM macro. + bool isTransparentTag() const { + if (CacheIsTransparentTag) + return CacheIsTransparentTag & 0x2; + return isTransparentTagSlow(); + } + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K >= firstTypedefName && K <= lastTypedefName; } + +private: + bool isTransparentTagSlow() const; }; /// TypedefDecl - Represents the declaration of a typedef-name via the 'typedef' diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 5b0404e38d..2b22e5bb50 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4242,6 +4242,30 @@ TagDecl *TypedefNameDecl::getAnonDeclWithTypedefName(bool AnyRedecl) const { return nullptr; } +bool TypedefNameDecl::isTransparentTagSlow() const { + auto determineIsTransparent = [&]() { + if (auto *TT = getUnderlyingType()->getAs()) { + if (auto *TD = TT->getDecl()) { + if (TD->getName() != getName()) + return false; + SourceLocation TTLoc = getLocation(); + SourceLocation TDLoc = TD->getLocation(); + if (!TTLoc.isMacroID() || !TDLoc.isMacroID()) + return false; + SourceManager &SM = getASTContext().getSourceManager(); + return SM.getSpellingLoc(TTLoc) == SM.getSpellingLoc(TDLoc); + } + } + return false; + }; + + bool isTransparent = determineIsTransparent(); + CacheIsTransparentTag = 1; + if (isTransparent) + CacheIsTransparentTag |= 0x2; + return isTransparent; +} + TypedefDecl *TypedefDecl::CreateDeserialized(ASTContext &C, unsigned ID) { return new (C, ID) TypedefDecl(C, nullptr, SourceLocation(), SourceLocation(), nullptr, nullptr); diff --git a/lib/Index/IndexDecl.cpp b/lib/Index/IndexDecl.cpp index f9710236c9..dae0cdc0d9 100644 --- a/lib/Index/IndexDecl.cpp +++ b/lib/Index/IndexDecl.cpp @@ -231,8 +231,9 @@ public: } bool VisitTypedefNameDecl(const TypedefNameDecl *D) { - if (!IndexCtx.handleDecl(D)) - return false; + if (!D->isTransparentTag()) + if (!IndexCtx.handleDecl(D)) + return false; IndexCtx.indexTypeSourceInfo(D->getTypeSourceInfo(), D); return true; } diff --git a/lib/Index/IndexTypeSourceInfo.cpp b/lib/Index/IndexTypeSourceInfo.cpp index 33848da0cb..0645d5be52 100644 --- a/lib/Index/IndexTypeSourceInfo.cpp +++ b/lib/Index/IndexTypeSourceInfo.cpp @@ -47,9 +47,15 @@ public: } while (0) bool VisitTypedefTypeLoc(TypedefTypeLoc TL) { + SourceLocation Loc = TL.getNameLoc(); + TypedefNameDecl *ND = TL.getTypedefNameDecl(); + if (ND->isTransparentTag()) { + TagDecl *Underlying = ND->getUnderlyingType()->getAsTagDecl(); + return IndexCtx.handleReference(Underlying, Loc, Parent, + ParentDC, SymbolRoleSet(), Relations); + } if (IsBase) { - SourceLocation Loc = TL.getNameLoc(); - TRY_TO(IndexCtx.handleReference(TL.getTypedefNameDecl(), Loc, + TRY_TO(IndexCtx.handleReference(ND, Loc, Parent, ParentDC, SymbolRoleSet())); if (auto *CD = TL.getType()->getAsCXXRecordDecl()) { TRY_TO(IndexCtx.handleReference(CD, Loc, Parent, ParentDC, @@ -57,7 +63,7 @@ public: Relations)); } } else { - TRY_TO(IndexCtx.handleReference(TL.getTypedefNameDecl(), TL.getNameLoc(), + TRY_TO(IndexCtx.handleReference(ND, Loc, Parent, ParentDC, SymbolRoleSet(), Relations)); } diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index 32b38f41cc..f48891417f 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -348,3 +348,39 @@ typedef MyGenCls MyEnumerator; // CHECK: [[@LINE-1]]:3 | field/ObjC | _foo | c:objc(cs)I7@_foo | | Ref,Writ,RelCont | rel: 1 } @end + +#define NS_ENUM(_name, _type) enum _name:_type _name; enum _name : _type + +typedef NS_ENUM(AnotherEnum, int) { +// CHECK-NOT: [[@LINE-1]]:17 | type-alias/C | AnotherEnum | +// CHECK: [[@LINE-2]]:17 | enum/C | AnotherEnum | [[AnotherEnum_USR:.*]] | {{.*}} | Ref,RelCont | rel: 1 + AnotherEnumFirst = 0, + AnotherEnumSecond = 1, + AnotherEnumThird = 2, +}; + +AnotherEnum anotherT; +// CHECK: [[@LINE-1]]:1 | enum/C | AnotherEnum | [[AnotherEnum_USR]] | {{.*}} | Ref,RelCont | rel: 1 +enum AnotherEnum anotherE; +// CHECK: [[@LINE-1]]:6 | enum/C | AnotherEnum | [[AnotherEnum_USR]] | {{.*}} | Ref,RelCont | rel: 1 + +#define TRANSPARENT(_name) struct _name _name; struct _name +#define OPAQUE(_name) struct _name *_name; struct _name + +typedef TRANSPARENT(AStruct) { + int x; +}; + +AStruct aStructT; +// CHECK: [[@LINE-1]]:1 | struct/C | AStruct | {{.*}} | {{.*}} | Ref,RelCont | rel: 1 +struct AStruct aStructS; +// CHECK: [[@LINE-1]]:8 | struct/C | AStruct | {{.*}} | {{.*}} | Ref,RelCont | rel: 1 + +typedef OPAQUE(Separate) { + int x; +}; + +Separate separateT; +// CHECK: [[@LINE-1]]:1 | type-alias/C | Separate | {{.*}} | {{.*}} | Ref,RelCont | rel: 1 +struct Separate separateE; +// CHECK: [[@LINE-1]]:8 | struct/C | Separate | {{.*}} | {{.*}} | Ref,RelCont | rel: 1 diff --git a/test/Index/get-cursor.m b/test/Index/get-cursor.m index d321233401..af277d45fd 100644 --- a/test/Index/get-cursor.m +++ b/test/Index/get-cursor.m @@ -129,6 +129,31 @@ void foo3(Test3 *test3) { } @end +#define NS_ENUM(_name, _type) enum _name : _type _name; enum _name : _type +typedef NS_ENUM(TestTransparent, int) { + TestTransparentFirst = 0, + TestTransparentSecond = 1, +}; +typedef enum TestTransparent NotTransparent; + +TestTransparent transparentTypedef; +enum TestTransparent transparentUnderlying; +NotTransparent opaqueTypedef; + +#define MY_ENUM(_name, _type) enum _name : _type _name##_t; enum _name : _type +typedef MY_ENUM(TokenPaste, int) { + TokenPasteFirst = 0, +}; +TokenPaste_t opaqueTypedef2; + +#define MY_TYPE(_name) struct _name _name; struct _name +typedef MY_TYPE(SomeT) { int x; }; +SomeT someVar; + +#define MY_TYPE2(_name) struct _name *_name; struct _name +typedef MY_TYPE2(SomeT2) { int x; }; +SomeT2 someVar2; + // RUN: c-index-test -cursor-at=%s:4:28 -cursor-at=%s:5:28 %s | FileCheck -check-prefix=CHECK-PROP %s // CHECK-PROP: ObjCPropertyDecl=foo1:4:26 @@ -193,3 +218,11 @@ void foo3(Test3 *test3) { // RUN: c-index-test -cursor-at=%s:127:8 %s | FileCheck -check-prefix=CHECK-RECEIVER-WITH-NULLABILITY %s // RUN: c-index-test -cursor-at=%s:128:8 %s | FileCheck -check-prefix=CHECK-RECEIVER-WITH-NULLABILITY %s // CHECK-RECEIVER-WITH-NULLABILITY: Receiver-type=ObjCId + +// RUN: c-index-test -cursor-at=%s:139:1 -cursor-at=%s:140:6 -cursor-at=%s:141:1 -cursor-at=%s:147:1 -cursor-at=%s:151:1 -cursor-at=%s:155:1 %s | FileCheck -check-prefix=CHECK-TRANSPARENT %s +// CHECK-TRANSPARENT: 139:1 TypeRef=TestTransparent:133:17 (Transparent: enum TestTransparent) Extent=[139:1 - 139:16] Spelling=TestTransparent ([139:1 - 139:16]) +// CHECK-TRANSPARENT: 140:6 TypeRef=enum TestTransparent:133:17 Extent=[140:6 - 140:21] Spelling=enum TestTransparent ([140:6 - 140:21]) +// CHECK-TRANSPARENT: 141:1 TypeRef=NotTransparent:137:30 Extent=[141:1 - 141:15] Spelling=NotTransparent ([141:1 - 141:15]) +// CHECK-TRANSPARENT: 147:1 TypeRef=TokenPaste_t:144:9 Extent=[147:1 - 147:13] Spelling=TokenPaste_t ([147:1 - 147:13]) +// CHECK-TRANSPARENT: 151:1 TypeRef=SomeT:150:17 (Transparent: struct SomeT) Extent=[151:1 - 151:6] Spelling=SomeT ([151:1 - 151:6]) +// CHECK-TRANSPARENT: 155:1 TypeRef=SomeT2:154:18 Extent=[155:1 - 155:7] Spelling=SomeT2 ([155:1 - 155:7]) diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c index b6e6ded9cc..dbd40b86b7 100644 --- a/tools/c-index-test/c-index-test.c +++ b/tools/c-index-test/c-index-test.c @@ -710,6 +710,15 @@ static void PrintCursor(CXCursor Cursor, const char *CommentSchemaFile) { clang_getSpellingLocation(Loc, 0, &line, &column, 0); printf(":%d:%d", line, column); } + + if (clang_getCursorKind(Referenced) == CXCursor_TypedefDecl) { + CXType T = clang_getCursorType(Referenced); + if (clang_Type_isTransparentTagTypedef(T)) { + CXType Underlying = clang_getTypedefDeclUnderlyingType(Referenced); + CXString S = clang_getTypeSpelling(Underlying); + printf(" (Transparent: %s)", clang_getCString(S)); + } + } } if (clang_isCursorDefinition(Cursor)) diff --git a/tools/libclang/CXType.cpp b/tools/libclang/CXType.cpp index 54549ef1e3..16e993e2ac 100644 --- a/tools/libclang/CXType.cpp +++ b/tools/libclang/CXType.cpp @@ -1039,3 +1039,12 @@ CXType clang_Type_getNamedType(CXType CT){ return MakeCXType(QualType(), GetTU(CT)); } + +unsigned clang_Type_isTransparentTagTypedef(CXType TT){ + QualType T = GetQualType(TT); + if (auto *TT = dyn_cast_or_null(T.getTypePtrOrNull())) { + if (auto *D = TT->getDecl()) + return D->isTransparentTag(); + } + return false; +} diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports index 222cb67839..38ecedae3c 100644 --- a/tools/libclang/libclang.exports +++ b/tools/libclang/libclang.exports @@ -88,6 +88,7 @@ clang_Type_getTemplateArgumentAsType clang_Type_getCXXRefQualifier clang_Type_visitFields clang_Type_getNamedType +clang_Type_isTransparentTagTypedef clang_VerbatimBlockLineComment_getText clang_VerbatimLineComment_getText clang_HTMLTagComment_getAsString -- 2.40.0