Code completion previously could not tell apart the declaration of a method from a call to it, and provided call-like behaviour even in declaration contexts. This included things like not offering completion for private methods, and inserting placeholders for the parameters as though the user was going to fill in arguments. This patch adds support to Parser and SemaCodeComplete to detect and provide dedicated behaviour for declaration contexts. In these contexts, the flag CodeCompletionResult::DeclaringEntity is set, and createCodeCompletionString() is adjusted to handle this flag, e.g. by inserting parameter declarations as text chunks rather than placeholder chunks. The DeclaringEntity flag is also available for consumers of SemaCodeComplete, such as clangd, to customize their behaviour. In addition, the patch tweaks the conditions under which the existing CodeCompletionResult::FunctionCanBeCall flag is set to be more accurate, excluding declaration contexts and cases where the address of the function is likely being taken. Fixes clangd/clangd#753 Fixes clangd/clangd#880 Fixes clangd/clangd#1752
154 lines
6.0 KiB
C++
154 lines
6.0 KiB
C++
struct A {
|
|
void foo(this auto&& self, int arg);
|
|
void bar(this A self, int arg);
|
|
};
|
|
|
|
int func1() {
|
|
A a {};
|
|
a.
|
|
}
|
|
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-2):5 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC1 %s
|
|
// CHECK-CC1: COMPLETION: A : A::
|
|
// CHECK-NEXT-CC1: COMPLETION: bar : [#void#]bar(<#int arg#>)
|
|
// CHECK-NEXT-CC1: COMPLETION: foo : [#void#]foo(<#int arg#>)
|
|
// CHECK-NEXT-CC1: COMPLETION: operator= : [#A &#]operator=(<#const A &#>)
|
|
// CHECK-NEXT-CC1: COMPLETION: operator= : [#A &#]operator=(<#A &&#>)
|
|
// CHECK-NEXT-CC1: COMPLETION: ~A : [#void#]~A()
|
|
|
|
struct B {
|
|
template <typename T>
|
|
void foo(this T&& self, int arg);
|
|
};
|
|
|
|
int func2() {
|
|
B b {};
|
|
b.foo();
|
|
}
|
|
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-2):9 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC2 %s
|
|
// CHECK-CC2: OVERLOAD: [#void#]foo(int arg)
|
|
|
|
// TODO: llvm/llvm-project/146649
|
|
// This is incorrect behavior. Correct Result should be a variant of,
|
|
// CC3: should be something like [#void#]foo(<#A self#>, <#int arg#>)
|
|
// CC4: should be something like [#void#]bar(<#A self#>, <#int arg#>)
|
|
int func3() {
|
|
(&A::foo)
|
|
(&A::bar)
|
|
}
|
|
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-3):10 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC3 %s
|
|
// CHECK-CC3: COMPLETION: foo : [#void#]foo<<#class self:auto#>>[#(#][#int arg#][#)#]
|
|
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-4):10 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC4 %s
|
|
// CHECK-CC4: COMPLETION: bar : [#void#]bar[#(#][#int arg#][#)#]
|
|
|
|
int func4() {
|
|
// TODO (&A::foo)(
|
|
(&A::bar)()
|
|
}
|
|
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-2):13 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC5 %s
|
|
// CHECK-CC5: OVERLOAD: [#void#](<#A#>, int)
|
|
|
|
struct C {
|
|
int member {};
|
|
int memberFnA(int a);
|
|
int memberFnA(this C&, float a);
|
|
|
|
void foo(this C& self) {
|
|
// Should not offer any members here, since
|
|
// it needs to be referenced through `self`.
|
|
mem
|
|
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):8 -std=c++23 %s | FileCheck --allow-empty %s
|
|
// CHECK-NOT: COMPLETION: member : [#int#]member
|
|
// CHECK-NOT: COMPLETION: memberFnA : [#int#]memberFnA(<#int a#>)
|
|
// CHECK-NOT: COMPLETION: memberFnA : [#int#]memberFnA(<#float a#>)
|
|
}
|
|
void bar(this C& self) {
|
|
// should offer all results
|
|
self.mem
|
|
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):13 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC6 %s
|
|
// CHECK-CC6: COMPLETION: member : [#int#]member
|
|
// CHECK-CC6: COMPLETION: memberFnA : [#int#]memberFnA(<#int a#>)
|
|
// CHECK-CC6: COMPLETION: memberFnA : [#int#]memberFnA(<#float a#>)
|
|
}
|
|
void baz(this C& self) {
|
|
[&]() {
|
|
// Should not offer any results
|
|
mem
|
|
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):10 -std=c++23 %s | FileCheck --allow-empty %s
|
|
// CHECK-NOT: COMPLETION: member : [#int#]member
|
|
// CHECK-NOT: COMPLETION: memberFnA : [#int#]memberFnA(<#int a#>)
|
|
// CHECK-NOT: COMPLETION: memberFnA : [#int#]memberFnA(<#float a#>)
|
|
}();
|
|
}
|
|
};
|
|
|
|
|
|
struct S {
|
|
void foo1(int a);
|
|
void foo2(int a) const;
|
|
void foo2(this const S& self, float a);
|
|
void foo3(this const S& self, int a);
|
|
void foo4(this S& self, int a);
|
|
};
|
|
|
|
void S::foo1(int a) {
|
|
this->;
|
|
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):9 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC7 %s
|
|
// CHECK-CC7: COMPLETION: foo1 : [#void#]foo1(<#int a#>)
|
|
// CHECK-CC7: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
|
|
// CHECK-CC7: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
|
|
// CHECK-CC7: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
|
|
// CHECK-CC7: COMPLETION: foo4 : [#void#]foo4(<#int a#>)
|
|
}
|
|
|
|
void S::foo2(int a) const {
|
|
this->;
|
|
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):9 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC8 %s
|
|
// CHECK-CC8: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
|
|
// CHECK-CC8: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
|
|
// CHECK-CC8: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
|
|
}
|
|
|
|
void S::foo3(this const S& self, int a) {
|
|
self.;
|
|
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):8 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC9 %s
|
|
// CHECK-CC9: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
|
|
// CHECK-CC9: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
|
|
// CHECK-CC9: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
|
|
}
|
|
|
|
void S::foo4(this S& self, int a) {
|
|
self.;
|
|
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):8 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC10 %s
|
|
// CHECK-CC10: COMPLETION: foo1 : [#void#]foo1(<#int a#>)
|
|
// CHECK-CC10: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
|
|
// CHECK-CC10: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
|
|
// CHECK-CC10: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
|
|
// CHECK-CC10: COMPLETION: foo4 : [#void#]foo4(<#int a#>)
|
|
}
|
|
|
|
void test1(S s) {
|
|
s.;
|
|
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):5 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC11 %s
|
|
// CHECK-CC11: COMPLETION: foo1 : [#void#]foo1(<#int a#>)
|
|
// CHECK-CC11: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
|
|
// CHECK-CC11: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
|
|
// CHECK-CC11: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
|
|
// CHECK-CC11: COMPLETION: foo4 : [#void#]foo4(<#int a#>)
|
|
}
|
|
|
|
void test2(const S s) {
|
|
s.;
|
|
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):5 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC12 %s
|
|
// CHECK-CC12: COMPLETION: foo2 : [#void#]foo2(<#int a#>)[# const#]
|
|
// CHECK-CC12: COMPLETION: foo2 : [#void#]foo2(<#float a#>)[# const#]
|
|
// CHECK-CC12: COMPLETION: foo3 : [#void#]foo3(<#int a#>)[# const#]
|
|
}
|
|
|
|
void test3(S s) {
|
|
s.foo2();
|
|
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-1):10 -std=c++23 %s | FileCheck -check-prefix=CHECK-CC13 %s
|
|
// CHECK-CC13: OVERLOAD: [#void#]foo2(<#int a#>)
|
|
// CHECK-CC13: OVERLOAD: [#void#]foo2(float a)
|
|
// TODO: foo2 should be OVERLOAD: [#void#]foo2(<#float a#>)
|
|
}
|