diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/ExtractAPI/DeclarationFragments.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/ExtractAPI/DeclarationFragments.cpp | 799 |
1 files changed, 799 insertions, 0 deletions
diff --git a/contrib/llvm-project/clang/lib/ExtractAPI/DeclarationFragments.cpp b/contrib/llvm-project/clang/lib/ExtractAPI/DeclarationFragments.cpp new file mode 100644 index 000000000000..75d360a3ba16 --- /dev/null +++ b/contrib/llvm-project/clang/lib/ExtractAPI/DeclarationFragments.cpp @@ -0,0 +1,799 @@ +//===- 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 "TypedefUnderlyingTypeResolver.h" +#include "clang/Index/USRGeneration.h" +#include "llvm/ADT/StringSwitch.h" + +using namespace clang::extractapi; +using namespace llvm; + +DeclarationFragments &DeclarationFragments::appendSpace() { + 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() != ' ') { // avoid extra trailing spaces. + Last.Spelling.push_back(' '); + } + } else { + append(" ", FragmentKind::Text); + } + } + + 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); +} + +// 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); + break; + } + + case NestedNameSpecifier::NamespaceAlias: { + const NamespaceAliasDecl *Alias = NNS->getAsNamespaceAlias(); + SmallString<128> USR; + index::generateUSRForDecl(Alias, USR); + Fragments.append(Alias->getName(), + DeclarationFragments::FragmentKind::Identifier, USR); + 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. + LLVM_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; + + // Declaration fragments of a pointer type is the declaration fragments of + // the pointee type followed by a `*`, except for Objective-C `id` and `Class` + // pointers, where we do not spell out the `*`. + if (T->isPointerType() || + (T->isObjCObjectPointerType() && + !T->getAs<ObjCObjectPointerType>()->isObjCIdOrClassType())) { + return Fragments + .append(getFragmentsForType(T->getPointeeType(), Context, After)) + .append(" *", DeclarationFragments::FragmentKind::Text); + } + + // 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 ArrayType::Normal: + break; + case ArrayType::Static: + Fragments.append("static", DeclarationFragments::FragmentKind::Keyword); + break; + case ArrayType::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)); + } + + // 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 != ETK_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)); + } + + // Everything we care about has been handled now, reduce to the canonical + // unqualified base type. + QualType Base = T->getCanonicalTypeUnqualified(); + + // Render Objective-C `id`/`instancetype` as keywords. + if (T->isObjCIdType()) + return Fragments.append(Base.getAsString(), + DeclarationFragments::FragmentKind::Keyword); + + // If the type is a typedefed type, get the underlying TypedefNameDecl for a + // direct reference to the typedef instead of the wrapped type. + if (const TypedefType *TypedefTy = dyn_cast<TypedefType>(T)) { + const TypedefNameDecl *Decl = TypedefTy->getDecl(); + std::string USR = + TypedefUnderlyingTypeResolver(Context).getUSRForType(QualType(T, 0)); + return Fragments.append(Decl->getName(), + DeclarationFragments::FragmentKind::TypeIdentifier, + USR); + } + + // 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; + SmallString<128> TagUSR; + clang::index::generateUSRForDecl(Decl, TagUSR); + return Fragments.append(Decl->getName(), + DeclarationFragments::FragmentKind::TypeIdentifier, + TagUSR); + } + + // 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); + } + + // 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 (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::getFragmentsForVar(const VarDecl *Var) { + DeclarationFragments Fragments; + StorageClass SC = Var->getStorageClass(); + if (SC != SC_None) + Fragments + .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() + .append(Var->getName(), DeclarationFragments::FragmentKind::Identifier) + .append(std::move(After)); +} + +DeclarationFragments +DeclarationFragmentsBuilder::getFragmentsForParam(const ParmVarDecl *Param) { + DeclarationFragments Fragments, After; + + QualType T = Param->getTypeSourceInfo() + ? Param->getTypeSourceInfo()->getType() + : Param->getASTContext().getUnqualifiedObjCPointerType( + Param->getType()); + + DeclarationFragments TypeFragments = + getFragmentsForType(T, Param->getASTContext(), After); + + if (Param->isObjCMethodParameter()) + Fragments.append("(", DeclarationFragments::FragmentKind::Text) + .append(std::move(TypeFragments)) + .append(") ", DeclarationFragments::FragmentKind::Text); + else + Fragments.append(std::move(TypeFragments)).appendSpace(); + + return Fragments + .append(Param->getName(), + DeclarationFragments::FragmentKind::InternalParam) + .append(std::move(After)); +} + +DeclarationFragments +DeclarationFragmentsBuilder::getFragmentsForFunction(const FunctionDecl *Func) { + DeclarationFragments Fragments; + // FIXME: Handle template specialization + 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"); + } + // FIXME: Handle C++ function specifiers: constexpr, consteval, explicit, etc. + + // FIXME: Is `after` actually needed here? + DeclarationFragments After; + Fragments + .append(getFragmentsForType(Func->getReturnType(), Func->getASTContext(), + After)) + .appendSpace() + .append(Func->getName(), DeclarationFragments::FragmentKind::Identifier) + .append(std::move(After)); + + Fragments.append("(", DeclarationFragments::FragmentKind::Text); + for (unsigned i = 0, end = Func->getNumParams(); i != end; ++i) { + if (i) + Fragments.append(", ", DeclarationFragments::FragmentKind::Text); + Fragments.append(getFragmentsForParam(Func->getParamDecl(i))); + } + Fragments.append(")", DeclarationFragments::FragmentKind::Text); + + // FIXME: Handle exception specifiers: throw, noexcept + return Fragments; +} + +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.append(": ", DeclarationFragments::FragmentKind::Text) + .append( + getFragmentsForType(IntegerType, EnumDecl->getASTContext(), After)) + .append(std::move(After)); + + return Fragments; +} + +DeclarationFragments +DeclarationFragmentsBuilder::getFragmentsForField(const FieldDecl *Field) { + DeclarationFragments After; + return getFragmentsForType(Field->getType(), Field->getASTContext(), After) + .appendSpace() + .append(Field->getName(), DeclarationFragments::FragmentKind::Identifier) + .append(std::move(After)); +} + +DeclarationFragments +DeclarationFragmentsBuilder::getFragmentsForStruct(const RecordDecl *Record) { + if (const auto *TypedefNameDecl = Record->getTypedefNameForAnonDecl()) + return getFragmentsForTypedef(TypedefNameDecl); + + DeclarationFragments Fragments; + Fragments.append("struct", DeclarationFragments::FragmentKind::Keyword); + + if (!Record->getName().empty()) + Fragments.appendSpace().append( + Record->getName(), DeclarationFragments::FragmentKind::Identifier); + return Fragments; +} + +DeclarationFragments +DeclarationFragmentsBuilder::getFragmentsForMacro(StringRef Name, + const MacroDirective *MD) { + DeclarationFragments Fragments; + Fragments.append("#define", DeclarationFragments::FragmentKind::Keyword) + .appendSpace(); + Fragments.append(Name, DeclarationFragments::FragmentKind::Identifier); + + auto *MI = MD->getMacroInfo(); + + 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; + + SmallString<128> InterfaceUSR; + index::generateUSRForDecl(Category->getClassInterface(), InterfaceUSR); + + Fragments.append("@interface", DeclarationFragments::FragmentKind::Keyword) + .appendSpace() + .append(Category->getClassInterface()->getName(), + DeclarationFragments::FragmentKind::TypeIdentifier, InterfaceUSR) + .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); + } + + 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.append(";", DeclarationFragments::FragmentKind::Text); +} + +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->getPropertyAttributes(); + // 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); + } + + // Build the property type and name, and return the completed fragments. + return Fragments.appendSpace() + .append(getFragmentsForType(Property->getType(), + Property->getASTContext(), After)) + .append(Property->getName(), + DeclarationFragments::FragmentKind::Identifier) + .append(std::move(After)); +} + +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); + } + 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; +} + +template <typename FunctionT> +FunctionSignature +DeclarationFragmentsBuilder::getFunctionSignature(const FunctionT *Function) { + FunctionSignature Signature; + + DeclarationFragments ReturnType, After; + ReturnType + .append(getFragmentsForType(Function->getReturnType(), + Function->getASTContext(), After)) + .append(std::move(After)); + Signature.setReturnType(ReturnType); + + for (const auto *Param : Function->parameters()) + Signature.addParameter(Param->getName(), getFragmentsForParam(Param)); + + return Signature; +} + +// 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 (!Decl->getName().empty()) + Fragments.append(Decl->getName(), + 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; +} |