[clang-tools-extra] 2a030e6 - [clangd][ObjC] Fix issue completing a method decl by name

David Goldman via cfe-commits cfe-commits at lists.llvm.org
Tue Jun 1 10:44:16 PDT 2021


Author: David Goldman
Date: 2021-06-01T13:35:05-04:00
New Revision: 2a030e680e0812c652ed4ae2b012e285a5514ffa

URL: https://github.com/llvm/llvm-project/commit/2a030e680e0812c652ed4ae2b012e285a5514ffa
DIFF: https://github.com/llvm/llvm-project/commit/2a030e680e0812c652ed4ae2b012e285a5514ffa.diff

LOG: [clangd][ObjC] Fix issue completing a method decl by name

When completing an Objective-C method declaration by name, we need to
preserve the leading text as a `qualifier` so we insert it properly
before the first typed text chunk.

Differential Revision: https://reviews.llvm.org/D100798

Added: 
    

Modified: 
    clang-tools-extra/clangd/CodeCompletionStrings.cpp
    clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clangd/CodeCompletionStrings.cpp b/clang-tools-extra/clangd/CodeCompletionStrings.cpp
index d4a3bdafcae0..8205c88a5b66 100644
--- a/clang-tools-extra/clangd/CodeCompletionStrings.cpp
+++ b/clang-tools-extra/clangd/CodeCompletionStrings.cpp
@@ -114,6 +114,7 @@ void getSignature(const CodeCompletionString &CCS, std::string *Signature,
   }
   unsigned SnippetArg = 0;
   bool HadObjCArguments = false;
+  bool HadInformativeChunks = false;
   for (const auto &Chunk : CCS) {
     // Informative qualifier chunks only clutter completion results, skip
     // them.
@@ -129,10 +130,14 @@ void getSignature(const CodeCompletionString &CCS, std::string *Signature,
       //   reclassified as qualifiers.
       //
       // Objective-C:
-      //   Objective-C methods may have multiple typed-text chunks, so we must
-      //   treat them carefully. For Objective-C methods, all typed-text chunks
-      //   will end in ':' (unless there are no arguments, in which case we
-      //   can safely treat them as C++).
+      //   Objective-C methods expressions may have multiple typed-text chunks,
+      //   so we must treat them carefully. For Objective-C methods, all
+      //   typed-text and informative chunks will end in ':' (unless there are
+      //   no arguments, in which case we can safely treat them as C++).
+      //
+      //   Completing a method declaration itself (not a method expression) is
+      //   similar except that we use the `RequiredQualifiers` to store the
+      //   text before the selector, e.g. `- (void)`.
       if (!llvm::StringRef(Chunk.Text).endswith(":")) { // Treat as C++.
         if (RequiredQualifiers)
           *RequiredQualifiers = std::move(*Signature);
@@ -147,6 +152,28 @@ void getSignature(const CodeCompletionString &CCS, std::string *Signature,
         // methods.
         if (!HadObjCArguments) {
           HadObjCArguments = true;
+          // If we have no previous informative chunks (informative selector
+          // fragments in practice), we treat any previous chunks as
+          // `RequiredQualifiers` so they will be added as a prefix during the
+          // completion.
+          //
+          // e.g. to complete `- (void)doSomething:(id)argument`:
+          // - Completion name: `doSomething:`
+          // - RequiredQualifiers: `- (void)`
+          // - Snippet/Signature suffix: `(id)argument`
+          //
+          // This 
diff ers from the case when we're completing a method
+          // expression with a previous informative selector fragment.
+          //
+          // e.g. to complete `[self doSomething:nil ^somethingElse:(id)]`:
+          // - Previous Informative Chunk: `doSomething:`
+          // - Completion name: `somethingElse:`
+          // - Snippet/Signature suffix: `(id)`
+          if (!HadInformativeChunks) {
+            if (RequiredQualifiers)
+              *RequiredQualifiers = std::move(*Signature);
+            Snippet->clear();
+          }
           Signature->clear();
         } else { // Subsequent argument, considered part of snippet/signature.
           *Signature += Chunk.Text;
@@ -173,6 +200,7 @@ void getSignature(const CodeCompletionString &CCS, std::string *Signature,
       *Snippet += '}';
       break;
     case CodeCompletionString::CK_Informative:
+      HadInformativeChunks = true;
       // For example, the word "const" for a const method, or the name of
       // the base class for methods that are part of the base class.
       *Signature += Chunk.Text;

diff  --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
index e4e56f2b5e38..78f6e7c4aa9f 100644
--- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -2796,6 +2796,79 @@ TEST(CompletionTest, ObjectiveCMethodTwoArgumentsFromMiddle) {
   EXPECT_THAT(C, ElementsAre(SnippetSuffix("${1:(unsigned int)}")));
 }
 
+TEST(CompletionTest, ObjectiveCSimpleMethodDeclaration) {
+  auto Results = completions(R"objc(
+      @interface Foo
+      - (void)foo;
+      @end
+      @implementation Foo
+      fo^
+      @end
+    )objc",
+                             /*IndexSymbols=*/{},
+                             /*Opts=*/{}, "Foo.m");
+
+  auto C = Results.Completions;
+  EXPECT_THAT(C, ElementsAre(Named("foo")));
+  EXPECT_THAT(C, ElementsAre(Kind(CompletionItemKind::Method)));
+  EXPECT_THAT(C, ElementsAre(Qualifier("- (void)")));
+}
+
+TEST(CompletionTest, ObjectiveCMethodDeclaration) {
+  auto Results = completions(R"objc(
+      @interface Foo
+      - (int)valueForCharacter:(char)c secondArgument:(id)object;
+      @end
+      @implementation Foo
+      valueFor^
+      @end
+    )objc",
+                             /*IndexSymbols=*/{},
+                             /*Opts=*/{}, "Foo.m");
+
+  auto C = Results.Completions;
+  EXPECT_THAT(C, ElementsAre(Named("valueForCharacter:")));
+  EXPECT_THAT(C, ElementsAre(Kind(CompletionItemKind::Method)));
+  EXPECT_THAT(C, ElementsAre(Qualifier("- (int)")));
+  EXPECT_THAT(C, ElementsAre(Signature("(char)c secondArgument:(id)object")));
+}
+
+TEST(CompletionTest, ObjectiveCMethodDeclarationPrefixTyped) {
+  auto Results = completions(R"objc(
+      @interface Foo
+      - (int)valueForCharacter:(char)c;
+      @end
+      @implementation Foo
+      - (int)valueFor^
+      @end
+    )objc",
+                             /*IndexSymbols=*/{},
+                             /*Opts=*/{}, "Foo.m");
+
+  auto C = Results.Completions;
+  EXPECT_THAT(C, ElementsAre(Named("valueForCharacter:")));
+  EXPECT_THAT(C, ElementsAre(Kind(CompletionItemKind::Method)));
+  EXPECT_THAT(C, ElementsAre(Signature("(char)c")));
+}
+
+TEST(CompletionTest, ObjectiveCMethodDeclarationFromMiddle) {
+  auto Results = completions(R"objc(
+      @interface Foo
+      - (int)valueForCharacter:(char)c secondArgument:(id)object;
+      @end
+      @implementation Foo
+      - (int)valueForCharacter:(char)c second^
+      @end
+    )objc",
+                             /*IndexSymbols=*/{},
+                             /*Opts=*/{}, "Foo.m");
+
+  auto C = Results.Completions;
+  EXPECT_THAT(C, ElementsAre(Named("secondArgument:")));
+  EXPECT_THAT(C, ElementsAre(Kind(CompletionItemKind::Method)));
+  EXPECT_THAT(C, ElementsAre(Signature("(id)object")));
+}
+
 TEST(CompletionTest, CursorInSnippets) {
   clangd::CodeCompleteOptions Options;
   Options.EnableSnippets = true;


        


More information about the cfe-commits mailing list