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-exception 6 // 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 "gmock/gmock.h" 17 #include "gtest/gtest.h" 18 #include <cstddef> 19 #include <string> 20 21 namespace { 22 23 using namespace clang; 24 using namespace clang::tooling; 25 using ::testing::Each; 26 using ::testing::UnorderedElementsAre; 27 28 const char TestCCName[] = "test.cc"; 29 30 struct CompletionContext { 31 std::vector<std::string> VisitedNamespaces; 32 std::string PreferredType; 33 // String representation of std::ptrdiff_t on a given platform. This is a hack 34 // to properly account for different configurations of clang. 35 std::string PtrDiffType; 36 }; 37 38 class VisitedContextFinder : public CodeCompleteConsumer { 39 public: 40 VisitedContextFinder(CompletionContext &ResultCtx) 41 : CodeCompleteConsumer(/*CodeCompleteOpts=*/{}, 42 /*CodeCompleteConsumer*/ false), 43 ResultCtx(ResultCtx), 44 CCTUInfo(std::make_shared<GlobalCodeCompletionAllocator>()) {} 45 46 void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context, 47 CodeCompletionResult *Results, 48 unsigned NumResults) override { 49 ResultCtx.VisitedNamespaces = 50 getVisitedNamespace(Context.getVisitedContexts()); 51 ResultCtx.PreferredType = Context.getPreferredType().getAsString(); 52 ResultCtx.PtrDiffType = 53 S.getASTContext().getPointerDiffType().getAsString(); 54 } 55 56 CodeCompletionAllocator &getAllocator() override { 57 return CCTUInfo.getAllocator(); 58 } 59 60 CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; } 61 62 private: 63 std::vector<std::string> getVisitedNamespace( 64 CodeCompletionContext::VisitedContextSet VisitedContexts) const { 65 std::vector<std::string> NSNames; 66 for (const auto *Context : VisitedContexts) 67 if (const auto *NS = llvm::dyn_cast<NamespaceDecl>(Context)) 68 NSNames.push_back(NS->getQualifiedNameAsString()); 69 return NSNames; 70 } 71 72 CompletionContext &ResultCtx; 73 CodeCompletionTUInfo CCTUInfo; 74 }; 75 76 class CodeCompleteAction : public SyntaxOnlyAction { 77 public: 78 CodeCompleteAction(ParsedSourceLocation P, CompletionContext &ResultCtx) 79 : CompletePosition(std::move(P)), ResultCtx(ResultCtx) {} 80 81 bool BeginInvocation(CompilerInstance &CI) override { 82 CI.getFrontendOpts().CodeCompletionAt = CompletePosition; 83 CI.setCodeCompletionConsumer(new VisitedContextFinder(ResultCtx)); 84 return true; 85 } 86 87 private: 88 // 1-based code complete position <Line, Col>; 89 ParsedSourceLocation CompletePosition; 90 CompletionContext &ResultCtx; 91 }; 92 93 ParsedSourceLocation offsetToPosition(llvm::StringRef Code, size_t Offset) { 94 Offset = std::min(Code.size(), Offset); 95 StringRef Before = Code.substr(0, Offset); 96 int Lines = Before.count('\n'); 97 size_t PrevNL = Before.rfind('\n'); 98 size_t StartOfLine = (PrevNL == StringRef::npos) ? 0 : (PrevNL + 1); 99 return {TestCCName, static_cast<unsigned>(Lines + 1), 100 static_cast<unsigned>(Offset - StartOfLine + 1)}; 101 } 102 103 CompletionContext runCompletion(StringRef Code, size_t Offset) { 104 CompletionContext ResultCtx; 105 auto Action = llvm::make_unique<CodeCompleteAction>( 106 offsetToPosition(Code, Offset), ResultCtx); 107 clang::tooling::runToolOnCodeWithArgs(Action.release(), Code, {"-std=c++11"}, 108 TestCCName); 109 return ResultCtx; 110 } 111 112 struct ParsedAnnotations { 113 std::vector<size_t> Points; 114 std::string Code; 115 }; 116 117 ParsedAnnotations parseAnnotations(StringRef AnnotatedCode) { 118 ParsedAnnotations R; 119 while (!AnnotatedCode.empty()) { 120 size_t NextPoint = AnnotatedCode.find('^'); 121 if (NextPoint == StringRef::npos) { 122 R.Code += AnnotatedCode; 123 AnnotatedCode = ""; 124 break; 125 } 126 R.Code += AnnotatedCode.substr(0, NextPoint); 127 R.Points.push_back(R.Code.size()); 128 129 AnnotatedCode = AnnotatedCode.substr(NextPoint + 1); 130 } 131 return R; 132 } 133 134 CompletionContext runCodeCompleteOnCode(StringRef AnnotatedCode) { 135 ParsedAnnotations P = parseAnnotations(AnnotatedCode); 136 assert(P.Points.size() == 1 && "expected exactly one annotation point"); 137 return runCompletion(P.Code, P.Points.front()); 138 } 139 140 std::vector<std::string> 141 collectPreferredTypes(StringRef AnnotatedCode, 142 std::string *PtrDiffType = nullptr) { 143 ParsedAnnotations P = parseAnnotations(AnnotatedCode); 144 std::vector<std::string> Types; 145 for (size_t Point : P.Points) { 146 auto Results = runCompletion(P.Code, Point); 147 if (PtrDiffType) { 148 assert(PtrDiffType->empty() || *PtrDiffType == Results.PtrDiffType); 149 *PtrDiffType = Results.PtrDiffType; 150 } 151 Types.push_back(Results.PreferredType); 152 } 153 return Types; 154 } 155 156 TEST(SemaCodeCompleteTest, VisitedNSForValidQualifiedId) { 157 auto VisitedNS = runCodeCompleteOnCode(R"cpp( 158 namespace ns1 {} 159 namespace ns2 {} 160 namespace ns3 {} 161 namespace ns3 { namespace nns3 {} } 162 163 namespace foo { 164 using namespace ns1; 165 namespace ns4 {} // not visited 166 namespace { using namespace ns2; } 167 inline namespace bar { using namespace ns3::nns3; } 168 } // foo 169 namespace ns { foo::^ } 170 )cpp") 171 .VisitedNamespaces; 172 EXPECT_THAT(VisitedNS, UnorderedElementsAre("foo", "ns1", "ns2", "ns3::nns3", 173 "foo::(anonymous)")); 174 } 175 176 TEST(SemaCodeCompleteTest, VisitedNSForInvalideQualifiedId) { 177 auto VisitedNS = runCodeCompleteOnCode(R"cpp( 178 namespace ns { foo::^ } 179 )cpp") 180 .VisitedNamespaces; 181 EXPECT_TRUE(VisitedNS.empty()); 182 } 183 184 TEST(SemaCodeCompleteTest, VisitedNSWithoutQualifier) { 185 auto VisitedNS = runCodeCompleteOnCode(R"cpp( 186 namespace n1 { 187 namespace n2 { 188 void f(^) {} 189 } 190 } 191 )cpp") 192 .VisitedNamespaces; 193 EXPECT_THAT(VisitedNS, UnorderedElementsAre("n1", "n1::n2")); 194 } 195 196 TEST(PreferredTypeTest, BinaryExpr) { 197 // Check various operations for arithmetic types. 198 StringRef Code = R"cpp( 199 void test(int x) { 200 x = ^10; 201 x += ^10; x -= ^10; x *= ^10; x /= ^10; x %= ^10; 202 x + ^10; x - ^10; x * ^10; x / ^10; x % ^10; 203 })cpp"; 204 EXPECT_THAT(collectPreferredTypes(Code), Each("int")); 205 206 Code = R"cpp( 207 void test(float x) { 208 x = ^10; 209 x += ^10; x -= ^10; x *= ^10; x /= ^10; x %= ^10; 210 x + ^10; x - ^10; x * ^10; x / ^10; x % ^10; 211 })cpp"; 212 EXPECT_THAT(collectPreferredTypes(Code), Each("float")); 213 214 // Pointer types. 215 Code = R"cpp( 216 void test(int *ptr) { 217 ptr - ^ptr; 218 ptr = ^ptr; 219 })cpp"; 220 EXPECT_THAT(collectPreferredTypes(Code), Each("int *")); 221 222 Code = R"cpp( 223 void test(int *ptr) { 224 ptr + ^10; 225 ptr += ^10; 226 ptr -= ^10; 227 })cpp"; 228 { 229 std::string PtrDiff; 230 auto Types = collectPreferredTypes(Code, &PtrDiff); 231 EXPECT_THAT(Types, Each(PtrDiff)); 232 } 233 234 // Comparison operators. 235 Code = R"cpp( 236 void test(int i) { 237 i <= ^1; i < ^1; i >= ^1; i > ^1; i == ^1; i != ^1; 238 } 239 )cpp"; 240 EXPECT_THAT(collectPreferredTypes(Code), Each("int")); 241 242 Code = R"cpp( 243 void test(int *ptr) { 244 ptr <= ^ptr; ptr < ^ptr; ptr >= ^ptr; ptr > ^ptr; 245 ptr == ^ptr; ptr != ^ptr; 246 } 247 )cpp"; 248 EXPECT_THAT(collectPreferredTypes(Code), Each("int *")); 249 250 // Relational operations. 251 Code = R"cpp( 252 void test(int i, int *ptr) { 253 i && ^1; i || ^1; 254 ptr && ^1; ptr || ^1; 255 } 256 )cpp"; 257 EXPECT_THAT(collectPreferredTypes(Code), Each("_Bool")); 258 259 // Bitwise operations. 260 Code = R"cpp( 261 void test(long long ll) { 262 ll | ^1; ll & ^1; 263 } 264 )cpp"; 265 EXPECT_THAT(collectPreferredTypes(Code), Each("long long")); 266 267 Code = R"cpp( 268 enum A {}; 269 void test(A a) { 270 a | ^1; a & ^1; 271 } 272 )cpp"; 273 EXPECT_THAT(collectPreferredTypes(Code), Each("enum A")); 274 275 Code = R"cpp( 276 enum class A {}; 277 void test(A a) { 278 // This is technically illegal with the 'enum class' without overloaded 279 // operators, but we pretend it's fine. 280 a | ^a; a & ^a; 281 } 282 )cpp"; 283 EXPECT_THAT(collectPreferredTypes(Code), Each("enum A")); 284 285 // Binary shifts. 286 Code = R"cpp( 287 void test(int i, long long ll) { 288 i << ^1; ll << ^1; 289 i <<= ^1; i <<= ^1; 290 i >> ^1; ll >> ^1; 291 i >>= ^1; i >>= ^1; 292 } 293 )cpp"; 294 EXPECT_THAT(collectPreferredTypes(Code), Each("int")); 295 296 // Comma does not provide any useful information. 297 Code = R"cpp( 298 class Cls {}; 299 void test(int i, int* ptr, Cls x) { 300 (i, ^i); 301 (ptr, ^ptr); 302 (x, ^x); 303 } 304 )cpp"; 305 EXPECT_THAT(collectPreferredTypes(Code), Each("NULL TYPE")); 306 307 // User-defined types do not take operator overloading into account. 308 // However, they provide heuristics for some common cases. 309 Code = R"cpp( 310 class Cls {}; 311 void test(Cls c) { 312 // we assume arithmetic and comparions ops take the same type. 313 c + ^c; c - ^c; c * ^c; c / ^c; c % ^c; 314 c == ^c; c != ^c; c < ^c; c <= ^c; c > ^c; c >= ^c; 315 // same for the assignments. 316 c = ^c; c += ^c; c -= ^c; c *= ^c; c /= ^c; c %= ^c; 317 } 318 )cpp"; 319 EXPECT_THAT(collectPreferredTypes(Code), Each("class Cls")); 320 321 Code = R"cpp( 322 class Cls {}; 323 void test(Cls c) { 324 // we assume relational ops operate on bools. 325 c && ^c; c || ^c; 326 } 327 )cpp"; 328 EXPECT_THAT(collectPreferredTypes(Code), Each("_Bool")); 329 330 Code = R"cpp( 331 class Cls {}; 332 void test(Cls c) { 333 // we make no assumptions about the following operators, since they are 334 // often overloaded with a non-standard meaning. 335 c << ^c; c >> ^c; c | ^c; c & ^c; 336 c <<= ^c; c >>= ^c; c |= ^c; c &= ^c; 337 } 338 )cpp"; 339 EXPECT_THAT(collectPreferredTypes(Code), Each("NULL TYPE")); 340 } 341 342 TEST(PreferredTypeTest, Members) { 343 StringRef Code = R"cpp( 344 struct vector { 345 int *begin(); 346 vector clone(); 347 }; 348 349 void test(int *a) { 350 a = ^vector().^clone().^begin(); 351 } 352 )cpp"; 353 EXPECT_THAT(collectPreferredTypes(Code), Each("int *")); 354 } 355 356 TEST(PreferredTypeTest, Conditions) { 357 StringRef Code = R"cpp( 358 struct vector { 359 bool empty(); 360 }; 361 362 void test() { 363 if (^vector().^empty()) {} 364 while (^vector().^empty()) {} 365 for (; ^vector().^empty();) {} 366 } 367 )cpp"; 368 EXPECT_THAT(collectPreferredTypes(Code), Each("_Bool")); 369 } 370 371 TEST(PreferredTypeTest, InitAndAssignment) { 372 StringRef Code = R"cpp( 373 struct vector { 374 int* begin(); 375 }; 376 377 void test() { 378 const int* x = ^vector().^begin(); 379 x = ^vector().^begin(); 380 381 if (const int* y = ^vector().^begin()) {} 382 } 383 )cpp"; 384 EXPECT_THAT(collectPreferredTypes(Code), Each("const int *")); 385 } 386 387 TEST(PreferredTypeTest, UnaryExprs) { 388 StringRef Code = R"cpp( 389 void test(long long a) { 390 a = +^a; 391 a = -^a 392 a = ++^a; 393 a = --^a; 394 } 395 )cpp"; 396 EXPECT_THAT(collectPreferredTypes(Code), Each("long long")); 397 398 Code = R"cpp( 399 void test(int a, int *ptr) { 400 !^a; 401 !^ptr; 402 !!!^a; 403 404 a = !^a; 405 a = !^ptr; 406 a = !!!^a; 407 } 408 )cpp"; 409 EXPECT_THAT(collectPreferredTypes(Code), Each("_Bool")); 410 411 Code = R"cpp( 412 void test(int a) { 413 const int* x = &^a; 414 } 415 )cpp"; 416 EXPECT_THAT(collectPreferredTypes(Code), Each("const int")); 417 418 Code = R"cpp( 419 void test(int *a) { 420 int x = *^a; 421 int &r = *^a; 422 } 423 )cpp"; 424 EXPECT_THAT(collectPreferredTypes(Code), Each("int *")); 425 426 Code = R"cpp( 427 void test(int a) { 428 *^a; 429 &^a; 430 } 431 432 )cpp"; 433 } 434 435 TEST(PreferredTypeTest, ParenExpr) { 436 StringRef Code = R"cpp( 437 const int *i = ^(^(^(^10))); 438 )cpp"; 439 EXPECT_THAT(collectPreferredTypes(Code), Each("const int *")); 440 } 441 } // namespace 442