[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
Tue Jul 16 08:10:15 PDT 2024
https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/86629
>From b8a69cbd9e0ee0aa35b38b7e3a78048cbe61447e Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sat, 16 Mar 2024 23:30:10 +0800
Subject: [PATCH 01/36] [clangd] Support go-to-definition on type hints. The
core part
---
clang-tools-extra/clangd/AST.cpp | 9 +
clang-tools-extra/clangd/AST.h | 2 +
clang-tools-extra/clangd/InlayHints.cpp | 251 +++++++++++++++++-
.../clangd/index/IndexAction.cpp | 9 +-
.../clangd/unittests/InlayHintTests.cpp | 22 ++
5 files changed, 279 insertions(+), 14 deletions(-)
diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index 1b86ea19cf28d..ef87f1bcb8443 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -1019,5 +1019,14 @@ bool isExpandedFromParameterPack(const ParmVarDecl *D) {
return getUnderlyingPackType(D) != nullptr;
}
+std::optional<URI> toURI(OptionalFileEntryRef File) {
+ if (!File)
+ return std::nullopt;
+ auto AbsolutePath = File->getFileEntry().tryGetRealPathName();
+ if (AbsolutePath.empty())
+ return std::nullopt;
+ return URI::create(AbsolutePath);
+}
+
} // namespace clangd
} // namespace clang
diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h
index fb0722d697cd0..3ae624b1ab741 100644
--- a/clang-tools-extra/clangd/AST.h
+++ b/clang-tools-extra/clangd/AST.h
@@ -250,6 +250,8 @@ resolveForwardingParameters(const FunctionDecl *D, unsigned MaxDepth = 10);
/// reference to one (e.g. `Args&...` or `Args&&...`).
bool isExpandedFromParameterPack(const ParmVarDecl *D);
+std::optional<URI> toURI(OptionalFileEntryRef File);
+
} // namespace clangd
} // namespace clang
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index cd4f1931b3ce1..f9e0a51ddcc9f 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -21,6 +21,7 @@
#include "clang/AST/Stmt.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/Type.h"
+#include "clang/AST/TypeVisitor.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/SourceManager.h"
@@ -372,6 +373,197 @@ maybeDropCxxExplicitObjectParameters(ArrayRef<const ParmVarDecl *> Params) {
return Params;
}
+std::optional<Location> toLocation(SourceManager &SM, SourceRange Range) {
+ if (Range.isInvalid())
+ return std::nullopt;
+ if (auto URI =
+ toURI(SM.getFileEntryRefForID(SM.getFileID(Range.getBegin())))) {
+ Location L;
+ L.range.start = sourceLocToPosition(SM, Range.getBegin());
+ L.range.end = sourceLocToPosition(SM, Range.getEnd());
+ if (auto File = URIForFile::fromURI(*URI, ""))
+ L.uri = File.get();
+ return L;
+ }
+ return std::nullopt;
+}
+
+class TypeInlayHintLabelPartBuilder
+ : public TypeVisitor<TypeInlayHintLabelPartBuilder> {
+ QualType Current;
+ ASTContext &Context;
+ const PrintingPolicy &PP;
+ std::vector<InlayHintLabelPart> &LabelChunks;
+
+ bool ShouldAddLinksToTagTypes = false;
+
+ struct CurrentTypeRAII {
+ TypeInlayHintLabelPartBuilder &Builder;
+ QualType PreviousType;
+ bool PreviousShouldAddLinksToTagTypes;
+ CurrentTypeRAII(TypeInlayHintLabelPartBuilder &Builder, QualType New,
+ bool ShouldAddLinksToTagTypes)
+ : Builder(Builder), PreviousType(Builder.Current) {
+ Builder.Current = New;
+ Builder.ShouldAddLinksToTagTypes = ShouldAddLinksToTagTypes;
+ }
+ ~CurrentTypeRAII() {
+ Builder.Current = PreviousType;
+ Builder.ShouldAddLinksToTagTypes = PreviousShouldAddLinksToTagTypes;
+ }
+ };
+
+ void addLabel(llvm::function_ref<void(llvm::raw_ostream &)> NamePrinter,
+ llvm::function_ref<SourceRange()> SourceRangeGetter) {
+ auto &Name = LabelChunks.emplace_back();
+ llvm::raw_string_ostream OS(Name.value);
+ NamePrinter(OS);
+ Name.location = toLocation(Context.getSourceManager(), SourceRangeGetter());
+ }
+
+ 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)
+ LabelChunks.emplace_back(", ");
+ printTemplateArgument(TA);
+ }
+ }
+
+ void printTemplateArgument(const TemplateArgument &TA) {
+ if (TA.getKind() == TemplateArgument::Pack)
+ return printTemplateArgumentList(TA.pack_elements());
+ if (TA.getKind() == TemplateArgument::Type) {
+ CurrentTypeRAII Guard(*this, TA.getAsType(),
+ /*ShouldAddLinksToTagTypes=*/true);
+ return Visit(TA.getAsType().getTypePtr());
+ }
+ llvm::raw_string_ostream OS(LabelChunks.emplace_back().value);
+ TA.print(PP, OS, /*IncludeType=*/true);
+ }
+
+ void
+ processTemplateSpecialization(TemplateName TN,
+ llvm::ArrayRef<TemplateArgument> Args,
+ SourceRange TemplateNameRange = SourceRange()) {
+ SourceRange Range;
+ TemplateDecl *TD = nullptr;
+ switch (TN.getKind()) {
+ case TemplateName::Template:
+ TD = TN.getAsTemplateDecl();
+ Range = TD->getSourceRange();
+ LLVM_FALLTHROUGH;
+ default:
+ break;
+ }
+
+ addLabel([&](llvm::raw_ostream &OS) { TN.print(OS, PP); },
+ [&] {
+ if (TemplateNameRange.isValid())
+ return TemplateNameRange;
+ return Range;
+ });
+
+ LabelChunks.emplace_back("<");
+ printTemplateArgumentList(Args);
+ LabelChunks.emplace_back(">");
+ }
+
+public:
+
+#ifndef NDEBUG
+ ~TypeInlayHintLabelPartBuilder() {
+ llvm::errs() << "TypeInlayHintLabelPartBuilder:\n";
+ Current->dump();
+ for (auto &L : LabelChunks)
+ llvm::errs() << L << ", ";
+ llvm::errs() << "\n";
+ }
+#endif
+
+ TypeInlayHintLabelPartBuilder(QualType Current, ASTContext &Context,
+ const PrintingPolicy &PP,
+ bool ShouldAddLinksToTagTypes,
+ std::vector<InlayHintLabelPart> &LabelChunks)
+ : Current(Current), Context(Context), PP(PP), LabelChunks(LabelChunks) {}
+
+ void VisitType(const Type *) {
+ LabelChunks.emplace_back(Current.getAsString(PP));
+ }
+
+ void VisitTagType(const TagType *TT) {
+ if (!ShouldAddLinksToTagTypes)
+ return VisitType(TT);
+ auto *D = TT->getDecl();
+ if (auto *Specialization = dyn_cast<ClassTemplateSpecializationDecl>(D))
+ return processTemplateSpecialization(
+ TemplateName(Specialization->getSpecializedTemplate()),
+ Specialization->getTemplateArgs().asArray());
+ if (auto *RD = dyn_cast<CXXRecordDecl>(D);
+ RD && !RD->getTemplateInstantiationPattern())
+ return addLabel(
+ [&](llvm::raw_ostream &OS) { return RD->printName(OS, PP); },
+ [&] { return RD->getSourceRange(); });
+ return VisitType(TT);
+ }
+
+ void VisitAutoType(const AutoType *AT) {
+ if (!AT->isDeduced() || AT->getDeducedType()->isDecltypeType())
+ return;
+ CurrentTypeRAII Guard(*this, AT->getDeducedType(),
+ ShouldAddLinksToTagTypes);
+ return Visit(AT->getDeducedType().getTypePtr());
+ }
+
+ void VisitElaboratedType(const ElaboratedType *ET) {
+ if (auto *NNS = ET->getQualifier()) {
+ switch (NNS->getKind()) {
+ case NestedNameSpecifier::Identifier:
+ case NestedNameSpecifier::Namespace:
+ case NestedNameSpecifier::NamespaceAlias:
+ case NestedNameSpecifier::Global:
+ case NestedNameSpecifier::Super: {
+ auto &Name = LabelChunks.emplace_back();
+ llvm::raw_string_ostream OS(Name.value);
+ NNS->print(OS, PP);
+ } break;
+ case NestedNameSpecifier::TypeSpec:
+ case NestedNameSpecifier::TypeSpecWithTemplate:
+ Visit(NNS->getAsType());
+ break;
+ }
+ }
+ CurrentTypeRAII Guard(*this, ET->getNamedType(), ShouldAddLinksToTagTypes);
+ return Visit(ET->getNamedType().getTypePtr());
+ }
+
+ void VisitTemplateSpecializationType(const TemplateSpecializationType *TST) {
+ SourceRange Range;
+ if (auto *Specialization =
+ dyn_cast_if_present<ClassTemplateSpecializationDecl>(
+ TST->desugar().getCanonicalType()->getAsCXXRecordDecl()))
+ Range = Specialization->getSourceRange();
+ return processTemplateSpecialization(TST->getTemplateName(),
+ TST->template_arguments(), Range);
+ }
+
+ void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *ST) {
+ CurrentTypeRAII Guard(*this, ST->getReplacementType(),
+ /*ShouldAddLinksToTagTypes=*/true);
+ return Visit(ST->getReplacementType().getTypePtr());
+ }
+};
+
+unsigned lengthOfInlayHintLabelPart(llvm::ArrayRef<InlayHintLabelPart> Labels) {
+ unsigned Size = 0;
+ for (auto &P : Labels)
+ Size += P.value.size();
+ return Size;
+}
+
struct Callee {
// Only one of Decl or Loc is set.
// Loc is for calls through function pointers.
@@ -949,9 +1141,29 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
addInlayHint(*LSPRange, Side, Kind, Prefix, Label, Suffix);
}
+ void addInlayHint(SourceRange R, HintSide Side, InlayHintKind Kind,
+ llvm::StringRef Prefix,
+ llvm::ArrayRef<InlayHintLabelPart> Labels,
+ llvm::StringRef Suffix) {
+ auto LSPRange = getHintRange(R);
+ if (!LSPRange)
+ return;
+
+ addInlayHint(*LSPRange, Side, Kind, Prefix, Labels, Suffix);
+ }
+
void addInlayHint(Range LSPRange, HintSide Side, InlayHintKind Kind,
llvm::StringRef Prefix, llvm::StringRef Label,
llvm::StringRef Suffix) {
+ return addInlayHint(LSPRange, Side, Kind, Prefix,
+ /*Labels=*/std::vector<InlayHintLabelPart>{Label.str()},
+ Suffix);
+ }
+
+ void addInlayHint(Range LSPRange, HintSide Side, InlayHintKind Kind,
+ llvm::StringRef Prefix,
+ llvm::ArrayRef<InlayHintLabelPart> Labels,
+ llvm::StringRef Suffix) {
// We shouldn't get as far as adding a hint if the category is disabled.
// We'd like to disable as much of the analysis as possible above instead.
// Assert in debug mode but add a dynamic check in production.
@@ -977,8 +1189,21 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
return;
bool PadLeft = Prefix.consume_front(" ");
bool PadRight = Suffix.consume_back(" ");
+ if (Prefix.empty() && Suffix.empty()) {
+ Results.push_back(InlayHint{LSPPos,
+ /*label=*/Labels, Kind, PadLeft, PadRight,
+ LSPRange});
+ return;
+ }
+ std::vector<InlayHintLabelPart> JoinedLabels;
+ JoinedLabels.reserve(Labels.size() + !Prefix.empty() + !Suffix.empty());
+ if (!Prefix.empty())
+ JoinedLabels.push_back(InlayHintLabelPart(Prefix.str()));
+ llvm::copy(Labels, std::back_inserter(JoinedLabels));
+ if (!Suffix.empty())
+ JoinedLabels.push_back(InlayHintLabelPart(Suffix.str()));
Results.push_back(InlayHint{LSPPos,
- /*label=*/{(Prefix + Label + Suffix).str()},
+ /*label=*/std::move(JoinedLabels),
Kind, PadLeft, PadRight, LSPRange});
}
@@ -1005,14 +1230,22 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
// The sugared type is more useful in some cases, and the canonical
// type in other cases.
auto Desugared = maybeDesugar(AST, T);
- std::string TypeName = Desugared.getAsString(TypeHintPolicy);
- if (T != Desugared && !shouldPrintTypeHint(TypeName)) {
+ std::vector<InlayHintLabelPart> Chunks;
+ TypeInlayHintLabelPartBuilder Builder(
+ Desugared, AST, TypeHintPolicy,
+ /*ShouldAddLinksToTagTypes=*/T != Desugared, Chunks);
+ Builder.Visit(Desugared.getTypePtr());
+ if (T != Desugared && shouldPrintTypeHint(Chunks)) {
// If the desugared type is too long to display, fallback to the sugared
// type.
- TypeName = T.getAsString(TypeHintPolicy);
+ Chunks.clear();
+ TypeInlayHintLabelPartBuilder Builder(T, AST, TypeHintPolicy,
+ /*ShouldAddLinksToTagTypes=*/false,
+ Chunks);
+ Builder.Visit(T.getTypePtr());
}
- if (shouldPrintTypeHint(TypeName))
- addInlayHint(R, HintSide::Right, InlayHintKind::Type, Prefix, TypeName,
+ if (shouldPrintTypeHint(Chunks))
+ addInlayHint(R, HintSide::Right, InlayHintKind::Type, Prefix, Chunks,
/*Suffix=*/"");
}
@@ -1021,9 +1254,11 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
/*Prefix=*/"", Text, /*Suffix=*/"=");
}
- bool shouldPrintTypeHint(llvm::StringRef TypeName) const noexcept {
+ bool shouldPrintTypeHint(
+ llvm::ArrayRef<InlayHintLabelPart> TypeLabels) const noexcept {
return Cfg.InlayHints.TypeNameLimit == 0 ||
- TypeName.size() < Cfg.InlayHints.TypeNameLimit;
+ lengthOfInlayHintLabelPart(TypeLabels) <
+ Cfg.InlayHints.TypeNameLimit;
}
void addBlockEndHint(SourceRange BraceRange, StringRef DeclPrefix,
diff --git a/clang-tools-extra/clangd/index/IndexAction.cpp b/clang-tools-extra/clangd/index/IndexAction.cpp
index ed56c2a9d2e81..f3fbb9fc307e2 100644
--- a/clang-tools-extra/clangd/index/IndexAction.cpp
+++ b/clang-tools-extra/clangd/index/IndexAction.cpp
@@ -32,12 +32,9 @@ namespace clangd {
namespace {
std::optional<std::string> toURI(OptionalFileEntryRef File) {
- if (!File)
- return std::nullopt;
- auto AbsolutePath = File->getFileEntry().tryGetRealPathName();
- if (AbsolutePath.empty())
- return std::nullopt;
- return URI::create(AbsolutePath).toString();
+ if (auto URI = clang::clangd::toURI(File))
+ return URI->toString();
+ return std::nullopt;
}
// Collects the nodes and edges of include graph during indexing action.
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index 5b1531eb2fa60..7f99a3522e490 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1637,6 +1637,28 @@ TEST(TypeHints, SubstTemplateParameterAliases) {
ExpectedHint{": static_vector<int>", "vector_name"});
}
+TEST(TypeHints, Links) {
+ TestTU TU = TestTU::withCode(R"cpp(
+struct S {};
+template <class T, unsigned V, class... U>
+struct W {
+};
+enum Kind {
+ K = 1,
+};
+namespace std {
+template <class E> struct vector {};
+template <> struct vector<bool> {};
+} // namespace std
+int main() {
+ auto v = std::vector<bool>();
+})cpp");
+ TU.ExtraArgs.push_back("-std=c++2c");
+ auto AST = TU.build();
+
+ hintsOfKind(AST, InlayHintKind::Type);
+}
+
TEST(DesignatorHints, Basic) {
assertDesignatorHints(R"cpp(
struct S { int x, y, z; };
>From fb3bbd0a53d3d4f85a3df4fefda4ae932a44beff Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 26 Mar 2024 14:41:38 +0800
Subject: [PATCH 02/36] Small fixes
---
clang-tools-extra/clangd/InlayHints.cpp | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index f9e0a51ddcc9f..ed84bd2a643b2 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -474,7 +474,7 @@ class TypeInlayHintLabelPartBuilder
public:
-#ifndef NDEBUG
+#if 0
~TypeInlayHintLabelPartBuilder() {
llvm::errs() << "TypeInlayHintLabelPartBuilder:\n";
Current->dump();
@@ -532,7 +532,13 @@ class TypeInlayHintLabelPartBuilder
} break;
case NestedNameSpecifier::TypeSpec:
case NestedNameSpecifier::TypeSpecWithTemplate:
+ CurrentTypeRAII Guard(
+ *this,
+ QualType(NNS->getAsType(),
+ /*Quals=*/0), // Do we need cv-qualifiers on type specifiers?
+ ShouldAddLinksToTagTypes);
Visit(NNS->getAsType());
+ LabelChunks.emplace_back("::");
break;
}
}
@@ -1235,7 +1241,7 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
Desugared, AST, TypeHintPolicy,
/*ShouldAddLinksToTagTypes=*/T != Desugared, Chunks);
Builder.Visit(Desugared.getTypePtr());
- if (T != Desugared && shouldPrintTypeHint(Chunks)) {
+ if (T != Desugared && !shouldPrintTypeHint(Chunks)) {
// If the desugared type is too long to display, fallback to the sugared
// type.
Chunks.clear();
>From 36199ce6b52acba62743adfc4ef80cb2640889f7 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sun, 31 Mar 2024 22:33:49 +0800
Subject: [PATCH 03/36] Add unittests
---
clang-tools-extra/clangd/InlayHints.cpp | 129 +++++++++------
.../clangd/unittests/InlayHintTests.cpp | 156 ++++++++++++++++--
2 files changed, 221 insertions(+), 64 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index ed84bd2a643b2..7304775a3d951 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -11,6 +11,7 @@
#include "Config.h"
#include "HeuristicResolver.h"
#include "ParsedAST.h"
+#include "Protocol.h"
#include "SourceCode.h"
#include "clang/AST/ASTDiagnostic.h"
#include "clang/AST/Decl.h"
@@ -415,12 +416,30 @@ class TypeInlayHintLabelPartBuilder
void addLabel(llvm::function_ref<void(llvm::raw_ostream &)> NamePrinter,
llvm::function_ref<SourceRange()> SourceRangeGetter) {
- auto &Name = LabelChunks.emplace_back();
- llvm::raw_string_ostream OS(Name.value);
+ std::string Label;
+ llvm::raw_string_ostream OS(Label);
NamePrinter(OS);
+ if (!ShouldAddLinksToTagTypes)
+ return addLabel(std::move(Label));
+ auto &Name = LabelChunks.emplace_back();
+ Name.value = std::move(Label);
Name.location = toLocation(Context.getSourceManager(), SourceRangeGetter());
}
+ 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) {
@@ -428,27 +447,41 @@ class TypeInlayHintLabelPartBuilder
if (PP.SuppressDefaultTemplateArgs && TA.getIsDefaulted())
continue;
if (I)
- LabelChunks.emplace_back(", ");
+ addLabel(", ");
printTemplateArgument(TA);
}
}
void printTemplateArgument(const TemplateArgument &TA) {
- if (TA.getKind() == TemplateArgument::Pack)
+ switch (TA.getKind()) {
+ case TemplateArgument::Pack:
return printTemplateArgumentList(TA.pack_elements());
- if (TA.getKind() == TemplateArgument::Type) {
+ case TemplateArgument::Type: {
CurrentTypeRAII Guard(*this, TA.getAsType(),
/*ShouldAddLinksToTagTypes=*/true);
return Visit(TA.getAsType().getTypePtr());
}
- llvm::raw_string_ostream OS(LabelChunks.emplace_back().value);
+ // 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
- processTemplateSpecialization(TemplateName TN,
- llvm::ArrayRef<TemplateArgument> Args,
- SourceRange TemplateNameRange = SourceRange()) {
+ handleTemplateSpecialization(TemplateName TN,
+ llvm::ArrayRef<TemplateArgument> Args,
+ SourceRange TemplateNameRange = SourceRange()) {
SourceRange Range;
TemplateDecl *TD = nullptr;
switch (TN.getKind()) {
@@ -467,39 +500,31 @@ class TypeInlayHintLabelPartBuilder
return Range;
});
- LabelChunks.emplace_back("<");
+ addLabel("<");
printTemplateArgumentList(Args);
- LabelChunks.emplace_back(">");
+ addLabel(">");
}
public:
-
-#if 0
- ~TypeInlayHintLabelPartBuilder() {
- llvm::errs() << "TypeInlayHintLabelPartBuilder:\n";
- Current->dump();
- for (auto &L : LabelChunks)
- llvm::errs() << L << ", ";
- llvm::errs() << "\n";
- }
-#endif
-
TypeInlayHintLabelPartBuilder(QualType Current, ASTContext &Context,
const PrintingPolicy &PP,
bool ShouldAddLinksToTagTypes,
+ llvm::StringRef Prefix,
std::vector<InlayHintLabelPart> &LabelChunks)
- : Current(Current), Context(Context), PP(PP), LabelChunks(LabelChunks) {}
-
- void VisitType(const Type *) {
- LabelChunks.emplace_back(Current.getAsString(PP));
+ : Current(Current), Context(Context), PP(PP), LabelChunks(LabelChunks) {
+ LabelChunks.reserve(16);
+ if (!Prefix.empty())
+ addLabel(Prefix.str());
}
+ void VisitType(const Type *) { addLabel(Current.getAsString(PP)); }
+
void VisitTagType(const TagType *TT) {
if (!ShouldAddLinksToTagTypes)
return VisitType(TT);
auto *D = TT->getDecl();
if (auto *Specialization = dyn_cast<ClassTemplateSpecializationDecl>(D))
- return processTemplateSpecialization(
+ return handleTemplateSpecialization(
TemplateName(Specialization->getSpecializedTemplate()),
Specialization->getTemplateArgs().asArray());
if (auto *RD = dyn_cast<CXXRecordDecl>(D);
@@ -526,9 +551,9 @@ class TypeInlayHintLabelPartBuilder
case NestedNameSpecifier::NamespaceAlias:
case NestedNameSpecifier::Global:
case NestedNameSpecifier::Super: {
- auto &Name = LabelChunks.emplace_back();
- llvm::raw_string_ostream OS(Name.value);
- NNS->print(OS, PP);
+ std::string Label;
+ llvm::raw_string_ostream OS(Label);
+ addLabel(std::move(Label));
} break;
case NestedNameSpecifier::TypeSpec:
case NestedNameSpecifier::TypeSpecWithTemplate:
@@ -538,7 +563,7 @@ class TypeInlayHintLabelPartBuilder
/*Quals=*/0), // Do we need cv-qualifiers on type specifiers?
ShouldAddLinksToTagTypes);
Visit(NNS->getAsType());
- LabelChunks.emplace_back("::");
+ addLabel("::");
break;
}
}
@@ -552,8 +577,8 @@ class TypeInlayHintLabelPartBuilder
dyn_cast_if_present<ClassTemplateSpecializationDecl>(
TST->desugar().getCanonicalType()->getAsCXXRecordDecl()))
Range = Specialization->getSourceRange();
- return processTemplateSpecialization(TST->getTemplateName(),
- TST->template_arguments(), Range);
+ return handleTemplateSpecialization(TST->getTemplateName(),
+ TST->template_arguments(), Range);
}
void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *ST) {
@@ -1149,13 +1174,13 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
void addInlayHint(SourceRange R, HintSide Side, InlayHintKind Kind,
llvm::StringRef Prefix,
- llvm::ArrayRef<InlayHintLabelPart> Labels,
+ std::vector<InlayHintLabelPart> Labels,
llvm::StringRef Suffix) {
auto LSPRange = getHintRange(R);
if (!LSPRange)
return;
- addInlayHint(*LSPRange, Side, Kind, Prefix, Labels, Suffix);
+ addInlayHint(*LSPRange, Side, Kind, Prefix, std::move(Labels), Suffix);
}
void addInlayHint(Range LSPRange, HintSide Side, InlayHintKind Kind,
@@ -1168,8 +1193,9 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
void addInlayHint(Range LSPRange, HintSide Side, InlayHintKind Kind,
llvm::StringRef Prefix,
- llvm::ArrayRef<InlayHintLabelPart> Labels,
+ std::vector<InlayHintLabelPart> Labels,
llvm::StringRef Suffix) {
+ assert(!Labels.empty() && "Expected non-empty labels");
// We shouldn't get as far as adding a hint if the category is disabled.
// We'd like to disable as much of the analysis as possible above instead.
// Assert in debug mode but add a dynamic check in production.
@@ -1201,16 +1227,21 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
LSPRange});
return;
}
- std::vector<InlayHintLabelPart> JoinedLabels;
- JoinedLabels.reserve(Labels.size() + !Prefix.empty() + !Suffix.empty());
- if (!Prefix.empty())
- JoinedLabels.push_back(InlayHintLabelPart(Prefix.str()));
- llvm::copy(Labels, std::back_inserter(JoinedLabels));
- if (!Suffix.empty())
- JoinedLabels.push_back(InlayHintLabelPart(Suffix.str()));
+ if (!Prefix.empty()) {
+ if (auto &Label = Labels.front(); !Label.location)
+ Label.value = Prefix.str() + Label.value;
+ else
+ Labels.insert(Labels.begin(), InlayHintLabelPart(Prefix.str()));
+ }
+ if (!Suffix.empty()) {
+ if (auto &Label = Labels.back(); !Label.location)
+ Label.value += Suffix.str();
+ else
+ Labels.push_back(InlayHintLabelPart(Suffix.str()));
+ }
Results.push_back(InlayHint{LSPPos,
- /*label=*/std::move(JoinedLabels),
- Kind, PadLeft, PadRight, LSPRange});
+ /*label=*/std::move(Labels), Kind, PadLeft,
+ PadRight, LSPRange});
}
// Get the range of the main file that *exactly* corresponds to R.
@@ -1239,7 +1270,7 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
std::vector<InlayHintLabelPart> Chunks;
TypeInlayHintLabelPartBuilder Builder(
Desugared, AST, TypeHintPolicy,
- /*ShouldAddLinksToTagTypes=*/T != Desugared, Chunks);
+ /*ShouldAddLinksToTagTypes=*/T != Desugared, Prefix, Chunks);
Builder.Visit(Desugared.getTypePtr());
if (T != Desugared && !shouldPrintTypeHint(Chunks)) {
// If the desugared type is too long to display, fallback to the sugared
@@ -1247,11 +1278,13 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
Chunks.clear();
TypeInlayHintLabelPartBuilder Builder(T, AST, TypeHintPolicy,
/*ShouldAddLinksToTagTypes=*/false,
- Chunks);
+ Prefix, Chunks);
Builder.Visit(T.getTypePtr());
}
if (shouldPrintTypeHint(Chunks))
- addInlayHint(R, HintSide::Right, InlayHintKind::Type, Prefix, Chunks,
+ addInlayHint(R, HintSide::Right, InlayHintKind::Type,
+ /*Prefix=*/"", // We have handled prefixes in the builder.
+ std::move(Chunks),
/*Suffix=*/"");
}
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index 7f99a3522e490..20f32bc7b7f6b 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -55,6 +55,19 @@ struct ExpectedHint {
}
};
+struct ExpectedHintLabelPiece {
+ std::string Label;
+ std::optional<std::string> TargetRangeName = std::nullopt;
+
+ friend llvm::raw_ostream &operator<<(llvm::raw_ostream &Stream,
+ const ExpectedHintLabelPiece &Hint) {
+ Stream << Hint.Label;
+ if (!Hint.TargetRangeName)
+ Stream << " that points to $" << Hint.TargetRangeName;
+ return Stream;
+ }
+};
+
MATCHER_P2(HintMatcher, Expected, Code, llvm::to_string(Expected)) {
llvm::StringRef ExpectedView(Expected.Label);
std::string ResultLabel = arg.joinLabels();
@@ -73,6 +86,34 @@ MATCHER_P2(HintMatcher, Expected, Code, llvm::to_string(Expected)) {
return true;
}
+MATCHER_P2(HintLabelPieceMatcher, Expected, Code, llvm::to_string(Expected)) {
+ llvm::StringRef ExpectedView(Expected.Label);
+ std::string ResultLabel = arg.value;
+ if (ResultLabel != ExpectedView.trim(" ")) {
+ *result_listener << "label is '" << ResultLabel << "'";
+ return false;
+ }
+ if (!Expected.TargetRangeName && !arg.location)
+ return true;
+ if (Expected.TargetRangeName && !arg.location) {
+ *result_listener << " range " << *Expected.TargetRangeName
+ << " is expected, but we have nothing.";
+ return false;
+ }
+ if (!Expected.TargetRangeName && arg.location) {
+ *result_listener << " the link points to " << llvm::to_string(arg.location)
+ << ", but we expect nothing.";
+ return false;
+ }
+ if (arg.location->range != Code.range(*Expected.TargetRangeName)) {
+ *result_listener << "range is " << llvm::to_string(arg.location->range)
+ << " but $" << *Expected.TargetRangeName << " points to "
+ << llvm::to_string(Code.range(*Expected.TargetRangeName));
+ return false;
+ }
+ return true;
+}
+
MATCHER_P(labelIs, Label, "") { return arg.joinLabels() == Label; }
Config noHintsConfig() {
@@ -1638,25 +1679,108 @@ TEST(TypeHints, SubstTemplateParameterAliases) {
}
TEST(TypeHints, Links) {
- TestTU TU = TestTU::withCode(R"cpp(
-struct S {};
-template <class T, unsigned V, class... U>
-struct W {
-};
-enum Kind {
- K = 1,
-};
-namespace std {
-template <class E> struct vector {};
-template <> struct vector<bool> {};
-} // namespace std
-int main() {
- auto v = std::vector<bool>();
-})cpp");
+ Annotations Source(R"cpp(
+ $Package[[template <class T, class U>
+ struct Package {]]};
+
+ $SpecializationOfPackage[[template <>
+ struct Package<float, const int> {]]};
+
+ $Container[[template <class... T>
+ struct Container {]]};
+
+ $NttpContainer[[template <auto... T>
+ struct NttpContainer {]]};
+
+ enum struct ScopedEnum {
+ X = 1,
+ };
+
+ enum Enum {
+ E = 2,
+ };
+
+ namespace ns {
+ template <class T>
+ struct Nested {
+ template <class U>
+ struct Class {
+ };
+ };
+ }
+
+ void foo() {
+ auto $1[[C]] = Container<Package<char, int>>();
+ auto $2[[D]] = Container<Package<float, const int>>();
+ auto $3[[E]] = Container<Container<int, int>, long>();
+ auto $4[[F]] = NttpContainer<D, E, ScopedEnum::X, Enum::E>();
+ auto $5[[G]] = ns::Nested<Container<int>>::Class<Package<char, int>>();
+ }
+ )cpp");
+ TestTU TU = TestTU::withCode(Source.code());
TU.ExtraArgs.push_back("-std=c++2c");
auto AST = TU.build();
- hintsOfKind(AST, InlayHintKind::Type);
+ auto HintAt = [&](llvm::ArrayRef<InlayHint> InlayHints,
+ llvm::StringRef Range) {
+ auto *Hint = llvm::find_if(InlayHints, [&](const InlayHint &InlayHint) {
+ return InlayHint.range == Source.range(Range);
+ });
+ assert(Hint && "No range was found");
+ return llvm::ArrayRef(Hint->label);
+ };
+
+ Config C;
+ C.InlayHints.TypeNameLimit = 0;
+ WithContextValue WithCfg(Config::Key, std::move(C));
+
+ auto Hints = hintsOfKind(AST, InlayHintKind::Type);
+
+ EXPECT_THAT(
+ HintAt(Hints, "1"),
+ ElementsAre(
+ HintLabelPieceMatcher(ExpectedHintLabelPiece{": Container<"}, Source),
+ HintLabelPieceMatcher(ExpectedHintLabelPiece{"Package", "Package"},
+ Source),
+ HintLabelPieceMatcher(ExpectedHintLabelPiece{"<char, int>>"},
+ Source)));
+
+ EXPECT_THAT(
+ HintAt(Hints, "2"),
+ ElementsAre(
+ HintLabelPieceMatcher(ExpectedHintLabelPiece{": Container<"}, Source),
+ HintLabelPieceMatcher(
+ ExpectedHintLabelPiece{"Package", "SpecializationOfPackage"},
+ Source),
+ HintLabelPieceMatcher(ExpectedHintLabelPiece{"<float, const int>>"},
+ Source)));
+
+ EXPECT_THAT(
+ HintAt(Hints, "3"),
+ ElementsAre(
+ HintLabelPieceMatcher(ExpectedHintLabelPiece{": Container<"}, Source),
+ HintLabelPieceMatcher(
+ ExpectedHintLabelPiece{"Container", "Container"}, Source),
+ HintLabelPieceMatcher(ExpectedHintLabelPiece{"<int, int>, long>"},
+ Source)));
+
+ EXPECT_THAT(HintAt(Hints, "4"),
+ ElementsAre(HintLabelPieceMatcher(
+ ExpectedHintLabelPiece{
+ ": NttpContainer<D, E, ScopedEnum::X, Enum::E>"},
+ Source)));
+ EXPECT_THAT(
+ HintAt(Hints, "5"),
+ ElementsAre(
+ HintLabelPieceMatcher(ExpectedHintLabelPiece{": Nested<"}, Source),
+ HintLabelPieceMatcher(
+ ExpectedHintLabelPiece{"Container", "Container"}, Source),
+ HintLabelPieceMatcher(ExpectedHintLabelPiece{"<int>>::Class<"},
+ Source),
+ HintLabelPieceMatcher(ExpectedHintLabelPiece{"Package", "Package"},
+ Source),
+ HintLabelPieceMatcher(ExpectedHintLabelPiece{"<char, int>>"},
+ Source)));
}
TEST(DesignatorHints, Basic) {
>From fa5fb4a93ca64163428cc5ccab25391d4ff4580d Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sun, 31 Mar 2024 23:13:03 +0800
Subject: [PATCH 04/36] Fix an uninitialization bug
---
clang-tools-extra/clangd/InlayHints.cpp | 14 ++++++++------
.../clangd/unittests/InlayHintTests.cpp | 5 ++++-
2 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 7304775a3d951..11254c8fc81af 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -391,7 +391,7 @@ std::optional<Location> toLocation(SourceManager &SM, SourceRange Range) {
class TypeInlayHintLabelPartBuilder
: public TypeVisitor<TypeInlayHintLabelPartBuilder> {
- QualType Current;
+ QualType CurrentType;
ASTContext &Context;
const PrintingPolicy &PP;
std::vector<InlayHintLabelPart> &LabelChunks;
@@ -404,12 +404,13 @@ class TypeInlayHintLabelPartBuilder
bool PreviousShouldAddLinksToTagTypes;
CurrentTypeRAII(TypeInlayHintLabelPartBuilder &Builder, QualType New,
bool ShouldAddLinksToTagTypes)
- : Builder(Builder), PreviousType(Builder.Current) {
- Builder.Current = New;
+ : Builder(Builder), PreviousType(Builder.CurrentType),
+ PreviousShouldAddLinksToTagTypes(Builder.ShouldAddLinksToTagTypes) {
+ Builder.CurrentType = New;
Builder.ShouldAddLinksToTagTypes = ShouldAddLinksToTagTypes;
}
~CurrentTypeRAII() {
- Builder.Current = PreviousType;
+ Builder.CurrentType = PreviousType;
Builder.ShouldAddLinksToTagTypes = PreviousShouldAddLinksToTagTypes;
}
};
@@ -511,13 +512,14 @@ class TypeInlayHintLabelPartBuilder
bool ShouldAddLinksToTagTypes,
llvm::StringRef Prefix,
std::vector<InlayHintLabelPart> &LabelChunks)
- : Current(Current), Context(Context), PP(PP), LabelChunks(LabelChunks) {
+ : CurrentType(Current), Context(Context), PP(PP),
+ LabelChunks(LabelChunks) {
LabelChunks.reserve(16);
if (!Prefix.empty())
addLabel(Prefix.str());
}
- void VisitType(const Type *) { addLabel(Current.getAsString(PP)); }
+ void VisitType(const Type *) { addLabel(CurrentType.getAsString(PP)); }
void VisitTagType(const TagType *TT) {
if (!ShouldAddLinksToTagTypes)
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index 20f32bc7b7f6b..2464172a3c68a 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -62,7 +62,7 @@ struct ExpectedHintLabelPiece {
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &Stream,
const ExpectedHintLabelPiece &Hint) {
Stream << Hint.Label;
- if (!Hint.TargetRangeName)
+ if (Hint.TargetRangeName)
Stream << " that points to $" << Hint.TargetRangeName;
return Stream;
}
@@ -1769,12 +1769,15 @@ TEST(TypeHints, Links) {
ExpectedHintLabelPiece{
": NttpContainer<D, E, ScopedEnum::X, Enum::E>"},
Source)));
+
EXPECT_THAT(
HintAt(Hints, "5"),
ElementsAre(
HintLabelPieceMatcher(ExpectedHintLabelPiece{": Nested<"}, Source),
HintLabelPieceMatcher(
ExpectedHintLabelPiece{"Container", "Container"}, Source),
+ // We don't have links on the inner 'Class' because the location is
+ // where the 'auto' links to.
HintLabelPieceMatcher(ExpectedHintLabelPiece{"<int>>::Class<"},
Source),
HintLabelPieceMatcher(ExpectedHintLabelPiece{"Package", "Package"},
>From 31a0dafeae52e3b1b90bc191c0e595596d3b6a26 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sun, 31 Mar 2024 23:30:18 +0800
Subject: [PATCH 05/36] format
---
clang-tools-extra/clangd/InlayHints.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 11254c8fc81af..5045eb9b6fd26 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -561,8 +561,9 @@ class TypeInlayHintLabelPartBuilder
case NestedNameSpecifier::TypeSpecWithTemplate:
CurrentTypeRAII Guard(
*this,
- QualType(NNS->getAsType(),
- /*Quals=*/0), // Do we need cv-qualifiers on type specifiers?
+ QualType(
+ NNS->getAsType(),
+ /*Quals=*/0), // Do we need cv-qualifiers on type specifiers?
ShouldAddLinksToTagTypes);
Visit(NNS->getAsType());
addLabel("::");
>From 9edd0f63541630a0594a4db8b5f517d6e682ddc2 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 1 Apr 2024 13:28:27 +0800
Subject: [PATCH 06/36] Handle reference/pointer types & Preserve qualifiers &
Refactor tests slightly
---
clang-tools-extra/clangd/InlayHints.cpp | 30 +++
.../clangd/unittests/InlayHintTests.cpp | 174 +++++++++---------
2 files changed, 122 insertions(+), 82 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 5045eb9b6fd26..c28e7671cb401 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -506,6 +506,14 @@ class TypeInlayHintLabelPartBuilder
addLabel(">");
}
+ void maybeAddQualifiers() {
+ auto Quals = CurrentType.split().Quals;
+ if (!Quals.empty()) {
+ addLabel(Quals.getAsString());
+ addLabel(" ");
+ }
+ }
+
public:
TypeInlayHintLabelPartBuilder(QualType Current, ASTContext &Context,
const PrintingPolicy &PP,
@@ -540,12 +548,14 @@ class TypeInlayHintLabelPartBuilder
void VisitAutoType(const AutoType *AT) {
if (!AT->isDeduced() || AT->getDeducedType()->isDecltypeType())
return;
+ maybeAddQualifiers();
CurrentTypeRAII Guard(*this, AT->getDeducedType(),
ShouldAddLinksToTagTypes);
return Visit(AT->getDeducedType().getTypePtr());
}
void VisitElaboratedType(const ElaboratedType *ET) {
+ maybeAddQualifiers();
if (auto *NNS = ET->getQualifier()) {
switch (NNS->getKind()) {
case NestedNameSpecifier::Identifier:
@@ -574,7 +584,26 @@ class TypeInlayHintLabelPartBuilder
return Visit(ET->getNamedType().getTypePtr());
}
+ void VisitReferenceType(const ReferenceType *RT) {
+ maybeAddQualifiers();
+ CurrentTypeRAII Guard(*this, RT->getPointeeTypeAsWritten(), ShouldAddLinksToTagTypes);
+ Visit(RT->getPointeeTypeAsWritten().getTypePtr());
+ if (RT->isLValueReferenceType())
+ addLabel(" &");
+ if (RT->isRValueReferenceType())
+ addLabel(" &&");
+ }
+
+ void VisitPointerType(const PointerType *PT) {
+ maybeAddQualifiers();
+ CurrentTypeRAII Guard(*this, PT->getPointeeType(),
+ ShouldAddLinksToTagTypes);
+ Visit(PT->getPointeeType().getTypePtr());
+ addLabel(" *");
+ }
+
void VisitTemplateSpecializationType(const TemplateSpecializationType *TST) {
+ maybeAddQualifiers();
SourceRange Range;
if (auto *Specialization =
dyn_cast_if_present<ClassTemplateSpecializationDecl>(
@@ -585,6 +614,7 @@ class TypeInlayHintLabelPartBuilder
}
void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *ST) {
+ maybeAddQualifiers();
CurrentTypeRAII Guard(*this, ST->getReplacementType(),
/*ShouldAddLinksToTagTypes=*/true);
return Visit(ST->getReplacementType().getTypePtr());
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index 2464172a3c68a..238a0a120a46e 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -57,13 +57,13 @@ struct ExpectedHint {
struct ExpectedHintLabelPiece {
std::string Label;
- std::optional<std::string> TargetRangeName = std::nullopt;
+ std::optional<std::string> PointsTo = std::nullopt;
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &Stream,
const ExpectedHintLabelPiece &Hint) {
Stream << Hint.Label;
- if (Hint.TargetRangeName)
- Stream << " that points to $" << Hint.TargetRangeName;
+ if (Hint.PointsTo)
+ Stream << " that points to $" << Hint.PointsTo;
return Stream;
}
};
@@ -89,26 +89,26 @@ MATCHER_P2(HintMatcher, Expected, Code, llvm::to_string(Expected)) {
MATCHER_P2(HintLabelPieceMatcher, Expected, Code, llvm::to_string(Expected)) {
llvm::StringRef ExpectedView(Expected.Label);
std::string ResultLabel = arg.value;
- if (ResultLabel != ExpectedView.trim(" ")) {
+ if (ResultLabel != ExpectedView) {
*result_listener << "label is '" << ResultLabel << "'";
return false;
}
- if (!Expected.TargetRangeName && !arg.location)
+ if (!Expected.PointsTo && !arg.location)
return true;
- if (Expected.TargetRangeName && !arg.location) {
- *result_listener << " range " << *Expected.TargetRangeName
+ if (Expected.PointsTo && !arg.location) {
+ *result_listener << " range " << *Expected.PointsTo
<< " is expected, but we have nothing.";
return false;
}
- if (!Expected.TargetRangeName && arg.location) {
+ if (!Expected.PointsTo && arg.location) {
*result_listener << " the link points to " << llvm::to_string(arg.location)
<< ", but we expect nothing.";
return false;
}
- if (arg.location->range != Code.range(*Expected.TargetRangeName)) {
+ if (arg.location->range != Code.range(*Expected.PointsTo)) {
*result_listener << "range is " << llvm::to_string(arg.location->range)
- << " but $" << *Expected.TargetRangeName << " points to "
- << llvm::to_string(Code.range(*Expected.TargetRangeName));
+ << " but $" << *Expected.PointsTo << " points to "
+ << llvm::to_string(Code.range(*Expected.PointsTo));
return false;
}
return true;
@@ -1678,8 +1678,33 @@ TEST(TypeHints, SubstTemplateParameterAliases) {
ExpectedHint{": static_vector<int>", "vector_name"});
}
+template <typename ...Labels>
+void assertTypeLinkHints(StringRef Code, StringRef HintRange, Labels ...ExpectedLabels) {
+ Annotations Source(Code);
+ auto HintAt = [&](llvm::ArrayRef<InlayHint> InlayHints,
+ llvm::StringRef Range) {
+ auto *Hint = llvm::find_if(InlayHints, [&](const InlayHint &InlayHint) {
+ return InlayHint.range == Source.range(Range);
+ });
+ assert(Hint && "No range was found");
+ return llvm::ArrayRef(Hint->label);
+ };
+
+ TestTU TU = TestTU::withCode(Source.code());
+ TU.ExtraArgs.push_back("-std=c++2c");
+ auto AST = TU.build();
+
+ Config C;
+ C.InlayHints.TypeNameLimit = 0;
+ WithContextValue WithCfg(Config::Key, std::move(C));
+
+ auto Hints = hintsOfKind(AST, InlayHintKind::Type);
+ EXPECT_THAT(HintAt(Hints, HintRange),
+ ElementsAre(HintLabelPieceMatcher(ExpectedLabels, Source)...));
+}
+
TEST(TypeHints, Links) {
- Annotations Source(R"cpp(
+ StringRef Source(R"cpp(
$Package[[template <class T, class U>
struct Package {]]};
@@ -1701,89 +1726,74 @@ TEST(TypeHints, Links) {
};
namespace ns {
- template <class T>
+ $Nested[[template <class T>
struct Nested {
- template <class U>
- struct Class {
+ $NestedClass[[template <class U>
+ struct ]]Class {
};
- };
+ ]]};
}
- void foo() {
+ void basic() {
auto $1[[C]] = Container<Package<char, int>>();
auto $2[[D]] = Container<Package<float, const int>>();
auto $3[[E]] = Container<Container<int, int>, long>();
auto $4[[F]] = NttpContainer<D, E, ScopedEnum::X, Enum::E>();
auto $5[[G]] = ns::Nested<Container<int>>::Class<Package<char, int>>();
}
- )cpp");
- TestTU TU = TestTU::withCode(Source.code());
- TU.ExtraArgs.push_back("-std=c++2c");
- auto AST = TU.build();
-
- auto HintAt = [&](llvm::ArrayRef<InlayHint> InlayHints,
- llvm::StringRef Range) {
- auto *Hint = llvm::find_if(InlayHints, [&](const InlayHint &InlayHint) {
- return InlayHint.range == Source.range(Range);
- });
- assert(Hint && "No range was found");
- return llvm::ArrayRef(Hint->label);
- };
- Config C;
- C.InlayHints.TypeNameLimit = 0;
- WithContextValue WithCfg(Config::Key, std::move(C));
+ void compounds() {
+ auto $6[[A]] = Container<ns::Nested<int>::Class<float>&>();
+ auto $7[[B]] = Container<ns::Nested<int>::Class<float>&&>();
+ auto $8[[C]] = Container<ns::Nested<int>::Class<const Container<int>> *>();
+ }
- auto Hints = hintsOfKind(AST, InlayHintKind::Type);
+ )cpp");
- EXPECT_THAT(
- HintAt(Hints, "1"),
- ElementsAre(
- HintLabelPieceMatcher(ExpectedHintLabelPiece{": Container<"}, Source),
- HintLabelPieceMatcher(ExpectedHintLabelPiece{"Package", "Package"},
- Source),
- HintLabelPieceMatcher(ExpectedHintLabelPiece{"<char, int>>"},
- Source)));
-
- EXPECT_THAT(
- HintAt(Hints, "2"),
- ElementsAre(
- HintLabelPieceMatcher(ExpectedHintLabelPiece{": Container<"}, Source),
- HintLabelPieceMatcher(
- ExpectedHintLabelPiece{"Package", "SpecializationOfPackage"},
- Source),
- HintLabelPieceMatcher(ExpectedHintLabelPiece{"<float, const int>>"},
- Source)));
-
- EXPECT_THAT(
- HintAt(Hints, "3"),
- ElementsAre(
- HintLabelPieceMatcher(ExpectedHintLabelPiece{": Container<"}, Source),
- HintLabelPieceMatcher(
- ExpectedHintLabelPiece{"Container", "Container"}, Source),
- HintLabelPieceMatcher(ExpectedHintLabelPiece{"<int, int>, long>"},
- Source)));
-
- EXPECT_THAT(HintAt(Hints, "4"),
- ElementsAre(HintLabelPieceMatcher(
- ExpectedHintLabelPiece{
- ": NttpContainer<D, E, ScopedEnum::X, Enum::E>"},
- Source)));
-
- EXPECT_THAT(
- HintAt(Hints, "5"),
- ElementsAre(
- HintLabelPieceMatcher(ExpectedHintLabelPiece{": Nested<"}, Source),
- HintLabelPieceMatcher(
- ExpectedHintLabelPiece{"Container", "Container"}, Source),
- // We don't have links on the inner 'Class' because the location is
- // where the 'auto' links to.
- HintLabelPieceMatcher(ExpectedHintLabelPiece{"<int>>::Class<"},
- Source),
- HintLabelPieceMatcher(ExpectedHintLabelPiece{"Package", "Package"},
- Source),
- HintLabelPieceMatcher(ExpectedHintLabelPiece{"<char, int>>"},
- Source)));
+ assertTypeLinkHints(Source, "1", ExpectedHintLabelPiece{": Container<"},
+ ExpectedHintLabelPiece{"Package", "Package"},
+ ExpectedHintLabelPiece{"<char, int>>"});
+
+ assertTypeLinkHints(
+ Source, "2", ExpectedHintLabelPiece{": Container<"},
+ ExpectedHintLabelPiece{"Package", "SpecializationOfPackage"},
+ ExpectedHintLabelPiece{"<float, const int>>"});
+
+ assertTypeLinkHints(Source, "3", ExpectedHintLabelPiece{": Container<"},
+ ExpectedHintLabelPiece{"Container", "Container"},
+ ExpectedHintLabelPiece{"<int, int>, long>"});
+
+ assertTypeLinkHints(
+ Source, "4",
+ ExpectedHintLabelPiece{": NttpContainer<D, E, ScopedEnum::X, Enum::E>"});
+
+ assertTypeLinkHints(Source, "5", ExpectedHintLabelPiece{": Nested<"},
+ ExpectedHintLabelPiece{"Container", "Container"},
+ // We don't have links on the inner 'Class' because the
+ // location is where the 'auto' links to.
+ ExpectedHintLabelPiece{"<int>>::Class<"},
+ ExpectedHintLabelPiece{"Package", "Package"},
+ ExpectedHintLabelPiece{"<char, int>>"});
+
+ assertTypeLinkHints(Source, "6", ExpectedHintLabelPiece{": Container<"},
+ ExpectedHintLabelPiece{"Nested", "Nested"},
+ ExpectedHintLabelPiece{"<int>::"},
+ ExpectedHintLabelPiece{"Class", "NestedClass"},
+ ExpectedHintLabelPiece{"<float> &>"});
+
+ assertTypeLinkHints(Source, "7", ExpectedHintLabelPiece{": Container<"},
+ ExpectedHintLabelPiece{"Nested", "Nested"},
+ ExpectedHintLabelPiece{"<int>::"},
+ ExpectedHintLabelPiece{"Class", "NestedClass"},
+ ExpectedHintLabelPiece{"<float> &&>"});
+
+ assertTypeLinkHints(Source, "8", ExpectedHintLabelPiece{": Container<"},
+ ExpectedHintLabelPiece{"Nested", "Nested"},
+ ExpectedHintLabelPiece{"<int>::"},
+ ExpectedHintLabelPiece{"Class", "NestedClass"},
+ ExpectedHintLabelPiece{"<const "},
+ ExpectedHintLabelPiece{"Container", "Container"},
+ ExpectedHintLabelPiece{"<int>> *>"});
}
TEST(DesignatorHints, Basic) {
>From 48e21a102c11d4e9f6f6258bbe68085354ce6ef8 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 1 Apr 2024 13:29:38 +0800
Subject: [PATCH 07/36] Forgot to format, sorry
---
clang-tools-extra/clangd/InlayHints.cpp | 3 ++-
clang-tools-extra/clangd/unittests/InlayHintTests.cpp | 5 +++--
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index c28e7671cb401..73701c702e3f5 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -586,7 +586,8 @@ class TypeInlayHintLabelPartBuilder
void VisitReferenceType(const ReferenceType *RT) {
maybeAddQualifiers();
- CurrentTypeRAII Guard(*this, RT->getPointeeTypeAsWritten(), ShouldAddLinksToTagTypes);
+ CurrentTypeRAII Guard(*this, RT->getPointeeTypeAsWritten(),
+ ShouldAddLinksToTagTypes);
Visit(RT->getPointeeTypeAsWritten().getTypePtr());
if (RT->isLValueReferenceType())
addLabel(" &");
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index 238a0a120a46e..a69422d825f30 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1678,8 +1678,9 @@ TEST(TypeHints, SubstTemplateParameterAliases) {
ExpectedHint{": static_vector<int>", "vector_name"});
}
-template <typename ...Labels>
-void assertTypeLinkHints(StringRef Code, StringRef HintRange, Labels ...ExpectedLabels) {
+template <typename... Labels>
+void assertTypeLinkHints(StringRef Code, StringRef HintRange,
+ Labels... ExpectedLabels) {
Annotations Source(Code);
auto HintAt = [&](llvm::ArrayRef<InlayHint> InlayHints,
llvm::StringRef Range) {
>From 8c8c036994783c70a16446771a703c6c99094805 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 1 Apr 2024 14:02:26 +0800
Subject: [PATCH 08/36] Fix regressions
---
clang-tools-extra/clangd/InlayHints.cpp | 32 ++++++++++++++++++-------
1 file changed, 23 insertions(+), 9 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 73701c702e3f5..7a842e2f76423 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -514,6 +514,14 @@ class TypeInlayHintLabelPartBuilder
}
}
+ // 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,
const PrintingPolicy &PP,
@@ -586,21 +594,27 @@ class TypeInlayHintLabelPartBuilder
void VisitReferenceType(const ReferenceType *RT) {
maybeAddQualifiers();
- CurrentTypeRAII Guard(*this, RT->getPointeeTypeAsWritten(),
- ShouldAddLinksToTagTypes);
- Visit(RT->getPointeeTypeAsWritten().getTypePtr());
+ QualType Next = skipTopLevelReferences(RT->getPointeeTypeAsWritten());
+ CurrentTypeRAII Guard(*this, Next, ShouldAddLinksToTagTypes);
+ Visit(Next.getTypePtr());
+ if (Next->getPointeeType().isNull())
+ addLabel(" ");
if (RT->isLValueReferenceType())
- addLabel(" &");
+ addLabel("&");
if (RT->isRValueReferenceType())
- addLabel(" &&");
+ addLabel("&&");
}
void VisitPointerType(const PointerType *PT) {
+ QualType Next = PT->getPointeeType();
+ std::optional<CurrentTypeRAII> Guard(std::in_place, *this, Next,
+ ShouldAddLinksToTagTypes);
+ Visit(Next.getTypePtr());
+ if (Next->getPointeeType().isNull())
+ addLabel(" ");
+ addLabel("*");
+ Guard.reset();
maybeAddQualifiers();
- CurrentTypeRAII Guard(*this, PT->getPointeeType(),
- ShouldAddLinksToTagTypes);
- Visit(PT->getPointeeType().getTypePtr());
- addLabel(" *");
}
void VisitTemplateSpecializationType(const TemplateSpecializationType *TST) {
>From 0e25c18f6722d4afcf0a479fd0e88ba8f26bb218 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 1 Apr 2024 14:59:44 +0800
Subject: [PATCH 09/36] Initialize ShouldAddLinksToTagTypes
---
clang-tools-extra/clangd/InlayHints.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 7a842e2f76423..8d8fbc4bcde04 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -396,7 +396,7 @@ class TypeInlayHintLabelPartBuilder
const PrintingPolicy &PP;
std::vector<InlayHintLabelPart> &LabelChunks;
- bool ShouldAddLinksToTagTypes = false;
+ bool ShouldAddLinksToTagTypes;
struct CurrentTypeRAII {
TypeInlayHintLabelPartBuilder &Builder;
@@ -529,7 +529,8 @@ class TypeInlayHintLabelPartBuilder
llvm::StringRef Prefix,
std::vector<InlayHintLabelPart> &LabelChunks)
: CurrentType(Current), Context(Context), PP(PP),
- LabelChunks(LabelChunks) {
+ LabelChunks(LabelChunks),
+ ShouldAddLinksToTagTypes(ShouldAddLinksToTagTypes) {
LabelChunks.reserve(16);
if (!Prefix.empty())
addLabel(Prefix.str());
>From 20607772fdc6e2f054485c3a56ebeab2feaf9e10 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 12 Apr 2024 17:58:32 +0800
Subject: [PATCH 10/36] Handle TypeAliases & UsingTypes
---
clang-tools-extra/clangd/InlayHints.cpp | 15 +++++++++++++++
.../clangd/unittests/InlayHintTests.cpp | 19 +++++++++++++++++++
2 files changed, 34 insertions(+)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 8d8fbc4bcde04..9c02f3307611a 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -618,6 +618,21 @@ class TypeInlayHintLabelPartBuilder
maybeAddQualifiers();
}
+ void VisitUsingType(const UsingType *UT) {
+ addLabel([&](llvm::raw_ostream &OS) { UT->getFoundDecl()->printName(OS); },
+ [&] {
+ BaseUsingDecl *Introducer = UT->getFoundDecl()->getIntroducer();
+ if (auto *UD = dyn_cast<UsingDecl>(Introducer))
+ return UD->getSourceRange();
+ return Introducer->getSourceRange();
+ });
+ }
+
+ void VisitTypedefType(const TypedefType *TT) {
+ addLabel([&](llvm::raw_ostream &OS) { TT->getDecl()->printName(OS); },
+ [&] { return TT->getDecl()->getSourceRange(); });
+ }
+
void VisitTemplateSpecializationType(const TemplateSpecializationType *TST) {
maybeAddQualifiers();
SourceRange Range;
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index a69422d825f30..a43273a35fcd9 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1733,6 +1733,8 @@ TEST(TypeHints, Links) {
struct ]]Class {
};
]]};
+
+ $NestedInt[[using NestedInt = Nested<int]]>;
}
void basic() {
@@ -1749,6 +1751,15 @@ TEST(TypeHints, Links) {
auto $8[[C]] = Container<ns::Nested<int>::Class<const Container<int>> *>();
}
+ namespace nns {
+ $UsingShadow[[using ns::]]NestedInt;
+
+ void aliases() {
+ auto $9[[A]] = Container<NestedInt>();
+ auto $10[[B]] = Container<ns::NestedInt>();
+ }
+ }
+
)cpp");
assertTypeLinkHints(Source, "1", ExpectedHintLabelPiece{": Container<"},
@@ -1795,6 +1806,14 @@ TEST(TypeHints, Links) {
ExpectedHintLabelPiece{"<const "},
ExpectedHintLabelPiece{"Container", "Container"},
ExpectedHintLabelPiece{"<int>> *>"});
+
+ assertTypeLinkHints(Source, "9", ExpectedHintLabelPiece{": Container<"},
+ ExpectedHintLabelPiece{"NestedInt", "UsingShadow"},
+ ExpectedHintLabelPiece{">"});
+
+ assertTypeLinkHints(Source, "10", ExpectedHintLabelPiece{": Container<"},
+ ExpectedHintLabelPiece{"NestedInt", "NestedInt"},
+ ExpectedHintLabelPiece{">"});
}
TEST(DesignatorHints, Basic) {
>From acbf62ed201f9ad22dde81dbc870b4cd056b8f9b Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sat, 1 Jun 2024 19:22:45 +0800
Subject: [PATCH 11/36] Fix printing on NNS; Always offer links; Address some
of the feedback
---
clang-tools-extra/clangd/InlayHints.cpp | 76 ++++++++++---------
.../clangd/unittests/InlayHintTests.cpp | 75 +++++++++++-------
2 files changed, 87 insertions(+), 64 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 9c02f3307611a..39bf40c0e8b00 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -392,26 +392,26 @@ std::optional<Location> toLocation(SourceManager &SM, SourceRange Range) {
class TypeInlayHintLabelPartBuilder
: public TypeVisitor<TypeInlayHintLabelPartBuilder> {
QualType CurrentType;
+ NestedNameSpecifier *CurrentNestedNameSpecifier;
ASTContext &Context;
const PrintingPolicy &PP;
std::vector<InlayHintLabelPart> &LabelChunks;
- bool ShouldAddLinksToTagTypes;
-
struct CurrentTypeRAII {
TypeInlayHintLabelPartBuilder &Builder;
QualType PreviousType;
- bool PreviousShouldAddLinksToTagTypes;
+ NestedNameSpecifier *PreviousNestedNameSpecifier;
CurrentTypeRAII(TypeInlayHintLabelPartBuilder &Builder, QualType New,
- bool ShouldAddLinksToTagTypes)
+ NestedNameSpecifier *NNS = nullptr)
: Builder(Builder), PreviousType(Builder.CurrentType),
- PreviousShouldAddLinksToTagTypes(Builder.ShouldAddLinksToTagTypes) {
+ PreviousNestedNameSpecifier(Builder.CurrentNestedNameSpecifier) {
Builder.CurrentType = New;
- Builder.ShouldAddLinksToTagTypes = ShouldAddLinksToTagTypes;
+ if (NNS)
+ Builder.CurrentNestedNameSpecifier = NNS;
}
~CurrentTypeRAII() {
Builder.CurrentType = PreviousType;
- Builder.ShouldAddLinksToTagTypes = PreviousShouldAddLinksToTagTypes;
+ Builder.CurrentNestedNameSpecifier = PreviousNestedNameSpecifier;
}
};
@@ -420,8 +420,6 @@ class TypeInlayHintLabelPartBuilder
std::string Label;
llvm::raw_string_ostream OS(Label);
NamePrinter(OS);
- if (!ShouldAddLinksToTagTypes)
- return addLabel(std::move(Label));
auto &Name = LabelChunks.emplace_back();
Name.value = std::move(Label);
Name.location = toLocation(Context.getSourceManager(), SourceRangeGetter());
@@ -458,8 +456,7 @@ class TypeInlayHintLabelPartBuilder
case TemplateArgument::Pack:
return printTemplateArgumentList(TA.pack_elements());
case TemplateArgument::Type: {
- CurrentTypeRAII Guard(*this, TA.getAsType(),
- /*ShouldAddLinksToTagTypes=*/true);
+ CurrentTypeRAII Guard(*this, TA.getAsType());
return Visit(TA.getAsType().getTypePtr());
}
// TODO: Add support for NTTP arguments.
@@ -485,16 +482,32 @@ class TypeInlayHintLabelPartBuilder
SourceRange TemplateNameRange = SourceRange()) {
SourceRange Range;
TemplateDecl *TD = nullptr;
+ auto PrintType = TemplateName::Qualified::AsWritten;
switch (TN.getKind()) {
case TemplateName::Template:
TD = TN.getAsTemplateDecl();
Range = TD->getSourceRange();
- LLVM_FALLTHROUGH;
- default:
+ 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); },
+ addLabel([&](llvm::raw_ostream &OS) { TN.print(OS, PP, PrintType); },
[&] {
if (TemplateNameRange.isValid())
return TemplateNameRange;
@@ -525,12 +538,10 @@ class TypeInlayHintLabelPartBuilder
public:
TypeInlayHintLabelPartBuilder(QualType Current, ASTContext &Context,
const PrintingPolicy &PP,
- bool ShouldAddLinksToTagTypes,
llvm::StringRef Prefix,
std::vector<InlayHintLabelPart> &LabelChunks)
- : CurrentType(Current), Context(Context), PP(PP),
- LabelChunks(LabelChunks),
- ShouldAddLinksToTagTypes(ShouldAddLinksToTagTypes) {
+ : CurrentType(Current), CurrentNestedNameSpecifier(nullptr), Context(Context), PP(PP),
+ LabelChunks(LabelChunks) {
LabelChunks.reserve(16);
if (!Prefix.empty())
addLabel(Prefix.str());
@@ -539,8 +550,6 @@ class TypeInlayHintLabelPartBuilder
void VisitType(const Type *) { addLabel(CurrentType.getAsString(PP)); }
void VisitTagType(const TagType *TT) {
- if (!ShouldAddLinksToTagTypes)
- return VisitType(TT);
auto *D = TT->getDecl();
if (auto *Specialization = dyn_cast<ClassTemplateSpecializationDecl>(D))
return handleTemplateSpecialization(
@@ -558,8 +567,7 @@ class TypeInlayHintLabelPartBuilder
if (!AT->isDeduced() || AT->getDeducedType()->isDecltypeType())
return;
maybeAddQualifiers();
- CurrentTypeRAII Guard(*this, AT->getDeducedType(),
- ShouldAddLinksToTagTypes);
+ CurrentTypeRAII Guard(*this, AT->getDeducedType());
return Visit(AT->getDeducedType().getTypePtr());
}
@@ -582,21 +590,21 @@ class TypeInlayHintLabelPartBuilder
*this,
QualType(
NNS->getAsType(),
- /*Quals=*/0), // Do we need cv-qualifiers on type specifiers?
- ShouldAddLinksToTagTypes);
+ /*Quals=*/0) // Do we need cv-qualifiers on type specifiers?
+ );
Visit(NNS->getAsType());
addLabel("::");
break;
}
}
- CurrentTypeRAII Guard(*this, ET->getNamedType(), ShouldAddLinksToTagTypes);
+ CurrentTypeRAII Guard(*this, ET->getNamedType(), ET->getQualifier());
return Visit(ET->getNamedType().getTypePtr());
}
void VisitReferenceType(const ReferenceType *RT) {
maybeAddQualifiers();
QualType Next = skipTopLevelReferences(RT->getPointeeTypeAsWritten());
- CurrentTypeRAII Guard(*this, Next, ShouldAddLinksToTagTypes);
+ CurrentTypeRAII Guard(*this, Next);
Visit(Next.getTypePtr());
if (Next->getPointeeType().isNull())
addLabel(" ");
@@ -608,8 +616,7 @@ class TypeInlayHintLabelPartBuilder
void VisitPointerType(const PointerType *PT) {
QualType Next = PT->getPointeeType();
- std::optional<CurrentTypeRAII> Guard(std::in_place, *this, Next,
- ShouldAddLinksToTagTypes);
+ std::optional<CurrentTypeRAII> Guard(std::in_place, *this, Next);
Visit(Next.getTypePtr());
if (Next->getPointeeType().isNull())
addLabel(" ");
@@ -646,8 +653,7 @@ class TypeInlayHintLabelPartBuilder
void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *ST) {
maybeAddQualifiers();
- CurrentTypeRAII Guard(*this, ST->getReplacementType(),
- /*ShouldAddLinksToTagTypes=*/true);
+ CurrentTypeRAII Guard(*this, ST->getReplacementType());
return Visit(ST->getReplacementType().getTypePtr());
}
};
@@ -1332,17 +1338,15 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
// type in other cases.
auto Desugared = maybeDesugar(AST, T);
std::vector<InlayHintLabelPart> Chunks;
- TypeInlayHintLabelPartBuilder Builder(
- Desugared, AST, TypeHintPolicy,
- /*ShouldAddLinksToTagTypes=*/T != Desugared, Prefix, Chunks);
+ TypeInlayHintLabelPartBuilder Builder(Desugared, AST, TypeHintPolicy,
+ Prefix, Chunks);
Builder.Visit(Desugared.getTypePtr());
if (T != Desugared && !shouldPrintTypeHint(Chunks)) {
// If the desugared type is too long to display, fallback to the sugared
// type.
Chunks.clear();
- TypeInlayHintLabelPartBuilder Builder(T, AST, TypeHintPolicy,
- /*ShouldAddLinksToTagTypes=*/false,
- Prefix, Chunks);
+ TypeInlayHintLabelPartBuilder Builder(T, AST, TypeHintPolicy, Prefix,
+ Chunks);
Builder.Visit(T.getTypePtr());
}
if (shouldPrintTypeHint(Chunks))
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index a43273a35fcd9..b719984d47164 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -57,6 +57,7 @@ struct ExpectedHint {
struct ExpectedHintLabelPiece {
std::string Label;
+ // Stores the name of a range annotation
std::optional<std::string> PointsTo = std::nullopt;
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &Stream,
@@ -87,9 +88,8 @@ MATCHER_P2(HintMatcher, Expected, Code, llvm::to_string(Expected)) {
}
MATCHER_P2(HintLabelPieceMatcher, Expected, Code, llvm::to_string(Expected)) {
- llvm::StringRef ExpectedView(Expected.Label);
std::string ResultLabel = arg.value;
- if (ResultLabel != ExpectedView) {
+ if (ResultLabel != Expected.Label) {
*result_listener << "label is '" << ResultLabel << "'";
return false;
}
@@ -1682,14 +1682,6 @@ template <typename... Labels>
void assertTypeLinkHints(StringRef Code, StringRef HintRange,
Labels... ExpectedLabels) {
Annotations Source(Code);
- auto HintAt = [&](llvm::ArrayRef<InlayHint> InlayHints,
- llvm::StringRef Range) {
- auto *Hint = llvm::find_if(InlayHints, [&](const InlayHint &InlayHint) {
- return InlayHint.range == Source.range(Range);
- });
- assert(Hint && "No range was found");
- return llvm::ArrayRef(Hint->label);
- };
TestTU TU = TestTU::withCode(Source.code());
TU.ExtraArgs.push_back("-std=c++2c");
@@ -1700,7 +1692,11 @@ void assertTypeLinkHints(StringRef Code, StringRef HintRange,
WithContextValue WithCfg(Config::Key, std::move(C));
auto Hints = hintsOfKind(AST, InlayHintKind::Type);
- EXPECT_THAT(HintAt(Hints, HintRange),
+ auto Hint = llvm::find_if(Hints, [&](const InlayHint &InlayHint) {
+ return InlayHint.range == Source.range(HintRange);
+ });
+ ASSERT_TRUE(Hint != Hints.end()) << "No hint was found at " << HintRange;
+ EXPECT_THAT(Hint->label,
ElementsAre(HintLabelPieceMatcher(ExpectedLabels, Source)...));
}
@@ -1761,57 +1757,80 @@ TEST(TypeHints, Links) {
}
)cpp");
-
- assertTypeLinkHints(Source, "1", ExpectedHintLabelPiece{": Container<"},
+ assertTypeLinkHints(Source, "1",
+ ExpectedHintLabelPiece{": "},
+ ExpectedHintLabelPiece{"Container", "Container"},
+ ExpectedHintLabelPiece{"<"},
ExpectedHintLabelPiece{"Package", "Package"},
ExpectedHintLabelPiece{"<char, int>>"});
assertTypeLinkHints(
- Source, "2", ExpectedHintLabelPiece{": Container<"},
+ Source, "2",
+ ExpectedHintLabelPiece{": "},
+ ExpectedHintLabelPiece{"Container", "Container"},
+ ExpectedHintLabelPiece{"<"},
ExpectedHintLabelPiece{"Package", "SpecializationOfPackage"},
ExpectedHintLabelPiece{"<float, const int>>"});
- assertTypeLinkHints(Source, "3", ExpectedHintLabelPiece{": Container<"},
+ assertTypeLinkHints(Source, "3",
+ ExpectedHintLabelPiece{": "},
+ ExpectedHintLabelPiece{"Container", "Container"},
+ ExpectedHintLabelPiece{"<"},
ExpectedHintLabelPiece{"Container", "Container"},
ExpectedHintLabelPiece{"<int, int>, long>"});
+ // TODO: Support links on NTTP arguments
assertTypeLinkHints(
Source, "4",
- ExpectedHintLabelPiece{": NttpContainer<D, E, ScopedEnum::X, Enum::E>"});
+ ExpectedHintLabelPiece{": "},
+ ExpectedHintLabelPiece{"NttpContainer", "NttpContainer"},
+ ExpectedHintLabelPiece{"<D, E, ScopedEnum::X, Enum::E>"});
- assertTypeLinkHints(Source, "5", ExpectedHintLabelPiece{": Nested<"},
+ assertTypeLinkHints(Source, "5", ExpectedHintLabelPiece{": "},
+ ExpectedHintLabelPiece{"ns::Nested", "Nested"},
+ ExpectedHintLabelPiece{"<"},
ExpectedHintLabelPiece{"Container", "Container"},
- // We don't have links on the inner 'Class' because the
- // location is where the 'auto' links to.
- ExpectedHintLabelPiece{"<int>>::Class<"},
+ ExpectedHintLabelPiece{"<int>>::"},
+ ExpectedHintLabelPiece{"Class", "NestedClass"},
+ ExpectedHintLabelPiece{"<"},
ExpectedHintLabelPiece{"Package", "Package"},
ExpectedHintLabelPiece{"<char, int>>"});
- assertTypeLinkHints(Source, "6", ExpectedHintLabelPiece{": Container<"},
- ExpectedHintLabelPiece{"Nested", "Nested"},
+ assertTypeLinkHints(Source, "6", ExpectedHintLabelPiece{": "},
+ ExpectedHintLabelPiece{"Container", "Container"},
+ ExpectedHintLabelPiece{"<"},
+ ExpectedHintLabelPiece{"ns::Nested", "Nested"},
ExpectedHintLabelPiece{"<int>::"},
ExpectedHintLabelPiece{"Class", "NestedClass"},
ExpectedHintLabelPiece{"<float> &>"});
- assertTypeLinkHints(Source, "7", ExpectedHintLabelPiece{": Container<"},
- ExpectedHintLabelPiece{"Nested", "Nested"},
+ assertTypeLinkHints(Source, "7", ExpectedHintLabelPiece{": "},
+ ExpectedHintLabelPiece{"Container", "Container"},
+ ExpectedHintLabelPiece{"<"},
+ ExpectedHintLabelPiece{"ns::Nested", "Nested"},
ExpectedHintLabelPiece{"<int>::"},
ExpectedHintLabelPiece{"Class", "NestedClass"},
ExpectedHintLabelPiece{"<float> &&>"});
- assertTypeLinkHints(Source, "8", ExpectedHintLabelPiece{": Container<"},
- ExpectedHintLabelPiece{"Nested", "Nested"},
+ assertTypeLinkHints(Source, "8", ExpectedHintLabelPiece{": "},
+ ExpectedHintLabelPiece{"Container", "Container"},
+ ExpectedHintLabelPiece{"<"},
+ ExpectedHintLabelPiece{"ns::Nested", "Nested"},
ExpectedHintLabelPiece{"<int>::"},
ExpectedHintLabelPiece{"Class", "NestedClass"},
ExpectedHintLabelPiece{"<const "},
ExpectedHintLabelPiece{"Container", "Container"},
ExpectedHintLabelPiece{"<int>> *>"});
- assertTypeLinkHints(Source, "9", ExpectedHintLabelPiece{": Container<"},
+ assertTypeLinkHints(Source, "9", ExpectedHintLabelPiece{": "},
+ ExpectedHintLabelPiece{"Container", "Container"},
+ ExpectedHintLabelPiece{"<"},
ExpectedHintLabelPiece{"NestedInt", "UsingShadow"},
ExpectedHintLabelPiece{">"});
- assertTypeLinkHints(Source, "10", ExpectedHintLabelPiece{": Container<"},
+ assertTypeLinkHints(Source, "10", ExpectedHintLabelPiece{": "},
+ ExpectedHintLabelPiece{"Container", "Container"},
+ ExpectedHintLabelPiece{"<"},
ExpectedHintLabelPiece{"NestedInt", "NestedInt"},
ExpectedHintLabelPiece{">"});
}
>From d862ce4263fbc5a4fd29996d7dec30754506e7b5 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sat, 1 Jun 2024 20:13:26 +0800
Subject: [PATCH 12/36] Forget to print non-type specifiers. Oof
---
clang-tools-extra/clangd/InlayHints.cpp | 3 ++-
clang-tools-extra/clangd/unittests/InlayHintTests.cpp | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 39bf40c0e8b00..878ed2b799c32 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -491,7 +491,7 @@ class TypeInlayHintLabelPartBuilder
case TemplateName::QualifiedTemplate:
if (NestedNameSpecifier *NNS =
TN.getAsQualifiedTemplateName()->getQualifier();
- NNS == CurrentNestedNameSpecifier) {
+ NNS && NNS == CurrentNestedNameSpecifier) {
// We have handled the NNS in VisitElaboratedType(). Avoid printing it
// twice.
TN = TN.getAsQualifiedTemplateName()->getUnderlyingTemplate();
@@ -582,6 +582,7 @@ class TypeInlayHintLabelPartBuilder
case NestedNameSpecifier::Super: {
std::string Label;
llvm::raw_string_ostream OS(Label);
+ NNS->print(OS, PP);
addLabel(std::move(Label));
} break;
case NestedNameSpecifier::TypeSpec:
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index b719984d47164..fe1fd6eda863c 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1779,7 +1779,7 @@ TEST(TypeHints, Links) {
ExpectedHintLabelPiece{"Container", "Container"},
ExpectedHintLabelPiece{"<int, int>, long>"});
- // TODO: Support links on NTTP arguments
+ // TODO: Support links on NTTP arguments.
assertTypeLinkHints(
Source, "4",
ExpectedHintLabelPiece{": "},
>From 9ebbbd6170b0ffd733e0416727bd557d358c9269 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sat, 1 Jun 2024 20:14:10 +0800
Subject: [PATCH 13/36] Run clang-format
---
clang-tools-extra/clangd/InlayHints.cpp | 4 ++--
.../clangd/unittests/InlayHintTests.cpp | 17 ++++++-----------
2 files changed, 8 insertions(+), 13 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 878ed2b799c32..3a010adf95e32 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -540,8 +540,8 @@ class TypeInlayHintLabelPartBuilder
const PrintingPolicy &PP,
llvm::StringRef Prefix,
std::vector<InlayHintLabelPart> &LabelChunks)
- : CurrentType(Current), CurrentNestedNameSpecifier(nullptr), Context(Context), PP(PP),
- LabelChunks(LabelChunks) {
+ : CurrentType(Current), CurrentNestedNameSpecifier(nullptr),
+ Context(Context), PP(PP), LabelChunks(LabelChunks) {
LabelChunks.reserve(16);
if (!Prefix.empty())
addLabel(Prefix.str());
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index fe1fd6eda863c..05598fd973610 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1757,34 +1757,29 @@ TEST(TypeHints, Links) {
}
)cpp");
- assertTypeLinkHints(Source, "1",
- ExpectedHintLabelPiece{": "},
+ assertTypeLinkHints(Source, "1", ExpectedHintLabelPiece{": "},
ExpectedHintLabelPiece{"Container", "Container"},
ExpectedHintLabelPiece{"<"},
ExpectedHintLabelPiece{"Package", "Package"},
ExpectedHintLabelPiece{"<char, int>>"});
assertTypeLinkHints(
- Source, "2",
- ExpectedHintLabelPiece{": "},
+ Source, "2", ExpectedHintLabelPiece{": "},
ExpectedHintLabelPiece{"Container", "Container"},
ExpectedHintLabelPiece{"<"},
ExpectedHintLabelPiece{"Package", "SpecializationOfPackage"},
ExpectedHintLabelPiece{"<float, const int>>"});
- assertTypeLinkHints(Source, "3",
- ExpectedHintLabelPiece{": "},
+ assertTypeLinkHints(Source, "3", ExpectedHintLabelPiece{": "},
ExpectedHintLabelPiece{"Container", "Container"},
ExpectedHintLabelPiece{"<"},
ExpectedHintLabelPiece{"Container", "Container"},
ExpectedHintLabelPiece{"<int, int>, long>"});
// TODO: Support links on NTTP arguments.
- assertTypeLinkHints(
- Source, "4",
- ExpectedHintLabelPiece{": "},
- ExpectedHintLabelPiece{"NttpContainer", "NttpContainer"},
- ExpectedHintLabelPiece{"<D, E, ScopedEnum::X, Enum::E>"});
+ assertTypeLinkHints(Source, "4", ExpectedHintLabelPiece{": "},
+ ExpectedHintLabelPiece{"NttpContainer", "NttpContainer"},
+ ExpectedHintLabelPiece{"<D, E, ScopedEnum::X, Enum::E>"});
assertTypeLinkHints(Source, "5", ExpectedHintLabelPiece{": "},
ExpectedHintLabelPiece{"ns::Nested", "Nested"},
>From fcf6b6b0cf293df25475a59edd220c1123fbbbcf Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sat, 1 Jun 2024 22:54:41 +0800
Subject: [PATCH 14/36] Fix tests
---
clang-tools-extra/clangd/InlayHints.cpp | 2 +-
clang-tools-extra/clangd/unittests/InlayHintTests.cpp | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 3a010adf95e32..3953bf37d762f 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -491,7 +491,7 @@ class TypeInlayHintLabelPartBuilder
case TemplateName::QualifiedTemplate:
if (NestedNameSpecifier *NNS =
TN.getAsQualifiedTemplateName()->getQualifier();
- NNS && NNS == CurrentNestedNameSpecifier) {
+ NNS == CurrentNestedNameSpecifier) {
// We have handled the NNS in VisitElaboratedType(). Avoid printing it
// twice.
TN = TN.getAsQualifiedTemplateName()->getUnderlyingTemplate();
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index 05598fd973610..6e596b11b9a5d 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1825,7 +1825,7 @@ TEST(TypeHints, Links) {
assertTypeLinkHints(Source, "10", ExpectedHintLabelPiece{": "},
ExpectedHintLabelPiece{"Container", "Container"},
- ExpectedHintLabelPiece{"<"},
+ ExpectedHintLabelPiece{"<ns::"},
ExpectedHintLabelPiece{"NestedInt", "NestedInt"},
ExpectedHintLabelPiece{">"});
}
>From d7b0715f78787d5f33293ad4d551ce7aac6840b1 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sun, 16 Jun 2024 22:05:15 +0800
Subject: [PATCH 15/36] Use getLocation() for identifier's location. Handle
enum Decls.
---
clang-tools-extra/clangd/AST.cpp | 24 ++++++++
clang-tools-extra/clangd/AST.h | 3 +
clang-tools-extra/clangd/InlayHints.cpp | 58 +++++++++++--------
clang-tools-extra/clangd/XRefs.cpp | 23 --------
.../clangd/unittests/InlayHintTests.cpp | 44 ++++++++------
5 files changed, 88 insertions(+), 64 deletions(-)
diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index 98fe19000d97b..a634438edaef8 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -9,6 +9,7 @@
#include "AST.h"
#include "SourceCode.h"
+#include "support/Logger.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTTypeTraits.h"
#include "clang/AST/Decl.h"
@@ -174,6 +175,29 @@ SourceLocation nameLocation(const clang::Decl &D, const SourceManager &SM) {
return SM.getExpansionLoc(L);
}
+// Expects Loc to be a SpellingLocation, will bail out otherwise as it can't
+// figure out a filename.
+std::optional<Location> makeLocation(const ASTContext &AST, SourceLocation Loc,
+ llvm::StringRef TUPath) {
+ const auto &SM = AST.getSourceManager();
+ const auto F = SM.getFileEntryRefForID(SM.getFileID(Loc));
+ if (!F)
+ return std::nullopt;
+ auto FilePath = getCanonicalPath(*F, SM.getFileManager());
+ if (!FilePath) {
+ log("failed to get path!");
+ return std::nullopt;
+ }
+ Location L;
+ L.uri = URIForFile::canonicalize(*FilePath, TUPath);
+ // We call MeasureTokenLength here as TokenBuffer doesn't store spelled tokens
+ // outside the main file.
+ auto TokLen = Lexer::MeasureTokenLength(Loc, SM, AST.getLangOpts());
+ L.range = halfOpenToRange(
+ SM, CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(TokLen)));
+ return L;
+}
+
std::string printQualifiedName(const NamedDecl &ND) {
std::string QName;
llvm::raw_string_ostream OS(QName);
diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h
index 3ae624b1ab741..e048815b2870b 100644
--- a/clang-tools-extra/clangd/AST.h
+++ b/clang-tools-extra/clangd/AST.h
@@ -45,6 +45,9 @@ bool isImplementationDetail(const Decl *D);
/// this function.
SourceLocation nameLocation(const clang::Decl &D, const SourceManager &SM);
+std::optional<Location> makeLocation(const ASTContext &AST, SourceLocation Loc,
+ llvm::StringRef TUPath);
+
/// Returns the qualified name of ND. The scope doesn't contain unwritten scopes
/// like inline namespaces.
std::string printQualifiedName(const NamedDecl &ND);
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 3953bf37d762f..e0fef5758ef02 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -394,7 +394,9 @@ class TypeInlayHintLabelPartBuilder
QualType CurrentType;
NestedNameSpecifier *CurrentNestedNameSpecifier;
ASTContext &Context;
+ StringRef MainFilePath;
const PrintingPolicy &PP;
+ SourceManager &SM;
std::vector<InlayHintLabelPart> &LabelChunks;
struct CurrentTypeRAII {
@@ -416,13 +418,13 @@ class TypeInlayHintLabelPartBuilder
};
void addLabel(llvm::function_ref<void(llvm::raw_ostream &)> NamePrinter,
- llvm::function_ref<SourceRange()> SourceRangeGetter) {
+ 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 = toLocation(Context.getSourceManager(), SourceRangeGetter());
+ Name.location = makeLocation(Context, SourceLocationGetter(), MainFilePath);
}
void addLabel(std::string Label) {
@@ -476,17 +478,16 @@ class TypeInlayHintLabelPartBuilder
addLabel(std::move(Label));
}
- void
- handleTemplateSpecialization(TemplateName TN,
- llvm::ArrayRef<TemplateArgument> Args,
- SourceRange TemplateNameRange = SourceRange()) {
- SourceRange Range;
+ 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();
- Range = TD->getSourceRange();
+ Location = nameLocation(*TD, SM);
break;
case TemplateName::QualifiedTemplate:
if (NestedNameSpecifier *NNS =
@@ -509,9 +510,9 @@ class TypeInlayHintLabelPartBuilder
addLabel([&](llvm::raw_ostream &OS) { TN.print(OS, PP, PrintType); },
[&] {
- if (TemplateNameRange.isValid())
- return TemplateNameRange;
- return Range;
+ if (TemplateNameLocation.isValid())
+ return TemplateNameLocation;
+ return Location;
});
addLabel("<");
@@ -537,11 +538,13 @@ class TypeInlayHintLabelPartBuilder
public:
TypeInlayHintLabelPartBuilder(QualType Current, ASTContext &Context,
+ StringRef MainFilePath,
const PrintingPolicy &PP,
llvm::StringRef Prefix,
std::vector<InlayHintLabelPart> &LabelChunks)
: CurrentType(Current), CurrentNestedNameSpecifier(nullptr),
- Context(Context), PP(PP), LabelChunks(LabelChunks) {
+ Context(Context), MainFilePath(MainFilePath), PP(PP),
+ SM(Context.getSourceManager()), LabelChunks(LabelChunks) {
LabelChunks.reserve(16);
if (!Prefix.empty())
addLabel(Prefix.str());
@@ -559,10 +562,16 @@ class TypeInlayHintLabelPartBuilder
RD && !RD->getTemplateInstantiationPattern())
return addLabel(
[&](llvm::raw_ostream &OS) { return RD->printName(OS, PP); },
- [&] { return RD->getSourceRange(); });
+ [&] { return nameLocation(*RD, SM); });
return VisitType(TT);
}
+ void VisitEnumType(const EnumType *ET) {
+ return addLabel(
+ [&](llvm::raw_ostream &OS) { return ET->getDecl()->printName(OS, PP); },
+ [&] { return nameLocation(*ET->getDecl(), SM); });
+ }
+
void VisitAutoType(const AutoType *AT) {
if (!AT->isDeduced() || AT->getDeducedType()->isDecltypeType())
return;
@@ -631,25 +640,25 @@ class TypeInlayHintLabelPartBuilder
[&] {
BaseUsingDecl *Introducer = UT->getFoundDecl()->getIntroducer();
if (auto *UD = dyn_cast<UsingDecl>(Introducer))
- return UD->getSourceRange();
- return Introducer->getSourceRange();
+ return nameLocation(*UD, SM);
+ return nameLocation(*Introducer, SM);
});
}
void VisitTypedefType(const TypedefType *TT) {
addLabel([&](llvm::raw_ostream &OS) { TT->getDecl()->printName(OS); },
- [&] { return TT->getDecl()->getSourceRange(); });
+ [&] { return nameLocation(*TT->getDecl(), SM); });
}
void VisitTemplateSpecializationType(const TemplateSpecializationType *TST) {
maybeAddQualifiers();
- SourceRange Range;
+ SourceLocation Location;
if (auto *Specialization =
dyn_cast_if_present<ClassTemplateSpecializationDecl>(
TST->desugar().getCanonicalType()->getAsCXXRecordDecl()))
- Range = Specialization->getSourceRange();
+ Location = nameLocation(*Specialization, SM);
return handleTemplateSpecialization(TST->getTemplateName(),
- TST->template_arguments(), Range);
+ TST->template_arguments(), Location);
}
void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *ST) {
@@ -680,7 +689,7 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
: Results(Results), AST(AST.getASTContext()), Tokens(AST.getTokens()),
Cfg(Cfg), RestrictRange(std::move(RestrictRange)),
MainFileID(AST.getSourceManager().getMainFileID()),
- Resolver(AST.getHeuristicResolver()),
+ MainFilePath(AST.tuPath()), Resolver(AST.getHeuristicResolver()),
TypeHintPolicy(this->AST.getPrintingPolicy()) {
bool Invalid = false;
llvm::StringRef Buf =
@@ -1339,15 +1348,15 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
// type in other cases.
auto Desugared = maybeDesugar(AST, T);
std::vector<InlayHintLabelPart> Chunks;
- TypeInlayHintLabelPartBuilder Builder(Desugared, AST, TypeHintPolicy,
- Prefix, Chunks);
+ TypeInlayHintLabelPartBuilder Builder(Desugared, AST, MainFilePath,
+ TypeHintPolicy, Prefix, Chunks);
Builder.Visit(Desugared.getTypePtr());
if (T != Desugared && !shouldPrintTypeHint(Chunks)) {
// If the desugared type is too long to display, fallback to the sugared
// type.
Chunks.clear();
- TypeInlayHintLabelPartBuilder Builder(T, AST, TypeHintPolicy, Prefix,
- Chunks);
+ TypeInlayHintLabelPartBuilder Builder(T, AST, MainFilePath,
+ TypeHintPolicy, Prefix, Chunks);
Builder.Visit(T.getTypePtr());
}
if (shouldPrintTypeHint(Chunks))
@@ -1452,6 +1461,7 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
std::optional<Range> RestrictRange;
FileID MainFileID;
StringRef MainFileBuf;
+ StringRef MainFilePath;
const HeuristicResolver *Resolver;
PrintingPolicy TypeHintPolicy;
};
diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp
index f94cadeffaa29..b012edec9a8ff 100644
--- a/clang-tools-extra/clangd/XRefs.cpp
+++ b/clang-tools-extra/clangd/XRefs.cpp
@@ -209,29 +209,6 @@ getDeclAtPosition(ParsedAST &AST, SourceLocation Pos, DeclRelationSet Relations,
return Result;
}
-// Expects Loc to be a SpellingLocation, will bail out otherwise as it can't
-// figure out a filename.
-std::optional<Location> makeLocation(const ASTContext &AST, SourceLocation Loc,
- llvm::StringRef TUPath) {
- const auto &SM = AST.getSourceManager();
- const auto F = SM.getFileEntryRefForID(SM.getFileID(Loc));
- if (!F)
- return std::nullopt;
- auto FilePath = getCanonicalPath(*F, SM.getFileManager());
- if (!FilePath) {
- log("failed to get path!");
- return std::nullopt;
- }
- Location L;
- L.uri = URIForFile::canonicalize(*FilePath, TUPath);
- // We call MeasureTokenLength here as TokenBuffer doesn't store spelled tokens
- // outside the main file.
- auto TokLen = Lexer::MeasureTokenLength(Loc, SM, AST.getLangOpts());
- L.range = halfOpenToRange(
- SM, CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(TokLen)));
- return L;
-}
-
// Treat #included files as symbols, to enable go-to-definition on them.
std::optional<LocatedSymbol> locateFileReferent(const Position &Pos,
ParsedAST &AST,
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index 6e596b11b9a5d..2bd2130e42907 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1702,35 +1702,35 @@ void assertTypeLinkHints(StringRef Code, StringRef HintRange,
TEST(TypeHints, Links) {
StringRef Source(R"cpp(
- $Package[[template <class T, class U>
- struct Package {]]};
+ template <class T, class U>
+ struct $Package[[Package]] {};
- $SpecializationOfPackage[[template <>
- struct Package<float, const int> {]]};
+ template <>
+ struct $SpecializationOfPackage[[Package]]<float, const int> {};
- $Container[[template <class... T>
- struct Container {]]};
+ template <class... T>
+ struct $Container[[Container]] {};
- $NttpContainer[[template <auto... T>
- struct NttpContainer {]]};
+ template <auto... T>
+ struct $NttpContainer[[NttpContainer]] {};
- enum struct ScopedEnum {
+ enum struct $ScopedEnum[[ScopedEnum]] {
X = 1,
};
- enum Enum {
+ enum $Enum[[Enum]] {
E = 2,
};
namespace ns {
- $Nested[[template <class T>
- struct Nested {
- $NestedClass[[template <class U>
- struct ]]Class {
+ template <class T>
+ struct $Nested[[Nested]] {
+ template <class U>
+ struct $NestedClass[[Class]] {
};
- ]]};
+ };
- $NestedInt[[using NestedInt = Nested<int]]>;
+ using $NestedInt[[NestedInt]] = Nested<int>;
}
void basic() {
@@ -1748,7 +1748,7 @@ TEST(TypeHints, Links) {
}
namespace nns {
- $UsingShadow[[using ns::]]NestedInt;
+ using ns::$UsingShadow[[NestedInt]];
void aliases() {
auto $9[[A]] = Container<NestedInt>();
@@ -1756,6 +1756,8 @@ TEST(TypeHints, Links) {
}
}
+ auto $11[[A]] = Container<Enum, ScopedEnum>();
+
)cpp");
assertTypeLinkHints(Source, "1", ExpectedHintLabelPiece{": "},
ExpectedHintLabelPiece{"Container", "Container"},
@@ -1828,6 +1830,14 @@ TEST(TypeHints, Links) {
ExpectedHintLabelPiece{"<ns::"},
ExpectedHintLabelPiece{"NestedInt", "NestedInt"},
ExpectedHintLabelPiece{">"});
+
+ assertTypeLinkHints(Source, "11", ExpectedHintLabelPiece{": "},
+ ExpectedHintLabelPiece{"Container", "Container"},
+ ExpectedHintLabelPiece{"<"},
+ ExpectedHintLabelPiece{"Enum", "Enum"},
+ ExpectedHintLabelPiece{", "},
+ ExpectedHintLabelPiece{"ScopedEnum", "ScopedEnum"},
+ ExpectedHintLabelPiece{">"});
}
TEST(DesignatorHints, Basic) {
>From 38662163231d958db7de22bfffa939653b4d9015 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sun, 16 Jun 2024 23:00:33 +0800
Subject: [PATCH 16/36] Remove unused toLocation function
---
clang-tools-extra/clangd/InlayHints.cpp | 15 ---------------
1 file changed, 15 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index e0fef5758ef02..0594fa74ad665 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -374,21 +374,6 @@ maybeDropCxxExplicitObjectParameters(ArrayRef<const ParmVarDecl *> Params) {
return Params;
}
-std::optional<Location> toLocation(SourceManager &SM, SourceRange Range) {
- if (Range.isInvalid())
- return std::nullopt;
- if (auto URI =
- toURI(SM.getFileEntryRefForID(SM.getFileID(Range.getBegin())))) {
- Location L;
- L.range.start = sourceLocToPosition(SM, Range.getBegin());
- L.range.end = sourceLocToPosition(SM, Range.getEnd());
- if (auto File = URIForFile::fromURI(*URI, ""))
- L.uri = File.get();
- return L;
- }
- return std::nullopt;
-}
-
class TypeInlayHintLabelPartBuilder
: public TypeVisitor<TypeInlayHintLabelPartBuilder> {
QualType CurrentType;
>From 65493f0885cf8d8653e15f83c74b9c9609781bdd Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sun, 7 Jul 2024 22:03:33 +0800
Subject: [PATCH 17/36] Address some nits
---
clang-tools-extra/clangd/AST.cpp | 2 --
clang-tools-extra/clangd/AST.h | 3 ++
clang-tools-extra/clangd/InlayHints.cpp | 37 ++++++++++---------------
3 files changed, 17 insertions(+), 25 deletions(-)
diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index a634438edaef8..dd645861dda8b 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -175,8 +175,6 @@ SourceLocation nameLocation(const clang::Decl &D, const SourceManager &SM) {
return SM.getExpansionLoc(L);
}
-// Expects Loc to be a SpellingLocation, will bail out otherwise as it can't
-// figure out a filename.
std::optional<Location> makeLocation(const ASTContext &AST, SourceLocation Loc,
llvm::StringRef TUPath) {
const auto &SM = AST.getSourceManager();
diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h
index e048815b2870b..f56759656997b 100644
--- a/clang-tools-extra/clangd/AST.h
+++ b/clang-tools-extra/clangd/AST.h
@@ -45,6 +45,9 @@ bool isImplementationDetail(const Decl *D);
/// this function.
SourceLocation nameLocation(const clang::Decl &D, const SourceManager &SM);
+/// Convert a \p SourceLocation to an LSP \p Location.
+/// Expects Loc to be a SpellingLocation, will bail out otherwise as it can't
+/// figure out a filename.
std::optional<Location> makeLocation(const ASTContext &AST, SourceLocation Loc,
llvm::StringRef TUPath);
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 0594fa74ad665..71d3fdd814cef 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -374,8 +374,7 @@ maybeDropCxxExplicitObjectParameters(ArrayRef<const ParmVarDecl *> Params) {
return Params;
}
-class TypeInlayHintLabelPartBuilder
- : public TypeVisitor<TypeInlayHintLabelPartBuilder> {
+class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
QualType CurrentType;
NestedNameSpecifier *CurrentNestedNameSpecifier;
ASTContext &Context;
@@ -385,10 +384,10 @@ class TypeInlayHintLabelPartBuilder
std::vector<InlayHintLabelPart> &LabelChunks;
struct CurrentTypeRAII {
- TypeInlayHintLabelPartBuilder &Builder;
+ TypeHintBuilder &Builder;
QualType PreviousType;
NestedNameSpecifier *PreviousNestedNameSpecifier;
- CurrentTypeRAII(TypeInlayHintLabelPartBuilder &Builder, QualType New,
+ CurrentTypeRAII(TypeHintBuilder &Builder, QualType New,
NestedNameSpecifier *NNS = nullptr)
: Builder(Builder), PreviousType(Builder.CurrentType),
PreviousNestedNameSpecifier(Builder.CurrentNestedNameSpecifier) {
@@ -522,11 +521,9 @@ class TypeInlayHintLabelPartBuilder
}
public:
- TypeInlayHintLabelPartBuilder(QualType Current, ASTContext &Context,
- StringRef MainFilePath,
- const PrintingPolicy &PP,
- llvm::StringRef Prefix,
- std::vector<InlayHintLabelPart> &LabelChunks)
+ TypeHintBuilder(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) {
@@ -578,7 +575,8 @@ class TypeInlayHintLabelPartBuilder
llvm::raw_string_ostream OS(Label);
NNS->print(OS, PP);
addLabel(std::move(Label));
- } break;
+ break;
+ }
case NestedNameSpecifier::TypeSpec:
case NestedNameSpecifier::TypeSpecWithTemplate:
CurrentTypeRAII Guard(
@@ -653,7 +651,7 @@ class TypeInlayHintLabelPartBuilder
}
};
-unsigned lengthOfInlayHintLabelPart(llvm::ArrayRef<InlayHintLabelPart> Labels) {
+unsigned lengthOfInlayHintLabel(llvm::ArrayRef<InlayHintLabelPart> Labels) {
unsigned Size = 0;
for (auto &P : Labels)
Size += P.value.size();
@@ -1286,12 +1284,6 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
return;
bool PadLeft = Prefix.consume_front(" ");
bool PadRight = Suffix.consume_back(" ");
- if (Prefix.empty() && Suffix.empty()) {
- Results.push_back(InlayHint{LSPPos,
- /*label=*/Labels, Kind, PadLeft, PadRight,
- LSPRange});
- return;
- }
if (!Prefix.empty()) {
if (auto &Label = Labels.front(); !Label.location)
Label.value = Prefix.str() + Label.value;
@@ -1333,15 +1325,15 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
// type in other cases.
auto Desugared = maybeDesugar(AST, T);
std::vector<InlayHintLabelPart> Chunks;
- TypeInlayHintLabelPartBuilder Builder(Desugared, AST, MainFilePath,
- TypeHintPolicy, Prefix, Chunks);
+ TypeHintBuilder Builder(Desugared, AST, MainFilePath, TypeHintPolicy,
+ Prefix, Chunks);
Builder.Visit(Desugared.getTypePtr());
if (T != Desugared && !shouldPrintTypeHint(Chunks)) {
// If the desugared type is too long to display, fallback to the sugared
// type.
Chunks.clear();
- TypeInlayHintLabelPartBuilder Builder(T, AST, MainFilePath,
- TypeHintPolicy, Prefix, Chunks);
+ TypeHintBuilder Builder(T, AST, MainFilePath, TypeHintPolicy, Prefix,
+ Chunks);
Builder.Visit(T.getTypePtr());
}
if (shouldPrintTypeHint(Chunks))
@@ -1359,8 +1351,7 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
bool shouldPrintTypeHint(
llvm::ArrayRef<InlayHintLabelPart> TypeLabels) const noexcept {
return Cfg.InlayHints.TypeNameLimit == 0 ||
- lengthOfInlayHintLabelPart(TypeLabels) <
- Cfg.InlayHints.TypeNameLimit;
+ lengthOfInlayHintLabel(TypeLabels) < Cfg.InlayHints.TypeNameLimit;
}
void addBlockEndHint(SourceRange BraceRange, StringRef DeclPrefix,
>From 1195d25e1c96bb8547d3630b2d509624c8c1e399 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sun, 7 Jul 2024 22:10:16 +0800
Subject: [PATCH 18/36] Restructure the logic in addTypeHint()
---
clang-tools-extra/clangd/InlayHints.cpp | 28 +++++++++++++++++--------
1 file changed, 19 insertions(+), 9 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 71d3fdd814cef..04e94bcc6774d 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -1317,6 +1317,15 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
sourceLocToPosition(SM, Spelled->back().endLocation())};
}
+ std::vector<InlayHintLabelPart> buildTypeHint(QualType T,
+ llvm::StringRef Prefix) {
+ std::vector<InlayHintLabelPart> Chunks;
+ TypeHintBuilder Builder(T, AST, MainFilePath, TypeHintPolicy, Prefix,
+ Chunks);
+ Builder.Visit(T.getTypePtr());
+ return Chunks;
+ }
+
void addTypeHint(SourceRange R, QualType T, llvm::StringRef Prefix) {
if (!Cfg.InlayHints.DeducedTypes || T.isNull())
return;
@@ -1324,17 +1333,18 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
// The sugared type is more useful in some cases, and the canonical
// type in other cases.
auto Desugared = maybeDesugar(AST, T);
- std::vector<InlayHintLabelPart> Chunks;
- TypeHintBuilder Builder(Desugared, AST, MainFilePath, TypeHintPolicy,
- Prefix, Chunks);
- Builder.Visit(Desugared.getTypePtr());
- if (T != Desugared && !shouldPrintTypeHint(Chunks)) {
+ auto Chunks = buildTypeHint(Desugared, Prefix);
+ if (T != Desugared) {
+ if (shouldPrintTypeHint(Chunks)) {
+ addInlayHint(R, HintSide::Right, InlayHintKind::Type,
+ /*Prefix=*/"", // We have handled prefixes in the builder.
+ std::move(Chunks),
+ /*Suffix=*/"");
+ return;
+ }
// If the desugared type is too long to display, fallback to the sugared
// type.
- Chunks.clear();
- TypeHintBuilder Builder(T, AST, MainFilePath, TypeHintPolicy, Prefix,
- Chunks);
- Builder.Visit(T.getTypePtr());
+ Chunks = buildTypeHint(T, Prefix);
}
if (shouldPrintTypeHint(Chunks))
addInlayHint(R, HintSide::Right, InlayHintKind::Type,
>From e2b843c44ff252aea586118214e370d61bc647b1 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sun, 7 Jul 2024 23:31:57 +0800
Subject: [PATCH 19/36] Special case ClassTemplateSpecializationDecl for
forward declarations
---
clang-tools-extra/clangd/InlayHints.cpp | 32 ++++++++++++++-----
.../clangd/unittests/InlayHintTests.cpp | 29 +++++++++++++++++
2 files changed, 53 insertions(+), 8 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 04e94bcc6774d..67e16673b1123 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -402,13 +402,13 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
};
void addLabel(llvm::function_ref<void(llvm::raw_ostream &)> NamePrinter,
- llvm::function_ref<SourceLocation()> SourceLocationGetter) {
+ SourceLocation Location) {
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);
+ Name.location = makeLocation(Context, Location, MainFilePath);
}
void addLabel(std::string Label) {
@@ -497,7 +497,7 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
if (TemplateNameLocation.isValid())
return TemplateNameLocation;
return Location;
- });
+ }());
addLabel("<");
printTemplateArgumentList(Args);
@@ -544,14 +544,14 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
RD && !RD->getTemplateInstantiationPattern())
return addLabel(
[&](llvm::raw_ostream &OS) { return RD->printName(OS, PP); },
- [&] { return nameLocation(*RD, SM); });
+ [&] { return nameLocation(*RD, SM); }());
return VisitType(TT);
}
void VisitEnumType(const EnumType *ET) {
return addLabel(
[&](llvm::raw_ostream &OS) { return ET->getDecl()->printName(OS, PP); },
- [&] { return nameLocation(*ET->getDecl(), SM); });
+ [&] { return nameLocation(*ET->getDecl(), SM); }());
}
void VisitAutoType(const AutoType *AT) {
@@ -625,21 +625,37 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
if (auto *UD = dyn_cast<UsingDecl>(Introducer))
return nameLocation(*UD, SM);
return nameLocation(*Introducer, SM);
- });
+ }());
}
void VisitTypedefType(const TypedefType *TT) {
addLabel([&](llvm::raw_ostream &OS) { TT->getDecl()->printName(OS); },
- [&] { return nameLocation(*TT->getDecl(), SM); });
+ [&] { return nameLocation(*TT->getDecl(), SM); }());
}
void VisitTemplateSpecializationType(const TemplateSpecializationType *TST) {
maybeAddQualifiers();
SourceLocation Location;
+ // Special case the ClassTemplateSpecializationDecl:
+ // 1) We want the location of an explicit specialization, if present.
+ // 2) For implicit specializations, the associated CXXRecordDecl might be
+ // pointing to a *declaration*. We want to find out the definition if
+ // possible. Unfortunately, RecordDecl::getDefinition() doesn't work
+ // straightforwardly in this case.
if (auto *Specialization =
dyn_cast_if_present<ClassTemplateSpecializationDecl>(
- TST->desugar().getCanonicalType()->getAsCXXRecordDecl()))
+ TST->desugar().getCanonicalType()->getAsCXXRecordDecl())) {
Location = nameLocation(*Specialization, SM);
+ // The template argument might be associated to a Decl whose
+ // specialization kind is TSK_Undeclared.
+ if (!Specialization->isExplicitInstantiationOrSpecialization()) {
+ auto Pattern = Specialization->getSpecializedTemplateOrPartial();
+ if (auto *Template = Pattern.dyn_cast<ClassTemplateDecl *>())
+ if (CXXRecordDecl *Definition =
+ Template->getTemplatedDecl()->getDefinition())
+ Location = nameLocation(*Definition, SM);
+ }
+ }
return handleTemplateSpecialization(TST->getTemplateName(),
TST->template_arguments(), Location);
}
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index 327931857a660..f4f21c9224d1f 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1840,6 +1840,35 @@ TEST(TypeHints, Links) {
ExpectedHintLabelPiece{">"});
}
+TEST(TypeHints, LinksForForwardDeclarations) {
+ StringRef Source(R"cpp(
+ template <class... T>
+ struct $Container[[Container]] {};
+
+ // Test that we always prefer the location of the definition in the
+ // presence of a forward declaration.
+ template <class... T>
+ struct Container;
+
+ struct $S[[S]] {};
+
+ struct S;
+
+ struct $W[[W]];
+
+ auto $1[[Use]] = Container<Container<S>, W>();
+ )cpp");
+
+ assertTypeLinkHints(
+ Source, "1", ExpectedHintLabelPiece{": "},
+ ExpectedHintLabelPiece{"Container", "Container"},
+ ExpectedHintLabelPiece{"<"},
+ ExpectedHintLabelPiece{"Container", "Container"},
+ ExpectedHintLabelPiece{"<"}, ExpectedHintLabelPiece{"S", "S"},
+ ExpectedHintLabelPiece{">, "}, ExpectedHintLabelPiece{"W", "W"},
+ ExpectedHintLabelPiece{">"});
+}
+
TEST(DesignatorHints, Basic) {
assertDesignatorHints(R"cpp(
struct S { int x, y, z; };
>From 6930a5582ad75bc3835e53d75b4ae376fa5fe29b Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sun, 7 Jul 2024 23:36:41 +0800
Subject: [PATCH 20/36] Cleanup more inline lambdas
---
clang-tools-extra/clangd/InlayHints.cpp | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 67e16673b1123..0f4d3371bf178 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -464,7 +464,7 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
void handleTemplateSpecialization(
TemplateName TN, llvm::ArrayRef<TemplateArgument> Args,
- SourceLocation TemplateNameLocation = SourceLocation()) {
+ SourceLocation LocationForOverride = SourceLocation()) {
SourceLocation Location;
TemplateDecl *TD = nullptr;
auto PrintType = TemplateName::Qualified::AsWritten;
@@ -492,12 +492,11 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
break;
}
+ if (LocationForOverride.isValid())
+ Location = LocationForOverride;
+
addLabel([&](llvm::raw_ostream &OS) { TN.print(OS, PP, PrintType); },
- [&] {
- if (TemplateNameLocation.isValid())
- return TemplateNameLocation;
- return Location;
- }());
+ Location);
addLabel("<");
printTemplateArgumentList(Args);
@@ -544,14 +543,14 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
RD && !RD->getTemplateInstantiationPattern())
return addLabel(
[&](llvm::raw_ostream &OS) { return RD->printName(OS, PP); },
- [&] { return nameLocation(*RD, SM); }());
+ nameLocation(*RD, SM));
return VisitType(TT);
}
void VisitEnumType(const EnumType *ET) {
return addLabel(
[&](llvm::raw_ostream &OS) { return ET->getDecl()->printName(OS, PP); },
- [&] { return nameLocation(*ET->getDecl(), SM); }());
+ nameLocation(*ET->getDecl(), SM));
}
void VisitAutoType(const AutoType *AT) {
@@ -630,7 +629,7 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
void VisitTypedefType(const TypedefType *TT) {
addLabel([&](llvm::raw_ostream &OS) { TT->getDecl()->printName(OS); },
- [&] { return nameLocation(*TT->getDecl(), SM); }());
+ nameLocation(*TT->getDecl(), SM));
}
void VisitTemplateSpecializationType(const TemplateSpecializationType *TST) {
>From b89cda3ea00cb4e416c5cd4cb49823f0328702b5 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sun, 7 Jul 2024 23:47:47 +0800
Subject: [PATCH 21/36] Remove the dead codes
---
clang-tools-extra/clangd/InlayHints.cpp | 4 ----
1 file changed, 4 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 0f4d3371bf178..f621356e04c77 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -535,10 +535,6 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
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())
return addLabel(
>From c6e691ee052c8f523e001876b8d6bd1907b8208a Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 8 Jul 2024 00:08:52 +0800
Subject: [PATCH 22/36] Respect PP.SuppressScope
---
clang-tools-extra/clangd/InlayHints.cpp | 4 ++++
.../clangd/unittests/InlayHintTests.cpp | 20 ++-----------------
2 files changed, 6 insertions(+), 18 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index f621356e04c77..4972943c4270d 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -566,6 +566,8 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
case NestedNameSpecifier::NamespaceAlias:
case NestedNameSpecifier::Global:
case NestedNameSpecifier::Super: {
+ if (PP.SuppressScope)
+ break;
std::string Label;
llvm::raw_string_ostream OS(Label);
NNS->print(OS, PP);
@@ -574,6 +576,8 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
}
case NestedNameSpecifier::TypeSpec:
case NestedNameSpecifier::TypeSpecWithTemplate:
+ if (PP.SuppressScope)
+ break;
CurrentTypeRAII Guard(
*this,
QualType(
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index f4f21c9224d1f..50f584720a54f 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1312,13 +1312,7 @@ TEST(TypeHints, NoQualifiers) {
}
)cpp",
ExpectedHint{": S1", "x"},
- // FIXME: We want to suppress scope specifiers
- // here because we are into the whole
- // brevity thing, but the ElaboratedType
- // printer does not honor the SuppressScope
- // flag by design, so we need to extend the
- // PrintingPolicy to support this use case.
- ExpectedHint{": S2::Inner<int>", "y"});
+ ExpectedHint{": Inner<int>", "y"});
}
TEST(TypeHints, Lambda) {
@@ -1784,10 +1778,6 @@ TEST(TypeHints, Links) {
ExpectedHintLabelPiece{"<D, E, ScopedEnum::X, Enum::E>"});
assertTypeLinkHints(Source, "5", ExpectedHintLabelPiece{": "},
- ExpectedHintLabelPiece{"ns::Nested", "Nested"},
- ExpectedHintLabelPiece{"<"},
- ExpectedHintLabelPiece{"Container", "Container"},
- ExpectedHintLabelPiece{"<int>>::"},
ExpectedHintLabelPiece{"Class", "NestedClass"},
ExpectedHintLabelPiece{"<"},
ExpectedHintLabelPiece{"Package", "Package"},
@@ -1796,24 +1786,18 @@ TEST(TypeHints, Links) {
assertTypeLinkHints(Source, "6", ExpectedHintLabelPiece{": "},
ExpectedHintLabelPiece{"Container", "Container"},
ExpectedHintLabelPiece{"<"},
- ExpectedHintLabelPiece{"ns::Nested", "Nested"},
- ExpectedHintLabelPiece{"<int>::"},
ExpectedHintLabelPiece{"Class", "NestedClass"},
ExpectedHintLabelPiece{"<float> &>"});
assertTypeLinkHints(Source, "7", ExpectedHintLabelPiece{": "},
ExpectedHintLabelPiece{"Container", "Container"},
ExpectedHintLabelPiece{"<"},
- ExpectedHintLabelPiece{"ns::Nested", "Nested"},
- ExpectedHintLabelPiece{"<int>::"},
ExpectedHintLabelPiece{"Class", "NestedClass"},
ExpectedHintLabelPiece{"<float> &&>"});
assertTypeLinkHints(Source, "8", ExpectedHintLabelPiece{": "},
ExpectedHintLabelPiece{"Container", "Container"},
ExpectedHintLabelPiece{"<"},
- ExpectedHintLabelPiece{"ns::Nested", "Nested"},
- ExpectedHintLabelPiece{"<int>::"},
ExpectedHintLabelPiece{"Class", "NestedClass"},
ExpectedHintLabelPiece{"<const "},
ExpectedHintLabelPiece{"Container", "Container"},
@@ -1827,7 +1811,7 @@ TEST(TypeHints, Links) {
assertTypeLinkHints(Source, "10", ExpectedHintLabelPiece{": "},
ExpectedHintLabelPiece{"Container", "Container"},
- ExpectedHintLabelPiece{"<ns::"},
+ ExpectedHintLabelPiece{"<"},
ExpectedHintLabelPiece{"NestedInt", "NestedInt"},
ExpectedHintLabelPiece{">"});
>From 71604ef5b8cde764947652efc8701b5311d0369f Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 8 Jul 2024 00:09:24 +0800
Subject: [PATCH 23/36] clang-format
---
clang-tools-extra/clangd/unittests/InlayHintTests.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index 50f584720a54f..064556214f32b 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1311,8 +1311,7 @@ TEST(TypeHints, NoQualifiers) {
}
}
)cpp",
- ExpectedHint{": S1", "x"},
- ExpectedHint{": Inner<int>", "y"});
+ ExpectedHint{": S1", "x"}, ExpectedHint{": Inner<int>", "y"});
}
TEST(TypeHints, Lambda) {
>From fd1761a11edae15abe2d674c7477ff4a47ee4a1f Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 8 Jul 2024 16:45:00 +0800
Subject: [PATCH 24/36] Ensure go-to-def always goes to the definition
---
clang-tools-extra/clangd/InlayHints.cpp | 67 +++++++++++++------
.../clangd/unittests/InlayHintTests.cpp | 32 ++++++---
2 files changed, 72 insertions(+), 27 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 4972943c4270d..28bcb5e6ed158 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -471,7 +471,7 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
switch (TN.getKind()) {
case TemplateName::Template:
TD = TN.getAsTemplateDecl();
- Location = nameLocation(*TD, SM);
+ Location = nameLocation(TD, SM);
break;
case TemplateName::QualifiedTemplate:
if (NestedNameSpecifier *NNS =
@@ -519,6 +519,35 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
return T;
}
+ static SourceLocation nameLocation(Decl *D, const SourceManager &SM) {
+ // If this is a definition, find its *forward declaration* if possible.
+ //
+ // Per LSP specification, code actions, e.g., hover/go-to-def on the type
+ // link, would be performed as if at the location we have given.
+ //
+ // Therefore, we should provide the type part with a location that points to
+ // its declaration because we would otherwise take users to the
+ // *declaration* if they're at the definition.
+ if (auto *TD = dyn_cast<TemplateDecl>(D))
+ D = TD->getTemplatedDecl();
+ bool IsDefinition =
+ isa<TagDecl>(D) && cast<TagDecl>(D)->isThisDeclarationADefinition();
+ if (IsDefinition) {
+ // Happy path: if the canonical declaration is a forward declaration.
+ if (!cast<TagDecl>(D)->getCanonicalDecl()->isThisDeclarationADefinition())
+ D = D->getCanonicalDecl();
+ else {
+ // Otherwise, look through the redeclarations.
+ for (auto *Redecl : D->redecls())
+ if (!cast<TagDecl>(Redecl)->isThisDeclarationADefinition()) {
+ D = Redecl;
+ break;
+ }
+ }
+ }
+ return ::clang::clangd::nameLocation(*D, SM);
+ }
+
public:
TypeHintBuilder(QualType Current, ASTContext &Context, StringRef MainFilePath,
const PrintingPolicy &PP, llvm::StringRef Prefix,
@@ -539,14 +568,14 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
RD && !RD->getTemplateInstantiationPattern())
return addLabel(
[&](llvm::raw_ostream &OS) { return RD->printName(OS, PP); },
- nameLocation(*RD, SM));
+ nameLocation(RD, SM));
return VisitType(TT);
}
void VisitEnumType(const EnumType *ET) {
return addLabel(
[&](llvm::raw_ostream &OS) { return ET->getDecl()->printName(OS, PP); },
- nameLocation(*ET->getDecl(), SM));
+ nameLocation(ET->getDecl(), SM));
}
void VisitAutoType(const AutoType *AT) {
@@ -622,38 +651,38 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
[&] {
BaseUsingDecl *Introducer = UT->getFoundDecl()->getIntroducer();
if (auto *UD = dyn_cast<UsingDecl>(Introducer))
- return nameLocation(*UD, SM);
- return nameLocation(*Introducer, SM);
+ return nameLocation(UD, SM);
+ return nameLocation(Introducer, SM);
}());
}
void VisitTypedefType(const TypedefType *TT) {
addLabel([&](llvm::raw_ostream &OS) { TT->getDecl()->printName(OS); },
- nameLocation(*TT->getDecl(), SM));
+ nameLocation(TT->getDecl(), SM));
}
void VisitTemplateSpecializationType(const TemplateSpecializationType *TST) {
maybeAddQualifiers();
SourceLocation Location;
- // Special case the ClassTemplateSpecializationDecl:
- // 1) We want the location of an explicit specialization, if present.
- // 2) For implicit specializations, the associated CXXRecordDecl might be
- // pointing to a *declaration*. We want to find out the definition if
- // possible. Unfortunately, RecordDecl::getDefinition() doesn't work
- // straightforwardly in this case.
+ // Special case the ClassTemplateSpecializationDecl because
+ // we want the location of an explicit specialization, if present.
+ // FIXME: In practice, populating the location with that of the
+ // specialization would still take us to the primary template because we're
+ // actually sending a go-to-def request there.
if (auto *Specialization =
dyn_cast_if_present<ClassTemplateSpecializationDecl>(
TST->desugar().getCanonicalType()->getAsCXXRecordDecl())) {
- Location = nameLocation(*Specialization, SM);
- // The template argument might be associated to a Decl whose
- // specialization kind is TSK_Undeclared.
+ // Quirk as it is, the Specialization might have no associated forward
+ // declarations. So we have to find them through the Pattern.
if (!Specialization->isExplicitInstantiationOrSpecialization()) {
auto Pattern = Specialization->getSpecializedTemplateOrPartial();
if (auto *Template = Pattern.dyn_cast<ClassTemplateDecl *>())
- if (CXXRecordDecl *Definition =
- Template->getTemplatedDecl()->getDefinition())
- Location = nameLocation(*Definition, SM);
- }
+ Location = nameLocation(Template, SM);
+ if (auto *Template =
+ Pattern.dyn_cast<ClassTemplatePartialSpecializationDecl *>())
+ Location = nameLocation(Template, SM);
+ } else
+ Location = nameLocation(Specialization, SM);
}
return handleTemplateSpecialization(TST->getTemplateName(),
TST->template_arguments(), Location);
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index 064556214f32b..230aa6ca3c8ca 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1824,29 +1824,45 @@ TEST(TypeHints, Links) {
}
TEST(TypeHints, LinksForForwardDeclarations) {
+ // Test that we always prefer the location of the forward declaration in the
+ // presence of it. This way, the LSP client would take us to the definition.
StringRef Source(R"cpp(
template <class... T>
- struct $Container[[Container]] {};
+ struct Container {};
- // Test that we always prefer the location of the definition in the
- // presence of a forward declaration.
template <class... T>
- struct Container;
+ struct $Container[[Container]];
+
+ // The order of occurrence matters.
+ template <class>
+ struct $optional[[optional]];
+
+ template <class>
+ struct optional {};
+
+ // FIXME: go-to-def on `optional<int>` would take us to its primary template.
+ template <>
+ struct optional<int> {};
- struct $S[[S]] {};
+ template <>
+ struct optional<int>;
+
+ struct S {};
- struct S;
+ struct $S[[S]];
struct $W[[W]];
- auto $1[[Use]] = Container<Container<S>, W>();
+ struct W {};
+
+ auto $1[[Use]] = Container<optional<S>, W>();
)cpp");
assertTypeLinkHints(
Source, "1", ExpectedHintLabelPiece{": "},
ExpectedHintLabelPiece{"Container", "Container"},
ExpectedHintLabelPiece{"<"},
- ExpectedHintLabelPiece{"Container", "Container"},
+ ExpectedHintLabelPiece{"optional", "optional"},
ExpectedHintLabelPiece{"<"}, ExpectedHintLabelPiece{"S", "S"},
ExpectedHintLabelPiece{">, "}, ExpectedHintLabelPiece{"W", "W"},
ExpectedHintLabelPiece{">"});
>From 57efdd4b42d698c7d8c370e187803413fe97fd17 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 8 Jul 2024 18:14:07 +0800
Subject: [PATCH 25/36] Handle links for structure bindings
---
clang-tools-extra/clangd/InlayHints.cpp | 83 +++++++++++++------
.../clangd/unittests/InlayHintTests.cpp | 14 ++++
2 files changed, 70 insertions(+), 27 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 28bcb5e6ed158..480ffb2c531a7 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -381,7 +381,7 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
StringRef MainFilePath;
const PrintingPolicy &PP;
SourceManager &SM;
- std::vector<InlayHintLabelPart> &LabelChunks;
+ std::vector<InlayHintLabelPart> LabelChunks;
struct CurrentTypeRAII {
TypeHintBuilder &Builder;
@@ -495,6 +495,8 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
if (LocationForOverride.isValid())
Location = LocationForOverride;
+ assert(Location.isValid() || LocationForOverride.isValid());
+
addLabel([&](llvm::raw_ostream &OS) { TN.print(OS, PP, PrintType); },
Location);
@@ -548,13 +550,29 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
return ::clang::clangd::nameLocation(*D, SM);
}
+ SourceLocation getPreferredLocationFromSpecialization(
+ ClassTemplateSpecializationDecl *Specialization) const {
+ SourceLocation Location;
+ // Quirk as it is, the Specialization might have no associated forward
+ // declarations. So we have to find them through the Pattern.
+ if (!Specialization->isExplicitInstantiationOrSpecialization()) {
+ auto Pattern = Specialization->getSpecializedTemplateOrPartial();
+ if (auto *Template = Pattern.dyn_cast<ClassTemplateDecl *>())
+ Location = nameLocation(Template, SM);
+ if (auto *Template =
+ Pattern.dyn_cast<ClassTemplatePartialSpecializationDecl *>())
+ Location = nameLocation(Template, SM);
+ } else
+ Location = nameLocation(Specialization, SM);
+ return Location;
+ }
+
public:
TypeHintBuilder(QualType Current, ASTContext &Context, StringRef MainFilePath,
- const PrintingPolicy &PP, llvm::StringRef Prefix,
- std::vector<InlayHintLabelPart> &LabelChunks)
+ const PrintingPolicy &PP, llvm::StringRef Prefix)
: CurrentType(Current), CurrentNestedNameSpecifier(nullptr),
Context(Context), MainFilePath(MainFilePath), PP(PP),
- SM(Context.getSourceManager()), LabelChunks(LabelChunks) {
+ SM(Context.getSourceManager()) {
LabelChunks.reserve(16);
if (!Prefix.empty())
addLabel(Prefix.str());
@@ -563,12 +581,34 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
void VisitType(const Type *) { addLabel(CurrentType.getAsString(PP)); }
void VisitTagType(const TagType *TT) {
- auto *D = TT->getDecl();
- if (auto *RD = dyn_cast<CXXRecordDecl>(D);
- RD && !RD->getTemplateInstantiationPattern())
+ // Note that we have cases where the type of a template specialization is
+ // modeled as a RecordType rather than a TemplateSpecializationType. (Type
+ // sugars are not preserved?)
+ // Example:
+ //
+ // template <typename, typename = int>
+ // struct A {};
+ // A<float> bar[1];
+ //
+ // auto [value] = bar;
+ //
+ // The type of value is modeled as a RecordType here.
+ auto *CXXRD = dyn_cast<CXXRecordDecl>(TT->getDecl());
+ if (!CXXRD)
+ return VisitType(TT);
+ CXXRecordDecl *Pattern = CXXRD->getTemplateInstantiationPattern();
+ if (!Pattern)
return addLabel(
- [&](llvm::raw_ostream &OS) { return RD->printName(OS, PP); },
- nameLocation(RD, SM));
+ [&](llvm::raw_ostream &OS) { return CXXRD->printName(OS, PP); },
+ nameLocation(CXXRD, SM));
+
+ // FIXME: Do we have other kind of specializations?
+ if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CXXRD))
+ return handleTemplateSpecialization(
+ TemplateName(Pattern->getDescribedClassTemplate()),
+ CTSD->getTemplateArgs().asArray(),
+ getPreferredLocationFromSpecialization(CTSD));
+
return VisitType(TT);
}
@@ -668,22 +708,11 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
// we want the location of an explicit specialization, if present.
// FIXME: In practice, populating the location with that of the
// specialization would still take us to the primary template because we're
- // actually sending a go-to-def request there.
+ // actually sending a go-to-def request from the explicit specialization.
if (auto *Specialization =
dyn_cast_if_present<ClassTemplateSpecializationDecl>(
- TST->desugar().getCanonicalType()->getAsCXXRecordDecl())) {
- // Quirk as it is, the Specialization might have no associated forward
- // declarations. So we have to find them through the Pattern.
- if (!Specialization->isExplicitInstantiationOrSpecialization()) {
- auto Pattern = Specialization->getSpecializedTemplateOrPartial();
- if (auto *Template = Pattern.dyn_cast<ClassTemplateDecl *>())
- Location = nameLocation(Template, SM);
- if (auto *Template =
- Pattern.dyn_cast<ClassTemplatePartialSpecializationDecl *>())
- Location = nameLocation(Template, SM);
- } else
- Location = nameLocation(Specialization, SM);
- }
+ TST->desugar().getCanonicalType()->getAsCXXRecordDecl()))
+ Location = getPreferredLocationFromSpecialization(Specialization);
return handleTemplateSpecialization(TST->getTemplateName(),
TST->template_arguments(), Location);
}
@@ -693,6 +722,8 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
CurrentTypeRAII Guard(*this, ST->getReplacementType());
return Visit(ST->getReplacementType().getTypePtr());
}
+
+ std::vector<InlayHintLabelPart> take() { return std::move(LabelChunks); }
};
unsigned lengthOfInlayHintLabel(llvm::ArrayRef<InlayHintLabelPart> Labels) {
@@ -1363,11 +1394,9 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
std::vector<InlayHintLabelPart> buildTypeHint(QualType T,
llvm::StringRef Prefix) {
- std::vector<InlayHintLabelPart> Chunks;
- TypeHintBuilder Builder(T, AST, MainFilePath, TypeHintPolicy, Prefix,
- Chunks);
+ TypeHintBuilder Builder(T, AST, MainFilePath, TypeHintPolicy, Prefix);
Builder.Visit(T.getTypePtr());
- return Chunks;
+ return Builder.take();
}
void addTypeHint(SourceRange R, QualType T, llvm::StringRef Prefix) {
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index 230aa6ca3c8ca..dcfbaa35b2872 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1868,6 +1868,20 @@ TEST(TypeHints, LinksForForwardDeclarations) {
ExpectedHintLabelPiece{">"});
}
+TEST(TypeHints, LinksForStructureBindings) {
+ StringRef Source(R"cpp(
+ template <typename>
+ struct $A[[A]] {};
+ A<float> bar[1];
+
+ auto [$1[[value]]] = bar;
+ )cpp");
+
+ assertTypeLinkHints(Source, "1", ExpectedHintLabelPiece{": "},
+ ExpectedHintLabelPiece{"A", "A"},
+ ExpectedHintLabelPiece{"<float>"});
+}
+
TEST(DesignatorHints, Basic) {
assertDesignatorHints(R"cpp(
struct S { int x, y, z; };
>From 2973958743d6337cdba84f882efdcd3031d079c6 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 8 Jul 2024 18:31:59 +0800
Subject: [PATCH 26/36] Almost there!
---
clang-tools-extra/clangd/InlayHints.cpp | 63 +++++++++++--------------
1 file changed, 28 insertions(+), 35 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 480ffb2c531a7..4df0be7492a7f 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -441,10 +441,8 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
switch (TA.getKind()) {
case TemplateArgument::Pack:
return printTemplateArgumentList(TA.pack_elements());
- case TemplateArgument::Type: {
- CurrentTypeRAII Guard(*this, TA.getAsType());
- return Visit(TA.getAsType().getTypePtr());
- }
+ case TemplateArgument::Type:
+ return VisitQualType(TA.getAsType());
// TODO: Add support for NTTP arguments.
case TemplateArgument::Expression:
case TemplateArgument::StructuralValue:
@@ -568,11 +566,10 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
}
public:
- TypeHintBuilder(QualType Current, ASTContext &Context, StringRef MainFilePath,
+ TypeHintBuilder(ASTContext &Context, StringRef MainFilePath,
const PrintingPolicy &PP, llvm::StringRef Prefix)
- : CurrentType(Current), CurrentNestedNameSpecifier(nullptr),
- Context(Context), MainFilePath(MainFilePath), PP(PP),
- SM(Context.getSourceManager()) {
+ : CurrentNestedNameSpecifier(nullptr), Context(Context),
+ MainFilePath(MainFilePath), PP(PP), SM(Context.getSourceManager()) {
LabelChunks.reserve(16);
if (!Prefix.empty())
addLabel(Prefix.str());
@@ -580,6 +577,18 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
void VisitType(const Type *) { addLabel(CurrentType.getAsString(PP)); }
+ void VisitQualType(QualType Q, NestedNameSpecifier *NNS = nullptr) {
+ QualType PreviousType = CurrentType;
+ NestedNameSpecifier *PreviousNNS = CurrentNestedNameSpecifier;
+ CurrentType = Q;
+ CurrentNestedNameSpecifier = NNS;
+
+ TypeVisitor::Visit(Q.getTypePtr());
+
+ CurrentType = PreviousType;
+ CurrentNestedNameSpecifier = PreviousNNS;
+ }
+
void VisitTagType(const TagType *TT) {
// Note that we have cases where the type of a template specialization is
// modeled as a RecordType rather than a TemplateSpecializationType. (Type
@@ -620,10 +629,9 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
void VisitAutoType(const AutoType *AT) {
if (!AT->isDeduced() || AT->getDeducedType()->isDecltypeType())
- return;
+ return VisitType(AT);
maybeAddQualifiers();
- CurrentTypeRAII Guard(*this, AT->getDeducedType());
- return Visit(AT->getDeducedType().getTypePtr());
+ return VisitQualType(AT->getDeducedType());
}
void VisitElaboratedType(const ElaboratedType *ET) {
@@ -647,26 +655,19 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
case NestedNameSpecifier::TypeSpecWithTemplate:
if (PP.SuppressScope)
break;
- CurrentTypeRAII Guard(
- *this,
- QualType(
- NNS->getAsType(),
- /*Quals=*/0) // Do we need cv-qualifiers on type specifiers?
- );
- Visit(NNS->getAsType());
+ // Do we need cv-qualifiers on type specifiers?
+ VisitQualType(QualType(NNS->getAsType(), /*Quals=*/0));
addLabel("::");
break;
}
}
- CurrentTypeRAII Guard(*this, ET->getNamedType(), ET->getQualifier());
- return Visit(ET->getNamedType().getTypePtr());
+ return VisitQualType(ET->getNamedType(), ET->getQualifier());
}
void VisitReferenceType(const ReferenceType *RT) {
maybeAddQualifiers();
QualType Next = skipTopLevelReferences(RT->getPointeeTypeAsWritten());
- CurrentTypeRAII Guard(*this, Next);
- Visit(Next.getTypePtr());
+ VisitQualType(Next);
if (Next->getPointeeType().isNull())
addLabel(" ");
if (RT->isLValueReferenceType())
@@ -677,23 +678,16 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
void VisitPointerType(const PointerType *PT) {
QualType Next = PT->getPointeeType();
- std::optional<CurrentTypeRAII> Guard(std::in_place, *this, Next);
- Visit(Next.getTypePtr());
+ VisitQualType(Next);
if (Next->getPointeeType().isNull())
addLabel(" ");
addLabel("*");
- Guard.reset();
maybeAddQualifiers();
}
void VisitUsingType(const UsingType *UT) {
addLabel([&](llvm::raw_ostream &OS) { UT->getFoundDecl()->printName(OS); },
- [&] {
- BaseUsingDecl *Introducer = UT->getFoundDecl()->getIntroducer();
- if (auto *UD = dyn_cast<UsingDecl>(Introducer))
- return nameLocation(UD, SM);
- return nameLocation(Introducer, SM);
- }());
+ nameLocation(UT->getFoundDecl()->getIntroducer(), SM));
}
void VisitTypedefType(const TypedefType *TT) {
@@ -719,8 +713,7 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *ST) {
maybeAddQualifiers();
- CurrentTypeRAII Guard(*this, ST->getReplacementType());
- return Visit(ST->getReplacementType().getTypePtr());
+ return VisitQualType(ST->getReplacementType());
}
std::vector<InlayHintLabelPart> take() { return std::move(LabelChunks); }
@@ -1394,8 +1387,8 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
std::vector<InlayHintLabelPart> buildTypeHint(QualType T,
llvm::StringRef Prefix) {
- TypeHintBuilder Builder(T, AST, MainFilePath, TypeHintPolicy, Prefix);
- Builder.Visit(T.getTypePtr());
+ TypeHintBuilder Builder(AST, MainFilePath, TypeHintPolicy, Prefix);
+ Builder.VisitQualType(T);
return Builder.take();
}
>From d4e3ff742abaf7915da4f317f2d12d12094bf3af Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 8 Jul 2024 18:50:07 +0800
Subject: [PATCH 27/36] Add handling for C recordTypes.
---
clang-tools-extra/clangd/InlayHints.cpp | 20 ++++++++++---
.../clangd/unittests/InlayHintTests.cpp | 28 +++++++++++++++++--
2 files changed, 42 insertions(+), 6 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 4df0be7492a7f..449524f31dffa 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -590,6 +590,21 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
}
void VisitTagType(const TagType *TT) {
+ auto *CXXRD = dyn_cast<CXXRecordDecl>(TT->getDecl());
+ if (!CXXRD) {
+ // This might be a C TagDecl.
+ if (auto *RD = dyn_cast<RecordDecl>(TT->getDecl())) {
+ // FIXME: Respect SuppressTagKeyword in other cases.
+ if (!PP.SuppressTagKeyword && !RD->getTypedefNameForAnonDecl()) {
+ addLabel(RD->getKindName().str());
+ addLabel(" ");
+ }
+ return addLabel(
+ [&](llvm::raw_ostream &OS) { return RD->printName(OS, PP); },
+ nameLocation(RD, SM));
+ }
+ return VisitType(TT);
+ }
// Note that we have cases where the type of a template specialization is
// modeled as a RecordType rather than a TemplateSpecializationType. (Type
// sugars are not preserved?)
@@ -602,16 +617,13 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
// auto [value] = bar;
//
// The type of value is modeled as a RecordType here.
- auto *CXXRD = dyn_cast<CXXRecordDecl>(TT->getDecl());
- if (!CXXRD)
- return VisitType(TT);
CXXRecordDecl *Pattern = CXXRD->getTemplateInstantiationPattern();
if (!Pattern)
return addLabel(
[&](llvm::raw_ostream &OS) { return CXXRD->printName(OS, PP); },
nameLocation(CXXRD, SM));
- // FIXME: Do we have other kind of specializations?
+ // FIXME: Do we have other kinds of specializations?
if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CXXRD))
return handleTemplateSpecialization(
TemplateName(Pattern->getDescribedClassTemplate()),
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index dcfbaa35b2872..8f295c4608d18 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1671,13 +1671,23 @@ TEST(TypeHints, SubstTemplateParameterAliases) {
ExpectedHint{": static_vector<int>", "vector_name"});
}
-template <typename... Labels>
+enum struct Language {
+ CXX26 = 1,
+ C23 = 2,
+};
+
+template <Language L = Language::CXX26, typename... Labels>
void assertTypeLinkHints(StringRef Code, StringRef HintRange,
Labels... ExpectedLabels) {
Annotations Source(Code);
TestTU TU = TestTU::withCode(Source.code());
- TU.ExtraArgs.push_back("-std=c++2c");
+ if constexpr (L == Language::CXX26) {
+ TU.ExtraArgs.push_back("-std=c++2c");
+ } else if constexpr (L == Language::C23) {
+ TU.ExtraArgs.push_back("-xc");
+ TU.ExtraArgs.push_back("-std=c23");
+ }
auto AST = TU.build();
Config C;
@@ -1882,6 +1892,20 @@ TEST(TypeHints, LinksForStructureBindings) {
ExpectedHintLabelPiece{"<float>"});
}
+TEST(TypeHints, TypeDeductionForC) {
+ StringRef Source(R"cpp(
+ struct $Waldo[[Waldo]] {};
+ struct Waldo foo();
+ int main() {
+ auto $theres[[w]] /*: struct Waldo */ = foo();
+ }
+ )cpp");
+
+ assertTypeLinkHints<Language::C23>(Source, "theres",
+ ExpectedHintLabelPiece{": struct "},
+ ExpectedHintLabelPiece{"Waldo", "Waldo"});
+}
+
TEST(DesignatorHints, Basic) {
assertDesignatorHints(R"cpp(
struct S { int x, y, z; };
>From 77b552520c866ac72008e63decc9c19c15fbe1ea Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 8 Jul 2024 18:52:00 +0800
Subject: [PATCH 28/36] Remove CurrentTypeRAII
---
clang-tools-extra/clangd/InlayHints.cpp | 18 ------------------
1 file changed, 18 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 449524f31dffa..45e2facd865f3 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -383,24 +383,6 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
SourceManager &SM;
std::vector<InlayHintLabelPart> LabelChunks;
- struct CurrentTypeRAII {
- TypeHintBuilder &Builder;
- QualType PreviousType;
- NestedNameSpecifier *PreviousNestedNameSpecifier;
- CurrentTypeRAII(TypeHintBuilder &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,
SourceLocation Location) {
std::string Label;
>From 1be1f754929649422d405051a824dbfc46f4fbf6 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 8 Jul 2024 19:02:22 +0800
Subject: [PATCH 29/36] Revert unrelated changes
---
clang-tools-extra/clangd/AST.cpp | 9 ---------
clang-tools-extra/clangd/AST.h | 2 --
clang-tools-extra/clangd/index/IndexAction.cpp | 9 ++++++---
3 files changed, 6 insertions(+), 14 deletions(-)
diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index dd645861dda8b..e5d6ec748f689 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -1026,14 +1026,5 @@ bool isExpandedFromParameterPack(const ParmVarDecl *D) {
return getUnderlyingPackType(D) != nullptr;
}
-std::optional<URI> toURI(OptionalFileEntryRef File) {
- if (!File)
- return std::nullopt;
- auto AbsolutePath = File->getFileEntry().tryGetRealPathName();
- if (AbsolutePath.empty())
- return std::nullopt;
- return URI::create(AbsolutePath);
-}
-
} // namespace clangd
} // namespace clang
diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h
index f56759656997b..e1582f9d0e743 100644
--- a/clang-tools-extra/clangd/AST.h
+++ b/clang-tools-extra/clangd/AST.h
@@ -256,8 +256,6 @@ resolveForwardingParameters(const FunctionDecl *D, unsigned MaxDepth = 10);
/// reference to one (e.g. `Args&...` or `Args&&...`).
bool isExpandedFromParameterPack(const ParmVarDecl *D);
-std::optional<URI> toURI(OptionalFileEntryRef File);
-
} // namespace clangd
} // namespace clang
diff --git a/clang-tools-extra/clangd/index/IndexAction.cpp b/clang-tools-extra/clangd/index/IndexAction.cpp
index f3fbb9fc307e2..ed56c2a9d2e81 100644
--- a/clang-tools-extra/clangd/index/IndexAction.cpp
+++ b/clang-tools-extra/clangd/index/IndexAction.cpp
@@ -32,9 +32,12 @@ namespace clangd {
namespace {
std::optional<std::string> toURI(OptionalFileEntryRef File) {
- if (auto URI = clang::clangd::toURI(File))
- return URI->toString();
- return std::nullopt;
+ if (!File)
+ return std::nullopt;
+ auto AbsolutePath = File->getFileEntry().tryGetRealPathName();
+ if (AbsolutePath.empty())
+ return std::nullopt;
+ return URI::create(AbsolutePath).toString();
}
// Collects the nodes and edges of include graph during indexing action.
>From 35816e0d1fb4347432583d1c0265369c3ab0a49f Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 9 Jul 2024 20:40:46 +0800
Subject: [PATCH 30/36] Refactor handleTemplateSpecialization
---
clang-tools-extra/clangd/InlayHints.cpp | 86 ++++++++++++-------------
1 file changed, 42 insertions(+), 44 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 45e2facd865f3..5db46a58a4c4b 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -31,6 +31,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
#include "llvm/Support/SaveAndRestore.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/raw_ostream.h"
@@ -442,44 +443,10 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
addLabel(std::move(Label));
}
- void handleTemplateSpecialization(
- TemplateName TN, llvm::ArrayRef<TemplateArgument> Args,
- SourceLocation LocationForOverride = 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;
- }
-
- if (LocationForOverride.isValid())
- Location = LocationForOverride;
-
- assert(Location.isValid() || LocationForOverride.isValid());
-
- addLabel([&](llvm::raw_ostream &OS) { TN.print(OS, PP, PrintType); },
- Location);
-
+ void handleTemplateSpecialization(llvm::StringRef TemplateId,
+ llvm::ArrayRef<TemplateArgument> Args,
+ SourceLocation Location) {
+ addLabel([&](llvm::raw_ostream &OS) { OS << TemplateId; }, Location);
addLabel("<");
printTemplateArgumentList(Args);
addLabel(">");
@@ -606,11 +573,14 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
nameLocation(CXXRD, SM));
// FIXME: Do we have other kinds of specializations?
- if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CXXRD))
+ if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CXXRD)) {
+ std::string TemplateId;
+ llvm::raw_string_ostream OS(TemplateId);
+ CTSD->printName(OS);
return handleTemplateSpecialization(
- TemplateName(Pattern->getDescribedClassTemplate()),
- CTSD->getTemplateArgs().asArray(),
+ TemplateId, CTSD->getTemplateArgs().asArray(),
getPreferredLocationFromSpecialization(CTSD));
+ }
return VisitType(TT);
}
@@ -690,8 +660,33 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
}
void VisitTemplateSpecializationType(const TemplateSpecializationType *TST) {
- maybeAddQualifiers();
SourceLocation Location;
+ TemplateName Name = TST->getTemplateName();
+ TemplateName::Qualified PrintQual = TemplateName::Qualified::AsWritten;
+ switch (Name.getKind()) {
+ case TemplateName::Template:
+ case TemplateName::QualifiedTemplate: {
+ QualifiedTemplateName *Qual = Name.getAsQualifiedTemplateName();
+ if (Qual->getQualifier() == CurrentNestedNameSpecifier) {
+ // We have handled the NNS in VisitElaboratedType(). Avoid printing it
+ // twice.
+ Name = Qual->getUnderlyingTemplate();
+ PrintQual = TemplateName::Qualified::None;
+ }
+ [[fallthrough]];
+ }
+ case TemplateName::SubstTemplateTemplateParm:
+ case TemplateName::UsingTemplate:
+ Location = Name.getAsTemplateDecl()->getLocation();
+ break;
+ case TemplateName::OverloadedTemplate:
+ case TemplateName::AssumedTemplate:
+ case TemplateName::DependentTemplate:
+ case TemplateName::SubstTemplateTemplateParmPack:
+ // FIXME: Handle these cases.
+ return VisitType(TST);
+ }
+ maybeAddQualifiers();
// Special case the ClassTemplateSpecializationDecl because
// we want the location of an explicit specialization, if present.
// FIXME: In practice, populating the location with that of the
@@ -701,8 +696,11 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
dyn_cast_if_present<ClassTemplateSpecializationDecl>(
TST->desugar().getCanonicalType()->getAsCXXRecordDecl()))
Location = getPreferredLocationFromSpecialization(Specialization);
- return handleTemplateSpecialization(TST->getTemplateName(),
- TST->template_arguments(), Location);
+ std::string TemplateId;
+ llvm::raw_string_ostream OS(TemplateId);
+ Name.print(OS, PP, PrintQual);
+ return handleTemplateSpecialization(TemplateId, TST->template_arguments(),
+ Location);
}
void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *ST) {
>From 96611a3b3b9a95ea758712bf2fa8d16a5ace3229 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Thu, 11 Jul 2024 20:09:31 +0800
Subject: [PATCH 31/36] Avoid a nullptr dereference
---
clang-tools-extra/clangd/InlayHints.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 5db46a58a4c4b..ba2c440c42069 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -666,8 +666,8 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
switch (Name.getKind()) {
case TemplateName::Template:
case TemplateName::QualifiedTemplate: {
- QualifiedTemplateName *Qual = Name.getAsQualifiedTemplateName();
- if (Qual->getQualifier() == CurrentNestedNameSpecifier) {
+ if (QualifiedTemplateName *Qual = Name.getAsQualifiedTemplateName();
+ Qual && Qual->getQualifier() == CurrentNestedNameSpecifier) {
// We have handled the NNS in VisitElaboratedType(). Avoid printing it
// twice.
Name = Qual->getUnderlyingTemplate();
>From 49f9ec5999214dd9b7388c827b6c68cd2d8bc0e4 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sat, 13 Jul 2024 00:34:17 +0800
Subject: [PATCH 32/36] Use getTemplateSpecializationKind() instead of
getTemplateInstantiationPattern() for specializations
---
clang-tools-extra/clangd/InlayHints.cpp | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index ba2c440c42069..70f7815618311 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -566,12 +566,14 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
// auto [value] = bar;
//
// The type of value is modeled as a RecordType here.
- CXXRecordDecl *Pattern = CXXRD->getTemplateInstantiationPattern();
- if (!Pattern)
- return addLabel(
- [&](llvm::raw_ostream &OS) { return CXXRD->printName(OS, PP); },
- nameLocation(CXXRD, SM));
+ // The ClassTemplateSpecializationDecl could be of TSK_Undeclared
+ // kind. So handle the ClassTemplateSpecializationDecl case first for
+ // template arguments.
+ // E.g. when we're inside a range-based for loop:
+ // template <class T, class U> struct Pair;
+ // for (auto p : SmallVector<Pair<SourceLocation, SourceRange>>()) {}
+ // (Pair is of TSK_Undeclared kind here.)
// FIXME: Do we have other kinds of specializations?
if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CXXRD)) {
std::string TemplateId;
@@ -582,6 +584,12 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
getPreferredLocationFromSpecialization(CTSD));
}
+ // We don't have a template arguments now. Find the name and its location.
+ if (!CXXRD->getTemplateSpecializationKind())
+ return addLabel(
+ [&](llvm::raw_ostream &OS) { return CXXRD->printName(OS, PP); },
+ nameLocation(CXXRD, SM));
+
return VisitType(TT);
}
>From 5595bdf9c81a7d46e72d8ba4005ae2fb410c7d5d Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sun, 14 Jul 2024 12:45:35 +0800
Subject: [PATCH 33/36] Handle DeducedTemplateSpecializationType for CTAD
---
clang-tools-extra/clangd/InlayHints.cpp | 11 +++++++++-
.../clangd/unittests/InlayHintTests.cpp | 22 +++++++++++++++++++
2 files changed, 32 insertions(+), 1 deletion(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 70f7815618311..ea0ab646c8973 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -668,6 +668,7 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
}
void VisitTemplateSpecializationType(const TemplateSpecializationType *TST) {
+ maybeAddQualifiers();
SourceLocation Location;
TemplateName Name = TST->getTemplateName();
TemplateName::Qualified PrintQual = TemplateName::Qualified::AsWritten;
@@ -694,7 +695,6 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
// FIXME: Handle these cases.
return VisitType(TST);
}
- maybeAddQualifiers();
// Special case the ClassTemplateSpecializationDecl because
// we want the location of an explicit specialization, if present.
// FIXME: In practice, populating the location with that of the
@@ -711,6 +711,15 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
Location);
}
+ void VisitDeducedTemplateSpecializationType(
+ const DeducedTemplateSpecializationType *TST) {
+ maybeAddQualifiers();
+ // FIXME: The TST->getTemplateName() might differ from the name of
+ // DeducedType, e.g. when the deduction guide is formed against a type alias
+ // Decl.
+ return VisitQualType(TST->getDeducedType());
+ }
+
void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *ST) {
maybeAddQualifiers();
return VisitQualType(ST->getReplacementType());
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index 8f295c4608d18..dbab49b516ea3 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1892,6 +1892,28 @@ TEST(TypeHints, LinksForStructureBindings) {
ExpectedHintLabelPiece{"<float>"});
}
+TEST(TypeHints, LinksForDeductionGuides) {
+ StringRef Source(R"cpp(
+ template <class T>
+ struct S { S(T); };
+
+ template <>
+ struct $S[[S]]<int> { S(int); };
+
+ template <class U>
+ using SS = S<U>;
+
+ // FIXME: Would better be SS<int>.
+ // (However, TypePrinter doesn't print SS<int> either.)
+ const auto $1[[w]] = S(42);
+ )cpp");
+
+ assertTypeLinkHints(Source, "1", ExpectedHintLabelPiece{": const "},
+ ExpectedHintLabelPiece{"S", "S"},
+ ExpectedHintLabelPiece{"<int>"});
+}
+
+
TEST(TypeHints, TypeDeductionForC) {
StringRef Source(R"cpp(
struct $Waldo[[Waldo]] {};
>From df828281ab8f4bc4b2168bc3b3a0d54840b97d43 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 16 Jul 2024 17:57:54 +0800
Subject: [PATCH 34/36] Refactor the handling of Qualifiers
---
clang-tools-extra/clangd/InlayHints.cpp | 117 ++++++++++++++----
.../clangd/unittests/InlayHintTests.cpp | 17 ++-
2 files changed, 108 insertions(+), 26 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index ea0ab646c8973..3213f7d028e9b 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -383,12 +383,15 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
const PrintingPolicy &PP;
SourceManager &SM;
std::vector<InlayHintLabelPart> LabelChunks;
+ bool AppendSpaceToQuals;
void addLabel(llvm::function_ref<void(llvm::raw_ostream &)> NamePrinter,
- SourceLocation Location) {
+ SourceLocation Location = SourceLocation()) {
std::string Label;
llvm::raw_string_ostream OS(Label);
NamePrinter(OS);
+ if (!Location.isValid())
+ return addLabel(std::move(Label));
auto &Name = LabelChunks.emplace_back();
Name.value = std::move(Label);
Name.location = makeLocation(Context, Location, MainFilePath);
@@ -452,12 +455,11 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
addLabel(">");
}
- void maybeAddQualifiers() {
- auto Quals = CurrentType.split().Quals;
- if (!Quals.empty()) {
- addLabel(Quals.getAsString());
- addLabel(" ");
- }
+ void maybeAddQualifiers(bool AppendSpaceToQuals) {
+ addLabel([&](llvm::raw_ostream &OS) {
+ CurrentType.split().Quals.print(
+ OS, PP, /*appendSpaceIfNonEmpty=*/AppendSpaceToQuals);
+ });
}
// When printing a reference, the referenced type might also be a reference.
@@ -497,6 +499,55 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
return ::clang::clangd::nameLocation(*D, SM);
}
+ // CanPrefixQualifiers - We prefer to print type qualifiers
+ // before the type, so that we get "const int" instead of "int const", but we
+ // can't do this if the type is complex. For example if the type is "int*",
+ // we *must* print "int * const", printing "const int *" is different. Only
+ // do this when the type expands to a simple string.
+ // This is similar to the private function \p
+ // TypePrinter::canPrefixQualifiers().
+ // FIXME: Refactor and share the same implementation.
+ static bool canPrefixQualifiers(const Type *T) {
+ bool CanPrefixQualifiers = false;
+ const Type *UnderlyingType = T;
+ if (const auto *AT = dyn_cast<AutoType>(T))
+ UnderlyingType = AT->desugar().getTypePtr();
+ if (const auto *Subst = dyn_cast<SubstTemplateTypeParmType>(T))
+ UnderlyingType = Subst->getReplacementType().getTypePtr();
+ Type::TypeClass TC = UnderlyingType->getTypeClass();
+
+ switch (TC) {
+ case Type::Adjusted:
+ case Type::Decayed:
+ case Type::ArrayParameter:
+ case Type::Pointer:
+ case Type::BlockPointer:
+ case Type::LValueReference:
+ case Type::RValueReference:
+ case Type::MemberPointer:
+ case Type::DependentAddressSpace:
+ case Type::DependentVector:
+ case Type::DependentSizedExtVector:
+ case Type::Vector:
+ case Type::ExtVector:
+ case Type::ConstantMatrix:
+ case Type::DependentSizedMatrix:
+ case Type::FunctionProto:
+ case Type::FunctionNoProto:
+ case Type::Paren:
+ case Type::PackExpansion:
+ case Type::SubstTemplateTypeParm:
+ case Type::MacroQualified:
+ case Type::CountAttributed:
+ CanPrefixQualifiers = false;
+ break;
+ default:
+ CanPrefixQualifiers = true;
+ }
+
+ return CanPrefixQualifiers;
+ }
+
SourceLocation getPreferredLocationFromSpecialization(
ClassTemplateSpecializationDecl *Specialization) const {
SourceLocation Location;
@@ -518,24 +569,39 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
TypeHintBuilder(ASTContext &Context, StringRef MainFilePath,
const PrintingPolicy &PP, llvm::StringRef Prefix)
: CurrentNestedNameSpecifier(nullptr), Context(Context),
- MainFilePath(MainFilePath), PP(PP), SM(Context.getSourceManager()) {
+ MainFilePath(MainFilePath), PP(PP), SM(Context.getSourceManager()),
+ AppendSpaceToQuals(true) {
LabelChunks.reserve(16);
if (!Prefix.empty())
addLabel(Prefix.str());
}
- void VisitType(const Type *) { addLabel(CurrentType.getAsString(PP)); }
+ void VisitType(const Type *T) {
+ // We should have handled qualifiers in VisitQualType(). Don't print them
+ // twice.
+ addLabel(QualType(T, /*Quals=*/0).getAsString());
+ }
+
+ void VisitQualType(QualType Q, bool AppendSpaceToTopLevelQuals = true,
+ NestedNameSpecifier *NNS = nullptr) {
+ QualType PreviousType = this->CurrentType;
+ NestedNameSpecifier *PreviousNNS = this->CurrentNestedNameSpecifier;
+ bool PrevAppendSpaceToQuals = this->AppendSpaceToQuals;
- void VisitQualType(QualType Q, NestedNameSpecifier *NNS = nullptr) {
- QualType PreviousType = CurrentType;
- NestedNameSpecifier *PreviousNNS = CurrentNestedNameSpecifier;
- CurrentType = Q;
- CurrentNestedNameSpecifier = NNS;
+ this->AppendSpaceToQuals = AppendSpaceToTopLevelQuals;
+ this->CurrentType = Q;
+ this->CurrentNestedNameSpecifier = NNS;
+ bool CanPrefixQualifiers = canPrefixQualifiers(CurrentType.getTypePtr());
+ if (CanPrefixQualifiers)
+ maybeAddQualifiers(/*AppendSpaceToQuals=*/true);
TypeVisitor::Visit(Q.getTypePtr());
- CurrentType = PreviousType;
- CurrentNestedNameSpecifier = PreviousNNS;
+ if (!CanPrefixQualifiers)
+ maybeAddQualifiers(/*AppendSpaceToQuals=*/AppendSpaceToTopLevelQuals);
+ this->CurrentType = PreviousType;
+ this->CurrentNestedNameSpecifier = PreviousNNS;
+ this->AppendSpaceToQuals = PrevAppendSpaceToQuals;
}
void VisitTagType(const TagType *TT) {
@@ -602,12 +668,11 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
void VisitAutoType(const AutoType *AT) {
if (!AT->isDeduced() || AT->getDeducedType()->isDecltypeType())
return VisitType(AT);
- maybeAddQualifiers();
return VisitQualType(AT->getDeducedType());
}
void VisitElaboratedType(const ElaboratedType *ET) {
- maybeAddQualifiers();
+ // maybeAddQualifiers();
if (auto *NNS = ET->getQualifier()) {
switch (NNS->getKind()) {
case NestedNameSpecifier::Identifier:
@@ -633,11 +698,13 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
break;
}
}
- return VisitQualType(ET->getNamedType(), ET->getQualifier());
+ return VisitQualType(ET->getNamedType(),
+ /*AppendSpaceToTopLevelQuals=*/true,
+ ET->getQualifier());
}
void VisitReferenceType(const ReferenceType *RT) {
- maybeAddQualifiers();
+ // maybeAddQualifiers();
QualType Next = skipTopLevelReferences(RT->getPointeeTypeAsWritten());
VisitQualType(Next);
if (Next->getPointeeType().isNull())
@@ -654,7 +721,7 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
if (Next->getPointeeType().isNull())
addLabel(" ");
addLabel("*");
- maybeAddQualifiers();
+ // maybeAddQualifiers();
}
void VisitUsingType(const UsingType *UT) {
@@ -668,7 +735,7 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
}
void VisitTemplateSpecializationType(const TemplateSpecializationType *TST) {
- maybeAddQualifiers();
+ // maybeAddQualifiers();
SourceLocation Location;
TemplateName Name = TST->getTemplateName();
TemplateName::Qualified PrintQual = TemplateName::Qualified::AsWritten;
@@ -713,7 +780,7 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
void VisitDeducedTemplateSpecializationType(
const DeducedTemplateSpecializationType *TST) {
- maybeAddQualifiers();
+ // maybeAddQualifiers();
// FIXME: The TST->getTemplateName() might differ from the name of
// DeducedType, e.g. when the deduction guide is formed against a type alias
// Decl.
@@ -721,7 +788,7 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
}
void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *ST) {
- maybeAddQualifiers();
+ // maybeAddQualifiers();
return VisitQualType(ST->getReplacementType());
}
@@ -1397,7 +1464,7 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
std::vector<InlayHintLabelPart> buildTypeHint(QualType T,
llvm::StringRef Prefix) {
TypeHintBuilder Builder(AST, MainFilePath, TypeHintPolicy, Prefix);
- Builder.VisitQualType(T);
+ Builder.VisitQualType(T, /*AppendSpaceToTopLevelQuals=*/false);
return Builder.take();
}
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index dbab49b516ea3..27d2eda602ed0 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1294,7 +1294,7 @@ TEST(TypeHints, DecltypeAuto) {
ExpectedHint{": int &", "z"});
}
-TEST(TypeHints, NoQualifiers) {
+TEST(TypeHints, NoNNS) {
assertTypeHints(R"cpp(
namespace A {
namespace B {
@@ -1913,6 +1913,21 @@ TEST(TypeHints, LinksForDeductionGuides) {
ExpectedHintLabelPiece{"<int>"});
}
+TEST(TypeHints, Qualifiers) {
+ StringRef Source(R"cpp(
+ struct Base { virtual ~Base() = default; };
+
+ struct $Derived[[Derived]] : virtual Base {};
+
+ Base *make();
+
+ const auto $1[[ptr]] = dynamic_cast<const Derived *>(make());
+ )cpp");
+
+ assertTypeLinkHints(Source, "1", ExpectedHintLabelPiece{": const "},
+ ExpectedHintLabelPiece{"Derived", "Derived"},
+ ExpectedHintLabelPiece{" *const"});
+}
TEST(TypeHints, TypeDeductionForC) {
StringRef Source(R"cpp(
>From dfb317e3850e4e663fccad5670cbae3ef79c721c Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 16 Jul 2024 18:47:03 +0800
Subject: [PATCH 35/36] cleanup
---
clang-tools-extra/clangd/InlayHints.cpp | 25 +++++++------------------
1 file changed, 7 insertions(+), 18 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 3213f7d028e9b..7542ece575bb7 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -383,7 +383,6 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
const PrintingPolicy &PP;
SourceManager &SM;
std::vector<InlayHintLabelPart> LabelChunks;
- bool AppendSpaceToQuals;
void addLabel(llvm::function_ref<void(llvm::raw_ostream &)> NamePrinter,
SourceLocation Location = SourceLocation()) {
@@ -569,8 +568,7 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
TypeHintBuilder(ASTContext &Context, StringRef MainFilePath,
const PrintingPolicy &PP, llvm::StringRef Prefix)
: CurrentNestedNameSpecifier(nullptr), Context(Context),
- MainFilePath(MainFilePath), PP(PP), SM(Context.getSourceManager()),
- AppendSpaceToQuals(true) {
+ MainFilePath(MainFilePath), PP(PP), SM(Context.getSourceManager()) {
LabelChunks.reserve(16);
if (!Prefix.empty())
addLabel(Prefix.str());
@@ -584,13 +582,11 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
void VisitQualType(QualType Q, bool AppendSpaceToTopLevelQuals = true,
NestedNameSpecifier *NNS = nullptr) {
- QualType PreviousType = this->CurrentType;
- NestedNameSpecifier *PreviousNNS = this->CurrentNestedNameSpecifier;
- bool PrevAppendSpaceToQuals = this->AppendSpaceToQuals;
+ QualType PreviousType = CurrentType;
+ NestedNameSpecifier *PreviousNNS = CurrentNestedNameSpecifier;
- this->AppendSpaceToQuals = AppendSpaceToTopLevelQuals;
- this->CurrentType = Q;
- this->CurrentNestedNameSpecifier = NNS;
+ CurrentType = Q;
+ CurrentNestedNameSpecifier = NNS;
bool CanPrefixQualifiers = canPrefixQualifiers(CurrentType.getTypePtr());
if (CanPrefixQualifiers)
maybeAddQualifiers(/*AppendSpaceToQuals=*/true);
@@ -599,9 +595,8 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
if (!CanPrefixQualifiers)
maybeAddQualifiers(/*AppendSpaceToQuals=*/AppendSpaceToTopLevelQuals);
- this->CurrentType = PreviousType;
- this->CurrentNestedNameSpecifier = PreviousNNS;
- this->AppendSpaceToQuals = PrevAppendSpaceToQuals;
+ CurrentType = PreviousType;
+ CurrentNestedNameSpecifier = PreviousNNS;
}
void VisitTagType(const TagType *TT) {
@@ -672,7 +667,6 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
}
void VisitElaboratedType(const ElaboratedType *ET) {
- // maybeAddQualifiers();
if (auto *NNS = ET->getQualifier()) {
switch (NNS->getKind()) {
case NestedNameSpecifier::Identifier:
@@ -704,7 +698,6 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
}
void VisitReferenceType(const ReferenceType *RT) {
- // maybeAddQualifiers();
QualType Next = skipTopLevelReferences(RT->getPointeeTypeAsWritten());
VisitQualType(Next);
if (Next->getPointeeType().isNull())
@@ -721,7 +714,6 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
if (Next->getPointeeType().isNull())
addLabel(" ");
addLabel("*");
- // maybeAddQualifiers();
}
void VisitUsingType(const UsingType *UT) {
@@ -735,7 +727,6 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
}
void VisitTemplateSpecializationType(const TemplateSpecializationType *TST) {
- // maybeAddQualifiers();
SourceLocation Location;
TemplateName Name = TST->getTemplateName();
TemplateName::Qualified PrintQual = TemplateName::Qualified::AsWritten;
@@ -780,7 +771,6 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
void VisitDeducedTemplateSpecializationType(
const DeducedTemplateSpecializationType *TST) {
- // maybeAddQualifiers();
// FIXME: The TST->getTemplateName() might differ from the name of
// DeducedType, e.g. when the deduction guide is formed against a type alias
// Decl.
@@ -788,7 +778,6 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
}
void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *ST) {
- // maybeAddQualifiers();
return VisitQualType(ST->getReplacementType());
}
>From 8e3826e37a8aa44619ac44f6ff5c5e5dca28473d Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 16 Jul 2024 23:09:03 +0800
Subject: [PATCH 36/36] Fix qualifiers further more
---
clang-tools-extra/clangd/InlayHints.cpp | 23 +++++++++---
.../clangd/unittests/InlayHintTests.cpp | 36 ++++++++++++++++++-
2 files changed, 53 insertions(+), 6 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 7542ece575bb7..d342ccf7d2da6 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -383,6 +383,7 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
const PrintingPolicy &PP;
SourceManager &SM;
std::vector<InlayHintLabelPart> LabelChunks;
+ bool AppendTrailingSpaceBeforeRightQual = true;
void addLabel(llvm::function_ref<void(llvm::raw_ostream &)> NamePrinter,
SourceLocation Location = SourceLocation()) {
@@ -588,13 +589,23 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
CurrentType = Q;
CurrentNestedNameSpecifier = NNS;
bool CanPrefixQualifiers = canPrefixQualifiers(CurrentType.getTypePtr());
+ bool PrevAppendTrailingSpaceBeforeRightQual =
+ AppendTrailingSpaceBeforeRightQual;
if (CanPrefixQualifiers)
maybeAddQualifiers(/*AppendSpaceToQuals=*/true);
TypeVisitor::Visit(Q.getTypePtr());
- if (!CanPrefixQualifiers)
+ bool HaveTrailingQuals =
+ !CanPrefixQualifiers && !CurrentType.split().Quals.empty();
+ if (AppendTrailingSpaceBeforeRightQual && HaveTrailingQuals)
+ addLabel(" ");
+ if (HaveTrailingQuals) {
maybeAddQualifiers(/*AppendSpaceToQuals=*/AppendSpaceToTopLevelQuals);
+ AppendTrailingSpaceBeforeRightQual =
+ PrevAppendTrailingSpaceBeforeRightQual;
+ }
+
CurrentType = PreviousType;
CurrentNestedNameSpecifier = PreviousNNS;
}
@@ -605,10 +616,9 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
// This might be a C TagDecl.
if (auto *RD = dyn_cast<RecordDecl>(TT->getDecl())) {
// FIXME: Respect SuppressTagKeyword in other cases.
- if (!PP.SuppressTagKeyword && !RD->getTypedefNameForAnonDecl()) {
- addLabel(RD->getKindName().str());
- addLabel(" ");
- }
+ if (!PP.SuppressTagKeyword && !RD->getTypedefNameForAnonDecl())
+ addLabel(
+ [&](llvm::raw_ostream &OS) { OS << RD->getKindName() << " "; });
return addLabel(
[&](llvm::raw_ostream &OS) { return RD->printName(OS, PP); },
nameLocation(RD, SM));
@@ -709,6 +719,9 @@ class TypeHintBuilder : public TypeVisitor<TypeHintBuilder> {
}
void VisitPointerType(const PointerType *PT) {
+ // We don't want a trailing space after the last asterisk, if it is followed
+ // by a qualifier. E.g. 'int *const' rather than 'int * const'.
+ AppendTrailingSpaceBeforeRightQual = false;
QualType Next = PT->getPointeeType();
VisitQualType(Next);
if (Next->getPointeeType().isNull())
diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
index 27d2eda602ed0..d28bbff714e1b 100644
--- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -1913,7 +1913,7 @@ TEST(TypeHints, LinksForDeductionGuides) {
ExpectedHintLabelPiece{"<int>"});
}
-TEST(TypeHints, Qualifiers) {
+TEST(TypeHints, LinksWithQualifiers) {
StringRef Source(R"cpp(
struct Base { virtual ~Base() = default; };
@@ -1922,11 +1922,45 @@ TEST(TypeHints, Qualifiers) {
Base *make();
const auto $1[[ptr]] = dynamic_cast<const Derived *>(make());
+
+ const Derived *volatile const *volatile *const *volatile p = nullptr;
+
+ volatile auto $2[[paranoid]] = p;
)cpp");
assertTypeLinkHints(Source, "1", ExpectedHintLabelPiece{": const "},
ExpectedHintLabelPiece{"Derived", "Derived"},
ExpectedHintLabelPiece{" *const"});
+ assertTypeLinkHints(
+ Source, "2", ExpectedHintLabelPiece{": const "},
+ ExpectedHintLabelPiece{"Derived", "Derived"},
+ ExpectedHintLabelPiece{" *const volatile *volatile *const *volatile"});
+}
+
+TEST(TypeHints, LinksWithRangeBasedForLoop) {
+ StringRef Source(R"cpp(
+ template <class T>
+ struct vector {
+ struct iterator {
+ T operator *() {}
+ bool operator==(iterator) const;
+ bool operator!=(iterator) const;
+ iterator operator++();
+ };
+
+ iterator begin();
+ iterator end();
+ };
+
+ void foo() {
+ struct $S[[S]] {};
+ for (const auto $1[[i]] : vector<S>()) {
+ }
+ }
+ )cpp");
+ assertTypeLinkHints(Source, "1", ExpectedHintLabelPiece{": "},
+ ExpectedHintLabelPiece{"S", "S"},
+ ExpectedHintLabelPiece{" const"});
}
TEST(TypeHints, TypeDeductionForC) {
More information about the cfe-commits
mailing list