-
Notifications
You must be signed in to change notification settings - Fork 10.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[clang][ExtractAPI] Add support for blocks in declaration fragments #73369
[clang][ExtractAPI] Add support for blocks in declaration fragments #73369
Conversation
Ensure that block types get represented correctly in declaration fragments, as block parameter names are important for documentation clients we need a separate system from getFragmentsForType in order to have access to full ParmVarDecls for the parameters. rdar://118257401
@llvm/pr-subscribers-clang Author: Daniel Grumberg (daniel-grumberg) ChangesEnsure that block types get represented correctly in declaration fragments, as block parameter names are important for documentation clients we need a separate system from getFragmentsForType in order to have access to full ParmVarDecls for the parameters. rdar://118257401 Patch is 36.77 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/73369.diff 7 Files Affected:
diff --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h
index 316d83df13e9359..d719196b9a43ecb 100644
--- a/clang/include/clang/ExtractAPI/DeclarationFragments.h
+++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h
@@ -24,6 +24,7 @@
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/ExprCXX.h"
+#include "clang/AST/TypeLoc.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Lex/MacroInfo.h"
#include "llvm/ADT/SmallVector.h"
@@ -410,6 +411,11 @@ class DeclarationFragmentsBuilder {
/// Build DeclarationFragments for a parameter variable declaration
/// ParmVarDecl.
static DeclarationFragments getFragmentsForParam(const ParmVarDecl *);
+
+ static DeclarationFragments
+ getFragmentsForBlock(const NamedDecl *BlockDecl, FunctionTypeLoc &Block,
+ FunctionProtoTypeLoc &BlockProto,
+ DeclarationFragments &After);
};
template <typename FunctionT>
diff --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp
index 02fa6cd6119ecac..eb6eea0aaf54655 100644
--- a/clang/lib/ExtractAPI/DeclarationFragments.cpp
+++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp
@@ -15,6 +15,8 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/QualTypeNames.h"
+#include "clang/AST/Type.h"
+#include "clang/AST/TypeLoc.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/ExtractAPI/TypedefUnderlyingTypeResolver.h"
#include "clang/Index/USRGeneration.h"
@@ -24,6 +26,40 @@
using namespace clang::extractapi;
using namespace llvm;
+namespace {
+
+void findTypeLocForBlockDecl(const clang::TypeSourceInfo *TSInfo,
+ clang::FunctionTypeLoc &Block,
+ clang::FunctionProtoTypeLoc &BlockProto) {
+ if (!TSInfo)
+ return;
+
+ clang::TypeLoc TL = TSInfo->getTypeLoc().getUnqualifiedLoc();
+ while (true) {
+ // Look through qualified types
+ if (auto QualifiedTL = TL.getAs<clang::QualifiedTypeLoc>()) {
+ TL = QualifiedTL.getUnqualifiedLoc();
+ continue;
+ }
+
+ if (auto AttrTL = TL.getAs<clang::AttributedTypeLoc>()) {
+ TL = AttrTL.getModifiedLoc();
+ continue;
+ }
+
+ // Try to get the function prototype behind the block pointer type,
+ // then we're done.
+ if (auto BlockPtr = TL.getAs<clang::BlockPointerTypeLoc>()) {
+ TL = BlockPtr.getPointeeLoc().IgnoreParens();
+ Block = TL.getAs<clang::FunctionTypeLoc>();
+ BlockProto = TL.getAs<clang::FunctionProtoTypeLoc>();
+ }
+ break;
+ }
+}
+
+} // namespace
+
DeclarationFragments &DeclarationFragments::appendSpace() {
if (!Fragments.empty()) {
Fragment &Last = Fragments.back();
@@ -218,7 +254,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(
// Declaration fragments of a pointer type is the declaration fragments of
// the pointee type followed by a `*`,
- if (T->isPointerType())
+ if (T->isPointerType() && !T->isFunctionPointerType())
return Fragments
.append(getFragmentsForType(T->getPointeeType(), Context, After))
.append(" *", DeclarationFragments::FragmentKind::Text);
@@ -449,10 +485,6 @@ DeclarationFragmentsBuilder::getFragmentsForVar(const VarDecl *Var) {
.append(VarDecl::getStorageClassSpecifierString(SC),
DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
- QualType T =
- Var->getTypeSourceInfo()
- ? Var->getTypeSourceInfo()->getType()
- : Var->getASTContext().getUnqualifiedObjCPointerType(Var->getType());
// Capture potential fragments that needs to be placed after the variable name
// ```
@@ -460,8 +492,23 @@ DeclarationFragmentsBuilder::getFragmentsForVar(const VarDecl *Var) {
// char (*ptr_to_array)[6];
// ```
DeclarationFragments After;
- return Fragments.append(getFragmentsForType(T, Var->getASTContext(), After))
- .appendSpace()
+ FunctionTypeLoc BlockLoc;
+ FunctionProtoTypeLoc BlockProtoLoc;
+ findTypeLocForBlockDecl(Var->getTypeSourceInfo(), BlockLoc, BlockProtoLoc);
+
+ if (!BlockLoc) {
+ QualType T = Var->getTypeSourceInfo()
+ ? Var->getTypeSourceInfo()->getType()
+ : Var->getASTContext().getUnqualifiedObjCPointerType(
+ Var->getType());
+
+ Fragments.append(getFragmentsForType(T, Var->getASTContext(), After))
+ .appendSpace();
+ } else {
+ Fragments.append(getFragmentsForBlock(Var, BlockLoc, BlockProtoLoc, After));
+ }
+
+ return Fragments
.append(Var->getName(), DeclarationFragments::FragmentKind::Identifier)
.append(std::move(After))
.append(";", DeclarationFragments::FragmentKind::Text);
@@ -504,13 +551,23 @@ DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForParam(const ParmVarDecl *Param) {
DeclarationFragments Fragments, After;
- QualType T = Param->getTypeSourceInfo()
- ? Param->getTypeSourceInfo()->getType()
- : Param->getASTContext().getUnqualifiedObjCPointerType(
- Param->getType());
+ auto *TSInfo = Param->getTypeSourceInfo();
+
+ QualType T = TSInfo ? TSInfo->getType()
+ : Param->getASTContext().getUnqualifiedObjCPointerType(
+ Param->getType());
+
+ FunctionTypeLoc BlockLoc;
+ FunctionProtoTypeLoc BlockProtoLoc;
+ findTypeLocForBlockDecl(TSInfo, BlockLoc, BlockProtoLoc);
+
+ DeclarationFragments TypeFragments;
+ if (BlockLoc)
+ TypeFragments.append(
+ getFragmentsForBlock(Param, BlockLoc, BlockProtoLoc, After));
+ else
+ TypeFragments.append(getFragmentsForType(T, Param->getASTContext(), After));
- DeclarationFragments TypeFragments =
- getFragmentsForType(T, Param->getASTContext(), After);
if (TypeFragments.begin()->Spelling.substr(0, 14).compare("type-parameter") ==
0) {
std::string ProperArgName = getNameForTemplateArgument(
@@ -522,17 +579,60 @@ DeclarationFragmentsBuilder::getFragmentsForParam(const ParmVarDecl *Param) {
TypeFragments.begin()->Spelling.swap(ProperArgName);
}
- if (Param->isObjCMethodParameter())
+ if (Param->isObjCMethodParameter()) {
Fragments.append("(", DeclarationFragments::FragmentKind::Text)
.append(std::move(TypeFragments))
- .append(") ", DeclarationFragments::FragmentKind::Text);
- else
- Fragments.append(std::move(TypeFragments)).appendSpace();
+ .append(std::move(After))
+ .append(") ", DeclarationFragments::FragmentKind::Text)
+ .append(Param->getName(),
+ DeclarationFragments::FragmentKind::InternalParam);
+ } else {
+ Fragments.append(std::move(TypeFragments));
+ if (!T->isBlockPointerType())
+ Fragments.appendSpace();
+ Fragments
+ .append(Param->getName(),
+ DeclarationFragments::FragmentKind::InternalParam)
+ .append(std::move(After));
+ }
+ return Fragments;
+}
- return Fragments
- .append(Param->getName(),
- DeclarationFragments::FragmentKind::InternalParam)
- .append(std::move(After));
+DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForBlock(
+ const NamedDecl *BlockDecl, FunctionTypeLoc &Block,
+ FunctionProtoTypeLoc &BlockProto, DeclarationFragments &After) {
+ DeclarationFragments Fragments;
+
+ DeclarationFragments RetTyAfter;
+ auto ReturnValueFragment = getFragmentsForType(
+ Block.getTypePtr()->getReturnType(), BlockDecl->getASTContext(), After);
+
+ Fragments.append(std::move(ReturnValueFragment))
+ .append(std::move(RetTyAfter))
+ .appendSpace()
+ .append("(^", DeclarationFragments::FragmentKind::Text);
+
+ After.append(")", DeclarationFragments::FragmentKind::Text);
+ unsigned NumParams = Block.getNumParams();
+
+ if (!BlockProto || NumParams == 0) {
+ if (BlockProto && BlockProto.getTypePtr()->isVariadic())
+ After.append("(...)", DeclarationFragments::FragmentKind::Text);
+ else
+ After.append("()", DeclarationFragments::FragmentKind::Text);
+ } else {
+ After.append("(", DeclarationFragments::FragmentKind::Text);
+ for (unsigned I = 0; I != NumParams; ++I) {
+ if (I)
+ After.append(", ", DeclarationFragments::FragmentKind::Text);
+ After.append(getFragmentsForParam(Block.getParam(I)));
+ if (I == NumParams - 1 && BlockProto.getTypePtr()->isVariadic())
+ After.append(", ...", DeclarationFragments::FragmentKind::Text);
+ }
+ After.append(")", DeclarationFragments::FragmentKind::Text);
+ }
+
+ return Fragments;
}
DeclarationFragments
@@ -595,11 +695,18 @@ DeclarationFragmentsBuilder::getFragmentsForFunction(const FunctionDecl *Func) {
Fragments.append(std::move(After));
Fragments.append("(", DeclarationFragments::FragmentKind::Text);
- for (unsigned i = 0, end = Func->getNumParams(); i != end; ++i) {
+ unsigned NumParams = Func->getNumParams();
+ for (unsigned i = 0; i != NumParams; ++i) {
if (i)
Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
Fragments.append(getFragmentsForParam(Func->getParamDecl(i)));
}
+
+ if (Func->isVariadic()) {
+ if (NumParams > 0)
+ Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
+ Fragments.append("...", DeclarationFragments::FragmentKind::Text);
+ }
Fragments.append(")", DeclarationFragments::FragmentKind::Text);
Fragments.append(DeclarationFragments::getExceptionSpecificationString(
@@ -1248,14 +1355,27 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProperty(
Fragments.append(")", DeclarationFragments::FragmentKind::Text);
}
- // Build the property type and name, and return the completed fragments.
- return Fragments.appendSpace()
- .append(getFragmentsForType(Property->getType(),
- Property->getASTContext(), After))
- .appendSpace()
+ Fragments.appendSpace();
+
+ FunctionTypeLoc BlockLoc;
+ FunctionProtoTypeLoc BlockProtoLoc;
+ findTypeLocForBlockDecl(Property->getTypeSourceInfo(), BlockLoc,
+ BlockProtoLoc);
+
+ auto PropType = Property->getType();
+ if (!BlockLoc)
+ Fragments
+ .append(getFragmentsForType(PropType, Property->getASTContext(), After))
+ .appendSpace();
+ else
+ Fragments.append(
+ getFragmentsForBlock(Property, BlockLoc, BlockProtoLoc, After));
+
+ return Fragments
.append(Property->getName(),
DeclarationFragments::FragmentKind::Identifier)
- .append(std::move(After));
+ .append(std::move(After))
+ .append(";", DeclarationFragments::FragmentKind::Text);
}
DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(
diff --git a/clang/test/ExtractAPI/objc_block.m b/clang/test/ExtractAPI/objc_block.m
new file mode 100644
index 000000000000000..a7a4f5696333c18
--- /dev/null
+++ b/clang/test/ExtractAPI/objc_block.m
@@ -0,0 +1,965 @@
+// 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 -fblocks -triple arm64-apple-macosx \
+// RUN: -x objective-c-header %t/input.h -o %t/output.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
+
+//--- input.h
+@interface Foo
+-(void)methodBlockNoParam:(void (^)())block;
+-(void)methodBlockWithParam:(int (^)(int foo))block;
+-(void)methodBlockWithMultipleParam:(int (^)(int foo, unsigned baz))block;
+-(void)methodBlockVariadic:(int (^)(int foo, ...))block;
+@end
+
+void func(int (^arg)(int foo));
+
+int (^global)(int foo);
+
+///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:objc(cs)Foo(im)methodBlockNoParam:",
+ "target": "c:objc(cs)Foo",
+ "targetFallback": "Foo"
+ },
+ {
+ "kind": "memberOf",
+ "source": "c:objc(cs)Foo(im)methodBlockWithParam:",
+ "target": "c:objc(cs)Foo",
+ "targetFallback": "Foo"
+ },
+ {
+ "kind": "memberOf",
+ "source": "c:objc(cs)Foo(im)methodBlockWithMultipleParam:",
+ "target": "c:objc(cs)Foo",
+ "targetFallback": "Foo"
+ },
+ {
+ "kind": "memberOf",
+ "source": "c:objc(cs)Foo(im)methodBlockVariadic:",
+ "target": "c:objc(cs)Foo",
+ "targetFallback": "Foo"
+ }
+ ],
+ "symbols": [
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " (^"
+ },
+ {
+ "kind": "identifier",
+ "spelling": "global"
+ },
+ {
+ "kind": "text",
+ "spelling": ")("
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "foo"
+ },
+ {
+ "kind": "text",
+ "spelling": ");"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "objective-c",
+ "precise": "c:@global"
+ },
+ "kind": {
+ "displayName": "Global Variable",
+ "identifier": "objective-c.var"
+ },
+ "location": {
+ "position": {
+ "character": 6,
+ "line": 9
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "global"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "global"
+ }
+ ],
+ "title": "global"
+ },
+ "pathComponents": [
+ "global"
+ ]
+ },
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:v",
+ "spelling": "void"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "func"
+ },
+ {
+ "kind": "text",
+ "spelling": "("
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " (^"
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "arg"
+ },
+ {
+ "kind": "text",
+ "spelling": ")("
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "foo"
+ },
+ {
+ "kind": "text",
+ "spelling": "));"
+ }
+ ],
+ "functionSignature": {
+ "parameters": [
+ {
+ "declarationFragments": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " (^"
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "arg"
+ },
+ {
+ "kind": "text",
+ "spelling": ")("
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "foo"
+ },
+ {
+ "kind": "text",
+ "spelling": ")"
+ }
+ ],
+ "name": "arg"
+ }
+ ],
+ "returns": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:v",
+ "spelling": "void"
+ }
+ ]
+ },
+ "identifier": {
+ "interfaceLanguage": "objective-c",
+ "precise": "c:@F@func"
+ },
+ "kind": {
+ "displayName": "Function",
+ "identifier": "objective-c.func"
+ },
+ "location": {
+ "position": {
+ "character": 5,
+ "line": 7
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "func"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "func"
+ }
+ ],
+ "title": "func"
+ },
+ "pathComponents": [
+ "func"
+ ]
+ },
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "@interface"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "objective-c",
+ "precise": "c:objc(cs)Foo"
+ },
+ "kind": {
+ "displayName": "Class",
+ "identifier": "objective-c.class"
+ },
+ "location": {
+ "position": {
+ "character": 11,
+ "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": "text",
+ "spelling": "- ("
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:v",
+ "spelling": "void"
+ },
+ {
+ "kind": "text",
+ "spelling": ") "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "methodBlockNoParam:"
+ },
+ {
+ "kind": "text",
+ "spelling": "("
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:v",
+ "spelling": "void"
+ },
+ {
+ "kind": "text",
+ "spelling": " (^"
+ },
+ {
+ "kind": "text",
+ "spelling": ")()) "
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "block"
+ },
+ {
+ "kind": "text",
+ "spelling": ";"
+ }
+ ],
+ "functionSignature": {
+ "parameters": [
+ {
+ "declarationFragments": [
+ {
+ "kind": "text",
+ "spelling": "("
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:v",
+ "spelling": "void...
[truncated]
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
…lvm#73369) Ensure that block types get represented correctly in declaration fragments, as block parameter names are important for documentation clients we need a separate system from getFragmentsForType in order to have access to full ParmVarDecls for the parameters. rdar://118257401
…lvm#73369) Ensure that block types get represented correctly in declaration fragments, as block parameter names are important for documentation clients we need a separate system from getFragmentsForType in order to have access to full ParmVarDecls for the parameters. rdar://118257401
…lvm#73369) Ensure that block types get represented correctly in declaration fragments, as block parameter names are important for documentation clients we need a separate system from getFragmentsForType in order to have access to full ParmVarDecls for the parameters. rdar://118257401
Ensure that block types get represented correctly in declaration fragments, as block parameter names are important for documentation clients we need a separate system from getFragmentsForType in order to have access to full ParmVarDecls for the parameters.
rdar://118257401