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 "gtest/gtest.h" 18 #include "gmock/gmock.h" 19 20 namespace { 21 22 using namespace clang; 23 using namespace clang::tooling; 24 using ::testing::UnorderedElementsAre; 25 26 const char TestCCName[] = "test.cc"; 27 using VisitedContextResults = std::vector<std::string>; 28 29 class VisitedContextFinder: public CodeCompleteConsumer { 30 public: 31 VisitedContextFinder(VisitedContextResults &Results) 32 : CodeCompleteConsumer(/*CodeCompleteOpts=*/{}, 33 /*CodeCompleteConsumer*/ false), 34 VCResults(Results), 35 CCTUInfo(std::make_shared<GlobalCodeCompletionAllocator>()) {} 36 37 void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context, 38 CodeCompletionResult *Results, 39 unsigned NumResults) override { 40 VisitedContexts = Context.getVisitedContexts(); 41 VCResults = getVisitedNamespace(); 42 } 43 44 CodeCompletionAllocator &getAllocator() override { 45 return CCTUInfo.getAllocator(); 46 } 47 48 CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; } 49 50 std::vector<std::string> getVisitedNamespace() const { 51 std::vector<std::string> NSNames; 52 for (const auto *Context : VisitedContexts) 53 if (const auto *NS = llvm::dyn_cast<NamespaceDecl>(Context)) 54 NSNames.push_back(NS->getQualifiedNameAsString()); 55 return NSNames; 56 } 57 58 private: 59 VisitedContextResults& VCResults; 60 CodeCompletionTUInfo CCTUInfo; 61 CodeCompletionContext::VisitedContextSet VisitedContexts; 62 }; 63 64 class CodeCompleteAction : public SyntaxOnlyAction { 65 public: 66 CodeCompleteAction(ParsedSourceLocation P, VisitedContextResults &Results) 67 : CompletePosition(std::move(P)), VCResults(Results) {} 68 69 bool BeginInvocation(CompilerInstance &CI) override { 70 CI.getFrontendOpts().CodeCompletionAt = CompletePosition; 71 CI.setCodeCompletionConsumer(new VisitedContextFinder(VCResults)); 72 return true; 73 } 74 75 private: 76 // 1-based code complete position <Line, Col>; 77 ParsedSourceLocation CompletePosition; 78 VisitedContextResults& VCResults; 79 }; 80 81 ParsedSourceLocation offsetToPosition(llvm::StringRef Code, size_t Offset) { 82 Offset = std::min(Code.size(), Offset); 83 StringRef Before = Code.substr(0, Offset); 84 int Lines = Before.count('\n'); 85 size_t PrevNL = Before.rfind('\n'); 86 size_t StartOfLine = (PrevNL == StringRef::npos) ? 0 : (PrevNL + 1); 87 return {TestCCName, static_cast<unsigned>(Lines + 1), 88 static_cast<unsigned>(Offset - StartOfLine + 1)}; 89 } 90 91 VisitedContextResults runCodeCompleteOnCode(StringRef Code) { 92 VisitedContextResults Results; 93 auto TokenOffset = Code.find('^'); 94 assert(TokenOffset != StringRef::npos && 95 "Completion token ^ wasn't found in Code."); 96 std::string WithoutToken = Code.take_front(TokenOffset); 97 WithoutToken += Code.drop_front(WithoutToken.size() + 1); 98 assert(StringRef(WithoutToken).find('^') == StringRef::npos && 99 "expected exactly one completion token ^ inside the code"); 100 101 auto Action = llvm::make_unique<CodeCompleteAction>( 102 offsetToPosition(WithoutToken, TokenOffset), Results); 103 clang::tooling::runToolOnCodeWithArgs(Action.release(), Code, {"-std=c++11"}, 104 TestCCName); 105 return Results; 106 } 107 108 TEST(SemaCodeCompleteTest, VisitedNSForValidQualifiedId) { 109 auto VisitedNS = runCodeCompleteOnCode(R"cpp( 110 namespace ns1 {} 111 namespace ns2 {} 112 namespace ns3 {} 113 namespace ns3 { namespace nns3 {} } 114 115 namespace foo { 116 using namespace ns1; 117 namespace ns4 {} // not visited 118 namespace { using namespace ns2; } 119 inline namespace bar { using namespace ns3::nns3; } 120 } // foo 121 namespace ns { foo::^ } 122 )cpp"); 123 EXPECT_THAT(VisitedNS, UnorderedElementsAre("foo", "ns1", "ns2", "ns3::nns3", 124 "foo::(anonymous)")); 125 } 126 127 TEST(SemaCodeCompleteTest, VisitedNSForInvalideQualifiedId) { 128 auto VisitedNS = runCodeCompleteOnCode(R"cpp( 129 namespace ns { foo::^ } 130 )cpp"); 131 EXPECT_TRUE(VisitedNS.empty()); 132 } 133 134 } // namespace 135