ilya-biryukov updated this revision to Diff 200717.
ilya-biryukov marked 3 inline comments as done.
ilya-biryukov added a comment.
- Add placeholder for captures.
- Only accept classes that have exactly one template argument.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D62238/new/
https://reviews.llvm.org/D62238
Files:
clang/lib/Sema/SemaCodeComplete.cpp
clang/test/CodeCompletion/lambdas.cpp
Index: clang/test/CodeCompletion/lambdas.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeCompletion/lambdas.cpp
@@ -0,0 +1,56 @@
+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> struct some_random_class {};
+template <class T, class U> struct weird_function {};
+void test4() {
+ some_random_class<void()> a = {};
+ weird_function<void(), int> b = {};
+ // RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:51:31 %s -o - | FileCheck -check-prefix=CHECK-6 %s
+ // RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:52:35 %s -o - | FileCheck -check-prefix=CHECK-6 %s
+ // CHECK-6-NOT: COMPLETION: Pattern : [<#=#>]{
+}
Index: clang/lib/Sema/SemaCodeComplete.cpp
===================================================================
--- clang/lib/Sema/SemaCodeComplete.cpp
+++ clang/lib/Sema/SemaCodeComplete.cpp
@@ -4108,6 +4108,84 @@
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.
+ DeclarationName PotentialTemplateName;
+ const TemplateArgument *PotentialFunctionArg = nullptr;
+ if (auto *Specialization = T->getAs<TemplateSpecializationType>()) {
+ // FIXME: handle other kinds of template names.
+ auto *Template = Specialization->getTemplateName().getAsTemplateDecl();
+ if (!Template || Specialization->getNumArgs() != 1)
+ return nullptr;
+ PotentialTemplateName = Template->getDeclName();
+ PotentialFunctionArg = &Specialization->getArg(0);
+ } else if (auto *Class = dyn_cast_or_null<ClassTemplateSpecializationDecl>(
+ T->getAsCXXRecordDecl())) {
+ if (Class->getTemplateArgs().size() != 1)
+ return nullptr;
+ PotentialTemplateName = Class->getDeclName();
+ PotentialFunctionArg = &Class->getTemplateArgs().get(0);
+ }
+ if (PotentialFunctionArg) {
+ if (!PotentialTemplateName.isIdentifier() ||
+ !PotentialTemplateName.getAsIdentifierInfo()->getName().contains(
+ "function"))
+ return nullptr;
+ if (PotentialFunctionArg->getKind() != TemplateArgument::Type)
+ return nullptr;
+ return PotentialFunctionArg->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 +4247,14 @@
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());
}
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits