diff --git a/clang-tools-extra/clangd/index/CanonicalIncludes.cpp b/clang-tools-extra/clangd/index/CanonicalIncludes.cpp --- a/clang-tools-extra/clangd/index/CanonicalIncludes.cpp +++ b/clang-tools-extra/clangd/index/CanonicalIncludes.cpp @@ -90,6 +90,8 @@ static const auto *Symbols = new llvm::StringMap({ #define SYMBOL(Name, NameSpace, Header) {#NameSpace #Name, #Header}, #include "StdSymbolMap.inc" + // There are two std::move()s, this is by far the most common. + SYMBOL(move, std::, ) #undef SYMBOL }); StdSymbolMapping = Symbols; diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp --- a/clang-tools-extra/clangd/index/SymbolCollector.cpp +++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp @@ -560,6 +560,11 @@ QName = S->Scope; QName.append(S->Name); if (auto Header = getIncludeHeader(QName, Entry.second)) { + // Hack: there are two std::move() overloads from different headers. + // CanonicalIncludes returns the common one-arg one from . + if (S->Name == "move" && S->Scope == "std::" && + S->Signature.contains(',')) + *Header = ""; Symbol NewSym = *S; NewSym.IncludeHeaders.push_back({*Header, 1}); Symbols.insert(NewSym); diff --git a/clang-tools-extra/clangd/unittests/CanonicalIncludesTests.cpp b/clang-tools-extra/clangd/unittests/CanonicalIncludesTests.cpp --- a/clang-tools-extra/clangd/unittests/CanonicalIncludesTests.cpp +++ b/clang-tools-extra/clangd/unittests/CanonicalIncludesTests.cpp @@ -36,9 +36,9 @@ // Usual standard library symbols are mapped correctly. EXPECT_EQ("", CI.mapHeader("path/vector.h", "std::vector")); EXPECT_EQ("", CI.mapHeader("path/stdio.h", "std::printf")); - // std::move is ambiguous, currently mapped only based on path - EXPECT_EQ("", CI.mapHeader("libstdc++/bits/move.h", "std::move")); - EXPECT_EQ("path/utility.h", CI.mapHeader("path/utility.h", "std::move")); + // std::move is ambiguous, currently always mapped to + EXPECT_EQ("", + CI.mapHeader("libstdc++/bits/stl_algo.h", "std::move")); // Unknown std symbols aren't mapped. EXPECT_EQ("foo/bar.h", CI.mapHeader("foo/bar.h", "std::notathing")); // iosfwd declares some symbols it doesn't own. diff --git a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp --- a/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp +++ b/clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp @@ -1280,10 +1280,27 @@ Language.CPlusPlus = true; Includes.addSystemHeadersMapping(Language); CollectorOpts.Includes = &Includes; - runSymbolCollector("namespace std { class string {}; }", /*Main=*/""); - EXPECT_THAT(Symbols, - Contains(AllOf(QName("std::string"), DeclURI(TestHeaderURI), - IncludeHeader("")))); + runSymbolCollector( + R"cpp( + namespace std { + class string {}; + // Move overloads have special handling. + template T&& move(T&&); + template O move(I, I, O); + } + )cpp", + /*Main=*/""); + for (const auto &S : Symbols) + llvm::errs() << S.Scope << S.Name << " in " << S.IncludeHeaders.size() + << "\n"; + EXPECT_THAT( + Symbols, + UnorderedElementsAre( + QName("std"), + AllOf(QName("std::string"), DeclURI(TestHeaderURI), + IncludeHeader("")), + AllOf(Labeled("move(T &&)"), IncludeHeader("")), + AllOf(Labeled("move(I, I, O)"), IncludeHeader("")))); } TEST_F(SymbolCollectorTest, IWYUPragma) {