diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -11930,6 +11930,7 @@ void CodeCompleteLambdaIntroducer(Scope *S, LambdaIntroducer &Intro, bool AfterAmpersand); + void CodeCompleteFunctionDeleteAndDefaultSpecifier(); void CodeCompleteObjCAtDirective(Scope *S); void CodeCompleteObjCAtVisibility(Scope *S); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2046,46 +2046,52 @@ } // Check to see if we have a function *definition* which must have a body. - if (D.isFunctionDeclarator() && - // Look at the next token to make sure that this isn't a function - // declaration. We have to check this because __attribute__ might be the - // start of a function definition in GCC-extended K&R C. - !isDeclarationAfterDeclarator()) { - - // Function definitions are only allowed at file scope and in C++ classes. - // The C++ inline method definition case is handled elsewhere, so we only - // need to handle the file scope definition case. - if (Context == DeclaratorContext::FileContext) { - if (isStartOfFunctionDefinition(D)) { - if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) { - Diag(Tok, diag::err_function_declared_typedef); - - // Recover by treating the 'typedef' as spurious. - DS.ClearStorageClassSpecs(); - } + if (D.isFunctionDeclarator()) { + if (Tok.is(tok::equal) && NextToken().is(tok::code_completion)) { + Actions.CodeCompleteFunctionDeleteAndDefaultSpecifier(); + cutOffParsing(); + return nullptr; + } + // Look at the next token to make sure that this isn't a function + // declaration. We have to check this because __attribute__ might be the + // start of a function definition in GCC-extended K&R C. + if (!isDeclarationAfterDeclarator()) { + + // Function definitions are only allowed at file scope and in C++ classes. + // The C++ inline method definition case is handled elsewhere, so we only + // need to handle the file scope definition case. + if (Context == DeclaratorContext::FileContext) { + if (isStartOfFunctionDefinition(D)) { + if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) { + Diag(Tok, diag::err_function_declared_typedef); + + // Recover by treating the 'typedef' as spurious. + DS.ClearStorageClassSpecs(); + } - Decl *TheDecl = - ParseFunctionDefinition(D, ParsedTemplateInfo(), &LateParsedAttrs); - return Actions.ConvertDeclToDeclGroup(TheDecl); - } + Decl *TheDecl = ParseFunctionDefinition(D, ParsedTemplateInfo(), + &LateParsedAttrs); + return Actions.ConvertDeclToDeclGroup(TheDecl); + } - if (isDeclarationSpecifier()) { - // If there is an invalid declaration specifier right after the - // function prototype, then we must be in a missing semicolon case - // where this isn't actually a body. Just fall through into the code - // that handles it as a prototype, and let the top-level code handle - // the erroneous declspec where it would otherwise expect a comma or - // semicolon. + if (isDeclarationSpecifier()) { + // If there is an invalid declaration specifier right after the + // function prototype, then we must be in a missing semicolon case + // where this isn't actually a body. Just fall through into the code + // that handles it as a prototype, and let the top-level code handle + // the erroneous declspec where it would otherwise expect a comma or + // semicolon. + } else { + Diag(Tok, diag::err_expected_fn_body); + SkipUntil(tok::semi); + return nullptr; + } } else { - Diag(Tok, diag::err_expected_fn_body); - SkipUntil(tok::semi); - return nullptr; - } - } else { - if (Tok.is(tok::l_brace)) { - Diag(Tok, diag::err_function_definition_not_allowed); - SkipMalformedDecl(); - return nullptr; + if (Tok.is(tok::l_brace)) { + Diag(Tok, diag::err_function_definition_not_allowed); + SkipMalformedDecl(); + return nullptr; + } } } } diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -2714,6 +2714,11 @@ DefinitionKind = FDK_Defaulted; else if (KW.is(tok::kw_delete)) DefinitionKind = FDK_Deleted; + else if (KW.is(tok::code_completion)) { + Actions.CodeCompleteFunctionDeleteAndDefaultSpecifier(); + cutOffParsing(); + return nullptr; + } } } DeclaratorInfo.setFunctionDefinitionKind(DefinitionKind); diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -6261,6 +6261,25 @@ Results.data(), Results.size()); } +void Sema::CodeCompleteFunctionDeleteAndDefaultSpecifier() { + if (!LangOpts.CPlusPlus11) { + return; + } + ResultBuilder Results(*this, CodeCompleter->getAllocator(), + CodeCompleter->getCodeCompletionTUInfo(), + CodeCompletionContext::CCC_Other); + Results.EnterNewScope(); + // FIXME(liuhui): Ideally, we should only provide `default` completion for + // special functions. + Results.AddResult("default"); + // FIXME(liuhui): Ideally, we should only provide `delete` completion for the + // first function declaration. + Results.AddResult("delete"); + Results.ExitScope(); + HandleCodeCompleteResults(this, CodeCompleter, Results.getCompletionContext(), + Results.data(), Results.size()); +} + /// Macro that optionally prepends an "@" to the string literal passed in via /// Keyword, depending on whether NeedAt is true or false. #define OBJC_AT_KEYWORD_NAME(NeedAt, Keyword) ((NeedAt) ? "@" Keyword : Keyword) diff --git a/clang/test/CodeCompletion/delete-default-specifier.cpp b/clang/test/CodeCompletion/delete-default-specifier.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeCompletion/delete-default-specifier.cpp @@ -0,0 +1,24 @@ +struct A { + A() = default; + // RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:2:9 -std=gnu++11 %s -o - | FileCheck -check-prefix=CHECK-CC1 %s + // CHECK-CC1: COMPLETION: default + // CHECK-CC1-NEXT: COMPLETION: delete + A(const A &); +}; + +A::A(const A &) = default; +// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:9:19 -std=gnu++11 %s -o - | FileCheck -check-prefix=CHECK-CC2 %s +// CHECK-CC2: COMPLETION: default +// CHECK-CC2-NEXT: COMPLETION: delete + +struct B { + void test() = delete; + // RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:15:17 -std=gnu++11 %s -o - | FileCheck -check-prefix=CHECK-CC3 %s + // CHECK-CC3: COMPLETION: default + // CHECK-CC3-NEXT: COMPLETION: delete +}; + +void test() = delete; +// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:21:15 -std=gnu++11 %s -o - | FileCheck -check-prefix=CHECK-CC4 %s +// CHECK-CC4: COMPLETION: default +// CHECK-CC4-NEXT: COMPLETION: delete