r361461 - [CodeComplete] Complete a lambda when preferred type is a function

Ilya Biryukov via cfe-commits cfe-commits at lists.llvm.org
Thu May 23 00:45:35 PDT 2019


Author: ibiryukov
Date: Thu May 23 00:45:35 2019
New Revision: 361461

URL: http://llvm.org/viewvc/llvm-project?rev=361461&view=rev
Log:
[CodeComplete] Complete a lambda when preferred type is a function

Summary: Uses a heuristic to detect std::function and friends.

Reviewers: kadircet

Reviewed By: kadircet

Subscribers: cfe-commits

Tags: #clang

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

Added:
    cfe/trunk/test/CodeCompletion/lambdas.cpp
Modified:
    cfe/trunk/lib/Sema/SemaCodeComplete.cpp

Modified: cfe/trunk/lib/Sema/SemaCodeComplete.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCodeComplete.cpp?rev=361461&r1=361460&r2=361461&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaCodeComplete.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCodeComplete.cpp Thu May 23 00:45:35 2019
@@ -4108,6 +4108,69 @@ static void AddEnumerators(ResultBuilder
   Results.ExitScope();
 }
 
+/// Try to find a corresponding FunctionProtoType for function-like types (e.g.
+/// function pointers, std::function, etc).
+static const FunctionProtoType *TryDeconstructFunctionLike(QualType T) {
+  assert(!T.isNull());
+  // Try to extract first template argument from std::function<> and similar.
+  // Note we only handle the sugared types, they closely match what users wrote.
+  // We explicitly choose to not handle ClassTemplateSpecializationDecl.
+  if (auto *Specialization = T->getAs<TemplateSpecializationType>()) {
+    if (Specialization->getNumArgs() != 1)
+      return nullptr;
+    const TemplateArgument &Argument = Specialization->getArg(0);
+    if (Argument.getKind() != TemplateArgument::Type)
+      return nullptr;
+    return Argument.getAsType()->getAs<FunctionProtoType>();
+  }
+  // Handle other cases.
+  if (T->isPointerType())
+    T = T->getPointeeType();
+  return T->getAs<FunctionProtoType>();
+}
+
+/// Adds a pattern completion for a lambda expression with the specified
+/// parameter types and placeholders for parameter names.
+static void AddLambdaCompletion(ResultBuilder &Results,
+                                llvm::ArrayRef<QualType> Parameters,
+                                const LangOptions &LangOpts) {
+  CodeCompletionBuilder Completion(Results.getAllocator(),
+                                   Results.getCodeCompletionTUInfo());
+  // [](<parameters>) {}
+  Completion.AddChunk(CodeCompletionString::CK_LeftBracket);
+  Completion.AddPlaceholderChunk("=");
+  Completion.AddChunk(CodeCompletionString::CK_RightBracket);
+  if (!Parameters.empty()) {
+    Completion.AddChunk(CodeCompletionString::CK_LeftParen);
+    bool First = true;
+    for (auto Parameter : Parameters) {
+      if (!First)
+        Completion.AddChunk(CodeCompletionString::ChunkKind::CK_Comma);
+      else
+        First = false;
+
+      constexpr llvm::StringLiteral NamePlaceholder = "!#!NAME_GOES_HERE!#!";
+      std::string Type = NamePlaceholder;
+      Parameter.getAsStringInternal(Type, PrintingPolicy(LangOpts));
+      llvm::StringRef Prefix, Suffix;
+      std::tie(Prefix, Suffix) = llvm::StringRef(Type).split(NamePlaceholder);
+      Prefix = Prefix.rtrim();
+      Suffix = Suffix.ltrim();
+
+      Completion.AddTextChunk(Completion.getAllocator().CopyString(Prefix));
+      Completion.AddChunk(CodeCompletionString::CK_HorizontalSpace);
+      Completion.AddPlaceholderChunk("parameter");
+      Completion.AddTextChunk(Completion.getAllocator().CopyString(Suffix));
+    };
+    Completion.AddChunk(CodeCompletionString::CK_RightParen);
+  }
+  Completion.AddChunk(CodeCompletionString::CK_LeftBrace);
+  Completion.AddPlaceholderChunk("body");
+  Completion.AddChunk(CodeCompletionString::CK_RightBrace);
+
+  Results.AddResult(Completion.TakeString());
+}
+
 /// Perform code-completion in an expression context when we know what
 /// type we're looking for.
 void Sema::CodeCompleteExpression(Scope *S,
@@ -4169,6 +4232,14 @@ void Sema::CodeCompleteExpression(Scope
   if (CodeCompleter->includeMacros())
     AddMacroResults(PP, Results, CodeCompleter->loadExternal(), false,
                     PreferredTypeIsPointer);
+
+  // Complete a lambda expression when preferred type is a function.
+  if (!Data.PreferredType.isNull() && getLangOpts().CPlusPlus11) {
+    if (const FunctionProtoType *F =
+            TryDeconstructFunctionLike(Data.PreferredType))
+      AddLambdaCompletion(Results, F->getParamTypes(), getLangOpts());
+  }
+
   HandleCodeCompleteResults(this, CodeCompleter, Results.getCompletionContext(),
                             Results.data(), Results.size());
 }

Added: cfe/trunk/test/CodeCompletion/lambdas.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeCompletion/lambdas.cpp?rev=361461&view=auto
==============================================================================
--- cfe/trunk/test/CodeCompletion/lambdas.cpp (added)
+++ cfe/trunk/test/CodeCompletion/lambdas.cpp Thu May 23 00:45:35 2019
@@ -0,0 +1,53 @@
+template <class T>
+struct function {
+};
+
+
+void test() {
+  void (*x)(int, double) = nullptr;
+
+  function<void(int, double)> y = {};
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:7:28 %s -o - | FileCheck -check-prefix=CHECK-1 %s
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:9:35 %s -o - | FileCheck -check-prefix=CHECK-1 %s
+  // CHECK-1: COMPLETION: Pattern : [<#=#>](int <#parameter#>, double <#parameter#>){<#body#>}
+
+  // == Placeholders for suffix types must be placed properly.
+  function<void(void(*)(int))> z = {};
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:15:36 %s -o - | FileCheck -check-prefix=CHECK-2 %s
+  // CHECK-2: COMPLETION: Pattern : [<#=#>](void (* <#parameter#>)(int)){<#body#>}
+
+  // == No need for a parameter list if function has no parameters.
+  function<void()> a = {};
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:20:24 %s -o - | FileCheck -check-prefix=CHECK-3 %s
+  // CHECK-3: COMPLETION: Pattern : [<#=#>]{<#body#>}
+}
+
+template <class T, class Allocator = int>
+struct vector {};
+
+void test2() {
+  // == Try to preserve types as written.
+  function<void(vector<int>)> a = {};
+
+  using function_typedef = function<void(vector<int>)>;
+  function_typedef b = {};
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:30:35 %s -o - | FileCheck -check-prefix=CHECK-4 %s
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:33:24 %s -o - | FileCheck -check-prefix=CHECK-4 %s
+  // CHECK-4: COMPLETION: Pattern : [<#=#>](vector<int> <#parameter#>){<#body#>}
+}
+
+// Check another common function wrapper name.
+template <class T> struct unique_function {};
+
+void test3() {
+  unique_function<void()> a = {};
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:43:31 %s -o - | FileCheck -check-prefix=CHECK-5 %s
+  // CHECK-5: COMPLETION: Pattern : [<#=#>]{<#body#>}
+}
+
+template <class T, class U> struct weird_function {};
+void test4() {
+  weird_function<void(), int> b = {};
+  // RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:50:35 %s -o - | FileCheck -check-prefix=CHECK-6 %s
+  // CHECK-6-NOT: COMPLETION: Pattern : [<#=
+}




More information about the cfe-commits mailing list