//===- 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 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()) { TL = QualifiedTL.getUnqualifiedLoc(); continue; } if (auto AttrTL = TL.getAs()) { TL = AttrTL.getModifiedLoc(); continue; } // Try to get the function prototype behind the block pointer type, // then we're done. if (auto BlockPtr = TL.getAs()) { TL = BlockPtr.getPointeeLoc().IgnoreParens(); Block = TL.getAs(); BlockProto = TL.getAs(); } 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(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; // 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(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(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()->isObjCIdOrClassType()) { Fragments.append(getFragmentsForType(T->getPointeeType(), Context, After)); // id is an qualified id type // id* is not an qualified id type if (!T->getAs()->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(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(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(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(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(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(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(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(Method)) { Name = Method->getNameAsString(); if (Constructor->isExplicit()) Fragments.append("explicit", DeclarationFragments::FragmentKind::Keyword) .appendSpace(); } else if (isa(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 ... DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForTemplateParameters( ArrayRef 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(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(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(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 // Foo; // // 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 TemplateArguments, ASTContext &Context, const std::optional> 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); OutStream.flush(); } } 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(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(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(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 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; 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(Decl) || isa(Decl)) Fragments.append(cast(Decl->getDeclContext())->getName(), DeclarationFragments::FragmentKind::Identifier); else if (isa(Decl)) { Fragments.append( cast(Decl)->getConversionType().getAsString(), DeclarationFragments::FragmentKind::Identifier); } else if (isa(Decl) && cast(Decl)->isOverloadedOperator()) { Fragments.append(Decl->getNameAsString(), DeclarationFragments::FragmentKind::Identifier); } 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; }