Skip to content

Commit

Permalink
[clang][ExtractAPI] Ensure typedef to pointer types are preserved (#7…
Browse files Browse the repository at this point in the history
…8584)

When generating declaration fragments for types that use typedefs to
pointer types ensure that we keep the user-defined typedef form instead
of desugaring the typedef.

rdar://102137655
  • Loading branch information
daniel-grumberg committed Jan 22, 2024
1 parent 74ab795 commit c553212
Show file tree
Hide file tree
Showing 2 changed files with 320 additions and 40 deletions.
80 changes: 40 additions & 40 deletions clang/lib/ExtractAPI/DeclarationFragments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,46 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(

DeclarationFragments Fragments;

// An ElaboratedType is a sugar for types that are referred to using an
// elaborated keyword, e.g., `struct S`, `enum E`, or (in C++) via a
// qualified name, e.g., `N::M::type`, or both.
if (const ElaboratedType *ET = dyn_cast<ElaboratedType>(T)) {
ElaboratedTypeKeyword Keyword = ET->getKeyword();
if (Keyword != ElaboratedTypeKeyword::None) {
Fragments
.append(ElaboratedType::getKeywordName(Keyword),
DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
}

if (const NestedNameSpecifier *NNS = ET->getQualifier())
Fragments.append(getFragmentsForNNS(NNS, Context, After));

// After handling the elaborated keyword or qualified name, build
// declaration fragments for the desugared underlying type.
return Fragments.append(getFragmentsForType(ET->desugar(), Context, After));
}

// If the type is a typedefed type, get the underlying TypedefNameDecl for a
// direct reference to the typedef instead of the wrapped type.

// 'id' type is a typedef for an ObjCObjectPointerType
// we treat it as a typedef
if (const TypedefType *TypedefTy = dyn_cast<TypedefType>(T)) {
const TypedefNameDecl *Decl = TypedefTy->getDecl();
TypedefUnderlyingTypeResolver TypedefResolver(Context);
std::string USR = TypedefResolver.getUSRForType(QualType(T, 0));

if (T->isObjCIdType()) {
return Fragments.append(Decl->getName(),
DeclarationFragments::FragmentKind::Keyword);
}

return Fragments.append(
Decl->getName(), DeclarationFragments::FragmentKind::TypeIdentifier,
USR, TypedefResolver.getUnderlyingTypeDecl(QualType(T, 0)));
}

// Declaration fragments of a pointer type is the declaration fragments of
// the pointee type followed by a `*`,
if (T->isPointerType() && !T->isFunctionPointerType())
Expand Down Expand Up @@ -328,46 +368,6 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(
getFragmentsForType(AT->getElementType(), Context, After));
}

// An ElaboratedType is a sugar for types that are referred to using an
// elaborated keyword, e.g., `struct S`, `enum E`, or (in C++) via a
// qualified name, e.g., `N::M::type`, or both.
if (const ElaboratedType *ET = dyn_cast<ElaboratedType>(T)) {
ElaboratedTypeKeyword Keyword = ET->getKeyword();
if (Keyword != ElaboratedTypeKeyword::None) {
Fragments
.append(ElaboratedType::getKeywordName(Keyword),
DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
}

if (const NestedNameSpecifier *NNS = ET->getQualifier())
Fragments.append(getFragmentsForNNS(NNS, Context, After));

// After handling the elaborated keyword or qualified name, build
// declaration fragments for the desugared underlying type.
return Fragments.append(getFragmentsForType(ET->desugar(), Context, After));
}

// If the type is a typedefed type, get the underlying TypedefNameDecl for a
// direct reference to the typedef instead of the wrapped type.

// 'id' type is a typedef for an ObjCObjectPointerType
// we treat it as a typedef
if (const TypedefType *TypedefTy = dyn_cast<TypedefType>(T)) {
const TypedefNameDecl *Decl = TypedefTy->getDecl();
TypedefUnderlyingTypeResolver TypedefResolver(Context);
std::string USR = TypedefResolver.getUSRForType(QualType(T, 0));

if (T->isObjCIdType()) {
return Fragments.append(Decl->getName(),
DeclarationFragments::FragmentKind::Keyword);
}

return Fragments.append(
Decl->getName(), DeclarationFragments::FragmentKind::TypeIdentifier,
USR, TypedefResolver.getUnderlyingTypeDecl(QualType(T, 0)));
}

// Everything we care about has been handled now, reduce to the canonical
// unqualified base type.
QualType Base = T->getCanonicalTypeUnqualified();
Expand Down
280 changes: 280 additions & 0 deletions clang/test/ExtractAPI/typedef.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
//--- input.h
typedef int MyInt;

typedef struct Bar *BarPtr;

void foo(BarPtr value);

void baz(BarPtr *value);

//--- reference.output.json.in
{
"metadata": {
Expand Down Expand Up @@ -43,6 +49,208 @@ typedef int MyInt;
},
"relationships": [],
"symbols": [
{
"accessLevel": "public",
"declarationFragments": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:v",
"spelling": "void"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "foo"
},
{
"kind": "text",
"spelling": "("
},
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:input.h@T@BarPtr",
"spelling": "BarPtr"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "internalParam",
"spelling": "value"
},
{
"kind": "text",
"spelling": ");"
}
],
"functionSignature": {
"parameters": [
{
"declarationFragments": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:input.h@T@BarPtr",
"spelling": "BarPtr"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "internalParam",
"spelling": "value"
}
],
"name": "value"
}
],
"returns": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:v",
"spelling": "void"
}
]
},
"identifier": {
"interfaceLanguage": "objective-c",
"precise": "c:@F@foo"
},
"kind": {
"displayName": "Function",
"identifier": "objective-c.func"
},
"location": {
"position": {
"character": 5,
"line": 4
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "foo"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "foo"
}
],
"title": "foo"
},
"pathComponents": [
"foo"
]
},
{
"accessLevel": "public",
"declarationFragments": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:v",
"spelling": "void"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "baz"
},
{
"kind": "text",
"spelling": "("
},
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:input.h@T@BarPtr",
"spelling": "BarPtr"
},
{
"kind": "text",
"spelling": " * "
},
{
"kind": "internalParam",
"spelling": "value"
},
{
"kind": "text",
"spelling": ");"
}
],
"functionSignature": {
"parameters": [
{
"declarationFragments": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:input.h@T@BarPtr",
"spelling": "BarPtr"
},
{
"kind": "text",
"spelling": " * "
},
{
"kind": "internalParam",
"spelling": "value"
}
],
"name": "value"
}
],
"returns": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:v",
"spelling": "void"
}
]
},
"identifier": {
"interfaceLanguage": "objective-c",
"precise": "c:@F@baz"
},
"kind": {
"displayName": "Function",
"identifier": "objective-c.func"
},
"location": {
"position": {
"character": 5,
"line": 6
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "baz"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "baz"
}
],
"title": "baz"
},
"pathComponents": [
"baz"
]
},
{
"accessLevel": "public",
"declarationFragments": [
Expand Down Expand Up @@ -106,6 +314,78 @@ typedef int MyInt;
"MyInt"
],
"type": "c:I"
},
{
"accessLevel": "public",
"declarationFragments": [
{
"kind": "keyword",
"spelling": "typedef"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "keyword",
"spelling": "struct"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:@S@Bar",
"spelling": "Bar"
},
{
"kind": "text",
"spelling": " * "
},
{
"kind": "identifier",
"spelling": "BarPtr"
},
{
"kind": "text",
"spelling": ";"
}
],
"identifier": {
"interfaceLanguage": "objective-c",
"precise": "c:input.h@T@BarPtr"
},
"kind": {
"displayName": "Type Alias",
"identifier": "objective-c.typealias"
},
"location": {
"position": {
"character": 20,
"line": 2
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "BarPtr"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "BarPtr"
}
],
"title": "BarPtr"
},
"pathComponents": [
"BarPtr"
],
"type": "c:*$@S@Bar"
}
]
}

0 comments on commit c553212

Please sign in to comment.