diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -2006,6 +2006,36 @@ UnorderedElementsAre(AllOf(Scope("ns::X::"), Named("x_")))); } +// Like other class members, constructor init lists have to parse what's below, +// after the completion point. +// But recovering from an incomplete constructor init list is particularly +// tricky because the bulk of the list is not surrounded by brackets. +TEST(CompletionTest, ConstructorInitListIncomplete) { + auto Results = completions( + R"cpp( + namespace ns { + struct X { + X() : x^ + int xyz_; + }; + } + )cpp"); + EXPECT_THAT(Results.Completions, ElementsAre(Named("xyz_"))); + + Results = completions( + R"cpp( + int foo(); + + namespace ns { + struct X { + X() : xyz_(fo^ + int xyz_; + }; + } + )cpp"); + EXPECT_THAT(Results.Completions, ElementsAre(Named("foo"))); +} + TEST(CompletionTest, CodeCompletionContext) { auto Results = completions( R"cpp( @@ -2650,9 +2680,7 @@ TEST(SignatureHelpTest, ConstructorInitializeFields) { { const auto Results = signatures(R"cpp( - struct A { - A(int); - }; + struct A { A(int); }; struct B { B() : a_elem(^) {} A a_elem; @@ -2662,6 +2690,31 @@ UnorderedElementsAre(Sig("A([[int]])"), Sig("A([[A &&]])"), Sig("A([[const A &]])"))); } + { + const auto Results = signatures(R"cpp( + struct A { A(int); }; + struct B { + B() : a_elem(^ + A a_elem; + }; + )cpp"); + // FIXME: currently the parser skips over the decl of a_elem as part of the + // (broken) init list, so we don't get signatures for the first member. + EXPECT_THAT(Results.signatures, IsEmpty()); + } + { + const auto Results = signatures(R"cpp( + struct A { A(int); }; + struct B { + B() : a_elem(^ + int dummy_elem; + A a_elem; + }; + )cpp"); + EXPECT_THAT(Results.signatures, + UnorderedElementsAre(Sig("A([[int]])"), Sig("A([[A &&]])"), + Sig("A([[const A &]])"))); + } { const auto Results = signatures(R"cpp( struct A { diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp --- a/clang/lib/Parse/ParseCXXInlineMethods.cpp +++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -140,8 +140,22 @@ // function body. if (ConsumeAndStoreFunctionPrologue(Toks)) { // We didn't find the left-brace we expected after the - // constructor initializer; we already printed an error, and it's likely - // impossible to recover, so don't try to parse this method later. + // constructor initializer. + + // If we're code-completing and the completion point was in the broken + // initializer, we want to parse it even though that will fail. + if (PP.isCodeCompletionEnabled() && + llvm::any_of(Toks, [](const Token &Tok) { + return Tok.is(tok::code_completion); + })) { + // If we gave up at the completion point, the initializer list was + // likely truncated, so don't eat more tokens. We'll hit some extra + // errors, but they should be ignored in code completion. + return FnD; + } + + // We already printed an error, and it's likely impossible to recover, + // so don't try to parse this method later. // Skip over the rest of the decl and back to somewhere that looks // reasonable. SkipMalformedDecl(); diff --git a/clang/test/CodeCompletion/ctor-initializer.cpp b/clang/test/CodeCompletion/ctor-initializer.cpp --- a/clang/test/CodeCompletion/ctor-initializer.cpp +++ b/clang/test/CodeCompletion/ctor-initializer.cpp @@ -103,3 +103,23 @@ // RUN: %clang_cc1 -fsyntax-only -std=c++98 -code-completion-at=%s:100:9 %s -o - | FileCheck -check-prefix=CHECK-CC11 %s // RUN: %clang_cc1 -fsyntax-only -std=c++14 -code-completion-at=%s:100:9 %s -o - | FileCheck -check-prefix=CHECK-CC11 %s // CHECK-CC11: Pattern : Y(<#Y#>) + +// Test with incomplete init lists. (Relevant as parsing is *not* cut off). +struct Incomplete1 { + Incomplete1() : mem + + int member1; + int member2; +}; +// RUN: not %clang_cc1 -fsyntax-only -std=c++14 -code-completion-at=%s:109:19 %s -o - | FileCheck -check-prefix=CHECK-CC12 %s +// CHECK-CC12: COMPLETION: Pattern : member1(<#int#>) +// CHECK-CC12: COMPLETION: Pattern : member2(<#int#>) + +struct Incomplete2 { + Incomplete2() : member2( + + int member1; + int member2; +}; +// RUN: not %clang_cc1 -fsyntax-only -std=c++14 -code-completion-at=%s:119:27 %s -o - | FileCheck -check-prefix=CHECK-CC13 %s +// CHECK-CC13: PREFERRED-TYPE: int