Skip to content

Commit

Permalink
[clang][ExtractAPI] Add support for blocks in declaration fragments (#…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
daniel-grumberg committed Nov 28, 2023
1 parent 205f530 commit 6b89fab
Show file tree
Hide file tree
Showing 7 changed files with 1,159 additions and 28 deletions.
6 changes: 6 additions & 0 deletions clang/include/clang/ExtractAPI/DeclarationFragments.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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>
Expand Down
176 changes: 148 additions & 28 deletions clang/lib/ExtractAPI/DeclarationFragments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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();
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -449,19 +485,30 @@ 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
// ```
// int nums[5];
// 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);
Expand Down Expand Up @@ -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(
Expand All @@ -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
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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(
Expand Down

0 comments on commit 6b89fab

Please sign in to comment.