636 lines · cpp
1//=== unittests/Sema/CodeCompleteTest.cpp - Code Complete tests ==============//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/Frontend/CompilerInstance.h"10#include "clang/Frontend/FrontendActions.h"11#include "clang/Lex/Preprocessor.h"12#include "clang/Parse/ParseAST.h"13#include "clang/Sema/Sema.h"14#include "clang/Sema/SemaDiagnostic.h"15#include "clang/Tooling/Tooling.h"16#include "llvm/Testing/Annotations/Annotations.h"17#include "gmock/gmock.h"18#include "gtest/gtest.h"19#include <cstddef>20#include <string>21 22namespace {23 24using namespace clang;25using namespace clang::tooling;26using ::testing::AllOf;27using ::testing::Contains;28using ::testing::Each;29using ::testing::UnorderedElementsAre;30 31const char TestCCName[] = "test.cc";32 33struct CompletionContext {34 std::vector<std::string> VisitedNamespaces;35 std::string PreferredType;36 // String representation of std::ptrdiff_t on a given platform. This is a hack37 // to properly account for different configurations of clang.38 std::string PtrDiffType;39};40 41struct CompletedFunctionDecl {42 std::string Name;43 bool IsStatic;44 bool CanBeCall;45};46MATCHER_P(named, name, "") { return arg.Name == name; }47MATCHER_P(isStatic, value, "") { return arg.IsStatic == value; }48MATCHER_P(canBeCall, value, "") { return arg.CanBeCall == value; }49 50class SaveCompletedFunctions : public CodeCompleteConsumer {51public:52 SaveCompletedFunctions(std::vector<CompletedFunctionDecl> &CompletedFuncDecls)53 : CodeCompleteConsumer(/*CodeCompleteOpts=*/{}),54 CompletedFuncDecls(CompletedFuncDecls),55 CCTUInfo(std::make_shared<GlobalCodeCompletionAllocator>()) {}56 57 void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,58 CodeCompletionResult *Results,59 unsigned NumResults) override {60 for (unsigned I = 0; I < NumResults; ++I) {61 auto R = Results[I];62 if (R.Kind == CodeCompletionResult::RK_Declaration) {63 auto *ND = R.getDeclaration();64 if (auto *Template = llvm::dyn_cast<FunctionTemplateDecl>(ND))65 ND = Template->getTemplatedDecl();66 if (const auto *FD = llvm::dyn_cast<FunctionDecl>(ND)) {67 CompletedFunctionDecl D;68 D.Name = FD->getNameAsString();69 D.CanBeCall = R.FunctionCanBeCall;70 D.IsStatic = FD->isStatic();71 CompletedFuncDecls.emplace_back(std::move(D));72 }73 }74 }75 }76 77private:78 CodeCompletionAllocator &getAllocator() override {79 return CCTUInfo.getAllocator();80 }81 82 CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }83 84 std::vector<CompletedFunctionDecl> &CompletedFuncDecls;85 86 CodeCompletionTUInfo CCTUInfo;87};88 89class VisitedContextFinder : public CodeCompleteConsumer {90public:91 VisitedContextFinder(CompletionContext &ResultCtx)92 : CodeCompleteConsumer(/*CodeCompleteOpts=*/{}), ResultCtx(ResultCtx),93 CCTUInfo(std::make_shared<GlobalCodeCompletionAllocator>()) {}94 95 void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,96 CodeCompletionResult *Results,97 unsigned NumResults) override {98 ResultCtx.VisitedNamespaces =99 getVisitedNamespace(Context.getVisitedContexts());100 ResultCtx.PreferredType = Context.getPreferredType().getAsString();101 ResultCtx.PtrDiffType =102 S.getASTContext().getPointerDiffType().getAsString();103 }104 105 CodeCompletionAllocator &getAllocator() override {106 return CCTUInfo.getAllocator();107 }108 109 CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }110 111private:112 std::vector<std::string> getVisitedNamespace(113 CodeCompletionContext::VisitedContextSet VisitedContexts) const {114 std::vector<std::string> NSNames;115 for (const auto *Context : VisitedContexts)116 if (const auto *NS = llvm::dyn_cast<NamespaceDecl>(Context))117 NSNames.push_back(NS->getQualifiedNameAsString());118 return NSNames;119 }120 121 CompletionContext &ResultCtx;122 CodeCompletionTUInfo CCTUInfo;123};124 125class CodeCompleteAction : public SyntaxOnlyAction {126public:127 CodeCompleteAction(ParsedSourceLocation P, CodeCompleteConsumer *Consumer)128 : CompletePosition(std::move(P)), Consumer(Consumer) {}129 130 bool BeginInvocation(CompilerInstance &CI) override {131 CI.getFrontendOpts().CodeCompletionAt = CompletePosition;132 CI.setCodeCompletionConsumer(Consumer);133 return true;134 }135 136private:137 // 1-based code complete position <Line, Col>;138 ParsedSourceLocation CompletePosition;139 CodeCompleteConsumer *Consumer;140};141 142ParsedSourceLocation offsetToPosition(llvm::StringRef Code, size_t Offset) {143 Offset = std::min(Code.size(), Offset);144 StringRef Before = Code.substr(0, Offset);145 int Lines = Before.count('\n');146 size_t PrevNL = Before.rfind('\n');147 size_t StartOfLine = (PrevNL == StringRef::npos) ? 0 : (PrevNL + 1);148 return {TestCCName, static_cast<unsigned>(Lines + 1),149 static_cast<unsigned>(Offset - StartOfLine + 1)};150}151 152CompletionContext runCompletion(StringRef Code, size_t Offset) {153 CompletionContext ResultCtx;154 clang::tooling::runToolOnCodeWithArgs(155 std::make_unique<CodeCompleteAction>(offsetToPosition(Code, Offset),156 new VisitedContextFinder(ResultCtx)),157 Code, {"-std=c++11"}, TestCCName);158 return ResultCtx;159}160 161CompletionContext runCodeCompleteOnCode(StringRef AnnotatedCode) {162 llvm::Annotations A(AnnotatedCode);163 return runCompletion(A.code(), A.point());164}165 166std::vector<std::string>167collectPreferredTypes(StringRef AnnotatedCode,168 std::string *PtrDiffType = nullptr) {169 llvm::Annotations A(AnnotatedCode);170 std::vector<std::string> Types;171 for (size_t Point : A.points()) {172 auto Results = runCompletion(A.code(), Point);173 if (PtrDiffType) {174 assert(PtrDiffType->empty() || *PtrDiffType == Results.PtrDiffType);175 *PtrDiffType = Results.PtrDiffType;176 }177 Types.push_back(Results.PreferredType);178 }179 return Types;180}181 182std::vector<CompletedFunctionDecl>183CollectCompletedFunctions(StringRef Code, std::size_t Point) {184 std::vector<CompletedFunctionDecl> Result;185 clang::tooling::runToolOnCodeWithArgs(186 std::make_unique<CodeCompleteAction>(offsetToPosition(Code, Point),187 new SaveCompletedFunctions(Result)),188 Code, {"-std=c++11"}, TestCCName);189 return Result;190}191 192TEST(SemaCodeCompleteTest, FunctionCanBeCall) {193 llvm::Annotations Code(R"cpp(194 struct Foo {195 static int staticMethod();196 int method() const;197 template <typename T, typename U, typename V = int>198 T generic(U, V);199 template <typename T, int U = 3>200 static T staticGeneric();201 Foo() {202 this->$canBeCall^203 $canBeCall^204 Foo::$canBeCall^205 }206 };207 208 struct Derived : Foo {209 using Foo::method;210 using Foo::generic;211 Derived() {212 Foo::$canBeCall^213 }214 };215 216 struct OtherClass {217 OtherClass() {218 Foo f;219 Derived d;220 f.$canBeCall^221 ; // Prevent parsing as 'f.f'222 f.Foo::$canBeCall^223 &Foo::$cannotBeCall^224 ;225 d.Foo::$canBeCall^226 ;227 d.Derived::$canBeCall^228 }229 };230 231 int main() {232 Foo f;233 Derived d;234 f.$canBeCall^235 ; // Prevent parsing as 'f.f'236 f.Foo::$canBeCall^237 &Foo::$cannotBeCall^238 ;239 d.Foo::$canBeCall^240 ;241 d.Derived::$canBeCall^242 }243 )cpp");244 245 for (const auto &P : Code.points("canBeCall")) {246 auto Results = CollectCompletedFunctions(Code.code(), P);247 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),248 canBeCall(true))));249 EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),250 canBeCall(true))));251 }252 253 for (const auto &P : Code.points("cannotBeCall")) {254 auto Results = CollectCompletedFunctions(Code.code(), P);255 EXPECT_THAT(Results, Contains(AllOf(named("method"), isStatic(false),256 canBeCall(false))));257 EXPECT_THAT(Results, Contains(AllOf(named("generic"), isStatic(false),258 canBeCall(false))));259 }260 261 // static method can always be a call262 for (const auto &P : Code.points()) {263 auto Results = CollectCompletedFunctions(Code.code(), P);264 EXPECT_THAT(Results, Contains(AllOf(named("staticMethod"), isStatic(true),265 canBeCall(true))));266 EXPECT_THAT(Results, Contains(AllOf(named("staticGeneric"), isStatic(true),267 canBeCall(true))));268 }269}270 271TEST(SemaCodeCompleteTest, VisitedNSForValidQualifiedId) {272 auto VisitedNS = runCodeCompleteOnCode(R"cpp(273 namespace ns1 {}274 namespace ns2 {}275 namespace ns3 {}276 namespace ns3 { namespace nns3 {} }277 278 namespace foo {279 using namespace ns1;280 namespace ns4 {} // not visited281 namespace { using namespace ns2; }282 inline namespace bar { using namespace ns3::nns3; }283 } // foo284 namespace ns { foo::^ }285 )cpp")286 .VisitedNamespaces;287 EXPECT_THAT(VisitedNS, UnorderedElementsAre("foo", "ns1", "ns2", "ns3::nns3",288 "foo::(anonymous)"));289}290 291TEST(SemaCodeCompleteTest, VisitedNSForInvalidQualifiedId) {292 auto VisitedNS = runCodeCompleteOnCode(R"cpp(293 namespace na {}294 namespace ns1 {295 using namespace na;296 foo::^297 }298 )cpp")299 .VisitedNamespaces;300 EXPECT_THAT(VisitedNS, UnorderedElementsAre("ns1", "na"));301}302 303TEST(SemaCodeCompleteTest, VisitedNSWithoutQualifier) {304 auto VisitedNS = runCodeCompleteOnCode(R"cpp(305 namespace n1 {306 namespace n2 {307 void f(^) {}308 }309 }310 )cpp")311 .VisitedNamespaces;312 EXPECT_THAT(VisitedNS, UnorderedElementsAre("n1", "n1::n2"));313}314 315TEST(PreferredTypeTest, BinaryExpr) {316 // Check various operations for arithmetic types.317 StringRef Code = R"cpp(318 void test(int x) {319 x = ^10;320 x += ^10; x -= ^10; x *= ^10; x /= ^10; x %= ^10;321 x + ^10; x - ^10; x * ^10; x / ^10; x % ^10;322 })cpp";323 EXPECT_THAT(collectPreferredTypes(Code), Each("int"));324 325 Code = R"cpp(326 void test(float x) {327 x = ^10;328 x += ^10; x -= ^10; x *= ^10; x /= ^10; x %= ^10;329 x + ^10; x - ^10; x * ^10; x / ^10; x % ^10;330 })cpp";331 EXPECT_THAT(collectPreferredTypes(Code), Each("float"));332 333 // Pointer types.334 Code = R"cpp(335 void test(int *ptr) {336 ptr - ^ptr;337 ptr = ^ptr;338 })cpp";339 EXPECT_THAT(collectPreferredTypes(Code), Each("int *"));340 341 Code = R"cpp(342 void test(int *ptr) {343 ptr + ^10;344 ptr += ^10;345 ptr -= ^10;346 })cpp";347 {348 std::string PtrDiff;349 auto Types = collectPreferredTypes(Code, &PtrDiff);350 EXPECT_THAT(Types, Each(PtrDiff));351 }352 353 // Comparison operators.354 Code = R"cpp(355 void test(int i) {356 i <= ^1; i < ^1; i >= ^1; i > ^1; i == ^1; i != ^1;357 }358 )cpp";359 EXPECT_THAT(collectPreferredTypes(Code), Each("int"));360 361 Code = R"cpp(362 void test(int *ptr) {363 ptr <= ^ptr; ptr < ^ptr; ptr >= ^ptr; ptr > ^ptr;364 ptr == ^ptr; ptr != ^ptr;365 }366 )cpp";367 EXPECT_THAT(collectPreferredTypes(Code), Each("int *"));368 369 // Relational operations.370 Code = R"cpp(371 void test(int i, int *ptr) {372 i && ^1; i || ^1;373 ptr && ^1; ptr || ^1;374 }375 )cpp";376 EXPECT_THAT(collectPreferredTypes(Code), Each("_Bool"));377 378 // Bitwise operations.379 Code = R"cpp(380 void test(long long ll) {381 ll | ^1; ll & ^1;382 }383 )cpp";384 EXPECT_THAT(collectPreferredTypes(Code), Each("long long"));385 386 Code = R"cpp(387 enum A {};388 void test(A a) {389 a | ^1; a & ^1;390 }391 )cpp";392 EXPECT_THAT(collectPreferredTypes(Code), Each("A"));393 394 Code = R"cpp(395 enum class A {};396 void test(A a) {397 // This is technically illegal with the 'enum class' without overloaded398 // operators, but we pretend it's fine.399 a | ^a; a & ^a;400 }401 )cpp";402 EXPECT_THAT(collectPreferredTypes(Code), Each("A"));403 404 // Binary shifts.405 Code = R"cpp(406 void test(int i, long long ll) {407 i << ^1; ll << ^1;408 i <<= ^1; i <<= ^1;409 i >> ^1; ll >> ^1;410 i >>= ^1; i >>= ^1;411 }412 )cpp";413 EXPECT_THAT(collectPreferredTypes(Code), Each("int"));414 415 // Comma does not provide any useful information.416 Code = R"cpp(417 class Cls {};418 void test(int i, int* ptr, Cls x) {419 (i, ^i);420 (ptr, ^ptr);421 (x, ^x);422 }423 )cpp";424 EXPECT_THAT(collectPreferredTypes(Code), Each("NULL TYPE"));425 426 // User-defined types do not take operator overloading into account.427 // However, they provide heuristics for some common cases.428 Code = R"cpp(429 class Cls {};430 void test(Cls c) {431 // we assume arithmetic and comparions ops take the same type.432 c + ^c; c - ^c; c * ^c; c / ^c; c % ^c;433 c == ^c; c != ^c; c < ^c; c <= ^c; c > ^c; c >= ^c;434 // same for the assignments.435 c = ^c; c += ^c; c -= ^c; c *= ^c; c /= ^c; c %= ^c;436 }437 )cpp";438 EXPECT_THAT(collectPreferredTypes(Code), Each("Cls"));439 440 Code = R"cpp(441 class Cls {};442 void test(Cls c) {443 // we assume relational ops operate on bools.444 c && ^c; c || ^c;445 }446 )cpp";447 EXPECT_THAT(collectPreferredTypes(Code), Each("_Bool"));448 449 Code = R"cpp(450 class Cls {};451 void test(Cls c) {452 // we make no assumptions about the following operators, since they are453 // often overloaded with a non-standard meaning.454 c << ^c; c >> ^c; c | ^c; c & ^c;455 c <<= ^c; c >>= ^c; c |= ^c; c &= ^c;456 }457 )cpp";458 EXPECT_THAT(collectPreferredTypes(Code), Each("NULL TYPE"));459}460 461TEST(PreferredTypeTest, Members) {462 StringRef Code = R"cpp(463 struct vector {464 int *begin();465 vector clone();466 };467 468 void test(int *a) {469 a = ^vector().^clone().^begin();470 }471 )cpp";472 EXPECT_THAT(collectPreferredTypes(Code), Each("int *"));473}474 475TEST(PreferredTypeTest, Conditions) {476 StringRef Code = R"cpp(477 struct vector {478 bool empty();479 };480 481 void test() {482 if (^vector().^empty()) {}483 while (^vector().^empty()) {}484 for (; ^vector().^empty();) {}485 }486 )cpp";487 EXPECT_THAT(collectPreferredTypes(Code), Each("_Bool"));488}489 490TEST(PreferredTypeTest, InitAndAssignment) {491 StringRef Code = R"cpp(492 struct vector {493 int* begin();494 };495 496 void test() {497 const int* x = ^vector().^begin();498 x = ^vector().^begin();499 500 if (const int* y = ^vector().^begin()) {}501 }502 )cpp";503 EXPECT_THAT(collectPreferredTypes(Code), Each("const int *"));504}505 506TEST(PreferredTypeTest, UnaryExprs) {507 StringRef Code = R"cpp(508 void test(long long a) {509 a = +^a;510 a = -^a511 a = ++^a;512 a = --^a;513 }514 )cpp";515 EXPECT_THAT(collectPreferredTypes(Code), Each("long long"));516 517 Code = R"cpp(518 void test(int a, int *ptr) {519 !^a;520 !^ptr;521 !!!^a;522 523 a = !^a;524 a = !^ptr;525 a = !!!^a;526 }527 )cpp";528 EXPECT_THAT(collectPreferredTypes(Code), Each("_Bool"));529 530 Code = R"cpp(531 void test(int a) {532 const int* x = &^a;533 }534 )cpp";535 EXPECT_THAT(collectPreferredTypes(Code), Each("const int"));536 537 Code = R"cpp(538 void test(int *a) {539 int x = *^a;540 int &r = *^a;541 }542 )cpp";543 EXPECT_THAT(collectPreferredTypes(Code), Each("int *"));544 545 Code = R"cpp(546 void test(int a) {547 *^a;548 &^a;549 }550 551 )cpp";552}553 554TEST(PreferredTypeTest, ParenExpr) {555 StringRef Code = R"cpp(556 const int *i = ^(^(^(^10)));557 )cpp";558 EXPECT_THAT(collectPreferredTypes(Code), Each("const int *"));559}560 561TEST(PreferredTypeTest, FunctionArguments) {562 StringRef Code = R"cpp(563 void foo(const int*);564 565 void bar(const int*);566 void bar(const int*, int b);567 568 struct vector {569 const int *data();570 };571 void test() {572 foo(^(^(^(^vec^tor^().^da^ta^()))));573 bar(^(^(^(^vec^tor^().^da^ta^()))));574 }575 )cpp";576 EXPECT_THAT(collectPreferredTypes(Code), Each("const int *"));577 578 Code = R"cpp(579 void bar(int, volatile double *);580 void bar(int, volatile double *, int, int);581 582 struct vector {583 double *data();584 };585 586 struct class_members {587 void bar(int, volatile double *);588 void bar(int, volatile double *, int, int);589 };590 void test() {591 bar(10, ^(^(^(^vec^tor^().^da^ta^()))));592 class_members().bar(10, ^(^(^(^vec^tor^().^da^ta^()))));593 }594 )cpp";595 EXPECT_THAT(collectPreferredTypes(Code), Each("volatile double *"));596 597 Code = R"cpp(598 namespace ns {599 struct vector {600 };601 }602 void accepts_vector(ns::vector);603 604 void test() {605 accepts_vector(^::^ns::^vector());606 }607 )cpp";608 EXPECT_THAT(collectPreferredTypes(Code), Each("ns::vector"));609 610 Code = R"cpp(611 template <class T>612 struct vector { using self = vector; };613 614 void accepts_vector(vector<int>);615 int foo(int);616 617 void test() {618 accepts_vector(^::^vector<decltype(foo(1))>::^self);619 }620 )cpp";621 EXPECT_THAT(collectPreferredTypes(Code), Each("vector<int>"));622}623 624TEST(PreferredTypeTest, NoCrashOnInvalidTypes) {625 StringRef Code = R"cpp(626 auto x = decltype(&1)(^);627 auto y = new decltype(&1)(^);628 // GNU decimal type extension is not supported in clang.629 auto z = new _Decimal128(^);630 void foo() { (void)(foo)(^); }631 )cpp";632 EXPECT_THAT(collectPreferredTypes(Code), Each("NULL TYPE"));633}634 635} // namespace636