diff --git a/clang-tools-extra/clangd/test/repeated_includes.test b/clang-tools-extra/clangd/test/repeated_includes.test new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/test/repeated_includes.test @@ -0,0 +1,33 @@ +# RUN: rm -rf %/t +# RUN: mkdir -p %t && touch %t/t.h && touch %t/t2.h && touch %t/t3.h +# RUN: echo '#pragma once' > %t/t.h +# RUN: echo '#include "t2.h"' >> %t/t.h +# RUN: echo 'void bar();' >> %t/t.h +# RUN: echo '#pragma once' > %t/t2.h +# RUN: echo 'void bar2();' >> %t/t2.h + + +# Run clangd +# RUN: clangd -lit-test -log error --path-mappings 'C:\client=%t' < %s | FileCheck -strict-whitespace %s +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///C:/client/main.cpp","languageId":"cpp","version":1,"text":"#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\n#include \"t.h\"\nbar();\n"}}} +--- +{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///C:/client/main.cpp"},"position":{"line":40,"character":3}}} +# CHECK: "id": 1 +# CHECK-NEXT: "jsonrpc": "2.0", +# CHECK-NEXT: "result": { +# CHECK-NEXT: "isIncomplete": false, +# CHECK-NEXT: "items": [ +# CHECK-NEXT: { +# CHECK-NEXT: "detail": "void", +# CHECK-NEXT: "documentation": { +# CHECK-NEXT: "kind": "plaintext", +# CHECK-NEXT: "value": "From \"t.h\"" +# CHECK-NEXT: }, +# CHECK-NEXT: "filterText": "bar", +# CHECK-NEXT: "insertText": "bar", +--- +{"jsonrpc":"2.0","id":4,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} diff --git a/clang/include/clang/Tooling/Inclusions/HeaderIncludes.h b/clang/include/clang/Tooling/Inclusions/HeaderIncludes.h --- a/clang/include/clang/Tooling/Inclusions/HeaderIncludes.h +++ b/clang/include/clang/Tooling/Inclusions/HeaderIncludes.h @@ -105,8 +105,7 @@ /// in the order they appear in the source file. /// See comment for "FormatStyle::IncludeCategories" for details about include /// priorities. - std::unordered_map> - IncludesByPriority; + std::unordered_map> IncludesByPriority; int FirstIncludeOffset; // All new headers should be inserted after this offset (e.g. after header diff --git a/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp b/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp --- a/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp +++ b/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp @@ -333,7 +333,7 @@ int Priority = Categories.getIncludePriority( CurInclude.Name, /*CheckMainHeader=*/FirstIncludeOffset < 0); CategoryEndOffsets[Priority] = NextLineOffset; - IncludesByPriority[Priority].push_back(&CurInclude); + IncludesByPriority[Priority].push_back(CurInclude); if (FirstIncludeOffset < 0) FirstIncludeOffset = CurInclude.R.getOffset(); } @@ -361,9 +361,9 @@ unsigned InsertOffset = CatOffset->second; // Fall back offset auto Iter = IncludesByPriority.find(Priority); if (Iter != IncludesByPriority.end()) { - for (const auto *Inc : Iter->second) { - if (QuotedName < Inc->Name) { - InsertOffset = Inc->R.getOffset(); + for (const auto &Inc : Iter->second) { + if (QuotedName < Inc.Name) { + InsertOffset = Inc.R.getOffset(); break; } }