//===- 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/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(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()->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(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 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(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(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(); // 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 (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; } FunctionSignature DeclarationFragmentsBuilder::getFunctionSignature(const FunctionDecl *Func) { FunctionSignature Signature; for (const auto *Param : Func->parameters()) { StringRef Name = Param->getName(); DeclarationFragments Fragments = getFragmentsForParam(Param); Signature.addParameter(Name, Fragments); } DeclarationFragments After; DeclarationFragments Returns = getFragmentsForType(Func->getReturnType(), Func->getASTContext(), After) .append(std::move(After)); Signature.setReturnType(Returns); return Signature; } // 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; }