[clang-tools-extra] [clang] [Sema][clangd] add noexcept to override functions during code completion (PR #75937)
Sirui Mu via cfe-commits
cfe-commits at lists.llvm.org
Fri Dec 22 04:45:03 PST 2023
https://github.com/Lancern updated https://github.com/llvm/llvm-project/pull/75937
>From 6e5e6986559a8d8a72901baf60cbc3b9163a7cd7 Mon Sep 17 00:00:00 2001
From: Sirui Mu <msrlancern at gmail.com>
Date: Tue, 19 Dec 2023 22:24:23 +0800
Subject: [PATCH 1/3] [clangd][Sema] add noexcept to override functions during
code completion
---
.../test/completion-override-except-spec.test | 69 +++++++++++++++++++
clang/lib/Sema/SemaCodeComplete.cpp | 22 ++++++
2 files changed, 91 insertions(+)
create mode 100644 clang-tools-extra/clangd/test/completion-override-except-spec.test
diff --git a/clang-tools-extra/clangd/test/completion-override-except-spec.test b/clang-tools-extra/clangd/test/completion-override-except-spec.test
new file mode 100644
index 00000000000000..19c7f84bc679d8
--- /dev/null
+++ b/clang-tools-extra/clangd/test/completion-override-except-spec.test
@@ -0,0 +1,69 @@
+# RUN: clangd -lit-test < %s | FileCheck %s
+# RUN: clangd -lit-test -pch-storage=memory < %s | FileCheck %s
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
+---
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"struct Base {\n virtual void virt_method() noexcept = 0;\n};\n\nstruct Derived : Base {\n virt_\n};"}}}
+---
+{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"test:///main.cpp"},"position":{"line":5,"character":6}}}
+# CHECK: "id": 1,
+# CHECK-NEXT: "jsonrpc": "2.0",
+# CHECK-NEXT: "result": {
+# CHECK-NEXT: "isIncomplete": false,
+# CHECK-NEXT: "items": [
+# CHECK-NEXT: {
+# CHECK-NEXT: "filterText": "virt_method() noexcept override",
+# CHECK-NEXT: "insertText": "void virt_method() noexcept override",
+# CHECK-NEXT: "insertTextFormat": 1,
+# CHECK-NEXT: "kind": 2,
+# CHECK-NEXT: "label": " void virt_method() noexcept override",
+# CHECK-NEXT: "score": {{[0-9]+.[0-9]+}},
+# CHECK-NEXT: "sortText": "{{.*}}virt_method() noexcept override",
+# CHECK-NEXT: "textEdit": {
+# CHECK-NEXT: "newText": "void virt_method() noexcept override",
+# CHECK-NEXT: "range": {
+# CHECK-NEXT: "end": {
+# CHECK-NEXT: "character": 6,
+# CHECK-NEXT: "line": 5
+# CHECK-NEXT: },
+# CHECK-NEXT: "start": {
+# CHECK-NEXT: "character": 2,
+# CHECK-NEXT: "line": 5
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: },
+---
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///main.cpp","version":2},"contentChanges":[{"text":"struct Base {\n virtual void virt_method() = 0;\n};\n\nstruct Derived : Base {\n virt_\n};"}]}}
+---
+{"jsonrpc":"2.0","id":3,"method":"textDocument/completion","params":{"textDocument":{"uri":"test:///main.cpp"},"position":{"line":5,"character":6}}}
+# CHECK: "id": 3,
+# CHECK-NEXT: "jsonrpc": "2.0",
+# CHECK-NEXT: "result": {
+# CHECK-NEXT: "isIncomplete": false,
+# CHECK-NEXT: "items": [
+# CHECK-NEXT: {
+# CHECK-NEXT: "filterText": "virt_method() override",
+# CHECK-NEXT: "insertText": "void virt_method() override",
+# CHECK-NEXT: "insertTextFormat": 1,
+# CHECK-NEXT: "kind": 2,
+# CHECK-NEXT: "label": " void virt_method() override",
+# CHECK-NEXT: "score": {{[0-9]+.[0-9]+}},
+# CHECK-NEXT: "sortText": "{{.*}}virt_method() override",
+# CHECK-NEXT: "textEdit": {
+# CHECK-NEXT: "newText": "void virt_method() override",
+# CHECK-NEXT: "range": {
+# CHECK-NEXT: "end": {
+# CHECK-NEXT: "character": 6,
+# CHECK-NEXT: "line": 5
+# CHECK-NEXT: },
+# CHECK-NEXT: "start": {
+# CHECK-NEXT: "character": 2,
+# CHECK-NEXT: "line": 5
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: },
+---
+{"jsonrpc":"2.0","id":4,"method":"shutdown"}
+---
+{"jsonrpc":"2.0","method":"exit"}
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index c44be0df9b0a85..516936311a278d 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -25,6 +25,7 @@
#include "clang/AST/Type.h"
#include "clang/Basic/AttributeCommonInfo.h"
#include "clang/Basic/CharInfo.h"
+#include "clang/Basic/ExceptionSpecificationType.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Lex/HeaderSearch.h"
@@ -3292,6 +3293,25 @@ AddFunctionTypeQualsToCompletionString(CodeCompletionBuilder &Result,
Result.AddInformativeChunk(Result.getAllocator().CopyString(QualsStr));
}
+static void
+AddFunctionExceptSpecToCompletionString(CodeCompletionBuilder &Result,
+ const FunctionDecl *Function) {
+ const auto *Proto = Function->getType()->getAs<FunctionProtoType>();
+ if (!Proto)
+ return;
+
+ auto ExceptInfo = Proto->getExceptionSpecInfo();
+ switch (ExceptInfo.Type) {
+ case EST_BasicNoexcept:
+ case EST_NoexceptTrue:
+ Result.AddInformativeChunk(" noexcept");
+ break;
+
+ default:
+ break;
+ }
+}
+
/// Add the name of the given declaration
static void AddTypedNameChunk(ASTContext &Context, const PrintingPolicy &Policy,
const NamedDecl *ND,
@@ -3560,6 +3580,7 @@ CodeCompletionString *CodeCompletionResult::createCodeCompletionStringForDecl(
AddFunctionParameterChunks(PP, Policy, Function, Result);
Result.AddChunk(CodeCompletionString::CK_RightParen);
AddFunctionTypeQualsToCompletionString(Result, Function);
+ AddFunctionExceptSpecToCompletionString(Result, Function);
};
if (const auto *Function = dyn_cast<FunctionDecl>(ND)) {
@@ -3642,6 +3663,7 @@ CodeCompletionString *CodeCompletionResult::createCodeCompletionStringForDecl(
AddFunctionParameterChunks(PP, Policy, Function, Result);
Result.AddChunk(CodeCompletionString::CK_RightParen);
AddFunctionTypeQualsToCompletionString(Result, Function);
+ AddFunctionExceptSpecToCompletionString(Result, Function);
return Result.TakeString();
}
>From 71022ee5893d91b2c2274bfa924938264d457032 Mon Sep 17 00:00:00 2001
From: Sirui Mu <msrlancern at gmail.com>
Date: Thu, 21 Dec 2023 23:24:28 +0800
Subject: [PATCH 2/3] Move tests to clang/test/CodeCompletion/overrides.cpp
---
.../test/completion-override-except-spec.test | 69 -------------------
clang/test/CodeCompletion/overrides.cpp | 14 ++++
2 files changed, 14 insertions(+), 69 deletions(-)
delete mode 100644 clang-tools-extra/clangd/test/completion-override-except-spec.test
diff --git a/clang-tools-extra/clangd/test/completion-override-except-spec.test b/clang-tools-extra/clangd/test/completion-override-except-spec.test
deleted file mode 100644
index 19c7f84bc679d8..00000000000000
--- a/clang-tools-extra/clangd/test/completion-override-except-spec.test
+++ /dev/null
@@ -1,69 +0,0 @@
-# RUN: clangd -lit-test < %s | FileCheck %s
-# RUN: clangd -lit-test -pch-storage=memory < %s | FileCheck %s
-{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
----
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"struct Base {\n virtual void virt_method() noexcept = 0;\n};\n\nstruct Derived : Base {\n virt_\n};"}}}
----
-{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"test:///main.cpp"},"position":{"line":5,"character":6}}}
-# CHECK: "id": 1,
-# CHECK-NEXT: "jsonrpc": "2.0",
-# CHECK-NEXT: "result": {
-# CHECK-NEXT: "isIncomplete": false,
-# CHECK-NEXT: "items": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "filterText": "virt_method() noexcept override",
-# CHECK-NEXT: "insertText": "void virt_method() noexcept override",
-# CHECK-NEXT: "insertTextFormat": 1,
-# CHECK-NEXT: "kind": 2,
-# CHECK-NEXT: "label": " void virt_method() noexcept override",
-# CHECK-NEXT: "score": {{[0-9]+.[0-9]+}},
-# CHECK-NEXT: "sortText": "{{.*}}virt_method() noexcept override",
-# CHECK-NEXT: "textEdit": {
-# CHECK-NEXT: "newText": "void virt_method() noexcept override",
-# CHECK-NEXT: "range": {
-# CHECK-NEXT: "end": {
-# CHECK-NEXT: "character": 6,
-# CHECK-NEXT: "line": 5
-# CHECK-NEXT: },
-# CHECK-NEXT: "start": {
-# CHECK-NEXT: "character": 2,
-# CHECK-NEXT: "line": 5
-# CHECK-NEXT: }
-# CHECK-NEXT: }
-# CHECK-NEXT: }
-# CHECK-NEXT: },
----
-{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///main.cpp","version":2},"contentChanges":[{"text":"struct Base {\n virtual void virt_method() = 0;\n};\n\nstruct Derived : Base {\n virt_\n};"}]}}
----
-{"jsonrpc":"2.0","id":3,"method":"textDocument/completion","params":{"textDocument":{"uri":"test:///main.cpp"},"position":{"line":5,"character":6}}}
-# CHECK: "id": 3,
-# CHECK-NEXT: "jsonrpc": "2.0",
-# CHECK-NEXT: "result": {
-# CHECK-NEXT: "isIncomplete": false,
-# CHECK-NEXT: "items": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "filterText": "virt_method() override",
-# CHECK-NEXT: "insertText": "void virt_method() override",
-# CHECK-NEXT: "insertTextFormat": 1,
-# CHECK-NEXT: "kind": 2,
-# CHECK-NEXT: "label": " void virt_method() override",
-# CHECK-NEXT: "score": {{[0-9]+.[0-9]+}},
-# CHECK-NEXT: "sortText": "{{.*}}virt_method() override",
-# CHECK-NEXT: "textEdit": {
-# CHECK-NEXT: "newText": "void virt_method() override",
-# CHECK-NEXT: "range": {
-# CHECK-NEXT: "end": {
-# CHECK-NEXT: "character": 6,
-# CHECK-NEXT: "line": 5
-# CHECK-NEXT: },
-# CHECK-NEXT: "start": {
-# CHECK-NEXT: "character": 2,
-# CHECK-NEXT: "line": 5
-# CHECK-NEXT: }
-# CHECK-NEXT: }
-# CHECK-NEXT: }
-# CHECK-NEXT: },
----
-{"jsonrpc":"2.0","id":4,"method":"shutdown"}
----
-{"jsonrpc":"2.0","method":"exit"}
diff --git a/clang/test/CodeCompletion/overrides.cpp b/clang/test/CodeCompletion/overrides.cpp
index 7f330904eede98..0a12973f846b75 100644
--- a/clang/test/CodeCompletion/overrides.cpp
+++ b/clang/test/CodeCompletion/overrides.cpp
@@ -41,3 +41,17 @@ void func() {
// Runs completion at empty line on line 37.
// RUN: not %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-5):1 %s -o - | FileCheck -check-prefix=CHECK-CC4 %s
// CHECK-CC4: COMPLETION: Pattern : void vfunc(bool param, int p) override{{$}}
+
+class NoexceptBase {
+ public:
+ virtual void method() noexcept;
+};
+
+class NoexceptDerived : public NoexceptBase {
+ public:
+ met;
+};
+
+// Runs completion at met^ on line 52.
+// RUN: not %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-4):6 %s -o - | FileCheck -check-prefix=CHECK-CC5 %s
+// CHECK-CC5: COMPLETION: Pattern : void method() noexcept override{{$}}
>From c40dbbb7b609a0a3b2051d1af7a6789fb3f1302b Mon Sep 17 00:00:00 2001
From: Sirui Mu <msrlancern at gmail.com>
Date: Fri, 22 Dec 2023 20:44:14 +0800
Subject: [PATCH 3/3] Add noexcept to decl in override code completion
---
clang/lib/Sema/SemaCodeComplete.cpp | 63 +++++++++++++++--------------
1 file changed, 32 insertions(+), 31 deletions(-)
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index 516936311a278d..f3d5ee1ba67825 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -184,7 +184,7 @@ class ResultBuilder {
/// Overloaded C++ member functions found by SemaLookup.
/// Used to determine when one overload is dominated by another.
- llvm::DenseMap<std::pair<DeclContext *, /*Name*/uintptr_t>, ShadowMapEntry>
+ llvm::DenseMap<std::pair<DeclContext *, /*Name*/ uintptr_t>, ShadowMapEntry>
OverloadMap;
/// If we're potentially referring to a C++ member function, the set
@@ -1426,16 +1426,16 @@ void ResultBuilder::AddResult(Result R, DeclContext *CurContext,
}
// Detect cases where a ref-qualified method cannot be invoked.
switch (Method->getRefQualifier()) {
- case RQ_LValue:
- if (ObjectKind != VK_LValue && !MethodQuals.hasConst())
- return;
- break;
- case RQ_RValue:
- if (ObjectKind == VK_LValue)
- return;
- break;
- case RQ_None:
- break;
+ case RQ_LValue:
+ if (ObjectKind != VK_LValue && !MethodQuals.hasConst())
+ return;
+ break;
+ case RQ_RValue:
+ if (ObjectKind == VK_LValue)
+ return;
+ break;
+ case RQ_None:
+ break;
}
/// Check whether this dominates another overloaded method, which should
@@ -1483,9 +1483,7 @@ void ResultBuilder::AddResult(Result R) {
void ResultBuilder::EnterNewScope() { ShadowMaps.emplace_back(); }
/// Exit from the current scope.
-void ResultBuilder::ExitScope() {
- ShadowMaps.pop_back();
-}
+void ResultBuilder::ExitScope() { ShadowMaps.pop_back(); }
/// Determines whether this given declaration will be found by
/// ordinary name lookup.
@@ -2468,7 +2466,8 @@ static void AddOrdinaryNameResults(Sema::ParserCompletionContext CCC, Scope *S,
ReturnType = Method->getReturnType();
else if (SemaRef.getCurBlock() &&
!SemaRef.getCurBlock()->ReturnType.isNull())
- ReturnType = SemaRef.getCurBlock()->ReturnType;;
+ ReturnType = SemaRef.getCurBlock()->ReturnType;
+ ;
if (ReturnType.isNull() || ReturnType->isVoidType()) {
Builder.AddTypedTextChunk("return");
Builder.AddChunk(CodeCompletionString::CK_SemiColon);
@@ -3294,7 +3293,7 @@ AddFunctionTypeQualsToCompletionString(CodeCompletionBuilder &Result,
}
static void
-AddFunctionExceptSpecToCompletionString(CodeCompletionBuilder &Result,
+AddFunctionExceptSpecToCompletionString(std::string &NameAndSignature,
const FunctionDecl *Function) {
const auto *Proto = Function->getType()->getAs<FunctionProtoType>();
if (!Proto)
@@ -3304,7 +3303,7 @@ AddFunctionExceptSpecToCompletionString(CodeCompletionBuilder &Result,
switch (ExceptInfo.Type) {
case EST_BasicNoexcept:
case EST_NoexceptTrue:
- Result.AddInformativeChunk(" noexcept");
+ NameAndSignature += " noexcept";
break;
default:
@@ -3527,6 +3526,13 @@ CodeCompletionResult::createCodeCompletionStringForOverride(
std::string NameAndSignature;
// For overrides all chunks go into the result, none are informative.
printOverrideString(*CCS, BeforeName, NameAndSignature);
+
+ // If the virtual function is declared with "noexcept", add it in the result
+ // code completion string.
+ const auto *VirtualFunc = dyn_cast<FunctionDecl>(Declaration);
+ assert(VirtualFunc && "overridden decl must be a function");
+ AddFunctionExceptSpecToCompletionString(NameAndSignature, VirtualFunc);
+
NameAndSignature += " override";
Result.AddTextChunk(Result.getAllocator().CopyString(BeforeName));
@@ -3580,7 +3586,6 @@ CodeCompletionString *CodeCompletionResult::createCodeCompletionStringForDecl(
AddFunctionParameterChunks(PP, Policy, Function, Result);
Result.AddChunk(CodeCompletionString::CK_RightParen);
AddFunctionTypeQualsToCompletionString(Result, Function);
- AddFunctionExceptSpecToCompletionString(Result, Function);
};
if (const auto *Function = dyn_cast<FunctionDecl>(ND)) {
@@ -3663,7 +3668,6 @@ CodeCompletionString *CodeCompletionResult::createCodeCompletionStringForDecl(
AddFunctionParameterChunks(PP, Policy, Function, Result);
Result.AddChunk(CodeCompletionString::CK_RightParen);
AddFunctionTypeQualsToCompletionString(Result, Function);
- AddFunctionExceptSpecToCompletionString(Result, Function);
return Result.TakeString();
}
@@ -4770,7 +4774,8 @@ static void AddEnumerators(ResultBuilder &Results, ASTContext &Context,
EnumDecl *Enum, DeclContext *CurContext,
const CoveredEnumerators &Enumerators) {
NestedNameSpecifier *Qualifier = Enumerators.SuggestedQualifier;
- if (Context.getLangOpts().CPlusPlus && !Qualifier && Enumerators.Seen.empty()) {
+ if (Context.getLangOpts().CPlusPlus && !Qualifier &&
+ Enumerators.Seen.empty()) {
// If there are no prior enumerators in C++, check whether we have to
// qualify the names of the enumerators that we suggest, because they
// may not be visible in this scope.
@@ -5173,8 +5178,7 @@ AddObjCProperties(const CodeCompletionContext &CCContext,
AllowNullaryMethods, CurContext, AddedProperties,
Results, IsBaseExprStatement, IsClassProperty,
/*InOriginalClass*/ false);
- } else if (const auto *Category =
- dyn_cast<ObjCCategoryDecl>(Container)) {
+ } else if (const auto *Category = dyn_cast<ObjCCategoryDecl>(Container)) {
// Look through protocols.
for (auto *P : Category->protocols())
AddObjCProperties(CCContext, P, AllowCategories, AllowNullaryMethods,
@@ -6032,8 +6036,7 @@ void Sema::CodeCompleteCase(Scope *S) {
Expr *CaseVal = Case->getLHS()->IgnoreParenCasts();
if (auto *DRE = dyn_cast<DeclRefExpr>(CaseVal))
- if (auto *Enumerator =
- dyn_cast<EnumConstantDecl>(DRE->getDecl())) {
+ if (auto *Enumerator = dyn_cast<EnumConstantDecl>(DRE->getDecl())) {
// We look into the AST of the case statement to determine which
// enumerator was named. Alternatively, we could compute the value of
// the integral constant expression, then compare it against the
@@ -8260,11 +8263,10 @@ void Sema::CodeCompleteObjCInstanceMessage(Scope *S, Expr *Receiver,
return;
RecExpr = Conv.get();
}
- QualType ReceiverType = RecExpr
- ? RecExpr->getType()
- : Super ? Context.getObjCObjectPointerType(
- Context.getObjCInterfaceType(Super))
- : Context.getObjCIdType();
+ QualType ReceiverType = RecExpr ? RecExpr->getType()
+ : Super ? Context.getObjCObjectPointerType(
+ Context.getObjCInterfaceType(Super))
+ : Context.getObjCIdType();
// If we're messaging an expression with type "id" or "Class", check
// whether we know something special about the receiver that allows
@@ -10113,8 +10115,7 @@ void Sema::CodeCompleteIncludedFile(llvm::StringRef Dir, bool Angled) {
};
// Helper: scans IncludeDir for nice files, and adds results for each.
- auto AddFilesFromIncludeDir = [&](StringRef IncludeDir,
- bool IsSystem,
+ auto AddFilesFromIncludeDir = [&](StringRef IncludeDir, bool IsSystem,
DirectoryLookup::LookupType_t LookupType) {
llvm::SmallString<128> Dir = IncludeDir;
if (!NativeRelDir.empty()) {
More information about the cfe-commits
mailing list