[clang-tools-extra] [clangd] Support go-to-definition on type hints. The core part (PR #86629)

Younan Zhang via cfe-commits cfe-commits at lists.llvm.org
Mon Jul 8 02:06:21 PDT 2024


================
@@ -372,6 +374,292 @@ maybeDropCxxExplicitObjectParameters(ArrayRef<const ParmVarDecl *> Params) {
   return Params;
 }
 
+class TypeInlayHintLabelPartBuilder
+    : public TypeVisitor<TypeInlayHintLabelPartBuilder> {
+  QualType CurrentType;
+  NestedNameSpecifier *CurrentNestedNameSpecifier;
+  ASTContext &Context;
+  StringRef MainFilePath;
+  const PrintingPolicy &PP;
+  SourceManager &SM;
+  std::vector<InlayHintLabelPart> &LabelChunks;
+
+  struct CurrentTypeRAII {
+    TypeInlayHintLabelPartBuilder &Builder;
+    QualType PreviousType;
+    NestedNameSpecifier *PreviousNestedNameSpecifier;
+    CurrentTypeRAII(TypeInlayHintLabelPartBuilder &Builder, QualType New,
+                    NestedNameSpecifier *NNS = nullptr)
+        : Builder(Builder), PreviousType(Builder.CurrentType),
+          PreviousNestedNameSpecifier(Builder.CurrentNestedNameSpecifier) {
+      Builder.CurrentType = New;
+      if (NNS)
+        Builder.CurrentNestedNameSpecifier = NNS;
+    }
+    ~CurrentTypeRAII() {
+      Builder.CurrentType = PreviousType;
+      Builder.CurrentNestedNameSpecifier = PreviousNestedNameSpecifier;
+    }
+  };
+
+  void addLabel(llvm::function_ref<void(llvm::raw_ostream &)> NamePrinter,
+                llvm::function_ref<SourceLocation()> SourceLocationGetter) {
+    std::string Label;
+    llvm::raw_string_ostream OS(Label);
+    NamePrinter(OS);
+    auto &Name = LabelChunks.emplace_back();
+    Name.value = std::move(Label);
+    Name.location = makeLocation(Context, SourceLocationGetter(), MainFilePath);
+  }
+
+  void addLabel(std::string Label) {
+    if (LabelChunks.empty()) {
+      LabelChunks.emplace_back(std::move(Label));
+      return;
+    }
+    auto &Back = LabelChunks.back();
+    if (Back.location) {
+      LabelChunks.emplace_back(std::move(Label));
+      return;
+    }
+    // Let's combine the "unclickable" pieces together.
+    Back.value += std::move(Label);
+  }
+
+  void printTemplateArgumentList(llvm::ArrayRef<TemplateArgument> Args) {
+    unsigned Size = Args.size();
+    for (unsigned I = 0; I < Size; ++I) {
+      auto &TA = Args[I];
+      if (PP.SuppressDefaultTemplateArgs && TA.getIsDefaulted())
+        continue;
+      if (I)
+        addLabel(", ");
+      printTemplateArgument(TA);
+    }
+  }
+
+  void printTemplateArgument(const TemplateArgument &TA) {
+    switch (TA.getKind()) {
+    case TemplateArgument::Pack:
+      return printTemplateArgumentList(TA.pack_elements());
+    case TemplateArgument::Type: {
+      CurrentTypeRAII Guard(*this, TA.getAsType());
+      return Visit(TA.getAsType().getTypePtr());
+    }
+    // TODO: Add support for NTTP arguments.
+    case TemplateArgument::Expression:
+    case TemplateArgument::StructuralValue:
+    case TemplateArgument::Null:
+    case TemplateArgument::Declaration:
+    case TemplateArgument::NullPtr:
+    case TemplateArgument::Integral:
+    case TemplateArgument::Template:
+    case TemplateArgument::TemplateExpansion:
+      break;
+    }
+    std::string Label;
+    llvm::raw_string_ostream OS(Label);
+    TA.print(PP, OS, /*IncludeType=*/true);
+    addLabel(std::move(Label));
+  }
+
+  void handleTemplateSpecialization(
+      TemplateName TN, llvm::ArrayRef<TemplateArgument> Args,
+      SourceLocation TemplateNameLocation = SourceLocation()) {
+    SourceLocation Location;
+    TemplateDecl *TD = nullptr;
+    auto PrintType = TemplateName::Qualified::AsWritten;
+    switch (TN.getKind()) {
+    case TemplateName::Template:
+      TD = TN.getAsTemplateDecl();
+      Location = nameLocation(*TD, SM);
+      break;
+    case TemplateName::QualifiedTemplate:
+      if (NestedNameSpecifier *NNS =
+              TN.getAsQualifiedTemplateName()->getQualifier();
+          NNS == CurrentNestedNameSpecifier) {
+        // We have handled the NNS in VisitElaboratedType(). Avoid printing it
+        // twice.
+        TN = TN.getAsQualifiedTemplateName()->getUnderlyingTemplate();
+        PrintType = TemplateName::Qualified::None;
+      }
+      break;
+    case TemplateName::OverloadedTemplate:
+    case TemplateName::AssumedTemplate:
+    case TemplateName::DependentTemplate:
+    case TemplateName::SubstTemplateTemplateParm:
+    case TemplateName::SubstTemplateTemplateParmPack:
+    case TemplateName::UsingTemplate:
+      break;
+    }
+
+    addLabel([&](llvm::raw_ostream &OS) { TN.print(OS, PP, PrintType); },
+             [&] {
+               if (TemplateNameLocation.isValid())
+                 return TemplateNameLocation;
+               return Location;
+             });
+
+    addLabel("<");
+    printTemplateArgumentList(Args);
+    addLabel(">");
+  }
+
+  void maybeAddQualifiers() {
+    auto Quals = CurrentType.split().Quals;
+    if (!Quals.empty()) {
+      addLabel(Quals.getAsString());
+      addLabel(" ");
+    }
+  }
+
+  // When printing a reference, the referenced type might also be a reference.
+  // If so, we want to skip that before printing the inner type.
+  static QualType skipTopLevelReferences(QualType T) {
+    if (auto *Ref = T->getAs<ReferenceType>())
+      return skipTopLevelReferences(Ref->getPointeeTypeAsWritten());
+    return T;
+  }
+
+public:
+  TypeInlayHintLabelPartBuilder(QualType Current, ASTContext &Context,
+                                StringRef MainFilePath,
+                                const PrintingPolicy &PP,
+                                llvm::StringRef Prefix,
+                                std::vector<InlayHintLabelPart> &LabelChunks)
+      : CurrentType(Current), CurrentNestedNameSpecifier(nullptr),
+        Context(Context), MainFilePath(MainFilePath), PP(PP),
+        SM(Context.getSourceManager()), LabelChunks(LabelChunks) {
+    LabelChunks.reserve(16);
+    if (!Prefix.empty())
+      addLabel(Prefix.str());
+  }
+
+  void VisitType(const Type *) { addLabel(CurrentType.getAsString(PP)); }
+
+  void VisitTagType(const TagType *TT) {
+    auto *D = TT->getDecl();
+    if (auto *Specialization = dyn_cast<ClassTemplateSpecializationDecl>(D))
+      return handleTemplateSpecialization(
+          TemplateName(Specialization->getSpecializedTemplate()),
+          Specialization->getTemplateArgs().asArray());
+    if (auto *RD = dyn_cast<CXXRecordDecl>(D);
+        RD && !RD->getTemplateInstantiationPattern())
----------------
zyn0217 wrote:

Yeah, for example, given
```cpp
template <typename, typename = int>
struct A {};
A<float> bar[1];

auto [value] = bar;
```
where the type AST of `value` looks like:
```cpp
RecordType 0x7fd8dc0fac00 'A<float>'
`-ClassTemplateSpecialization 0x7fd8dc0faaf0 'A'
```

so we might have cases where the class template specialization is modeled as a `RecordType` rather than a `TemplateSpecializationType`.

https://github.com/llvm/llvm-project/pull/86629


More information about the cfe-commits mailing list