Author: ykiko Date: 2024-11-26T13:03:39+01:00 New Revision: ec4c47d9490c90a98f2dda3fc9ff7c51782678f8
URL: https://github.com/llvm/llvm-project/commit/ec4c47d9490c90a98f2dda3fc9ff7c51782678f8 DIFF: https://github.com/llvm/llvm-project/commit/ec4c47d9490c90a98f2dda3fc9ff7c51782678f8.diff LOG: Add code completion for C++20 keywords. (#107982) This commit adds code completion for C++20 keywords, fix https://github.com/llvm/llvm-project/issues/107868. 1. complete `concept` in template context - [x] `template<typename T> conce^` -> `concept` - [ ] `conce^` 2. complete `requires` - [x] constraints in template context: `template<typename T> requi^` -> `requires` - [x] requires expression: `int x = requ^` -> `requires (parameters) { requirements }` - [x] nested requirement: `requires { requ^ }` -> `requires expression ;` 3. complete coroutine keywords - [x] `co_await^` in expression: `co_aw^` -> `co_await expression;` - [x] `co_yield` in function body: `co_yi^` -> `co_yield expression;` - [x] `co_return` in function body: `co_re^` -> `co_return expression;` 4. specifiers: `char8_t`, `consteval`, `constinit` Added: clang/test/CodeCompletion/keywords-cxx20.cpp Modified: clang-tools-extra/docs/ReleaseNotes.rst clang/lib/Parse/ParseDeclCXX.cpp clang/lib/Sema/SemaCodeComplete.cpp Removed: ################################################################################ diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index fec2c20206bc4d..f8507156aa4198 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -73,6 +73,8 @@ Hover Code completion ^^^^^^^^^^^^^^^ +- Added completion for C++20 keywords. + Code actions ^^^^^^^^^^^^ diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 010ac9c1a3e3a9..f30603feb65c5d 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -460,6 +460,15 @@ Decl *Parser::ParseExportDeclaration() { assert(Tok.is(tok::kw_export)); SourceLocation ExportLoc = ConsumeToken(); + if (Tok.is(tok::code_completion)) { + cutOffParsing(); + Actions.CodeCompletion().CodeCompleteOrdinaryName( + getCurScope(), PP.isIncrementalProcessingEnabled() + ? SemaCodeCompletion::PCC_TopLevelOrExpression + : SemaCodeCompletion::PCC_Namespace); + return nullptr; + } + ParseScope ExportScope(this, Scope::DeclScope); Decl *ExportDecl = Actions.ActOnStartExportDecl( getCurScope(), ExportLoc, diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index 12da3a2cbca314..60ea1383b2a6ee 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -1836,6 +1836,9 @@ static void AddTypeSpecifierResults(const LangOptions &LangOpts, Builder.AddChunk(CodeCompletionString::CK_RightParen); Results.AddResult(Result(Builder.TakeString())); } + + if (LangOpts.Char8 || LangOpts.CPlusPlus20) + Results.AddResult(Result("char8_t", CCP_Type)); } else Results.AddResult(Result("__auto_type", CCP_Type)); @@ -1888,6 +1891,9 @@ AddStorageSpecifiers(SemaCodeCompletion::ParserCompletionContext CCC, Results.AddResult(Result("constexpr")); Results.AddResult(Result("thread_local")); } + + if (LangOpts.CPlusPlus20) + Results.AddResult(Result("constinit")); } static void @@ -1911,6 +1917,9 @@ AddFunctionSpecifiers(SemaCodeCompletion::ParserCompletionContext CCC, case SemaCodeCompletion::PCC_Template: if (LangOpts.CPlusPlus || LangOpts.C99) Results.AddResult(Result("inline")); + + if (LangOpts.CPlusPlus20) + Results.AddResult(Result("consteval")); break; case SemaCodeCompletion::PCC_ObjCInstanceVariableList: @@ -2186,6 +2195,69 @@ AddOrdinaryNameResults(SemaCodeCompletion::ParserCompletionContext CCC, } else { Results.AddResult(Result("template", CodeCompletionResult::RK_Keyword)); } + + if (SemaRef.getLangOpts().CPlusPlus20 && + SemaRef.getLangOpts().CPlusPlusModules) { + clang::Module *CurrentModule = SemaRef.getCurrentModule(); + if (SemaRef.CurContext->isTranslationUnit()) { + /// Global module fragment can only be declared in the beginning of + /// the file. CurrentModule should be null in this case. + if (!CurrentModule) { + // module; + Builder.AddTypedTextChunk("module"); + Builder.AddChunk(CodeCompletionString::CK_SemiColon); + Builder.AddChunk(CodeCompletionString::CK_VerticalSpace); + Results.AddResult(Result(Builder.TakeString())); + } + + /// Named module should be declared in the beginning of the file, + /// or after the global module fragment. + if (!CurrentModule || + CurrentModule->Kind == Module::ExplicitGlobalModuleFragment || + CurrentModule->Kind == Module::ImplicitGlobalModuleFragment) { + // export module; + // module name; + Builder.AddTypedTextChunk("module"); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddPlaceholderChunk("name"); + Builder.AddChunk(CodeCompletionString::CK_SemiColon); + Builder.AddChunk(CodeCompletionString::CK_VerticalSpace); + Results.AddResult(Result(Builder.TakeString())); + } + + /// Import can occur in non module file or after the named module + /// declaration. + if (!CurrentModule || + CurrentModule->Kind == Module::ModuleInterfaceUnit || + CurrentModule->Kind == Module::ModulePartitionInterface) { + // import name; + Builder.AddTypedTextChunk("import"); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddPlaceholderChunk("name"); + Builder.AddChunk(CodeCompletionString::CK_SemiColon); + Builder.AddChunk(CodeCompletionString::CK_VerticalSpace); + Results.AddResult(Result(Builder.TakeString())); + } + + if (CurrentModule && + (CurrentModule->Kind == Module::ModuleInterfaceUnit || + CurrentModule->Kind == Module::ModulePartitionInterface)) { + // module: private; + Builder.AddTypedTextChunk("module"); + Builder.AddChunk(CodeCompletionString::CK_Colon); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddTypedTextChunk("private"); + Builder.AddChunk(CodeCompletionString::CK_SemiColon); + Builder.AddChunk(CodeCompletionString::CK_VerticalSpace); + Results.AddResult(Result(Builder.TakeString())); + } + } + + // export + if (!CurrentModule || + CurrentModule->Kind != Module::ModuleKind::PrivateModuleFragment) + Results.AddResult(Result("export", CodeCompletionResult::RK_Keyword)); + } } if (SemaRef.getLangOpts().ObjC) @@ -2253,6 +2325,11 @@ AddOrdinaryNameResults(SemaCodeCompletion::ParserCompletionContext CCC, [[fallthrough]]; case SemaCodeCompletion::PCC_Template: + if (SemaRef.getLangOpts().CPlusPlus20 && + CCC == SemaCodeCompletion::PCC_Template) + Results.AddResult(Result("concept", CCP_Keyword)); + [[fallthrough]]; + case SemaCodeCompletion::PCC_MemberTemplate: if (SemaRef.getLangOpts().CPlusPlus && Results.includeCodePatterns()) { // template < parameters > @@ -2265,6 +2342,11 @@ AddOrdinaryNameResults(SemaCodeCompletion::ParserCompletionContext CCC, Results.AddResult(Result("template", CodeCompletionResult::RK_Keyword)); } + if (SemaRef.getLangOpts().CPlusPlus20 && + (CCC == SemaCodeCompletion::PCC_Template || + CCC == SemaCodeCompletion::PCC_MemberTemplate)) + Results.AddResult(Result("requires", CCP_Keyword)); + AddStorageSpecifiers(CCC, SemaRef.getLangOpts(), Results); AddFunctionSpecifiers(CCC, SemaRef.getLangOpts(), Results); break; @@ -2486,6 +2568,14 @@ AddOrdinaryNameResults(SemaCodeCompletion::ParserCompletionContext CCC, Builder.AddPlaceholderChunk("expression"); Builder.AddChunk(CodeCompletionString::CK_SemiColon); Results.AddResult(Result(Builder.TakeString())); + // "co_return expression ;" for coroutines(C++20). + if (SemaRef.getLangOpts().CPlusPlus20) { + Builder.AddTypedTextChunk("co_return"); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddPlaceholderChunk("expression"); + Builder.AddChunk(CodeCompletionString::CK_SemiColon); + Results.AddResult(Result(Builder.TakeString())); + } // When boolean, also add 'return true;' and 'return false;'. if (ReturnType->isBooleanType()) { Builder.AddTypedTextChunk("return true"); @@ -2706,6 +2796,44 @@ AddOrdinaryNameResults(SemaCodeCompletion::ParserCompletionContext CCC, Builder.AddChunk(CodeCompletionString::CK_RightParen); Results.AddResult(Result(Builder.TakeString())); } + + if (SemaRef.getLangOpts().CPlusPlus20) { + // co_await expression + Builder.AddTypedTextChunk("co_await"); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddPlaceholderChunk("expression"); + Results.AddResult(Result(Builder.TakeString())); + + // co_yield expression + Builder.AddTypedTextChunk("co_yield"); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddPlaceholderChunk("expression"); + Results.AddResult(Result(Builder.TakeString())); + + // requires (parameters) { requirements } + Builder.AddResultTypeChunk("bool"); + Builder.AddTypedTextChunk("requires"); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddChunk(CodeCompletionString::CK_LeftParen); + Builder.AddPlaceholderChunk("parameters"); + Builder.AddChunk(CodeCompletionString::CK_RightParen); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddChunk(CodeCompletionString::CK_LeftBrace); + Builder.AddChunk(CodeCompletionString::CK_VerticalSpace); + Builder.AddPlaceholderChunk("requirements"); + Builder.AddChunk(CodeCompletionString::CK_VerticalSpace); + Builder.AddChunk(CodeCompletionString::CK_RightBrace); + Results.AddResult(Result(Builder.TakeString())); + + if (SemaRef.CurContext->isRequiresExprBody()) { + // requires expression ; + Builder.AddTypedTextChunk("requires"); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddPlaceholderChunk("expression"); + Builder.AddChunk(CodeCompletionString::CK_SemiColon); + Results.AddResult(Result(Builder.TakeString())); + } + } } if (SemaRef.getLangOpts().ObjC) { diff --git a/clang/test/CodeCompletion/keywords-cxx20.cpp b/clang/test/CodeCompletion/keywords-cxx20.cpp new file mode 100644 index 00000000000000..612c3c0045e394 --- /dev/null +++ b/clang/test/CodeCompletion/keywords-cxx20.cpp @@ -0,0 +1,57 @@ +module; + +export module M; + +export const char8_t x = 1; + +template<typename T> requires true +const int y = requires { typename T::type; requires T::value; }; + +class co_test {}; + +int f(){ co_test test; return 1; } + +module: private; + +// RUN: %clang_cc1 -std=c++20 -code-completion-at=%s:1:3 %s | FileCheck --check-prefix=CHECK-MODULE1 %s +// CHECK-MODULE1: module; +// CHECK-MODULE1: module <#name#>; + +// RUN: %clang_cc1 -std=c++20 -code-completion-at=%s:3:11 %s | FileCheck --check-prefix=CHECK-MODULE2 %s +// CHECK-MODULE2: module <#name#>; + +// RUN: %clang_cc1 -std=c++20 -code-completion-at=%s:14:3 %s | FileCheck --check-prefix=CHECK-MODULE3 %s +// CHECK-MODULE3: module: private; + +// RUN: %clang_cc1 -std=c++20 -code-completion-at=%s:3:3 %s | FileCheck --check-prefix=CHECK-EXPORT %s +// CHECK-EXPORT: export + +// RUN: %clang_cc1 -std=c++20 -code-completion-at=%s:5:11 %s | FileCheck --check-prefix=CHECK-CONST %s +// CHECK-CONST: const +// CHECK-CONST: consteval +// CHECK-CONST: constexpr +// CHECK-CONST: constinit + +// RUN: %clang_cc1 -std=c++20 -code-completion-at=%s:5:19 %s | FileCheck --check-prefix=CHECK-CHAR %s +// CHECK-CHAR: char8_t + +// RUN: %clang_cc1 -std=c++20 -code-completion-at=%s:8:3 %s | FileCheck --check-prefix=CHECK-CONSTRAINT %s +// CHECK-CONSTRAINT: concept +// CHECK-CONSTRAINT: const +// CHECK-CONSTRAINT: consteval +// CHECK-CONSTRAINT: constexpr +// CHECK-CONSTRAINT: constinit + +// RUN: %clang_cc1 -std=c++20 -code-completion-at=%s:7:27 %s | FileCheck --check-prefix=CHECK-REQUIRES2 %s +// CHECK-REQUIRES2: requires + +// RUN: %clang_cc1 -std=c++20 -code-completion-at=%s:8:20 %s | FileCheck -check-prefix=CHECK-REQUIRE %s +// CHECK-REQUIRE: [#bool#]requires (<#parameters#>) { +// CHECK-REQUIRE: <#requirements#> +// CHECK-REQUIRE: } + +// RUN: %clang_cc1 -std=c++20 -code-completion-at=%s:12:13 %s | FileCheck --check-prefix=CHECK-COROUTINE %s +// CHECK-COROUTINE: co_await <#expression#> +// CHECK-COROUTINE: co_return <#expression#>; +// CHECK-COROUTINE: co_yield <#expression#> + _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits