diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 11:06:01 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 11:06:01 +0000 |
commit | 486754660bb926339aefcf012a3f848592babb8b (patch) | |
tree | ecdbc446c9876f4f120f701c243373cd3cb43db3 /unittests/Sema/CodeCompleteTest.cpp | |
parent | 55e6d896ad333f07bb3b1ba487df214fc268a4ab (diff) |
Notes
Diffstat (limited to 'unittests/Sema/CodeCompleteTest.cpp')
-rw-r--r-- | unittests/Sema/CodeCompleteTest.cpp | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/unittests/Sema/CodeCompleteTest.cpp b/unittests/Sema/CodeCompleteTest.cpp new file mode 100644 index 0000000000000..04cb14b971ec7 --- /dev/null +++ b/unittests/Sema/CodeCompleteTest.cpp @@ -0,0 +1,145 @@ +//=== 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<std::string>; + +class VisitedContextFinder: public CodeCompleteConsumer { +public: + VisitedContextFinder(VisitedContextResults &Results) + : CodeCompleteConsumer(/*CodeCompleteOpts=*/{}, + /*CodeCompleteConsumer*/ false), + VCResults(Results), + CCTUInfo(std::make_shared<GlobalCodeCompletionAllocator>()) {} + + 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<std::string> getVisitedNamespace() const { + std::vector<std::string> NSNames; + for (const auto *Context : VisitedContexts) + if (const auto *NS = llvm::dyn_cast<NamespaceDecl>(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 <Line, Col>; + 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<unsigned>(Lines + 1), + static_cast<unsigned>(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<CodeCompleteAction>( + 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 |