[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