diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp --- a/clang-tools-extra/clangd/InlayHints.cpp +++ b/clang-tools-extra/clangd/InlayHints.cpp @@ -366,6 +366,12 @@ std::vector Results; InlayHintVisitor Visitor(Results, AST); Visitor.TraverseAST(AST.getASTContext()); + + // De-duplicate hints. Duplicates can sometimes occur due to e.g. explicit + // template instantiations. + llvm::sort(Results); + Results.erase(std::unique(Results.begin(), Results.end()), Results.end()); + return Results; } diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -1548,6 +1548,8 @@ std::string label; }; llvm::json::Value toJSON(const InlayHint &); +bool operator==(const InlayHint &, const InlayHint &); +bool operator<(const InlayHint &, const InlayHint &); struct ReferenceContext { /// Include the declaration of the current symbol. diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -1326,6 +1326,14 @@ return llvm::json::Object{ {"range", H.range}, {"kind", H.kind}, {"label", H.label}}; } +bool operator==(const InlayHint &A, const InlayHint &B) { + return std::tie(A.kind, A.range, A.label) == + std::tie(B.kind, B.range, B.label); +} +bool operator<(const InlayHint &A, const InlayHint &B) { + return std::tie(A.kind, A.range, A.label) < + std::tie(B.kind, B.range, B.label); +} static const char *toString(OffsetEncoding OE) { switch (OE) { diff --git a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp --- a/clang-tools-extra/clangd/unittests/InlayHintTests.cpp +++ b/clang-tools-extra/clangd/unittests/InlayHintTests.cpp @@ -612,6 +612,18 @@ ExpectedHint{": A", "var"}); } +TEST(TypeHints, Deduplication) { + assertTypeHints(R"cpp( + template + void foo() { + auto $var[[var]] = 42; + } + template void foo(); + template void foo(); + )cpp", + ExpectedHint{": int", "var"}); +} + // FIXME: Low-hanging fruit where we could omit a type hint: // - auto x = TypeName(...); // - auto x = (TypeName) (...); @@ -625,4 +637,4 @@ } // namespace } // namespace clangd -} // namespace clang \ No newline at end of file +} // namespace clang