https://github.com/snprajwal updated https://github.com/llvm/llvm-project/pull/146182
>From 398c55dd352e0f38f5dc5b85a9f6dca836706597 Mon Sep 17 00:00:00 2001 From: Prajwal Nadig <pna...@apple.com> Date: Sat, 28 Jun 2025 01:33:42 +0100 Subject: [PATCH] [ExtractAPI] Format pointer types correctly Pointer types in function signatures must place the asterisk before the identifier without a space in between. This patch removes the space and also ensures that pointers to pointers are formatted correctly. rdar://131780418 rdar://154533037 --- clang/lib/ExtractAPI/DeclarationFragments.cpp | 29 +- clang/test/ExtractAPI/global_record.c | 4 +- .../test/ExtractAPI/global_record_multifile.c | 4 +- clang/test/ExtractAPI/macro_undefined.c | 4 +- clang/test/ExtractAPI/pointers.c | 388 ++++++++++++++++++ 5 files changed, 412 insertions(+), 17 deletions(-) create mode 100644 clang/test/ExtractAPI/pointers.c diff --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp index 348e7588690a2..52349324d7829 100644 --- a/clang/lib/ExtractAPI/DeclarationFragments.cpp +++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp @@ -324,10 +324,15 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType( // Declaration fragments of a pointer type is the declaration fragments of // the pointee type followed by a `*`, - if (T->isPointerType() && !T->isFunctionPointerType()) - return Fragments - .append(getFragmentsForType(T->getPointeeType(), Context, After)) - .append(" *", DeclarationFragments::FragmentKind::Text); + if (T->isPointerType() && !T->isFunctionPointerType()) { + QualType PointeeT = T->getPointeeType(); + Fragments.append(getFragmentsForType(PointeeT, Context, After)); + // If the pointee is itself a pointer, we do not want to insert a space + // before the `*` as the preceding character in the type name is a `*`. + if (!PointeeT->isAnyPointerType()) + Fragments.appendSpace(); + return Fragments.append("*", DeclarationFragments::FragmentKind::Text); + } // For Objective-C `id` and `Class` pointers // we do not spell out the `*`. @@ -631,7 +636,7 @@ DeclarationFragmentsBuilder::getFragmentsForParam(const ParmVarDecl *Param) { DeclarationFragments::FragmentKind::InternalParam); } else { Fragments.append(std::move(TypeFragments)); - if (!T->isBlockPointerType()) + if (!T->isAnyPointerType() && !T->isBlockPointerType()) Fragments.appendSpace(); Fragments .append(Param->getName(), @@ -706,18 +711,20 @@ DeclarationFragmentsBuilder::getFragmentsForFunction(const FunctionDecl *Func) { // FIXME: Is `after` actually needed here? DeclarationFragments After; + QualType ReturnType = Func->getReturnType(); auto ReturnValueFragment = - getFragmentsForType(Func->getReturnType(), Func->getASTContext(), After); + getFragmentsForType(ReturnType, Func->getASTContext(), After); if (StringRef(ReturnValueFragment.begin()->Spelling) .starts_with("type-parameter")) { - std::string ProperArgName = Func->getReturnType().getAsString(); + std::string ProperArgName = ReturnType.getAsString(); ReturnValueFragment.begin()->Spelling.swap(ProperArgName); } - Fragments.append(std::move(ReturnValueFragment)) - .appendSpace() - .append(Func->getNameAsString(), - DeclarationFragments::FragmentKind::Identifier); + Fragments.append(std::move(ReturnValueFragment)); + if (!ReturnType->isAnyPointerType()) + Fragments.appendSpace(); + Fragments.append(Func->getNameAsString(), + DeclarationFragments::FragmentKind::Identifier); if (Func->getTemplateSpecializationInfo()) { Fragments.append("<", DeclarationFragments::FragmentKind::Text); diff --git a/clang/test/ExtractAPI/global_record.c b/clang/test/ExtractAPI/global_record.c index a08d51d21f955..287fa24c4c64e 100644 --- a/clang/test/ExtractAPI/global_record.c +++ b/clang/test/ExtractAPI/global_record.c @@ -185,7 +185,7 @@ char unavailable __attribute__((unavailable)); }, { "kind": "text", - "spelling": " * " + "spelling": " *" }, { "kind": "internalParam", @@ -341,7 +341,7 @@ char unavailable __attribute__((unavailable)); }, { "kind": "text", - "spelling": " * " + "spelling": " *" }, { "kind": "internalParam", diff --git a/clang/test/ExtractAPI/global_record_multifile.c b/clang/test/ExtractAPI/global_record_multifile.c index ffdfbcb7eb808..b98cd27b1601e 100644 --- a/clang/test/ExtractAPI/global_record_multifile.c +++ b/clang/test/ExtractAPI/global_record_multifile.c @@ -187,7 +187,7 @@ char unavailable __attribute__((unavailable)); }, { "kind": "text", - "spelling": " * " + "spelling": " *" }, { "kind": "internalParam", @@ -343,7 +343,7 @@ char unavailable __attribute__((unavailable)); }, { "kind": "text", - "spelling": " * " + "spelling": " *" }, { "kind": "internalParam", diff --git a/clang/test/ExtractAPI/macro_undefined.c b/clang/test/ExtractAPI/macro_undefined.c index ec60f95d3d6c4..7bb50af380c24 100644 --- a/clang/test/ExtractAPI/macro_undefined.c +++ b/clang/test/ExtractAPI/macro_undefined.c @@ -148,7 +148,7 @@ FUNC_GEN(bar, const int *, unsigned); }, { "kind": "text", - "spelling": " * " + "spelling": " *" }, { "kind": "internalParam", @@ -195,7 +195,7 @@ FUNC_GEN(bar, const int *, unsigned); }, { "kind": "text", - "spelling": " * " + "spelling": " *" }, { "kind": "internalParam", diff --git a/clang/test/ExtractAPI/pointers.c b/clang/test/ExtractAPI/pointers.c new file mode 100644 index 0000000000000..d7baf541ec03e --- /dev/null +++ b/clang/test/ExtractAPI/pointers.c @@ -0,0 +1,388 @@ +// 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 -extract-api --pretty-sgf --product-name=Pointers -target arm64-apple-macosx \ +// RUN: %t/input.h -o %t/output.json | FileCheck -allow-empty %s + +// 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 + +// CHECK-NOT: error: +// CHECK-NOT: warning: + +//--- input.h +void foo(int *a); +void bar(int **a); +void *baz(); +void **qux(); + +//--- reference.output.json.in +{ + "metadata": { + "formatVersion": { + "major": 0, + "minor": 5, + "patch": 3 + }, + "generator": "?" + }, + "module": { + "name": "Pointers", + "platform": { + "architecture": "arm64", + "operatingSystem": { + "minimumVersion": { + "major": 11, + "minor": 0, + "patch": 0 + }, + "name": "macosx" + }, + "vendor": "apple" + } + }, + "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:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " *" + }, + { + "kind": "internalParam", + "spelling": "a" + }, + { + "kind": "text", + "spelling": ");" + } + ], + "functionSignature": { + "parameters": [ + { + "declarationFragments": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " *" + }, + { + "kind": "internalParam", + "spelling": "a" + } + ], + "name": "a" + } + ], + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + } + ] + }, + "identifier": { + "interfaceLanguage": "c", + "precise": "c:@F@foo" + }, + "kind": { + "displayName": "Function", + "identifier": "c.func" + }, + "location": { + "position": { + "character": 5, + "line": 0 + }, + "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": "bar" + }, + { + "kind": "text", + "spelling": "(" + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " **" + }, + { + "kind": "internalParam", + "spelling": "a" + }, + { + "kind": "text", + "spelling": ");" + } + ], + "functionSignature": { + "parameters": [ + { + "declarationFragments": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " **" + }, + { + "kind": "internalParam", + "spelling": "a" + } + ], + "name": "a" + } + ], + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + } + ] + }, + "identifier": { + "interfaceLanguage": "c", + "precise": "c:@F@bar" + }, + "kind": { + "displayName": "Function", + "identifier": "c.func" + }, + "location": { + "position": { + "character": 5, + "line": 1 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "bar" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "bar" + } + ], + "title": "bar" + }, + "pathComponents": [ + "bar" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + }, + { + "kind": "text", + "spelling": " *" + }, + { + "kind": "identifier", + "spelling": "baz" + }, + { + "kind": "text", + "spelling": "();" + } + ], + "functionSignature": { + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + }, + { + "kind": "text", + "spelling": " *" + } + ] + }, + "identifier": { + "interfaceLanguage": "c", + "precise": "c:@F@baz" + }, + "kind": { + "displayName": "Function", + "identifier": "c.func" + }, + "location": { + "position": { + "character": 6, + "line": 2 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "baz" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "baz" + } + ], + "title": "baz" + }, + "pathComponents": [ + "baz" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + }, + { + "kind": "text", + "spelling": " **" + }, + { + "kind": "identifier", + "spelling": "qux" + }, + { + "kind": "text", + "spelling": "();" + } + ], + "functionSignature": { + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + }, + { + "kind": "text", + "spelling": " **" + } + ] + }, + "identifier": { + "interfaceLanguage": "c", + "precise": "c:@F@qux" + }, + "kind": { + "displayName": "Function", + "identifier": "c.func" + }, + "location": { + "position": { + "character": 7, + "line": 3 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "qux" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "qux" + } + ], + "title": "qux" + }, + "pathComponents": [ + "qux" + ] + } + ] +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits