blob: 66c03863293c2fcaa85fe40f82a04b18844e16d4 [file] [log] [blame] [edit]
//===- ExtractAPI/DeclarationFragments.cpp ----------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file implements Declaration Fragments related classes.
///
//===----------------------------------------------------------------------===//
#include "clang/ExtractAPI/DeclarationFragments.h"
#include "clang/AST/ASTFwd.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/TemplateName.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
#include "clang/ExtractAPI/TypedefUnderlyingTypeResolver.h"
#include "clang/Index/USRGeneration.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include <optional>
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::appendUnduplicatedTextCharacter(char Character) {
if (!Fragments.empty()) {
Fragment &Last = Fragments.back();
if (Last.Kind == FragmentKind::Text) {
// Merge the extra space into the last fragment if the last fragment is
// also text.
if (Last.Spelling.back() != Character) { // avoid duplicates at end
Last.Spelling.push_back(Character);
}
} else {
append("", FragmentKind::Text);
Fragments.back().Spelling.push_back(Character);
}
}
return *this;
}
DeclarationFragments &DeclarationFragments::appendSpace() {
return appendUnduplicatedTextCharacter(' ');
}
DeclarationFragments &DeclarationFragments::appendSemicolon() {
return appendUnduplicatedTextCharacter(';');
}
DeclarationFragments &DeclarationFragments::removeTrailingSemicolon() {
if (Fragments.empty())
return *this;
Fragment &Last = Fragments.back();
if (Last.Kind == FragmentKind::Text && Last.Spelling.back() == ';')
Last.Spelling.pop_back();
return *this;
}
StringRef DeclarationFragments::getFragmentKindString(
DeclarationFragments::FragmentKind Kind) {
switch (Kind) {
case DeclarationFragments::FragmentKind::None:
return "none";
case DeclarationFragments::FragmentKind::Keyword:
return "keyword";
case DeclarationFragments::FragmentKind::Attribute:
return "attribute";
case DeclarationFragments::FragmentKind::NumberLiteral:
return "number";
case DeclarationFragments::FragmentKind::StringLiteral:
return "string";
case DeclarationFragments::FragmentKind::Identifier:
return "identifier";
case DeclarationFragments::FragmentKind::TypeIdentifier:
return "typeIdentifier";
case DeclarationFragments::FragmentKind::GenericParameter:
return "genericParameter";
case DeclarationFragments::FragmentKind::ExternalParam:
return "externalParam";
case DeclarationFragments::FragmentKind::InternalParam:
return "internalParam";
case DeclarationFragments::FragmentKind::Text:
return "text";
}
llvm_unreachable("Unhandled FragmentKind");
}
DeclarationFragments::FragmentKind
DeclarationFragments::parseFragmentKindFromString(StringRef S) {
return llvm::StringSwitch<FragmentKind>(S)
.Case("keyword", DeclarationFragments::FragmentKind::Keyword)
.Case("attribute", DeclarationFragments::FragmentKind::Attribute)
.Case("number", DeclarationFragments::FragmentKind::NumberLiteral)
.Case("string", DeclarationFragments::FragmentKind::StringLiteral)
.Case("identifier", DeclarationFragments::FragmentKind::Identifier)
.Case("typeIdentifier",
DeclarationFragments::FragmentKind::TypeIdentifier)
.Case("genericParameter",
DeclarationFragments::FragmentKind::GenericParameter)
.Case("internalParam", DeclarationFragments::FragmentKind::InternalParam)
.Case("externalParam", DeclarationFragments::FragmentKind::ExternalParam)
.Case("text", DeclarationFragments::FragmentKind::Text)
.Default(DeclarationFragments::FragmentKind::None);
}
DeclarationFragments DeclarationFragments::getExceptionSpecificationString(
ExceptionSpecificationType ExceptionSpec) {
DeclarationFragments Fragments;
switch (ExceptionSpec) {
case ExceptionSpecificationType::EST_None:
return Fragments;
case ExceptionSpecificationType::EST_DynamicNone:
return Fragments.append(" ", DeclarationFragments::FragmentKind::Text)
.append("throw", DeclarationFragments::FragmentKind::Keyword)
.append("(", DeclarationFragments::FragmentKind::Text)
.append(")", DeclarationFragments::FragmentKind::Text);
case ExceptionSpecificationType::EST_Dynamic:
// FIXME: throw(int), get types of inner expression
return Fragments;
case ExceptionSpecificationType::EST_BasicNoexcept:
return Fragments.append(" ", DeclarationFragments::FragmentKind::Text)
.append("noexcept", DeclarationFragments::FragmentKind::Keyword);
case ExceptionSpecificationType::EST_DependentNoexcept:
// FIXME: throw(conditional-expression), get expression
break;
case ExceptionSpecificationType::EST_NoexceptFalse:
return Fragments.append(" ", DeclarationFragments::FragmentKind::Text)
.append("noexcept", DeclarationFragments::FragmentKind::Keyword)
.append("(", DeclarationFragments::FragmentKind::Text)
.append("false", DeclarationFragments::FragmentKind::Keyword)
.append(")", DeclarationFragments::FragmentKind::Text);
case ExceptionSpecificationType::EST_NoexceptTrue:
return Fragments.append(" ", DeclarationFragments::FragmentKind::Text)
.append("noexcept", DeclarationFragments::FragmentKind::Keyword)
.append("(", DeclarationFragments::FragmentKind::Text)
.append("true", DeclarationFragments::FragmentKind::Keyword)
.append(")", DeclarationFragments::FragmentKind::Text);
default:
return Fragments;
}
llvm_unreachable("Unhandled exception specification");
}
DeclarationFragments
DeclarationFragments::getStructureTypeFragment(const RecordDecl *Record) {
DeclarationFragments Fragments;
if (Record->isStruct())
Fragments.append("struct", DeclarationFragments::FragmentKind::Keyword);
else if (Record->isUnion())
Fragments.append("union", DeclarationFragments::FragmentKind::Keyword);
else
Fragments.append("class", DeclarationFragments::FragmentKind::Keyword);
return Fragments;
}
// NNS stores C++ nested name specifiers, which are prefixes to qualified names.
// Build declaration fragments for NNS recursively so that we have the USR for
// every part in a qualified name, and also leaves the actual underlying type
// cleaner for its own fragment.
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForNNS(const NestedNameSpecifier *NNS,
ASTContext &Context,
DeclarationFragments &After) {
DeclarationFragments Fragments;
if (NNS->getPrefix())
Fragments.append(getFragmentsForNNS(NNS->getPrefix(), Context, After));
switch (NNS->getKind()) {
case NestedNameSpecifier::Identifier:
Fragments.append(NNS->getAsIdentifier()->getName(),
DeclarationFragments::FragmentKind::Identifier);
break;
case NestedNameSpecifier::Namespace: {
const NamespaceDecl *NS = NNS->getAsNamespace();
if (NS->isAnonymousNamespace())
return Fragments;
SmallString<128> USR;
index::generateUSRForDecl(NS, USR);
Fragments.append(NS->getName(),
DeclarationFragments::FragmentKind::Identifier, USR, NS);
break;
}
case NestedNameSpecifier::NamespaceAlias: {
const NamespaceAliasDecl *Alias = NNS->getAsNamespaceAlias();
SmallString<128> USR;
index::generateUSRForDecl(Alias, USR);
Fragments.append(Alias->getName(),
DeclarationFragments::FragmentKind::Identifier, USR,
Alias);
break;
}
case NestedNameSpecifier::Global:
// The global specifier `::` at the beginning. No stored value.
break;
case NestedNameSpecifier::Super:
// Microsoft's `__super` specifier.
Fragments.append("__super", DeclarationFragments::FragmentKind::Keyword);
break;
case NestedNameSpecifier::TypeSpecWithTemplate:
// A type prefixed by the `template` keyword.
Fragments.append("template", DeclarationFragments::FragmentKind::Keyword);
Fragments.appendSpace();
// Fallthrough after adding the keyword to handle the actual type.
[[fallthrough]];
case NestedNameSpecifier::TypeSpec: {
const Type *T = NNS->getAsType();
// FIXME: Handle C++ template specialization type
Fragments.append(getFragmentsForType(T, Context, After));
break;
}
}
// Add the separator text `::` for this segment.
return Fragments.append("::", DeclarationFragments::FragmentKind::Text);
}
// Recursively build the declaration fragments for an underlying `Type` with
// qualifiers removed.
DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(
const Type *T, ASTContext &Context, DeclarationFragments &After) {
assert(T && "invalid type");
DeclarationFragments Fragments;
if (const MacroQualifiedType *MQT = dyn_cast<MacroQualifiedType>(T)) {
Fragments.append(
getFragmentsForType(MQT->getUnderlyingType(), Context, After));
return Fragments;
}
if (const AttributedType *AT = dyn_cast<AttributedType>(T)) {
// FIXME: Serialize Attributes correctly
Fragments.append(
getFragmentsForType(AT->getModifiedType(), Context, After));
return 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())
return Fragments
.append(getFragmentsForType(T->getPointeeType(), Context, After))
.append(" *", DeclarationFragments::FragmentKind::Text);
// For Objective-C `id` and `Class` pointers
// we do not spell out the `*`.
if (T->isObjCObjectPointerType() &&
!T->getAs<ObjCObjectPointerType>()->isObjCIdOrClassType()) {
Fragments.append(getFragmentsForType(T->getPointeeType(), Context, After));
// id<protocol> is an qualified id type
// id<protocol>* is not an qualified id type
if (!T->getAs<ObjCObjectPointerType>()->isObjCQualifiedIdType()) {
Fragments.append(" *", DeclarationFragments::FragmentKind::Text);
}
return Fragments;
}
// Declaration fragments of a lvalue reference type is the declaration
// fragments of the underlying type followed by a `&`.
if (const LValueReferenceType *LRT = dyn_cast<LValueReferenceType>(T))
return Fragments
.append(
getFragmentsForType(LRT->getPointeeTypeAsWritten(), Context, After))
.append(" &", DeclarationFragments::FragmentKind::Text);
// Declaration fragments of a rvalue reference type is the declaration
// fragments of the underlying type followed by a `&&`.
if (const RValueReferenceType *RRT = dyn_cast<RValueReferenceType>(T))
return Fragments
.append(
getFragmentsForType(RRT->getPointeeTypeAsWritten(), Context, After))
.append(" &&", DeclarationFragments::FragmentKind::Text);
// Declaration fragments of an array-typed variable have two parts:
// 1. the element type of the array that appears before the variable name;
// 2. array brackets `[(0-9)?]` that appear after the variable name.
if (const ArrayType *AT = T->getAsArrayTypeUnsafe()) {
// Build the "after" part first because the inner element type might also
// be an array-type. For example `int matrix[3][4]` which has a type of
// "(array 3 of (array 4 of ints))."
// Push the array size part first to make sure they are in the right order.
After.append("[", DeclarationFragments::FragmentKind::Text);
switch (AT->getSizeModifier()) {
case ArraySizeModifier::Normal:
break;
case ArraySizeModifier::Static:
Fragments.append("static", DeclarationFragments::FragmentKind::Keyword);
break;
case ArraySizeModifier::Star:
Fragments.append("*", DeclarationFragments::FragmentKind::Text);
break;
}
if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(AT)) {
// FIXME: right now this would evaluate any expressions/macros written in
// the original source to concrete values. For example
// `int nums[MAX]` -> `int nums[100]`
// `char *str[5 + 1]` -> `char *str[6]`
SmallString<128> Size;
CAT->getSize().toStringUnsigned(Size);
After.append(Size, DeclarationFragments::FragmentKind::NumberLiteral);
}
After.append("]", DeclarationFragments::FragmentKind::Text);
return Fragments.append(
getFragmentsForType(AT->getElementType(), Context, After));
}
if (const TemplateSpecializationType *TemplSpecTy =
dyn_cast<TemplateSpecializationType>(T)) {
const auto TemplName = TemplSpecTy->getTemplateName();
std::string Str;
raw_string_ostream Stream(Str);
TemplName.print(Stream, Context.getPrintingPolicy(),
TemplateName::Qualified::AsWritten);
SmallString<64> USR("");
if (const auto *TemplDecl = TemplName.getAsTemplateDecl())
index::generateUSRForDecl(TemplDecl, USR);
return Fragments
.append(Str, DeclarationFragments::FragmentKind::TypeIdentifier, USR)
.append("<", DeclarationFragments::FragmentKind::Text)
.append(getFragmentsForTemplateArguments(
TemplSpecTy->template_arguments(), Context, std::nullopt))
.append(">", DeclarationFragments::FragmentKind::Text);
}
// Everything we care about has been handled now, reduce to the canonical
// unqualified base type.
QualType Base = T->getCanonicalTypeUnqualified();
// If the base type is a TagType (struct/interface/union/class/enum), let's
// get the underlying Decl for better names and USRs.
if (const TagType *TagTy = dyn_cast<TagType>(Base)) {
const TagDecl *Decl = TagTy->getDecl();
// Anonymous decl, skip this fragment.
if (Decl->getName().empty())
return Fragments.append("{ ... }",
DeclarationFragments::FragmentKind::Text);
SmallString<128> TagUSR;
clang::index::generateUSRForDecl(Decl, TagUSR);
return Fragments.append(Decl->getName(),
DeclarationFragments::FragmentKind::TypeIdentifier,
TagUSR, Decl);
}
// If the base type is an ObjCInterfaceType, use the underlying
// ObjCInterfaceDecl for the true USR.
if (const auto *ObjCIT = dyn_cast<ObjCInterfaceType>(Base)) {
const auto *Decl = ObjCIT->getDecl();
SmallString<128> USR;
index::generateUSRForDecl(Decl, USR);
return Fragments.append(Decl->getName(),
DeclarationFragments::FragmentKind::TypeIdentifier,
USR, Decl);
}
// Default fragment builder for other kinds of types (BuiltinType etc.)
SmallString<128> USR;
clang::index::generateUSRForType(Base, Context, USR);
Fragments.append(Base.getAsString(),
DeclarationFragments::FragmentKind::TypeIdentifier, USR);
return Fragments;
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForQualifiers(const Qualifiers Quals) {
DeclarationFragments Fragments;
if (Quals.hasConst())
Fragments.append("const", DeclarationFragments::FragmentKind::Keyword);
if (Quals.hasVolatile())
Fragments.append("volatile", DeclarationFragments::FragmentKind::Keyword);
if (Quals.hasRestrict())
Fragments.append("restrict", DeclarationFragments::FragmentKind::Keyword);
return Fragments;
}
DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(
const QualType QT, ASTContext &Context, DeclarationFragments &After) {
assert(!QT.isNull() && "invalid type");
if (const ParenType *PT = dyn_cast<ParenType>(QT)) {
After.append(")", DeclarationFragments::FragmentKind::Text);
return getFragmentsForType(PT->getInnerType(), Context, After)
.append("(", DeclarationFragments::FragmentKind::Text);
}
const SplitQualType SQT = QT.split();
DeclarationFragments QualsFragments = getFragmentsForQualifiers(SQT.Quals),
TypeFragments =
getFragmentsForType(SQT.Ty, Context, After);
if (QT.getAsString() == "_Bool")
TypeFragments.replace("bool", 0);
if (QualsFragments.getFragments().empty())
return TypeFragments;
// Use east qualifier for pointer types
// For example:
// ```
// int * const
// ^---- ^----
// type qualifier
// ^-----------------
// const pointer to int
// ```
// should not be reconstructed as
// ```
// const int *
// ^---- ^--
// qualifier type
// ^---------------- ^
// pointer to const int
// ```
if (SQT.Ty->isAnyPointerType())
return TypeFragments.appendSpace().append(std::move(QualsFragments));
return QualsFragments.appendSpace().append(std::move(TypeFragments));
}
DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForNamespace(
const NamespaceDecl *Decl) {
DeclarationFragments Fragments;
Fragments.append("namespace", DeclarationFragments::FragmentKind::Keyword);
if (!Decl->isAnonymousNamespace())
Fragments.appendSpace().append(
Decl->getName(), DeclarationFragments::FragmentKind::Identifier);
return Fragments.appendSemicolon();
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForVar(const VarDecl *Var) {
DeclarationFragments Fragments;
if (Var->isConstexpr())
Fragments.append("constexpr", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
StorageClass SC = Var->getStorageClass();
if (SC != SC_None)
Fragments
.append(VarDecl::getStorageClassSpecifierString(SC),
DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
// Capture potential fragments that needs to be placed after the variable name
// ```
// int nums[5];
// char (*ptr_to_array)[6];
// ```
DeclarationFragments After;
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))
.appendSemicolon();
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForVarTemplate(const VarDecl *Var) {
DeclarationFragments Fragments;
if (Var->isConstexpr())
Fragments.append("constexpr", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
QualType T =
Var->getTypeSourceInfo()
? Var->getTypeSourceInfo()->getType()
: Var->getASTContext().getUnqualifiedObjCPointerType(Var->getType());
// Might be a member, so might be static.
if (Var->isStaticDataMember())
Fragments.append("static", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
DeclarationFragments After;
DeclarationFragments ArgumentFragment =
getFragmentsForType(T, Var->getASTContext(), After);
if (StringRef(ArgumentFragment.begin()->Spelling)
.starts_with("type-parameter")) {
std::string ProperArgName = T.getAsString();
ArgumentFragment.begin()->Spelling.swap(ProperArgName);
}
Fragments.append(std::move(ArgumentFragment))
.appendSpace()
.append(Var->getName(), DeclarationFragments::FragmentKind::Identifier)
.appendSemicolon();
return Fragments;
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForParam(const ParmVarDecl *Param) {
DeclarationFragments Fragments, After;
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));
if (StringRef(TypeFragments.begin()->Spelling)
.starts_with("type-parameter")) {
std::string ProperArgName = Param->getOriginalType().getAsString();
TypeFragments.begin()->Spelling.swap(ProperArgName);
}
if (Param->isObjCMethodParameter()) {
Fragments.append("(", DeclarationFragments::FragmentKind::Text)
.append(std::move(TypeFragments))
.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;
}
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
DeclarationFragmentsBuilder::getFragmentsForFunction(const FunctionDecl *Func) {
DeclarationFragments Fragments;
switch (Func->getStorageClass()) {
case SC_None:
case SC_PrivateExtern:
break;
case SC_Extern:
Fragments.append("extern", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
break;
case SC_Static:
Fragments.append("static", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
break;
case SC_Auto:
case SC_Register:
llvm_unreachable("invalid for functions");
}
if (Func->isConsteval()) // if consteval, it is also constexpr
Fragments.append("consteval", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
else if (Func->isConstexpr())
Fragments.append("constexpr", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
// FIXME: Is `after` actually needed here?
DeclarationFragments After;
auto ReturnValueFragment =
getFragmentsForType(Func->getReturnType(), Func->getASTContext(), After);
if (StringRef(ReturnValueFragment.begin()->Spelling)
.starts_with("type-parameter")) {
std::string ProperArgName = Func->getReturnType().getAsString();
ReturnValueFragment.begin()->Spelling.swap(ProperArgName);
}
Fragments.append(std::move(ReturnValueFragment))
.appendSpace()
.append(Func->getNameAsString(),
DeclarationFragments::FragmentKind::Identifier);
if (Func->getTemplateSpecializationInfo()) {
Fragments.append("<", DeclarationFragments::FragmentKind::Text);
for (unsigned i = 0, end = Func->getNumParams(); i != end; ++i) {
if (i)
Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
Fragments.append(
getFragmentsForType(Func->getParamDecl(i)->getType(),
Func->getParamDecl(i)->getASTContext(), After));
}
Fragments.append(">", DeclarationFragments::FragmentKind::Text);
}
Fragments.append(std::move(After));
Fragments.append("(", DeclarationFragments::FragmentKind::Text);
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(
Func->getExceptionSpecType()));
return Fragments.appendSemicolon();
}
DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForEnumConstant(
const EnumConstantDecl *EnumConstDecl) {
DeclarationFragments Fragments;
return Fragments.append(EnumConstDecl->getName(),
DeclarationFragments::FragmentKind::Identifier);
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForEnum(const EnumDecl *EnumDecl) {
if (const auto *TypedefNameDecl = EnumDecl->getTypedefNameForAnonDecl())
return getFragmentsForTypedef(TypedefNameDecl);
DeclarationFragments Fragments, After;
Fragments.append("enum", DeclarationFragments::FragmentKind::Keyword);
if (!EnumDecl->getName().empty())
Fragments.appendSpace().append(
EnumDecl->getName(), DeclarationFragments::FragmentKind::Identifier);
QualType IntegerType = EnumDecl->getIntegerType();
if (!IntegerType.isNull())
Fragments.appendSpace()
.append(": ", DeclarationFragments::FragmentKind::Text)
.append(
getFragmentsForType(IntegerType, EnumDecl->getASTContext(), After))
.append(std::move(After));
if (EnumDecl->getName().empty())
Fragments.appendSpace().append("{ ... }",
DeclarationFragments::FragmentKind::Text);
return Fragments.appendSemicolon();
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForField(const FieldDecl *Field) {
DeclarationFragments After;
DeclarationFragments Fragments;
if (Field->isMutable())
Fragments.append("mutable", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
return Fragments
.append(
getFragmentsForType(Field->getType(), Field->getASTContext(), After))
.appendSpace()
.append(Field->getName(), DeclarationFragments::FragmentKind::Identifier)
.append(std::move(After))
.appendSemicolon();
}
DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForRecordDecl(
const RecordDecl *Record) {
if (const auto *TypedefNameDecl = Record->getTypedefNameForAnonDecl())
return getFragmentsForTypedef(TypedefNameDecl);
DeclarationFragments Fragments;
if (Record->isUnion())
Fragments.append("union", DeclarationFragments::FragmentKind::Keyword);
else
Fragments.append("struct", DeclarationFragments::FragmentKind::Keyword);
Fragments.appendSpace();
if (!Record->getName().empty())
Fragments.append(Record->getName(),
DeclarationFragments::FragmentKind::Identifier);
else
Fragments.append("{ ... }", DeclarationFragments::FragmentKind::Text);
return Fragments.appendSemicolon();
}
DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForCXXClass(
const CXXRecordDecl *Record) {
if (const auto *TypedefNameDecl = Record->getTypedefNameForAnonDecl())
return getFragmentsForTypedef(TypedefNameDecl);
DeclarationFragments Fragments;
Fragments.append(DeclarationFragments::getStructureTypeFragment(Record));
if (!Record->getName().empty())
Fragments.appendSpace().append(
Record->getName(), DeclarationFragments::FragmentKind::Identifier);
return Fragments.appendSemicolon();
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForSpecialCXXMethod(
const CXXMethodDecl *Method) {
DeclarationFragments Fragments;
std::string Name;
if (const auto *Constructor = dyn_cast<CXXConstructorDecl>(Method)) {
Name = Method->getNameAsString();
if (Constructor->isExplicit())
Fragments.append("explicit", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
} else if (isa<CXXDestructorDecl>(Method))
Name = Method->getNameAsString();
DeclarationFragments After;
Fragments.append(Name, DeclarationFragments::FragmentKind::Identifier)
.append(std::move(After));
Fragments.append("(", DeclarationFragments::FragmentKind::Text);
for (unsigned i = 0, end = Method->getNumParams(); i != end; ++i) {
if (i)
Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
Fragments.append(getFragmentsForParam(Method->getParamDecl(i)));
}
Fragments.append(")", DeclarationFragments::FragmentKind::Text);
Fragments.append(DeclarationFragments::getExceptionSpecificationString(
Method->getExceptionSpecType()));
return Fragments.appendSemicolon();
}
DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForCXXMethod(
const CXXMethodDecl *Method) {
DeclarationFragments Fragments;
StringRef Name = Method->getName();
if (Method->isStatic())
Fragments.append("static", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
if (Method->isConstexpr())
Fragments.append("constexpr", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
if (Method->isVolatile())
Fragments.append("volatile", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
// Build return type
DeclarationFragments After;
Fragments
.append(getFragmentsForType(Method->getReturnType(),
Method->getASTContext(), After))
.appendSpace()
.append(Name, DeclarationFragments::FragmentKind::Identifier)
.append(std::move(After));
Fragments.append("(", DeclarationFragments::FragmentKind::Text);
for (unsigned i = 0, end = Method->getNumParams(); i != end; ++i) {
if (i)
Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
Fragments.append(getFragmentsForParam(Method->getParamDecl(i)));
}
Fragments.append(")", DeclarationFragments::FragmentKind::Text);
if (Method->isConst())
Fragments.appendSpace().append("const",
DeclarationFragments::FragmentKind::Keyword);
Fragments.append(DeclarationFragments::getExceptionSpecificationString(
Method->getExceptionSpecType()));
return Fragments.appendSemicolon();
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForConversionFunction(
const CXXConversionDecl *ConversionFunction) {
DeclarationFragments Fragments;
if (ConversionFunction->isExplicit())
Fragments.append("explicit", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
Fragments.append("operator", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
Fragments
.append(ConversionFunction->getConversionType().getAsString(),
DeclarationFragments::FragmentKind::TypeIdentifier)
.append("(", DeclarationFragments::FragmentKind::Text);
for (unsigned i = 0, end = ConversionFunction->getNumParams(); i != end;
++i) {
if (i)
Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
Fragments.append(getFragmentsForParam(ConversionFunction->getParamDecl(i)));
}
Fragments.append(")", DeclarationFragments::FragmentKind::Text);
if (ConversionFunction->isConst())
Fragments.appendSpace().append("const",
DeclarationFragments::FragmentKind::Keyword);
return Fragments.appendSemicolon();
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForOverloadedOperator(
const CXXMethodDecl *Method) {
DeclarationFragments Fragments;
// Build return type
DeclarationFragments After;
Fragments
.append(getFragmentsForType(Method->getReturnType(),
Method->getASTContext(), After))
.appendSpace()
.append(Method->getNameAsString(),
DeclarationFragments::FragmentKind::Identifier)
.append(std::move(After));
Fragments.append("(", DeclarationFragments::FragmentKind::Text);
for (unsigned i = 0, end = Method->getNumParams(); i != end; ++i) {
if (i)
Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
Fragments.append(getFragmentsForParam(Method->getParamDecl(i)));
}
Fragments.append(")", DeclarationFragments::FragmentKind::Text);
if (Method->isConst())
Fragments.appendSpace().append("const",
DeclarationFragments::FragmentKind::Keyword);
Fragments.append(DeclarationFragments::getExceptionSpecificationString(
Method->getExceptionSpecType()));
return Fragments.appendSemicolon();
}
// Get fragments for template parameters, e.g. T in tempalte<typename T> ...
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForTemplateParameters(
ArrayRef<NamedDecl *> ParameterArray) {
DeclarationFragments Fragments;
for (unsigned i = 0, end = ParameterArray.size(); i != end; ++i) {
if (i)
Fragments.append(",", DeclarationFragments::FragmentKind::Text)
.appendSpace();
if (const auto *TemplateParam =
dyn_cast<TemplateTypeParmDecl>(ParameterArray[i])) {
if (TemplateParam->hasTypeConstraint())
Fragments.append(TemplateParam->getTypeConstraint()
->getNamedConcept()
->getName()
.str(),
DeclarationFragments::FragmentKind::TypeIdentifier);
else if (TemplateParam->wasDeclaredWithTypename())
Fragments.append("typename",
DeclarationFragments::FragmentKind::Keyword);
else
Fragments.append("class", DeclarationFragments::FragmentKind::Keyword);
if (TemplateParam->isParameterPack())
Fragments.append("...", DeclarationFragments::FragmentKind::Text);
if (!TemplateParam->getName().empty())
Fragments.appendSpace().append(
TemplateParam->getName(),
DeclarationFragments::FragmentKind::GenericParameter);
if (TemplateParam->hasDefaultArgument()) {
const auto Default = TemplateParam->getDefaultArgument();
Fragments.append(" = ", DeclarationFragments::FragmentKind::Text)
.append(getFragmentsForTemplateArguments(
{Default.getArgument()}, TemplateParam->getASTContext(),
{Default}));
}
} else if (const auto *NTP =
dyn_cast<NonTypeTemplateParmDecl>(ParameterArray[i])) {
DeclarationFragments After;
const auto TyFragments =
getFragmentsForType(NTP->getType(), NTP->getASTContext(), After);
Fragments.append(std::move(TyFragments)).append(std::move(After));
if (NTP->isParameterPack())
Fragments.append("...", DeclarationFragments::FragmentKind::Text);
if (!NTP->getName().empty())
Fragments.appendSpace().append(
NTP->getName(),
DeclarationFragments::FragmentKind::GenericParameter);
if (NTP->hasDefaultArgument()) {
SmallString<8> ExprStr;
raw_svector_ostream Output(ExprStr);
NTP->getDefaultArgument().getArgument().print(
NTP->getASTContext().getPrintingPolicy(), Output,
/*IncludeType=*/false);
Fragments.append(" = ", DeclarationFragments::FragmentKind::Text)
.append(ExprStr, DeclarationFragments::FragmentKind::Text);
}
} else if (const auto *TTP =
dyn_cast<TemplateTemplateParmDecl>(ParameterArray[i])) {
Fragments.append("template", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append("<", DeclarationFragments::FragmentKind::Text)
.append(getFragmentsForTemplateParameters(
TTP->getTemplateParameters()->asArray()))
.append(">", DeclarationFragments::FragmentKind::Text)
.appendSpace()
.append(TTP->wasDeclaredWithTypename() ? "typename" : "class",
DeclarationFragments::FragmentKind::Keyword);
if (TTP->isParameterPack())
Fragments.append("...", DeclarationFragments::FragmentKind::Text);
if (!TTP->getName().empty())
Fragments.appendSpace().append(
TTP->getName(),
DeclarationFragments::FragmentKind::GenericParameter);
if (TTP->hasDefaultArgument()) {
const auto Default = TTP->getDefaultArgument();
Fragments.append(" = ", DeclarationFragments::FragmentKind::Text)
.append(getFragmentsForTemplateArguments(
{Default.getArgument()}, TTP->getASTContext(), {Default}));
}
}
}
return Fragments;
}
// Get fragments for template arguments, e.g. int in template<typename T>
// Foo<int>;
//
// Note: TemplateParameters is only necessary if the Decl is a
// PartialSpecialization, where we need the parameters to deduce the name of the
// generic arguments.
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForTemplateArguments(
const ArrayRef<TemplateArgument> TemplateArguments, ASTContext &Context,
const std::optional<ArrayRef<TemplateArgumentLoc>> TemplateArgumentLocs) {
DeclarationFragments Fragments;
for (unsigned i = 0, end = TemplateArguments.size(); i != end; ++i) {
if (i)
Fragments.append(",", DeclarationFragments::FragmentKind::Text)
.appendSpace();
const auto &CTA = TemplateArguments[i];
switch (CTA.getKind()) {
case TemplateArgument::Type: {
DeclarationFragments After;
DeclarationFragments ArgumentFragment =
getFragmentsForType(CTA.getAsType(), Context, After);
if (StringRef(ArgumentFragment.begin()->Spelling)
.starts_with("type-parameter")) {
if (TemplateArgumentLocs.has_value() &&
TemplateArgumentLocs->size() > i) {
std::string ProperArgName = TemplateArgumentLocs.value()[i]
.getTypeSourceInfo()
->getType()
.getAsString();
ArgumentFragment.begin()->Spelling.swap(ProperArgName);
} else {
auto &Spelling = ArgumentFragment.begin()->Spelling;
Spelling.clear();
raw_string_ostream OutStream(Spelling);
CTA.print(Context.getPrintingPolicy(), OutStream, false);
}
}
Fragments.append(std::move(ArgumentFragment));
break;
}
case TemplateArgument::Declaration: {
const auto *VD = CTA.getAsDecl();
SmallString<128> USR;
index::generateUSRForDecl(VD, USR);
Fragments.append(VD->getNameAsString(),
DeclarationFragments::FragmentKind::Identifier, USR);
break;
}
case TemplateArgument::NullPtr:
Fragments.append("nullptr", DeclarationFragments::FragmentKind::Keyword);
break;
case TemplateArgument::Integral: {
SmallString<4> Str;
CTA.getAsIntegral().toString(Str);
Fragments.append(Str, DeclarationFragments::FragmentKind::Text);
break;
}
case TemplateArgument::StructuralValue: {
const auto SVTy = CTA.getStructuralValueType();
Fragments.append(CTA.getAsStructuralValue().getAsString(Context, SVTy),
DeclarationFragments::FragmentKind::Text);
break;
}
case TemplateArgument::TemplateExpansion:
case TemplateArgument::Template: {
std::string Str;
raw_string_ostream Stream(Str);
CTA.getAsTemplate().print(Stream, Context.getPrintingPolicy());
SmallString<64> USR("");
if (const auto *TemplDecl =
CTA.getAsTemplateOrTemplatePattern().getAsTemplateDecl())
index::generateUSRForDecl(TemplDecl, USR);
Fragments.append(Str, DeclarationFragments::FragmentKind::TypeIdentifier,
USR);
if (CTA.getKind() == TemplateArgument::TemplateExpansion)
Fragments.append("...", DeclarationFragments::FragmentKind::Text);
break;
}
case TemplateArgument::Pack:
Fragments.append("<", DeclarationFragments::FragmentKind::Text)
.append(getFragmentsForTemplateArguments(CTA.pack_elements(), Context,
{}))
.append(">", DeclarationFragments::FragmentKind::Text);
break;
case TemplateArgument::Expression: {
SmallString<8> ExprStr;
raw_svector_ostream Output(ExprStr);
CTA.getAsExpr()->printPretty(Output, nullptr,
Context.getPrintingPolicy());
Fragments.append(ExprStr, DeclarationFragments::FragmentKind::Text);
break;
}
case TemplateArgument::Null:
break;
}
}
return Fragments;
}
DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForConcept(
const ConceptDecl *Concept) {
DeclarationFragments Fragments;
return Fragments
.append("template", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append("<", DeclarationFragments::FragmentKind::Text)
.append(getFragmentsForTemplateParameters(
Concept->getTemplateParameters()->asArray()))
.append("> ", DeclarationFragments::FragmentKind::Text)
.appendSpace()
.append("concept", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append(Concept->getName().str(),
DeclarationFragments::FragmentKind::Identifier)
.appendSemicolon();
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForRedeclarableTemplate(
const RedeclarableTemplateDecl *RedeclarableTemplate) {
DeclarationFragments Fragments;
Fragments.append("template", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append("<", DeclarationFragments::FragmentKind::Text)
.append(getFragmentsForTemplateParameters(
RedeclarableTemplate->getTemplateParameters()->asArray()))
.append(">", DeclarationFragments::FragmentKind::Text)
.appendSpace();
if (isa<TypeAliasTemplateDecl>(RedeclarableTemplate))
Fragments.appendSpace()
.append("using", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append(RedeclarableTemplate->getName(),
DeclarationFragments::FragmentKind::Identifier);
// the templated records will be resposbible for injecting their templates
return Fragments.appendSpace();
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForClassTemplateSpecialization(
const ClassTemplateSpecializationDecl *Decl) {
DeclarationFragments Fragments;
return Fragments
.append("template", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append("<", DeclarationFragments::FragmentKind::Text)
.append(">", DeclarationFragments::FragmentKind::Text)
.appendSpace()
.append(DeclarationFragmentsBuilder::getFragmentsForCXXClass(
cast<CXXRecordDecl>(Decl)))
.pop_back() // there is an extra semicolon now
.append("<", DeclarationFragments::FragmentKind::Text)
.append(getFragmentsForTemplateArguments(
Decl->getTemplateArgs().asArray(), Decl->getASTContext(),
Decl->getTemplateArgsAsWritten()->arguments()))
.append(">", DeclarationFragments::FragmentKind::Text)
.appendSemicolon();
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForClassTemplatePartialSpecialization(
const ClassTemplatePartialSpecializationDecl *Decl) {
DeclarationFragments Fragments;
return Fragments
.append("template", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append("<", DeclarationFragments::FragmentKind::Text)
.append(getFragmentsForTemplateParameters(
Decl->getTemplateParameters()->asArray()))
.append(">", DeclarationFragments::FragmentKind::Text)
.appendSpace()
.append(DeclarationFragmentsBuilder::getFragmentsForCXXClass(
cast<CXXRecordDecl>(Decl)))
.pop_back() // there is an extra semicolon now
.append("<", DeclarationFragments::FragmentKind::Text)
.append(getFragmentsForTemplateArguments(
Decl->getTemplateArgs().asArray(), Decl->getASTContext(),
Decl->getTemplateArgsAsWritten()->arguments()))
.append(">", DeclarationFragments::FragmentKind::Text)
.appendSemicolon();
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForVarTemplateSpecialization(
const VarTemplateSpecializationDecl *Decl) {
DeclarationFragments Fragments;
return Fragments
.append("template", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append("<", DeclarationFragments::FragmentKind::Text)
.append(">", DeclarationFragments::FragmentKind::Text)
.appendSpace()
.append(DeclarationFragmentsBuilder::getFragmentsForVarTemplate(Decl))
.pop_back() // there is an extra semicolon now
.append("<", DeclarationFragments::FragmentKind::Text)
.append(getFragmentsForTemplateArguments(
Decl->getTemplateArgs().asArray(), Decl->getASTContext(),
Decl->getTemplateArgsAsWritten()->arguments()))
.append(">", DeclarationFragments::FragmentKind::Text)
.appendSemicolon();
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForVarTemplatePartialSpecialization(
const VarTemplatePartialSpecializationDecl *Decl) {
DeclarationFragments Fragments;
return Fragments
.append("template", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append("<", DeclarationFragments::FragmentKind::Text)
// Partial specs may have new params.
.append(getFragmentsForTemplateParameters(
Decl->getTemplateParameters()->asArray()))
.append(">", DeclarationFragments::FragmentKind::Text)
.appendSpace()
.append(DeclarationFragmentsBuilder::getFragmentsForVarTemplate(Decl))
.pop_back() // there is an extra semicolon now
.append("<", DeclarationFragments::FragmentKind::Text)
.append(getFragmentsForTemplateArguments(
Decl->getTemplateArgs().asArray(), Decl->getASTContext(),
Decl->getTemplateArgsAsWritten()->arguments()))
.append(">", DeclarationFragments::FragmentKind::Text)
.appendSemicolon();
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForFunctionTemplate(
const FunctionTemplateDecl *Decl) {
DeclarationFragments Fragments;
return Fragments
.append("template", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append("<", DeclarationFragments::FragmentKind::Text)
// Partial specs may have new params.
.append(getFragmentsForTemplateParameters(
Decl->getTemplateParameters()->asArray()))
.append(">", DeclarationFragments::FragmentKind::Text)
.appendSpace()
.append(DeclarationFragmentsBuilder::getFragmentsForFunction(
Decl->getAsFunction()));
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForFunctionTemplateSpecialization(
const FunctionDecl *Decl) {
DeclarationFragments Fragments;
return Fragments
.append("template", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append("<>", DeclarationFragments::FragmentKind::Text)
.appendSpace()
.append(DeclarationFragmentsBuilder::getFragmentsForFunction(Decl));
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForMacro(StringRef Name,
const MacroInfo *MI) {
DeclarationFragments Fragments;
Fragments.append("#define", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
Fragments.append(Name, DeclarationFragments::FragmentKind::Identifier);
if (MI->isFunctionLike()) {
Fragments.append("(", DeclarationFragments::FragmentKind::Text);
unsigned numParameters = MI->getNumParams();
if (MI->isC99Varargs())
--numParameters;
for (unsigned i = 0; i < numParameters; ++i) {
if (i)
Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
Fragments.append(MI->params()[i]->getName(),
DeclarationFragments::FragmentKind::InternalParam);
}
if (MI->isVariadic()) {
if (numParameters && MI->isC99Varargs())
Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
Fragments.append("...", DeclarationFragments::FragmentKind::Text);
}
Fragments.append(")", DeclarationFragments::FragmentKind::Text);
}
return Fragments;
}
DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCCategory(
const ObjCCategoryDecl *Category) {
DeclarationFragments Fragments;
auto *Interface = Category->getClassInterface();
SmallString<128> InterfaceUSR;
index::generateUSRForDecl(Interface, InterfaceUSR);
Fragments.append("@interface", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append(Interface->getName(),
DeclarationFragments::FragmentKind::TypeIdentifier, InterfaceUSR,
Interface)
.append(" (", DeclarationFragments::FragmentKind::Text)
.append(Category->getName(),
DeclarationFragments::FragmentKind::Identifier)
.append(")", DeclarationFragments::FragmentKind::Text);
return Fragments;
}
DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCInterface(
const ObjCInterfaceDecl *Interface) {
DeclarationFragments Fragments;
// Build the base of the Objective-C interface declaration.
Fragments.append("@interface", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append(Interface->getName(),
DeclarationFragments::FragmentKind::Identifier);
// Build the inheritance part of the declaration.
if (const ObjCInterfaceDecl *SuperClass = Interface->getSuperClass()) {
SmallString<128> SuperUSR;
index::generateUSRForDecl(SuperClass, SuperUSR);
Fragments.append(" : ", DeclarationFragments::FragmentKind::Text)
.append(SuperClass->getName(),
DeclarationFragments::FragmentKind::TypeIdentifier, SuperUSR,
SuperClass);
}
return Fragments;
}
DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCMethod(
const ObjCMethodDecl *Method) {
DeclarationFragments Fragments, After;
// Build the instance/class method indicator.
if (Method->isClassMethod())
Fragments.append("+ ", DeclarationFragments::FragmentKind::Text);
else if (Method->isInstanceMethod())
Fragments.append("- ", DeclarationFragments::FragmentKind::Text);
// Build the return type.
Fragments.append("(", DeclarationFragments::FragmentKind::Text)
.append(getFragmentsForType(Method->getReturnType(),
Method->getASTContext(), After))
.append(std::move(After))
.append(")", DeclarationFragments::FragmentKind::Text);
// Build the selector part.
Selector Selector = Method->getSelector();
if (Selector.getNumArgs() == 0)
// For Objective-C methods that don't take arguments, the first (and only)
// slot of the selector is the method name.
Fragments.appendSpace().append(
Selector.getNameForSlot(0),
DeclarationFragments::FragmentKind::Identifier);
// For Objective-C methods that take arguments, build the selector slots.
for (unsigned i = 0, end = Method->param_size(); i != end; ++i) {
// Objective-C method selector parts are considered as identifiers instead
// of "external parameters" as in Swift. This is because Objective-C method
// symbols are referenced with the entire selector, instead of just the
// method name in Swift.
SmallString<32> ParamID(Selector.getNameForSlot(i));
ParamID.append(":");
Fragments.appendSpace().append(
ParamID, DeclarationFragments::FragmentKind::Identifier);
// Build the internal parameter.
const ParmVarDecl *Param = Method->getParamDecl(i);
Fragments.append(getFragmentsForParam(Param));
}
return Fragments.appendSemicolon();
}
DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProperty(
const ObjCPropertyDecl *Property) {
DeclarationFragments Fragments, After;
// Build the Objective-C property keyword.
Fragments.append("@property", DeclarationFragments::FragmentKind::Keyword);
const auto Attributes = Property->getPropertyAttributesAsWritten();
// Build the attributes if there is any associated with the property.
if (Attributes != ObjCPropertyAttribute::kind_noattr) {
// No leading comma for the first attribute.
bool First = true;
Fragments.append(" (", DeclarationFragments::FragmentKind::Text);
// Helper function to render the attribute.
auto RenderAttribute =
[&](ObjCPropertyAttribute::Kind Kind, StringRef Spelling,
StringRef Arg = "",
DeclarationFragments::FragmentKind ArgKind =
DeclarationFragments::FragmentKind::Identifier) {
// Check if the `Kind` attribute is set for this property.
if ((Attributes & Kind) && !Spelling.empty()) {
// Add a leading comma if this is not the first attribute rendered.
if (!First)
Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
// Render the spelling of this attribute `Kind` as a keyword.
Fragments.append(Spelling,
DeclarationFragments::FragmentKind::Keyword);
// If this attribute takes in arguments (e.g. `getter=getterName`),
// render the arguments.
if (!Arg.empty())
Fragments.append("=", DeclarationFragments::FragmentKind::Text)
.append(Arg, ArgKind);
First = false;
}
};
// Go through all possible Objective-C property attributes and render set
// ones.
RenderAttribute(ObjCPropertyAttribute::kind_class, "class");
RenderAttribute(ObjCPropertyAttribute::kind_direct, "direct");
RenderAttribute(ObjCPropertyAttribute::kind_nonatomic, "nonatomic");
RenderAttribute(ObjCPropertyAttribute::kind_atomic, "atomic");
RenderAttribute(ObjCPropertyAttribute::kind_assign, "assign");
RenderAttribute(ObjCPropertyAttribute::kind_retain, "retain");
RenderAttribute(ObjCPropertyAttribute::kind_strong, "strong");
RenderAttribute(ObjCPropertyAttribute::kind_copy, "copy");
RenderAttribute(ObjCPropertyAttribute::kind_weak, "weak");
RenderAttribute(ObjCPropertyAttribute::kind_unsafe_unretained,
"unsafe_unretained");
RenderAttribute(ObjCPropertyAttribute::kind_readwrite, "readwrite");
RenderAttribute(ObjCPropertyAttribute::kind_readonly, "readonly");
RenderAttribute(ObjCPropertyAttribute::kind_getter, "getter",
Property->getGetterName().getAsString());
RenderAttribute(ObjCPropertyAttribute::kind_setter, "setter",
Property->getSetterName().getAsString());
// Render nullability attributes.
if (Attributes & ObjCPropertyAttribute::kind_nullability) {
QualType Type = Property->getType();
if (const auto Nullability =
AttributedType::stripOuterNullability(Type)) {
if (!First)
Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
if (*Nullability == NullabilityKind::Unspecified &&
(Attributes & ObjCPropertyAttribute::kind_null_resettable))
Fragments.append("null_resettable",
DeclarationFragments::FragmentKind::Keyword);
else
Fragments.append(
getNullabilitySpelling(*Nullability, /*isContextSensitive=*/true),
DeclarationFragments::FragmentKind::Keyword);
First = false;
}
}
Fragments.append(")", DeclarationFragments::FragmentKind::Text);
}
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))
.appendSemicolon();
}
DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(
const ObjCProtocolDecl *Protocol) {
DeclarationFragments Fragments;
// Build basic protocol declaration.
Fragments.append("@protocol", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append(Protocol->getName(),
DeclarationFragments::FragmentKind::Identifier);
// If this protocol conforms to other protocols, build the conformance list.
if (!Protocol->protocols().empty()) {
Fragments.append(" <", DeclarationFragments::FragmentKind::Text);
for (ObjCProtocolDecl::protocol_iterator It = Protocol->protocol_begin();
It != Protocol->protocol_end(); It++) {
// Add a leading comma if this is not the first protocol rendered.
if (It != Protocol->protocol_begin())
Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
SmallString<128> USR;
index::generateUSRForDecl(*It, USR);
Fragments.append((*It)->getName(),
DeclarationFragments::FragmentKind::TypeIdentifier, USR,
*It);
}
Fragments.append(">", DeclarationFragments::FragmentKind::Text);
}
return Fragments;
}
DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForTypedef(
const TypedefNameDecl *Decl) {
DeclarationFragments Fragments, After;
Fragments.append("typedef", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append(getFragmentsForType(Decl->getUnderlyingType(),
Decl->getASTContext(), After))
.append(std::move(After))
.appendSpace()
.append(Decl->getName(), DeclarationFragments::FragmentKind::Identifier);
return Fragments.appendSemicolon();
}
// Instantiate template for FunctionDecl.
template FunctionSignature
DeclarationFragmentsBuilder::getFunctionSignature(const FunctionDecl *);
// Instantiate template for ObjCMethodDecl.
template FunctionSignature
DeclarationFragmentsBuilder::getFunctionSignature(const ObjCMethodDecl *);
// Subheading of a symbol defaults to its name.
DeclarationFragments
DeclarationFragmentsBuilder::getSubHeading(const NamedDecl *Decl) {
DeclarationFragments Fragments;
if (isa<CXXConstructorDecl>(Decl) || isa<CXXDestructorDecl>(Decl))
Fragments.append(cast<CXXRecordDecl>(Decl->getDeclContext())->getName(),
DeclarationFragments::FragmentKind::Identifier);
else if (isa<CXXConversionDecl>(Decl)) {
Fragments.append(
cast<CXXConversionDecl>(Decl)->getConversionType().getAsString(),
DeclarationFragments::FragmentKind::Identifier);
} else if (isa<CXXMethodDecl>(Decl) &&
cast<CXXMethodDecl>(Decl)->isOverloadedOperator()) {
Fragments.append(Decl->getNameAsString(),
DeclarationFragments::FragmentKind::Identifier);
} else if (isa<TagDecl>(Decl) &&
cast<TagDecl>(Decl)->getTypedefNameForAnonDecl()) {
return getSubHeading(cast<TagDecl>(Decl)->getTypedefNameForAnonDecl());
} else if (Decl->getIdentifier()) {
Fragments.append(Decl->getName(),
DeclarationFragments::FragmentKind::Identifier);
} else
Fragments.append(Decl->getDeclName().getAsString(),
DeclarationFragments::FragmentKind::Identifier);
return Fragments;
}
// Subheading of an Objective-C method is a `+` or `-` sign indicating whether
// it's a class method or an instance method, followed by the selector name.
DeclarationFragments
DeclarationFragmentsBuilder::getSubHeading(const ObjCMethodDecl *Method) {
DeclarationFragments Fragments;
if (Method->isClassMethod())
Fragments.append("+ ", DeclarationFragments::FragmentKind::Text);
else if (Method->isInstanceMethod())
Fragments.append("- ", DeclarationFragments::FragmentKind::Text);
return Fragments.append(Method->getNameAsString(),
DeclarationFragments::FragmentKind::Identifier);
}
// Subheading of a symbol defaults to its name.
DeclarationFragments
DeclarationFragmentsBuilder::getSubHeadingForMacro(StringRef Name) {
DeclarationFragments Fragments;
Fragments.append(Name, DeclarationFragments::FragmentKind::Identifier);
return Fragments;
}