diff --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp index 0e6404cadd1ed..fd8c888daf7b5 100644 --- a/clang/lib/ExtractAPI/DeclarationFragments.cpp +++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp @@ -331,10 +331,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 `*`. @@ -638,7 +643,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(), @@ -713,18 +718,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); @@ -890,6 +897,9 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForCXXMethod( if (Method->isVolatile()) Fragments.append("volatile", DeclarationFragments::FragmentKind::Keyword) .appendSpace(); + if (Method->isVirtual()) + Fragments.append("virtual", DeclarationFragments::FragmentKind::Keyword) + .appendSpace(); // Build return type DeclarationFragments After; @@ -1615,10 +1625,13 @@ DeclarationFragmentsBuilder::getFunctionSignature(const ObjCMethodDecl *); DeclarationFragments DeclarationFragmentsBuilder::getSubHeading(const NamedDecl *Decl) { DeclarationFragments Fragments; - if (isa(Decl) || isa(Decl)) + if (isa(Decl)) { Fragments.append(cast(Decl->getDeclContext())->getName(), DeclarationFragments::FragmentKind::Identifier); - else if (isa(Decl)) { + } else if (isa(Decl)) { + Fragments.append(cast(Decl)->getNameAsString(), + DeclarationFragments::FragmentKind::Identifier); + } else if (isa(Decl)) { Fragments.append( cast(Decl)->getConversionType().getAsString(), DeclarationFragments::FragmentKind::Identifier); @@ -1632,9 +1645,11 @@ DeclarationFragmentsBuilder::getSubHeading(const NamedDecl *Decl) { } else if (Decl->getIdentifier()) { Fragments.append(Decl->getName(), DeclarationFragments::FragmentKind::Identifier); - } else + } else { Fragments.append(Decl->getDeclName().getAsString(), DeclarationFragments::FragmentKind::Identifier); + } + return Fragments; } diff --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp index 85e99e17fbf70..5dc8c72ea32b1 100644 --- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp +++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp @@ -344,6 +344,22 @@ Object serializeNames(const APIRecord *Record) { serializeArray(Names, "subHeading", serializeDeclarationFragments(Record->SubHeading)); DeclarationFragments NavigatorFragments; + // The +/- prefix for Objective-C methods is important information, and + // should be included in the navigator fragment. The entire subheading is + // not included as it can contain too much information for other records. + switch (Record->getKind()) { + case APIRecord::RK_ObjCClassMethod: + NavigatorFragments.append("+ ", DeclarationFragments::FragmentKind::Text, + /*PreciseIdentifier*/ ""); + break; + case APIRecord::RK_ObjCInstanceMethod: + NavigatorFragments.append("- ", DeclarationFragments::FragmentKind::Text, + /*PreciseIdentifier*/ ""); + break; + default: + break; + } + NavigatorFragments.append(Record->Name, DeclarationFragments::FragmentKind::Identifier, /*PreciseIdentifier*/ ""); diff --git a/clang/test/ExtractAPI/constructor_destructor.cpp b/clang/test/ExtractAPI/constructor_destructor.cpp index 27112c95ac45c..2f2150a6d0da0 100644 --- a/clang/test/ExtractAPI/constructor_destructor.cpp +++ b/clang/test/ExtractAPI/constructor_destructor.cpp @@ -213,7 +213,7 @@ class Foo { "subHeading": [ { "kind": "identifier", - "spelling": "Foo" + "spelling": "~Foo" } ], "title": "~Foo" 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/methods.cpp b/clang/test/ExtractAPI/methods.cpp index 67f04b4d33db8..180c2f92b2772 100644 --- a/clang/test/ExtractAPI/methods.cpp +++ b/clang/test/ExtractAPI/methods.cpp @@ -45,11 +45,19 @@ class Foo { // GETCOUNT-NEXT: ] // RUN: FileCheck %s --input-file %t/output.symbols.json --check-prefix SETL - void setLength(int length) noexcept; + virtual void setLength(int length) noexcept; // SETL: "!testRelLabel": "memberOf $ c:@S@Foo@F@setLength#I# $ c:@S@Foo" // SETL-LABEL: "!testLabel": "c:@S@Foo@F@setLength#I#" // SETL: "declarationFragments": [ // SETL-NEXT: { + // SETL-NEXT: "kind": "keyword", + // SETL-NEXT: "spelling": "virtual" + // SETL-NEXT: }, + // SETL-NEXT: { + // SETL-NEXT: "kind": "text", + // SETL-NEXT: "spelling": " " + // SETL-NEXT: }, + // SETL-NEXT: { // SETL-NEXT: "kind": "typeIdentifier", // SETL-NEXT: "preciseIdentifier": "c:v", // SETL-NEXT: "spelling": "void" diff --git a/clang/test/ExtractAPI/objc_instancetype.m b/clang/test/ExtractAPI/objc_instancetype.m index 071ebe440918a..dbd47a1f746ff 100644 --- a/clang/test/ExtractAPI/objc_instancetype.m +++ b/clang/test/ExtractAPI/objc_instancetype.m @@ -157,6 +157,10 @@ - (id) reset; }, "names": { "navigator": [ + { + "kind": "text", + "spelling": "- " + }, { "kind": "identifier", "spelling": "init" @@ -228,6 +232,10 @@ - (id) reset; }, "names": { "navigator": [ + { + "kind": "text", + "spelling": "- " + }, { "kind": "identifier", "spelling": "reset" 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" + ] + } + ] +}