Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ Hover
Code completion
^^^^^^^^^^^^^^^

- Added completion for C++20 keywords.

Code actions
^^^^^^^^^^^^

Expand Down
12 changes: 12 additions & 0 deletions clang/lib/Parse/ParseDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,18 @@ Decl *Parser::ParseExportDeclaration() {
assert(Tok.is(tok::kw_export));
SourceLocation ExportLoc = ConsumeToken();

if (Tok.is(tok::code_completion)) {
cutOffParsing();
SemaCodeCompletion::ParserCompletionContext PCC;
if (PP.isIncrementalProcessingEnabled()) {
PCC = SemaCodeCompletion::PCC_TopLevelOrExpression;
} else {
PCC = SemaCodeCompletion::PCC_Namespace;
};
Actions.CodeCompletion().CodeCompleteOrdinaryName(getCurScope(), PCC);
return nullptr;
}

ParseScope ExportScope(this, Scope::DeclScope);
Decl *ExportDecl = Actions.ActOnStartExportDecl(
getCurScope(), ExportLoc,
Expand Down
128 changes: 128 additions & 0 deletions clang/lib/Sema/SemaCodeComplete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));

Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down Expand Up @@ -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()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we restrict that using getCurrentModule()->Kind @ChuanqiXu9 ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we can't do it here. Since we don't the kind of the current module before we see module declarations.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, exactly.
If we haven't see module;, we should propose module; and export module
If we have seen module; - we should not propose it again
If we have seem export module, we should not propose import

Copy link
Contributor Author

@16bit-ykiko 16bit-ykiko Oct 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I push a new commit and try to make completion for module related keywords more context-sensitive.

And found something strange: https://godbolt.org/z/YM9dhEKKe

module;

export module M;
         ^

If I try to run code completion at ^, only get a compiler error.

<source>:3:1: error: export declaration can only be used within a module purview
    3 | export mo<U+0000>dule M;

The error shouldn't occur, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It only happens during completion so there is probably a bug with that https://godbolt.org/z/7M8EPodoP

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made some modification to ParseExportDeclaration to fix the problem.

/// 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)
Expand Down Expand Up @@ -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 >
Expand All @@ -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;
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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) {
Expand Down
57 changes: 57 additions & 0 deletions clang/test/CodeCompletion/keywords-cxx20.cpp
Original file line number Diff line number Diff line change
@@ -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#>