//=== unittests/Sema/CodeCompleteTest.cpp - Code Complete tests ==============// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Lex/Preprocessor.h" #include "clang/Parse/ParseAST.h" #include "clang/Sema/Sema.h" #include "clang/Sema/SemaDiagnostic.h" #include "clang/Tooling/Tooling.h" #include "gtest/gtest.h" #include "gmock/gmock.h" namespace { using namespace clang; using namespace clang::tooling; using ::testing::UnorderedElementsAre; const char TestCCName[] = "test.cc"; using VisitedContextResults = std::vector; class VisitedContextFinder: public CodeCompleteConsumer { public: VisitedContextFinder(VisitedContextResults &Results) : CodeCompleteConsumer(/*CodeCompleteOpts=*/{}, /*CodeCompleteConsumer*/ false), VCResults(Results), CCTUInfo(std::make_shared()) {} void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context, CodeCompletionResult *Results, unsigned NumResults) override { VisitedContexts = Context.getVisitedContexts(); VCResults = getVisitedNamespace(); } CodeCompletionAllocator &getAllocator() override { return CCTUInfo.getAllocator(); } CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; } std::vector getVisitedNamespace() const { std::vector NSNames; for (const auto *Context : VisitedContexts) if (const auto *NS = llvm::dyn_cast(Context)) NSNames.push_back(NS->getQualifiedNameAsString()); return NSNames; } private: VisitedContextResults& VCResults; CodeCompletionTUInfo CCTUInfo; CodeCompletionContext::VisitedContextSet VisitedContexts; }; class CodeCompleteAction : public SyntaxOnlyAction { public: CodeCompleteAction(ParsedSourceLocation P, VisitedContextResults &Results) : CompletePosition(std::move(P)), VCResults(Results) {} bool BeginInvocation(CompilerInstance &CI) override { CI.getFrontendOpts().CodeCompletionAt = CompletePosition; CI.setCodeCompletionConsumer(new VisitedContextFinder(VCResults)); return true; } private: // 1-based code complete position ; ParsedSourceLocation CompletePosition; VisitedContextResults& VCResults; }; ParsedSourceLocation offsetToPosition(llvm::StringRef Code, size_t Offset) { Offset = std::min(Code.size(), Offset); StringRef Before = Code.substr(0, Offset); int Lines = Before.count('\n'); size_t PrevNL = Before.rfind('\n'); size_t StartOfLine = (PrevNL == StringRef::npos) ? 0 : (PrevNL + 1); return {TestCCName, static_cast(Lines + 1), static_cast(Offset - StartOfLine + 1)}; } VisitedContextResults runCodeCompleteOnCode(StringRef Code) { VisitedContextResults Results; auto TokenOffset = Code.find('^'); assert(TokenOffset != StringRef::npos && "Completion token ^ wasn't found in Code."); std::string WithoutToken = Code.take_front(TokenOffset); WithoutToken += Code.drop_front(WithoutToken.size() + 1); assert(StringRef(WithoutToken).find('^') == StringRef::npos && "expected exactly one completion token ^ inside the code"); auto Action = llvm::make_unique( offsetToPosition(WithoutToken, TokenOffset), Results); clang::tooling::runToolOnCodeWithArgs(Action.release(), Code, {"-std=c++11"}, TestCCName); return Results; } TEST(SemaCodeCompleteTest, VisitedNSForValidQualifiedId) { auto VisitedNS = runCodeCompleteOnCode(R"cpp( namespace ns1 {} namespace ns2 {} namespace ns3 {} namespace ns3 { namespace nns3 {} } namespace foo { using namespace ns1; namespace ns4 {} // not visited namespace { using namespace ns2; } inline namespace bar { using namespace ns3::nns3; } } // foo namespace ns { foo::^ } )cpp"); EXPECT_THAT(VisitedNS, UnorderedElementsAre("foo", "ns1", "ns2", "ns3::nns3", "foo::(anonymous)")); } TEST(SemaCodeCompleteTest, VisitedNSForInvalideQualifiedId) { auto VisitedNS = runCodeCompleteOnCode(R"cpp( namespace ns { foo::^ } )cpp"); EXPECT_TRUE(VisitedNS.empty()); } TEST(SemaCodeCompleteTest, VisitedNSWithoutQualifier) { auto VisitedNS = runCodeCompleteOnCode(R"cpp( namespace n1 { namespace n2 { void f(^) {} } } )cpp"); EXPECT_THAT(VisitedNS, UnorderedElementsAre("n1", "n1::n2")); } } // namespace