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