diff options
Diffstat (limited to 'clang-tools-extra/unittests/clangd/DiagnosticsTests.cpp')
| -rw-r--r-- | clang-tools-extra/unittests/clangd/DiagnosticsTests.cpp | 652 |
1 files changed, 0 insertions, 652 deletions
diff --git a/clang-tools-extra/unittests/clangd/DiagnosticsTests.cpp b/clang-tools-extra/unittests/clangd/DiagnosticsTests.cpp deleted file mode 100644 index a742c9d6206..00000000000 --- a/clang-tools-extra/unittests/clangd/DiagnosticsTests.cpp +++ /dev/null @@ -1,652 +0,0 @@ -//===--- DiagnosticsTests.cpp ------------------------------------*- C++-*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "Annotations.h" -#include "ClangdUnit.h" -#include "Diagnostics.h" -#include "Protocol.h" -#include "SourceCode.h" -#include "TestIndex.h" -#include "TestFS.h" -#include "TestTU.h" -#include "index/MemIndex.h" -#include "clang/Basic/Diagnostic.h" -#include "clang/Basic/DiagnosticSema.h" -#include "llvm/Support/ScopedPrinter.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -namespace clang { -namespace clangd { -namespace { - -using testing::_; -using testing::ElementsAre; -using testing::Field; -using testing::IsEmpty; -using testing::Pair; -using testing::UnorderedElementsAre; - -testing::Matcher<const Diag &> WithFix(testing::Matcher<Fix> FixMatcher) { - return Field(&Diag::Fixes, ElementsAre(FixMatcher)); -} - -testing::Matcher<const Diag &> WithFix(testing::Matcher<Fix> FixMatcher1, - testing::Matcher<Fix> FixMatcher2) { - return Field(&Diag::Fixes, UnorderedElementsAre(FixMatcher1, FixMatcher2)); -} - -testing::Matcher<const Diag &> WithNote(testing::Matcher<Note> NoteMatcher) { - return Field(&Diag::Notes, ElementsAre(NoteMatcher)); -} - -MATCHER_P2(Diag, Range, Message, - "Diag at " + llvm::to_string(Range) + " = [" + Message + "]") { - return arg.Range == Range && arg.Message == Message; -} - -MATCHER_P3(Fix, Range, Replacement, Message, - "Fix " + llvm::to_string(Range) + " => " + - testing::PrintToString(Replacement) + " = [" + Message + "]") { - return arg.Message == Message && arg.Edits.size() == 1 && - arg.Edits[0].range == Range && arg.Edits[0].newText == Replacement; -} - -MATCHER_P(EqualToLSPDiag, LSPDiag, - "LSP diagnostic " + llvm::to_string(LSPDiag)) { - if (toJSON(arg) != toJSON(LSPDiag)) { - *result_listener << llvm::formatv("expected:\n{0:2}\ngot\n{1:2}", - toJSON(LSPDiag), toJSON(arg)).str(); - return false; - } - return true; -} - -MATCHER_P(DiagSource, S, "") { return arg.Source == S; } -MATCHER_P(DiagName, N, "") { return arg.Name == N; } - -MATCHER_P(EqualToFix, Fix, "LSP fix " + llvm::to_string(Fix)) { - if (arg.Message != Fix.Message) - return false; - if (arg.Edits.size() != Fix.Edits.size()) - return false; - for (std::size_t I = 0; I < arg.Edits.size(); ++I) { - if (arg.Edits[I].range != Fix.Edits[I].range || - arg.Edits[I].newText != Fix.Edits[I].newText) - return false; - } - return true; -} - - -// Helper function to make tests shorter. -Position pos(int line, int character) { - Position Res; - Res.line = line; - Res.character = character; - return Res; -} - -TEST(DiagnosticsTest, DiagnosticRanges) { - // Check we report correct ranges, including various edge-cases. - Annotations Test(R"cpp( - namespace test{}; - void $decl[[foo]](); - int main() { - $typo[[go\ -o]](); - foo()$semicolon[[]]//with comments - $unk[[unknown]](); - double $type[[bar]] = "foo"; - struct Foo { int x; }; Foo a; - a.$nomember[[y]]; - test::$nomembernamespace[[test]]; - } - )cpp"); - EXPECT_THAT( - TestTU::withCode(Test.code()).build().getDiagnostics(), - ElementsAre( - // This range spans lines. - AllOf(Diag(Test.range("typo"), - "use of undeclared identifier 'goo'; did you mean 'foo'?"), - DiagSource(Diag::Clang), - DiagName("undeclared_var_use_suggest"), - WithFix( - Fix(Test.range("typo"), "foo", "change 'go\\ o' to 'foo'")), - // This is a pretty normal range. - WithNote(Diag(Test.range("decl"), "'foo' declared here"))), - // This range is zero-width and insertion. Therefore make sure we are - // not expanding it into other tokens. Since we are not going to - // replace those. - AllOf(Diag(Test.range("semicolon"), "expected ';' after expression"), - WithFix(Fix(Test.range("semicolon"), ";", "insert ';'"))), - // This range isn't provided by clang, we expand to the token. - Diag(Test.range("unk"), "use of undeclared identifier 'unknown'"), - Diag(Test.range("type"), - "cannot initialize a variable of type 'double' with an lvalue " - "of type 'const char [4]'"), - Diag(Test.range("nomember"), "no member named 'y' in 'Foo'"), - Diag(Test.range("nomembernamespace"), - "no member named 'test' in namespace 'test'"))); -} - -TEST(DiagnosticsTest, FlagsMatter) { - Annotations Test("[[void]] main() {}"); - auto TU = TestTU::withCode(Test.code()); - EXPECT_THAT(TU.build().getDiagnostics(), - ElementsAre(AllOf(Diag(Test.range(), "'main' must return 'int'"), - WithFix(Fix(Test.range(), "int", - "change 'void' to 'int'"))))); - // Same code built as C gets different diagnostics. - TU.Filename = "Plain.c"; - EXPECT_THAT( - TU.build().getDiagnostics(), - ElementsAre(AllOf( - Diag(Test.range(), "return type of 'main' is not 'int'"), - WithFix(Fix(Test.range(), "int", "change return type to 'int'"))))); -} - -TEST(DiagnosticsTest, DiagnosticPreamble) { - Annotations Test(R"cpp( - #include $[["not-found.h"]] - )cpp"); - - auto TU = TestTU::withCode(Test.code()); - EXPECT_THAT(TU.build().getDiagnostics(), - ElementsAre(testing::AllOf( - Diag(Test.range(), "'not-found.h' file not found"), - DiagSource(Diag::Clang), DiagName("pp_file_not_found")))); -} - -TEST(DiagnosticsTest, ClangTidy) { - Annotations Test(R"cpp( - #include $deprecated[["assert.h"]] - - #define $macrodef[[SQUARE]](X) (X)*(X) - int main() { - return $doubled[[sizeof]](sizeof(int)); - int y = 4; - return SQUARE($macroarg[[++]]y); - } - )cpp"); - auto TU = TestTU::withCode(Test.code()); - TU.HeaderFilename = "assert.h"; // Suppress "not found" error. - TU.ClangTidyChecks = - "-*, bugprone-sizeof-expression, bugprone-macro-repeated-side-effects, " - "modernize-deprecated-headers"; - EXPECT_THAT( - TU.build().getDiagnostics(), - UnorderedElementsAre( - AllOf(Diag(Test.range("deprecated"), - "inclusion of deprecated C++ header 'assert.h'; consider " - "using 'cassert' instead"), - DiagSource(Diag::ClangTidy), - DiagName("modernize-deprecated-headers"), - WithFix(Fix(Test.range("deprecated"), "<cassert>", - "change '\"assert.h\"' to '<cassert>'"))), - Diag(Test.range("doubled"), - "suspicious usage of 'sizeof(sizeof(...))'"), - AllOf( - Diag(Test.range("macroarg"), - "side effects in the 1st macro argument 'X' are repeated in " - "macro expansion"), - DiagSource(Diag::ClangTidy), - DiagName("bugprone-macro-repeated-side-effects"), - WithNote( - Diag(Test.range("macrodef"), "macro 'SQUARE' defined here"))), - Diag(Test.range("macroarg"), - "multiple unsequenced modifications to 'y'"))); -} - -TEST(DiagnosticsTest, Preprocessor) { - // This looks like a preamble, but there's an #else in the middle! - // Check that: - // - the #else doesn't generate diagnostics (we had this bug) - // - we get diagnostics from the taken branch - // - we get no diagnostics from the not taken branch - Annotations Test(R"cpp( - #ifndef FOO - #define FOO - int a = [[b]]; - #else - int x = y; - #endif - )cpp"); - EXPECT_THAT( - TestTU::withCode(Test.code()).build().getDiagnostics(), - ElementsAre(Diag(Test.range(), "use of undeclared identifier 'b'"))); -} - -TEST(DiagnosticsTest, InsideMacros) { - Annotations Test(R"cpp( - #define TEN 10 - #define RET(x) return x + 10 - - int* foo() { - RET($foo[[0]]); - } - int* bar() { - return $bar[[TEN]]; - } - )cpp"); - EXPECT_THAT(TestTU::withCode(Test.code()).build().getDiagnostics(), - ElementsAre(Diag(Test.range("foo"), - "cannot initialize return object of type " - "'int *' with an rvalue of type 'int'"), - Diag(Test.range("bar"), - "cannot initialize return object of type " - "'int *' with an rvalue of type 'int'"))); -} - -TEST(DiagnosticsTest, NoFixItInMacro) { - Annotations Test(R"cpp( - #define Define(name) void name() {} - - [[Define]](main) - )cpp"); - auto TU = TestTU::withCode(Test.code()); - EXPECT_THAT(TU.build().getDiagnostics(), - ElementsAre(AllOf(Diag(Test.range(), "'main' must return 'int'"), - Not(WithFix(_))))); -} - -TEST(DiagnosticsTest, ToLSP) { - URIForFile MainFile = - URIForFile::canonicalize(testPath("foo/bar/main.cpp"), ""); - URIForFile HeaderFile = - URIForFile::canonicalize(testPath("foo/bar/header.h"), ""); - - clangd::Diag D; - D.ID = clang::diag::err_enum_class_reference; - D.Name = "enum_class_reference"; - D.Source = clangd::Diag::Clang; - D.Message = "something terrible happened"; - D.Range = {pos(1, 2), pos(3, 4)}; - D.InsideMainFile = true; - D.Severity = DiagnosticsEngine::Error; - D.File = "foo/bar/main.cpp"; - D.AbsFile = MainFile.file(); - - clangd::Note NoteInMain; - NoteInMain.Message = "declared somewhere in the main file"; - NoteInMain.Range = {pos(5, 6), pos(7, 8)}; - NoteInMain.Severity = DiagnosticsEngine::Remark; - NoteInMain.File = "../foo/bar/main.cpp"; - NoteInMain.InsideMainFile = true; - NoteInMain.AbsFile = MainFile.file(); - - D.Notes.push_back(NoteInMain); - - clangd::Note NoteInHeader; - NoteInHeader.Message = "declared somewhere in the header file"; - NoteInHeader.Range = {pos(9, 10), pos(11, 12)}; - NoteInHeader.Severity = DiagnosticsEngine::Note; - NoteInHeader.File = "../foo/baz/header.h"; - NoteInHeader.InsideMainFile = false; - NoteInHeader.AbsFile = HeaderFile.file(); - D.Notes.push_back(NoteInHeader); - - clangd::Fix F; - F.Message = "do something"; - D.Fixes.push_back(F); - - // Diagnostics should turn into these: - clangd::Diagnostic MainLSP; - MainLSP.range = D.Range; - MainLSP.severity = getSeverity(DiagnosticsEngine::Error); - MainLSP.code = "enum_class_reference"; - MainLSP.source = "clang"; - MainLSP.message = R"(Something terrible happened (fix available) - -main.cpp:6:7: remark: declared somewhere in the main file - -../foo/baz/header.h:10:11: -note: declared somewhere in the header file)"; - - clangd::Diagnostic NoteInMainLSP; - NoteInMainLSP.range = NoteInMain.Range; - NoteInMainLSP.severity = getSeverity(DiagnosticsEngine::Remark); - NoteInMainLSP.message = R"(Declared somewhere in the main file - -main.cpp:2:3: error: something terrible happened)"; - - ClangdDiagnosticOptions Opts; - // Transform diagnostics and check the results. - std::vector<std::pair<clangd::Diagnostic, std::vector<clangd::Fix>>> LSPDiags; - toLSPDiags(D, MainFile, Opts, - [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) { - LSPDiags.push_back( - {std::move(LSPDiag), - std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())}); - }); - - EXPECT_THAT( - LSPDiags, - ElementsAre(Pair(EqualToLSPDiag(MainLSP), ElementsAre(EqualToFix(F))), - Pair(EqualToLSPDiag(NoteInMainLSP), IsEmpty()))); - EXPECT_EQ(LSPDiags[0].first.code, "enum_class_reference"); - EXPECT_EQ(LSPDiags[0].first.source, "clang"); - EXPECT_EQ(LSPDiags[1].first.code, ""); - EXPECT_EQ(LSPDiags[1].first.source, ""); - - // Same thing, but don't flatten notes into the main list. - LSPDiags.clear(); - Opts.EmitRelatedLocations = true; - toLSPDiags(D, MainFile, Opts, - [&](clangd::Diagnostic LSPDiag, ArrayRef<clangd::Fix> Fixes) { - LSPDiags.push_back( - {std::move(LSPDiag), - std::vector<clangd::Fix>(Fixes.begin(), Fixes.end())}); - }); - MainLSP.message = "Something terrible happened (fix available)"; - DiagnosticRelatedInformation NoteInMainDRI; - NoteInMainDRI.message = "Declared somewhere in the main file"; - NoteInMainDRI.location.range = NoteInMain.Range; - NoteInMainDRI.location.uri = MainFile; - MainLSP.relatedInformation = {NoteInMainDRI}; - DiagnosticRelatedInformation NoteInHeaderDRI; - NoteInHeaderDRI.message = "Declared somewhere in the header file"; - NoteInHeaderDRI.location.range = NoteInHeader.Range; - NoteInHeaderDRI.location.uri = HeaderFile; - MainLSP.relatedInformation = {NoteInMainDRI, NoteInHeaderDRI}; - EXPECT_THAT( - LSPDiags, - ElementsAre(Pair(EqualToLSPDiag(MainLSP), ElementsAre(EqualToFix(F))))); -} - -struct SymbolWithHeader { - std::string QName; - std::string DeclaringFile; - std::string IncludeHeader; -}; - -std::unique_ptr<SymbolIndex> -buildIndexWithSymbol(llvm::ArrayRef<SymbolWithHeader> Syms) { - SymbolSlab::Builder Slab; - for (const auto &S : Syms) { - Symbol Sym = cls(S.QName); - Sym.Flags |= Symbol::IndexedForCodeCompletion; - Sym.CanonicalDeclaration.FileURI = S.DeclaringFile.c_str(); - Sym.Definition.FileURI = S.DeclaringFile.c_str(); - Sym.IncludeHeaders.emplace_back(S.IncludeHeader, 1); - Slab.insert(Sym); - } - return MemIndex::build(std::move(Slab).build(), RefSlab()); -} - -TEST(IncludeFixerTest, IncompleteType) { - Annotations Test(R"cpp( -$insert[[]]namespace ns { - class X; - $nested[[X::]]Nested n; -} -class Y : $base[[public ns::X]] {}; -int main() { - ns::X *x; - x$access[[->]]f(); -} - )cpp"); - auto TU = TestTU::withCode(Test.code()); - auto Index = buildIndexWithSymbol( - {SymbolWithHeader{"ns::X", "unittest:///x.h", "\"x.h\""}}); - TU.ExternalIndex = Index.get(); - - EXPECT_THAT( - TU.build().getDiagnostics(), - UnorderedElementsAre( - AllOf(Diag(Test.range("nested"), - "incomplete type 'ns::X' named in nested name specifier"), - WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n", - "Add include \"x.h\" for symbol ns::X"))), - AllOf(Diag(Test.range("base"), "base class has incomplete type"), - WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n", - "Add include \"x.h\" for symbol ns::X"))), - AllOf(Diag(Test.range("access"), - "member access into incomplete type 'ns::X'"), - WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n", - "Add include \"x.h\" for symbol ns::X"))))); -} - -TEST(IncludeFixerTest, NoSuggestIncludeWhenNoDefinitionInHeader) { - Annotations Test(R"cpp( -$insert[[]]namespace ns { - class X; -} -class Y : $base[[public ns::X]] {}; -int main() { - ns::X *x; - x$access[[->]]f(); -} - )cpp"); - auto TU = TestTU::withCode(Test.code()); - Symbol Sym = cls("ns::X"); - Sym.Flags |= Symbol::IndexedForCodeCompletion; - Sym.CanonicalDeclaration.FileURI = "unittest:///x.h"; - Sym.Definition.FileURI = "unittest:///x.cc"; - Sym.IncludeHeaders.emplace_back("\"x.h\"", 1); - - SymbolSlab::Builder Slab; - Slab.insert(Sym); - auto Index = MemIndex::build(std::move(Slab).build(), RefSlab()); - TU.ExternalIndex = Index.get(); - - EXPECT_THAT(TU.build().getDiagnostics(), - UnorderedElementsAre( - Diag(Test.range("base"), "base class has incomplete type"), - Diag(Test.range("access"), - "member access into incomplete type 'ns::X'"))); -} - -TEST(IncludeFixerTest, Typo) { - Annotations Test(R"cpp( -$insert[[]]namespace ns { -void foo() { - $unqualified1[[X]] x; - // No fix if the unresolved type is used as specifier. (ns::)X::Nested will be - // considered the unresolved type. - $unqualified2[[X]]::Nested n; -} -} -void bar() { - ns::$qualified1[[X]] x; // ns:: is valid. - ns::$qualified2[[X]](); // Error: no member in namespace - - ::$global[[Global]] glob; -} - )cpp"); - auto TU = TestTU::withCode(Test.code()); - auto Index = buildIndexWithSymbol( - {SymbolWithHeader{"ns::X", "unittest:///x.h", "\"x.h\""}, - SymbolWithHeader{"Global", "unittest:///global.h", "\"global.h\""}}); - TU.ExternalIndex = Index.get(); - - EXPECT_THAT( - TU.build().getDiagnostics(), - UnorderedElementsAre( - AllOf(Diag(Test.range("unqualified1"), "unknown type name 'X'"), - WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n", - "Add include \"x.h\" for symbol ns::X"))), - Diag(Test.range("unqualified2"), "use of undeclared identifier 'X'"), - AllOf(Diag(Test.range("qualified1"), - "no type named 'X' in namespace 'ns'"), - WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n", - "Add include \"x.h\" for symbol ns::X"))), - AllOf(Diag(Test.range("qualified2"), - "no member named 'X' in namespace 'ns'"), - WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n", - "Add include \"x.h\" for symbol ns::X"))), - AllOf(Diag(Test.range("global"), - "no type named 'Global' in the global namespace"), - WithFix(Fix(Test.range("insert"), "#include \"global.h\"\n", - "Add include \"global.h\" for symbol Global"))))); -} - -TEST(IncludeFixerTest, MultipleMatchedSymbols) { - Annotations Test(R"cpp( -$insert[[]]namespace na { -namespace nb { -void foo() { - $unqualified[[X]] x; -} -} -} - )cpp"); - auto TU = TestTU::withCode(Test.code()); - auto Index = buildIndexWithSymbol( - {SymbolWithHeader{"na::X", "unittest:///a.h", "\"a.h\""}, - SymbolWithHeader{"na::nb::X", "unittest:///b.h", "\"b.h\""}}); - TU.ExternalIndex = Index.get(); - - EXPECT_THAT(TU.build().getDiagnostics(), - UnorderedElementsAre(AllOf( - Diag(Test.range("unqualified"), "unknown type name 'X'"), - WithFix(Fix(Test.range("insert"), "#include \"a.h\"\n", - "Add include \"a.h\" for symbol na::X"), - Fix(Test.range("insert"), "#include \"b.h\"\n", - "Add include \"b.h\" for symbol na::nb::X"))))); -} - -TEST(IncludeFixerTest, NoCrashMemebrAccess) { - Annotations Test(R"cpp( - struct X { int xyz; }; - void g() { X x; x.$[[xy]] } - )cpp"); - auto TU = TestTU::withCode(Test.code()); - auto Index = buildIndexWithSymbol( - SymbolWithHeader{"na::X", "unittest:///a.h", "\"a.h\""}); - TU.ExternalIndex = Index.get(); - - EXPECT_THAT( - TU.build().getDiagnostics(), - UnorderedElementsAre(Diag(Test.range(), "no member named 'xy' in 'X'"))); -} - -TEST(IncludeFixerTest, UseCachedIndexResults) { - // As index results for the identical request are cached, more than 5 fixes - // are generated. - Annotations Test(R"cpp( -$insert[[]]void foo() { - $x1[[X]] x; - $x2[[X]] x; - $x3[[X]] x; - $x4[[X]] x; - $x5[[X]] x; - $x6[[X]] x; - $x7[[X]] x; -} - -class X; -void bar(X *x) { - x$a1[[->]]f(); - x$a2[[->]]f(); - x$a3[[->]]f(); - x$a4[[->]]f(); - x$a5[[->]]f(); - x$a6[[->]]f(); - x$a7[[->]]f(); -} - )cpp"); - auto TU = TestTU::withCode(Test.code()); - auto Index = - buildIndexWithSymbol(SymbolWithHeader{"X", "unittest:///a.h", "\"a.h\""}); - TU.ExternalIndex = Index.get(); - - auto Parsed = TU.build(); - for (const auto &D : Parsed.getDiagnostics()) { - EXPECT_EQ(D.Fixes.size(), 1u); - EXPECT_EQ(D.Fixes[0].Message, - std::string("Add include \"a.h\" for symbol X")); - } -} - -TEST(IncludeFixerTest, UnresolvedNameAsSpecifier) { - Annotations Test(R"cpp( -$insert[[]]namespace ns { -} -void g() { ns::$[[scope]]::X_Y(); } - )cpp"); - auto TU = TestTU::withCode(Test.code()); - auto Index = buildIndexWithSymbol( - SymbolWithHeader{"ns::scope::X_Y", "unittest:///x.h", "\"x.h\""}); - TU.ExternalIndex = Index.get(); - - EXPECT_THAT( - TU.build().getDiagnostics(), - UnorderedElementsAre(AllOf( - Diag(Test.range(), "no member named 'scope' in namespace 'ns'"), - WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n", - "Add include \"x.h\" for symbol ns::scope::X_Y"))))); -} - -TEST(IncludeFixerTest, UnresolvedSpecifierWithSemaCorrection) { - Annotations Test(R"cpp( -$insert[[]]namespace clang { -void f() { - // "clangd::" will be corrected to "clang::" by Sema. - $q1[[clangd]]::$x[[X]] x; - $q2[[clangd]]::$ns[[ns]]::Y y; -} -} - )cpp"); - auto TU = TestTU::withCode(Test.code()); - auto Index = buildIndexWithSymbol( - {SymbolWithHeader{"clang::clangd::X", "unittest:///x.h", "\"x.h\""}, - SymbolWithHeader{"clang::clangd::ns::Y", "unittest:///y.h", "\"y.h\""}}); - TU.ExternalIndex = Index.get(); - - EXPECT_THAT( - TU.build().getDiagnostics(), - UnorderedElementsAre( - AllOf( - Diag(Test.range("q1"), "use of undeclared identifier 'clangd'; " - "did you mean 'clang'?"), - WithFix(_, // change clangd to clang - Fix(Test.range("insert"), "#include \"x.h\"\n", - "Add include \"x.h\" for symbol clang::clangd::X"))), - AllOf( - Diag(Test.range("x"), "no type named 'X' in namespace 'clang'"), - WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n", - "Add include \"x.h\" for symbol clang::clangd::X"))), - AllOf( - Diag(Test.range("q2"), "use of undeclared identifier 'clangd'; " - "did you mean 'clang'?"), - WithFix( - _, // change clangd to clangd - Fix(Test.range("insert"), "#include \"y.h\"\n", - "Add include \"y.h\" for symbol clang::clangd::ns::Y"))), - AllOf(Diag(Test.range("ns"), - "no member named 'ns' in namespace 'clang'"), - WithFix(Fix( - Test.range("insert"), "#include \"y.h\"\n", - "Add include \"y.h\" for symbol clang::clangd::ns::Y"))))); -} - -TEST(IncludeFixerTest, SpecifiedScopeIsNamespaceAlias) { - Annotations Test(R"cpp( -$insert[[]]namespace a {} -namespace b = a; -namespace c { - b::$[[X]] x; -} - )cpp"); - auto TU = TestTU::withCode(Test.code()); - auto Index = buildIndexWithSymbol( - SymbolWithHeader{"a::X", "unittest:///x.h", "\"x.h\""}); - TU.ExternalIndex = Index.get(); - - EXPECT_THAT(TU.build().getDiagnostics(), - UnorderedElementsAre(AllOf( - Diag(Test.range(), "no type named 'X' in namespace 'a'"), - WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n", - "Add include \"x.h\" for symbol a::X"))))); -} - -} // namespace -} // namespace clangd -} // namespace clang - |

