[clang] 83fae3f - [CodeComplete] Add code completion after function equals
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 30 21:51:58 PDT 2020
Author: lh123
Date: 2020-07-01T12:51:25+08:00
New Revision: 83fae3f762699655a4329fe3cf6fd3e2a2617559
URL: https://github.com/llvm/llvm-project/commit/83fae3f762699655a4329fe3cf6fd3e2a2617559
DIFF: https://github.com/llvm/llvm-project/commit/83fae3f762699655a4329fe3cf6fd3e2a2617559.diff
LOG: [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
Added:
clang/test/CodeCompletion/after-function-equals.cpp
Modified:
clang/include/clang/Sema/Sema.h
clang/lib/Parse/ParseDecl.cpp
clang/lib/Parse/ParseDeclCXX.cpp
clang/lib/Sema/SemaCodeComplete.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 77eda3b25c83..9b82d2c984be 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11996,6 +11996,7 @@ class Sema final {
void CodeCompleteLambdaIntroducer(Scope *S, LambdaIntroducer &Intro,
bool AfterAmpersand);
+ void CodeCompleteAfterFunctionEquals(Declarator &D);
void CodeCompleteObjCAtDirective(Scope *S);
void CodeCompleteObjCAtVisibility(Scope *S);
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index f336af6f1abc..c87d240a8206 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -1898,46 +1898,52 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
}
// 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.CodeCompleteAfterFunctionEquals(D);
+ 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
index ab94e4c45709..ddcbb5615fee 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -2713,6 +2713,11 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
DefinitionKind = FDK_Defaulted;
else if (KW.is(tok::kw_delete))
DefinitionKind = FDK_Deleted;
+ else if (KW.is(tok::code_completion)) {
+ Actions.CodeCompleteAfterFunctionEquals(DeclaratorInfo);
+ cutOffParsing();
+ return nullptr;
+ }
}
}
DeclaratorInfo.setFunctionDefinitionKind(DefinitionKind);
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index 26d127e1262f..0a8a27068ebf 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -24,11 +24,13 @@
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Type.h"
#include "clang/Basic/CharInfo.h"
+#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/MacroInfo.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/CodeCompleteConsumer.h"
+#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/Designator.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Overload.h"
@@ -6266,6 +6268,53 @@ void Sema::CodeCompleteLambdaIntroducer(Scope *S, LambdaIntroducer &Intro,
Results.data(), Results.size());
}
+void Sema::CodeCompleteAfterFunctionEquals(Declarator &D) {
+ if (!LangOpts.CPlusPlus11)
+ return;
+ ResultBuilder Results(*this, CodeCompleter->getAllocator(),
+ CodeCompleter->getCodeCompletionTUInfo(),
+ CodeCompletionContext::CCC_Other);
+ auto ShouldAddDefault = [&D, this]() {
+ if (!D.isFunctionDeclarator())
+ return false;
+ auto &Id = D.getName();
+ if (Id.getKind() == UnqualifiedIdKind::IK_DestructorName)
+ return true;
+ // FIXME(liuhui): Ideally, we should check the constructor parameter list to
+ // verify that it is the default, copy or move constructor?
+ if (Id.getKind() == UnqualifiedIdKind::IK_ConstructorName &&
+ D.getFunctionTypeInfo().NumParams <= 1)
+ return true;
+ if (Id.getKind() == UnqualifiedIdKind::IK_OperatorFunctionId) {
+ auto Op = Id.OperatorFunctionId.Operator;
+ // FIXME(liuhui): Ideally, we should check the function parameter list to
+ // verify that it is the copy or move assignment?
+ if (Op == OverloadedOperatorKind::OO_Equal)
+ return true;
+ if (LangOpts.CPlusPlus20 &&
+ (Op == OverloadedOperatorKind::OO_EqualEqual ||
+ Op == OverloadedOperatorKind::OO_ExclaimEqual ||
+ Op == OverloadedOperatorKind::OO_Less ||
+ Op == OverloadedOperatorKind::OO_LessEqual ||
+ Op == OverloadedOperatorKind::OO_Greater ||
+ Op == OverloadedOperatorKind::OO_GreaterEqual ||
+ Op == OverloadedOperatorKind::OO_Spaceship))
+ return true;
+ }
+ return false;
+ };
+
+ Results.EnterNewScope();
+ if (ShouldAddDefault())
+ 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/after-function-equals.cpp b/clang/test/CodeCompletion/after-function-equals.cpp
new file mode 100644
index 000000000000..871c2a3f6d91
--- /dev/null
+++ b/clang/test/CodeCompletion/after-function-equals.cpp
@@ -0,0 +1,47 @@
+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 &) = default;
+ // 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
+ // CHECK-CC2: COMPLETION: default
+ // CHECK-CC2-NEXT: COMPLETION: delete
+
+ A(const A &, int) = delete;
+ // 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
+ // CHECK-CC3-NOT: COMPLETION: default
+ // CHECK-CC3: COMPLETION: delete
+
+ A(A &&);
+
+ A &operator=(const A &) = default;
+ // 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
+ // CHECK-CC4: COMPLETION: default
+ // CHECK-CC4-NEXT: COMPLETION: delete
+
+ bool operator==(const A &) const = delete;
+ // 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
+ // CHECK-CC5-NOT: COMPLETION: default
+ // CHECK-CC5: COMPLETION: delete
+
+ // 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
+ // CHECK-CC6: COMPLETION: default
+ // CHECK-CC6-NEXT: COMPLETION: delete
+
+ void test() = delete;
+ // 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
+ // CHECK-CC7-NOT: COMPLETION: default
+ // CHECK-CC7: COMPLETION: delete
+};
+
+A::A(A &&) = default;
+// 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
+// CHECK-CC8: COMPLETION: default
+// CHECK-CC8-NEXT: COMPLETION: delete
+
+void test() = delete;
+// 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
+// CHECK-CC9-NOT: COMPLETION: default
+// CHECK-CC9: COMPLETION: delete
\ No newline at end of file
More information about the cfe-commits
mailing list