Skip to content

Commit 83fae3f

Browse files
committed
[CodeComplete] Add code completion after function equals
Summary: Provide `default` and `delete` completion after the function equals. Reviewers: kadircet, sammccall Tags: #clang Differential Revision: https://reviews.llvm.org/D82548
1 parent ca134e4 commit 83fae3f

File tree

5 files changed

+145
-37
lines changed

5 files changed

+145
-37
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11996,6 +11996,7 @@ class Sema final {
1199611996

1199711997
void CodeCompleteLambdaIntroducer(Scope *S, LambdaIntroducer &Intro,
1199811998
bool AfterAmpersand);
11999+
void CodeCompleteAfterFunctionEquals(Declarator &D);
1199912000

1200012001
void CodeCompleteObjCAtDirective(Scope *S);
1200112002
void CodeCompleteObjCAtVisibility(Scope *S);

clang/lib/Parse/ParseDecl.cpp

Lines changed: 43 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1898,46 +1898,52 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
18981898
}
18991899

19001900
// Check to see if we have a function *definition* which must have a body.
1901-
if (D.isFunctionDeclarator() &&
1902-
// Look at the next token to make sure that this isn't a function
1903-
// declaration. We have to check this because __attribute__ might be the
1904-
// start of a function definition in GCC-extended K&R C.
1905-
!isDeclarationAfterDeclarator()) {
1906-
1907-
// Function definitions are only allowed at file scope and in C++ classes.
1908-
// The C++ inline method definition case is handled elsewhere, so we only
1909-
// need to handle the file scope definition case.
1910-
if (Context == DeclaratorContext::FileContext) {
1911-
if (isStartOfFunctionDefinition(D)) {
1912-
if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) {
1913-
Diag(Tok, diag::err_function_declared_typedef);
1914-
1915-
// Recover by treating the 'typedef' as spurious.
1916-
DS.ClearStorageClassSpecs();
1917-
}
1901+
if (D.isFunctionDeclarator()) {
1902+
if (Tok.is(tok::equal) && NextToken().is(tok::code_completion)) {
1903+
Actions.CodeCompleteAfterFunctionEquals(D);
1904+
cutOffParsing();
1905+
return nullptr;
1906+
}
1907+
// Look at the next token to make sure that this isn't a function
1908+
// declaration. We have to check this because __attribute__ might be the
1909+
// start of a function definition in GCC-extended K&R C.
1910+
if (!isDeclarationAfterDeclarator()) {
1911+
1912+
// Function definitions are only allowed at file scope and in C++ classes.
1913+
// The C++ inline method definition case is handled elsewhere, so we only
1914+
// need to handle the file scope definition case.
1915+
if (Context == DeclaratorContext::FileContext) {
1916+
if (isStartOfFunctionDefinition(D)) {
1917+
if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) {
1918+
Diag(Tok, diag::err_function_declared_typedef);
1919+
1920+
// Recover by treating the 'typedef' as spurious.
1921+
DS.ClearStorageClassSpecs();
1922+
}
19181923

1919-
Decl *TheDecl =
1920-
ParseFunctionDefinition(D, ParsedTemplateInfo(), &LateParsedAttrs);
1921-
return Actions.ConvertDeclToDeclGroup(TheDecl);
1922-
}
1924+
Decl *TheDecl = ParseFunctionDefinition(D, ParsedTemplateInfo(),
1925+
&LateParsedAttrs);
1926+
return Actions.ConvertDeclToDeclGroup(TheDecl);
1927+
}
19231928

1924-
if (isDeclarationSpecifier()) {
1925-
// If there is an invalid declaration specifier right after the
1926-
// function prototype, then we must be in a missing semicolon case
1927-
// where this isn't actually a body. Just fall through into the code
1928-
// that handles it as a prototype, and let the top-level code handle
1929-
// the erroneous declspec where it would otherwise expect a comma or
1930-
// semicolon.
1929+
if (isDeclarationSpecifier()) {
1930+
// If there is an invalid declaration specifier right after the
1931+
// function prototype, then we must be in a missing semicolon case
1932+
// where this isn't actually a body. Just fall through into the code
1933+
// that handles it as a prototype, and let the top-level code handle
1934+
// the erroneous declspec where it would otherwise expect a comma or
1935+
// semicolon.
1936+
} else {
1937+
Diag(Tok, diag::err_expected_fn_body);
1938+
SkipUntil(tok::semi);
1939+
return nullptr;
1940+
}
19311941
} else {
1932-
Diag(Tok, diag::err_expected_fn_body);
1933-
SkipUntil(tok::semi);
1934-
return nullptr;
1935-
}
1936-
} else {
1937-
if (Tok.is(tok::l_brace)) {
1938-
Diag(Tok, diag::err_function_definition_not_allowed);
1939-
SkipMalformedDecl();
1940-
return nullptr;
1942+
if (Tok.is(tok::l_brace)) {
1943+
Diag(Tok, diag::err_function_definition_not_allowed);
1944+
SkipMalformedDecl();
1945+
return nullptr;
1946+
}
19411947
}
19421948
}
19431949
}

clang/lib/Parse/ParseDeclCXX.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2713,6 +2713,11 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
27132713
DefinitionKind = FDK_Defaulted;
27142714
else if (KW.is(tok::kw_delete))
27152715
DefinitionKind = FDK_Deleted;
2716+
else if (KW.is(tok::code_completion)) {
2717+
Actions.CodeCompleteAfterFunctionEquals(DeclaratorInfo);
2718+
cutOffParsing();
2719+
return nullptr;
2720+
}
27162721
}
27172722
}
27182723
DeclaratorInfo.setFunctionDefinitionKind(DefinitionKind);

clang/lib/Sema/SemaCodeComplete.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@
2424
#include "clang/AST/RecursiveASTVisitor.h"
2525
#include "clang/AST/Type.h"
2626
#include "clang/Basic/CharInfo.h"
27+
#include "clang/Basic/OperatorKinds.h"
2728
#include "clang/Basic/Specifiers.h"
2829
#include "clang/Lex/HeaderSearch.h"
2930
#include "clang/Lex/MacroInfo.h"
3031
#include "clang/Lex/Preprocessor.h"
3132
#include "clang/Sema/CodeCompleteConsumer.h"
33+
#include "clang/Sema/DeclSpec.h"
3234
#include "clang/Sema/Designator.h"
3335
#include "clang/Sema/Lookup.h"
3436
#include "clang/Sema/Overload.h"
@@ -6266,6 +6268,53 @@ void Sema::CodeCompleteLambdaIntroducer(Scope *S, LambdaIntroducer &Intro,
62666268
Results.data(), Results.size());
62676269
}
62686270

6271+
void Sema::CodeCompleteAfterFunctionEquals(Declarator &D) {
6272+
if (!LangOpts.CPlusPlus11)
6273+
return;
6274+
ResultBuilder Results(*this, CodeCompleter->getAllocator(),
6275+
CodeCompleter->getCodeCompletionTUInfo(),
6276+
CodeCompletionContext::CCC_Other);
6277+
auto ShouldAddDefault = [&D, this]() {
6278+
if (!D.isFunctionDeclarator())
6279+
return false;
6280+
auto &Id = D.getName();
6281+
if (Id.getKind() == UnqualifiedIdKind::IK_DestructorName)
6282+
return true;
6283+
// FIXME(liuhui): Ideally, we should check the constructor parameter list to
6284+
// verify that it is the default, copy or move constructor?
6285+
if (Id.getKind() == UnqualifiedIdKind::IK_ConstructorName &&
6286+
D.getFunctionTypeInfo().NumParams <= 1)
6287+
return true;
6288+
if (Id.getKind() == UnqualifiedIdKind::IK_OperatorFunctionId) {
6289+
auto Op = Id.OperatorFunctionId.Operator;
6290+
// FIXME(liuhui): Ideally, we should check the function parameter list to
6291+
// verify that it is the copy or move assignment?
6292+
if (Op == OverloadedOperatorKind::OO_Equal)
6293+
return true;
6294+
if (LangOpts.CPlusPlus20 &&
6295+
(Op == OverloadedOperatorKind::OO_EqualEqual ||
6296+
Op == OverloadedOperatorKind::OO_ExclaimEqual ||
6297+
Op == OverloadedOperatorKind::OO_Less ||
6298+
Op == OverloadedOperatorKind::OO_LessEqual ||
6299+
Op == OverloadedOperatorKind::OO_Greater ||
6300+
Op == OverloadedOperatorKind::OO_GreaterEqual ||
6301+
Op == OverloadedOperatorKind::OO_Spaceship))
6302+
return true;
6303+
}
6304+
return false;
6305+
};
6306+
6307+
Results.EnterNewScope();
6308+
if (ShouldAddDefault())
6309+
Results.AddResult("default");
6310+
// FIXME(liuhui): Ideally, we should only provide `delete` completion for the
6311+
// first function declaration.
6312+
Results.AddResult("delete");
6313+
Results.ExitScope();
6314+
HandleCodeCompleteResults(this, CodeCompleter, Results.getCompletionContext(),
6315+
Results.data(), Results.size());
6316+
}
6317+
62696318
/// Macro that optionally prepends an "@" to the string literal passed in via
62706319
/// Keyword, depending on whether NeedAt is true or false.
62716320
#define OBJC_AT_KEYWORD_NAME(NeedAt, Keyword) ((NeedAt) ? "@" Keyword : Keyword)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
struct A {
2+
A() = default;
3+
// 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
4+
// CHECK-CC1: COMPLETION: default
5+
// CHECK-CC1-NEXT: COMPLETION: delete
6+
7+
A(const A &) = default;
8+
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:7:18 -std=gnu++11 %s -o - | FileCheck -check-prefix=CHECK-CC2 %s
9+
// CHECK-CC2: COMPLETION: default
10+
// CHECK-CC2-NEXT: COMPLETION: delete
11+
12+
A(const A &, int) = delete;
13+
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:12:23 -std=gnu++11 %s -o - | FileCheck -check-prefix=CHECK-CC3 %s
14+
// CHECK-CC3-NOT: COMPLETION: default
15+
// CHECK-CC3: COMPLETION: delete
16+
17+
A(A &&);
18+
19+
A &operator=(const A &) = default;
20+
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:19:29 -std=gnu++11 %s -o - | FileCheck -check-prefix=CHECK-CC4 %s
21+
// CHECK-CC4: COMPLETION: default
22+
// CHECK-CC4-NEXT: COMPLETION: delete
23+
24+
bool operator==(const A &) const = delete;
25+
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:24:38 -std=gnu++11 %s -o - | FileCheck -check-prefix=CHECK-CC5 %s
26+
// CHECK-CC5-NOT: COMPLETION: default
27+
// CHECK-CC5: COMPLETION: delete
28+
29+
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:24:38 -std=gnu++20 %s -o - | FileCheck -check-prefix=CHECK-CC6 %s
30+
// CHECK-CC6: COMPLETION: default
31+
// CHECK-CC6-NEXT: COMPLETION: delete
32+
33+
void test() = delete;
34+
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:33:17 -std=gnu++11 %s -o - | FileCheck -check-prefix=CHECK-CC7 %s
35+
// CHECK-CC7-NOT: COMPLETION: default
36+
// CHECK-CC7: COMPLETION: delete
37+
};
38+
39+
A::A(A &&) = default;
40+
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:39:14 -std=gnu++11 %s -o - | FileCheck -check-prefix=CHECK-CC8 %s
41+
// CHECK-CC8: COMPLETION: default
42+
// CHECK-CC8-NEXT: COMPLETION: delete
43+
44+
void test() = delete;
45+
// RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:44:15 -std=gnu++11 %s -o - | FileCheck -check-prefix=CHECK-CC9 %s
46+
// CHECK-CC9-NOT: COMPLETION: default
47+
// CHECK-CC9: COMPLETION: delete

0 commit comments

Comments
 (0)