clang-tools 20.0.0git
HeuristicResolverTests.cpp
Go to the documentation of this file.
1//===-- HeuristicResolverTests.cpp --------------------------*- C++ -*-----===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8#include "HeuristicResolver.h"
9#include "clang/ASTMatchers/ASTMatchFinder.h"
10#include "clang/ASTMatchers/ASTMatchers.h"
11#include "clang/Tooling/Tooling.h"
12#include "gmock/gmock-matchers.h"
13#include "gtest/gtest.h"
14
15using namespace clang::ast_matchers;
17using testing::ElementsAre;
18
19namespace clang {
20namespace {
21
22// Helper for matching a sequence of elements with a variadic list of matchers.
23// Usage: `ElementsAre(matchAdapter(Vs, MatchFunction)...)`, where `Vs...` is
24// a variadic list of matchers.
25// For each `V` in `Vs`, this will match the corresponding element `E` if
26// `MatchFunction(V, E)` is true.
27MATCHER_P2(matchAdapter, MatcherForElement, MatchFunction, "matchAdapter") {
28 return MatchFunction(MatcherForElement, arg);
29}
30
31template <typename InputNode>
32using ResolveFnT = std::function<std::vector<const NamedDecl *>(
33 const HeuristicResolver *, const InputNode *)>;
34
35// Test heuristic resolution on `Code` using the resolution procedure
36// `ResolveFn`, which takes a `HeuristicResolver` and an input AST node of type
37// `InputNode` and returns a `std::vector<const NamedDecl *>`.
38// `InputMatcher` should be an AST matcher that matches a single node to pass as
39// input to `ResolveFn`, bound to the ID "input". `OutputMatchers` should be AST
40// matchers that each match a single node, bound to the ID "output".
41template <typename InputNode, typename InputMatcher, typename... OutputMatchers>
42void expectResolution(llvm::StringRef Code, ResolveFnT<InputNode> ResolveFn,
43 const InputMatcher &IM, const OutputMatchers &...OMS) {
44 auto TU = tooling::buildASTFromCodeWithArgs(Code, {"-std=c++20"});
45 auto &Ctx = TU->getASTContext();
46 auto InputMatches = match(IM, Ctx);
47 ASSERT_EQ(1u, InputMatches.size());
48 const auto *Input = InputMatches[0].template getNodeAs<InputNode>("input");
49 ASSERT_TRUE(Input);
50
51 auto OutputNodeMatches = [&](auto &OutputMatcher, auto &Actual) {
52 auto OutputMatches = match(OutputMatcher, Ctx);
53 if (OutputMatches.size() != 1u)
54 return false;
55 const auto *ExpectedOutput =
56 OutputMatches[0].template getNodeAs<NamedDecl>("output");
57 if (!ExpectedOutput)
58 return false;
59 return ExpectedOutput == Actual;
60 };
61
62 HeuristicResolver H(Ctx);
63 auto Results = ResolveFn(&H, Input);
64 EXPECT_THAT(Results, ElementsAre(matchAdapter(OMS, OutputNodeMatches)...));
65}
66
67// Wrapper for the above that accepts a HeuristicResolver member function
68// pointer directly.
69template <typename InputNode, typename InputMatcher, typename... OutputMatchers>
70void expectResolution(llvm::StringRef Code,
71 std::vector<const NamedDecl *> (
72 HeuristicResolver::*ResolveFn)(const InputNode *)
73 const,
74 const InputMatcher &IM, const OutputMatchers &...OMS) {
75 expectResolution(Code, ResolveFnT<InputNode>(std::mem_fn(ResolveFn)), IM,
76 OMS...);
77}
78
79TEST(HeuristicResolver, MemberExpr) {
80 std::string Code = R"cpp(
81 template <typename T>
82 struct S {
83 void bar() {}
84 };
85
86 template <typename T>
87 void foo(S<T> arg) {
88 arg.bar();
89 }
90 )cpp";
91 // Test resolution of "bar" in "arg.bar()".
92 expectResolution(
93 Code, &HeuristicResolver::resolveMemberExpr,
94 cxxDependentScopeMemberExpr(hasMemberName("bar")).bind("input"),
95 cxxMethodDecl(hasName("bar")).bind("output"));
96}
97
98TEST(HeuristicResolver, MemberExpr_Overloads) {
99 std::string Code = R"cpp(
100 template <typename T>
101 struct S {
102 void bar(int);
103 void bar(float);
104 };
105
106 template <typename T, typename U>
107 void foo(S<T> arg, U u) {
108 arg.bar(u);
109 }
110 )cpp";
111 // Test resolution of "bar" in "arg.bar(u)". Both overloads should be found.
112 expectResolution(
113 Code, &HeuristicResolver::resolveMemberExpr,
114 cxxDependentScopeMemberExpr(hasMemberName("bar")).bind("input"),
115 cxxMethodDecl(hasName("bar"), hasParameter(0, hasType(asString("int"))))
116 .bind("output"),
117 cxxMethodDecl(hasName("bar"), hasParameter(0, hasType(asString("float"))))
118 .bind("output"));
119}
120
121TEST(HeuristicResolver, MemberExpr_SmartPointer) {
122 std::string Code = R"cpp(
123 template <typename> struct S { void foo() {} };
124 template <typename T> struct unique_ptr {
125 T* operator->();
126 };
127 template <typename T>
128 void test(unique_ptr<S<T>>& v) {
129 v->foo();
130 }
131 )cpp";
132 // Test resolution of "foo" in "v->foo()".
133 expectResolution(
134 Code, &HeuristicResolver::resolveMemberExpr,
135 cxxDependentScopeMemberExpr(hasMemberName("foo")).bind("input"),
136 cxxMethodDecl(hasName("foo")).bind("output"));
137}
138
139TEST(HeuristicResolver, MemberExpr_Chained) {
140 std::string Code = R"cpp(
141 struct A { void foo() {} };
142 template <typename T>
143 struct B {
144 A func(int);
145 void bar() {
146 func(1).foo();
147 }
148 };
149 )cpp";
150 // Test resolution of "foo" in "func(1).foo()".
151 expectResolution(
152 Code, &HeuristicResolver::resolveMemberExpr,
153 cxxDependentScopeMemberExpr(hasMemberName("foo")).bind("input"),
154 cxxMethodDecl(hasName("foo")).bind("output"));
155}
156
157TEST(HeuristicResolver, MemberExpr_TemplateArgs) {
158 std::string Code = R"cpp(
159 struct Foo {
160 static Foo k(int);
161 template <typename T> T convert();
162 };
163 template <typename T>
164 void test() {
165 Foo::k(T()).template convert<T>();
166 }
167 )cpp";
168 // Test resolution of "convert" in "Foo::k(T()).template convert<T>()".
169 expectResolution(
170 Code, &HeuristicResolver::resolveMemberExpr,
171 cxxDependentScopeMemberExpr(hasMemberName("convert")).bind("input"),
172 functionTemplateDecl(hasName("convert")).bind("output"));
173}
174
175TEST(HeuristicResolver, MemberExpr_TypeAlias) {
176 std::string Code = R"cpp(
177 template <typename T>
178 struct Waldo {
179 void find();
180 };
181 template <typename T>
182 using Wally = Waldo<T>;
183 template <typename T>
184 void foo(Wally<T> w) {
185 w.find();
186 }
187 )cpp";
188 // Test resolution of "find" in "w.find()".
189 expectResolution(
190 Code, &HeuristicResolver::resolveMemberExpr,
191 cxxDependentScopeMemberExpr(hasMemberName("find")).bind("input"),
192 cxxMethodDecl(hasName("find")).bind("output"));
193}
194
195TEST(HeuristicResolver, MemberExpr_BaseClass_TypeAlias) {
196 std::string Code = R"cpp(
197 template <typename T>
198 struct Waldo {
199 void find();
200 };
201 template <typename T>
202 using Wally = Waldo<T>;
203 template <typename T>
204 struct S : Wally<T> {
205 void foo() {
206 this->find();
207 }
208 };
209 )cpp";
210 // Test resolution of "find" in "this->find()".
211 expectResolution(
212 Code, &HeuristicResolver::resolveMemberExpr,
213 cxxDependentScopeMemberExpr(hasMemberName("find")).bind("input"),
214 cxxMethodDecl(hasName("find")).bind("output"));
215}
216
217TEST(HeuristicResolver, MemberExpr_Metafunction) {
218 std::string Code = R"cpp(
219 template <typename T>
220 struct Waldo {
221 void find();
222 };
223 template <typename T>
224 struct MetaWaldo {
225 using Type = Waldo<T>;
226 };
227 template <typename T>
228 void foo(typename MetaWaldo<T>::Type w) {
229 w.find();
230 }
231 )cpp";
232 // Test resolution of "find" in "w.find()".
233 expectResolution(
234 Code, &HeuristicResolver::resolveMemberExpr,
235 cxxDependentScopeMemberExpr(hasMemberName("find")).bind("input"),
236 cxxMethodDecl(hasName("find")).bind("output"));
237}
238
239TEST(HeuristicResolver, MemberExpr_DeducedNonTypeTemplateParameter) {
240 std::string Code = R"cpp(
241 template <int N>
242 struct Waldo {
243 const int found = N;
244 };
245 template <Waldo W>
246 int foo() {
247 return W.found;
248 }
249 )cpp";
250 // Test resolution of "found" in "W.found".
251 expectResolution(
252 Code, &HeuristicResolver::resolveMemberExpr,
253 cxxDependentScopeMemberExpr(hasMemberName("found")).bind("input"),
254 fieldDecl(hasName("found")).bind("output"));
255}
256
257TEST(HeuristicResolver, DeclRefExpr_StaticMethod) {
258 std::string Code = R"cpp(
259 template <typename T>
260 struct S {
261 static void bar() {}
262 };
263
264 template <typename T>
265 void foo() {
266 S<T>::bar();
267 }
268 )cpp";
269 // Test resolution of "bar" in "S<T>::bar()".
270 expectResolution(
271 Code, &HeuristicResolver::resolveDeclRefExpr,
272 dependentScopeDeclRefExpr(hasDependentName("bar")).bind("input"),
273 cxxMethodDecl(hasName("bar")).bind("output"));
274}
275
276TEST(HeuristicResolver, DeclRefExpr_StaticOverloads) {
277 std::string Code = R"cpp(
278 template <typename T>
279 struct S {
280 static void bar(int);
281 static void bar(float);
282 };
283
284 template <typename T, typename U>
285 void foo(U u) {
286 S<T>::bar(u);
287 }
288 )cpp";
289 // Test resolution of "bar" in "S<T>::bar(u)". Both overloads should be found.
290 expectResolution(
291 Code, &HeuristicResolver::resolveDeclRefExpr,
292 dependentScopeDeclRefExpr(hasDependentName("bar")).bind("input"),
293 cxxMethodDecl(hasName("bar"), hasParameter(0, hasType(asString("int"))))
294 .bind("output"),
295 cxxMethodDecl(hasName("bar"), hasParameter(0, hasType(asString("float"))))
296 .bind("output"));
297}
298
299TEST(HeuristicResolver, DeclRefExpr_Enumerator) {
300 std::string Code = R"cpp(
301 template <typename T>
302 struct Foo {
303 enum class E { A, B };
304 E e = E::A;
305 };
306 )cpp";
307 // Test resolution of "A" in "E::A".
308 expectResolution(
309 Code, &HeuristicResolver::resolveDeclRefExpr,
310 dependentScopeDeclRefExpr(hasDependentName("A")).bind("input"),
311 enumConstantDecl(hasName("A")).bind("output"));
312}
313
314TEST(HeuristicResolver, DeclRefExpr_RespectScope) {
315 std::string Code = R"cpp(
316 template <typename Info>
317 struct PointerIntPair {
318 void *getPointer() const { return Info::getPointer(); }
319 };
320 )cpp";
321 // Test resolution of "getPointer" in "Info::getPointer()".
322 // Here, we are testing that we do not incorrectly get the enclosing
323 // getPointer() function as a result.
324 expectResolution(
325 Code, &HeuristicResolver::resolveDeclRefExpr,
326 dependentScopeDeclRefExpr(hasDependentName("getPointer")).bind("input"));
327}
328
329TEST(HeuristicResolver, DependentNameType) {
330 std::string Code = R"cpp(
331 template <typename>
332 struct A {
333 struct B {};
334 };
335 template <typename T>
336 void foo(typename A<T>::B);
337 )cpp";
338 // Tests resolution of "B" in "A<T>::B".
339 expectResolution(
340 Code, &HeuristicResolver::resolveDependentNameType,
341 functionDecl(hasParameter(0, hasType(dependentNameType().bind("input")))),
342 classTemplateDecl(
343 has(cxxRecordDecl(has(cxxRecordDecl(hasName("B")).bind("output"))))));
344}
345
346TEST(HeuristicResolver, DependentNameType_Nested) {
347 std::string Code = R"cpp(
348 template <typename>
349 struct A {
350 struct B {
351 struct C {};
352 };
353 };
354 template <typename T>
355 void foo(typename A<T>::B::C);
356 )cpp";
357 // Tests resolution of "C" in "A<T>::B::C".
358 expectResolution(
359 Code, &HeuristicResolver::resolveDependentNameType,
360 functionDecl(hasParameter(0, hasType(dependentNameType().bind("input")))),
361 classTemplateDecl(has(cxxRecordDecl(has(
362 cxxRecordDecl(has(cxxRecordDecl(hasName("C")).bind("output"))))))));
363}
364
365TEST(HeuristicResolver, DependentNameType_Recursion) {
366 std::string Code = R"cpp(
367 template <int N>
368 struct Waldo {
369 using Type = typename Waldo<N - 1>::Type::Next;
370 };
371 )cpp";
372 // Test resolution of "Next" in "typename Waldo<N - 1>::Type::Next".
373 // Here, we are testing that we do not get into an infinite recursion.
374 expectResolution(Code, &HeuristicResolver::resolveDependentNameType,
375 typeAliasDecl(hasType(dependentNameType().bind("input"))));
376}
377
378TEST(HeuristicResolver, DependentNameType_MutualRecursion) {
379 std::string Code = R"cpp(
380 template <int N>
381 struct Odd;
382 template <int N>
383 struct Even {
384 using Type = typename Odd<N - 1>::Type::Next;
385 };
386 template <int N>
387 struct Odd {
388 using Type = typename Even<N - 1>::Type::Next;
389 };
390 )cpp";
391 // Test resolution of "Next" in "typename Even<N - 1>::Type::Next".
392 // Similar to the above but we have two mutually recursive templates.
393 expectResolution(
394 Code, &HeuristicResolver::resolveDependentNameType,
395 classTemplateDecl(hasName("Odd"),
396 has(cxxRecordDecl(has(typeAliasDecl(
397 hasType(dependentNameType().bind("input"))))))));
398}
399
400TEST(HeuristicResolver, NestedNameSpecifier) {
401 // Test resolution of "B" in "A<T>::B::C".
402 // Unlike the "C", the "B" does not get its own DependentNameTypeLoc node,
403 // so the resolution uses the NestedNameSpecifier as input.
404 std::string Code = R"cpp(
405 template <typename>
406 struct A {
407 struct B {
408 struct C {};
409 };
410 };
411 template <typename T>
412 void foo(typename A<T>::B::C);
413 )cpp";
414 // Adapt the call to resolveNestedNameSpecifierToType() to the interface
415 // expected by expectResolution() (returning a vector of decls).
416 ResolveFnT<NestedNameSpecifier> ResolveFn =
417 [](const HeuristicResolver *H,
418 const NestedNameSpecifier *NNS) -> std::vector<const NamedDecl *> {
419 return {H->resolveNestedNameSpecifierToType(NNS)->getAsCXXRecordDecl()};
420 };
421 expectResolution(Code, ResolveFn,
422 nestedNameSpecifier(hasPrefix(specifiesType(hasDeclaration(
423 classTemplateDecl(hasName("A"))))))
424 .bind("input"),
425 classTemplateDecl(has(cxxRecordDecl(
426 has(cxxRecordDecl(hasName("B")).bind("output"))))));
427}
428
429TEST(HeuristicResolver, TemplateSpecializationType) {
430 std::string Code = R"cpp(
431 template <typename>
432 struct A {
433 template <typename>
434 struct B {};
435 };
436 template <typename T>
437 void foo(typename A<T>::template B<int>);
438 )cpp";
439 // Test resolution of "B" in "A<T>::template B<int>".
440 expectResolution(Code, &HeuristicResolver::resolveTemplateSpecializationType,
441 functionDecl(hasParameter(0, hasType(type().bind("input")))),
442 classTemplateDecl(has(cxxRecordDecl(
443 has(classTemplateDecl(hasName("B")).bind("output"))))));
444}
445
446TEST(HeuristicResolver, DependentCall_NonMember) {
447 std::string Code = R"cpp(
448 template <typename T>
449 void nonmember(T);
450 template <typename T>
451 void bar(T t) {
452 nonmember(t);
453 }
454 )cpp";
455 // Test resolution of "nonmember" in "nonmember(t)".
456 expectResolution(Code, &HeuristicResolver::resolveCalleeOfCallExpr,
457 callExpr(callee(unresolvedLookupExpr(hasAnyDeclaration(
458 functionTemplateDecl(hasName("nonmember"))))))
459 .bind("input"),
460 functionTemplateDecl(hasName("nonmember")).bind("output"));
461}
462
463TEST(HeuristicResolver, DependentCall_Member) {
464 std::string Code = R"cpp(
465 template <typename T>
466 struct A {
467 void member(T);
468 };
469 template <typename T>
470 void bar(A<T> a, T t) {
471 a.member(t);
472 }
473 )cpp";
474 // Test resolution of "member" in "a.member(t)".
475 expectResolution(
476 Code, &HeuristicResolver::resolveCalleeOfCallExpr,
477 callExpr(callee(cxxDependentScopeMemberExpr(hasMemberName("member"))))
478 .bind("input"),
479 cxxMethodDecl(hasName("member")).bind("output"));
480}
481
482TEST(HeuristicResolver, DependentCall_StaticMember) {
483 std::string Code = R"cpp(
484 template <typename T>
485 struct A {
486 static void static_member(T);
487 };
488 template <typename T>
489 void bar(T t) {
490 A<T>::static_member(t);
491 }
492 )cpp";
493 // Test resolution of "static_member" in "A<T>::static_member(t)".
494 expectResolution(Code, &HeuristicResolver::resolveCalleeOfCallExpr,
495 callExpr(callee(dependentScopeDeclRefExpr(
496 hasDependentName("static_member"))))
497 .bind("input"),
498 cxxMethodDecl(hasName("static_member")).bind("output"));
499}
500
501TEST(HeuristicResolver, DependentCall_Overload) {
502 std::string Code = R"cpp(
503 void overload(int);
504 void overload(double);
505 template <typename T>
506 void bar(T t) {
507 overload(t);
508 }
509 )cpp";
510 // Test resolution of "overload" in "overload(t)". Both overload should be
511 // found.
512 expectResolution(Code, &HeuristicResolver::resolveCalleeOfCallExpr,
513 callExpr(callee(unresolvedLookupExpr(hasAnyDeclaration(
514 functionDecl(hasName("overload"))))))
515 .bind("input"),
516 functionDecl(hasName("overload"),
517 hasParameter(0, hasType(asString("double"))))
518 .bind("output"),
519 functionDecl(hasName("overload"),
520 hasParameter(0, hasType(asString("int"))))
521 .bind("output"));
522}
523
524TEST(HeuristicResolver, UsingValueDecl) {
525 std::string Code = R"cpp(
526 template <typename T>
527 struct Base {
528 void waldo();
529 };
530 template <typename T>
531 struct Derived : Base<T> {
532 using Base<T>::waldo;
533 };
534 )cpp";
535 // Test resolution of "waldo" in "Base<T>::waldo".
536 expectResolution(Code, &HeuristicResolver::resolveUsingValueDecl,
537 unresolvedUsingValueDecl(hasName("waldo")).bind("input"),
538 cxxMethodDecl(hasName("waldo")).bind("output"));
539}
540
541} // namespace
542} // namespace clang
std::vector< CodeCompletionResult > Results
MATCHER_P2(hasFlag, Flag, Path, "")
std::vector< std::string > match(const SymbolIndex &I, const FuzzyFindRequest &Req, bool *Incomplete)
Definition: TestIndex.cpp:139
TEST(BackgroundQueueTest, Priority)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//