diff options
| author | Haojian Wu <hokein@google.com> | 2018-01-17 14:29:25 +0000 |
|---|---|---|
| committer | Haojian Wu <hokein@google.com> | 2018-01-17 14:29:25 +0000 |
| commit | 10d95c53af3d30cd362ab798c51cdaaaac325c89 (patch) | |
| tree | e376e5d85c2edd7578fcd2ab15318f0ee69002b2 /clang/unittests/Sema/CodeCompleteTest.cpp | |
| parent | 178deccb63c30a0919064922fcec597fbe1039cd (diff) | |
| download | bcm5719-llvm-10d95c53af3d30cd362ab798c51cdaaaac325c89.tar.gz bcm5719-llvm-10d95c53af3d30cd362ab798c51cdaaaac325c89.zip | |
[Sema] Add visited contexts to CodeCompleteContext
Summary:
This would allow code completion clients to know which context is visited during Sema code completion.
Also some changes:
* add `EnteredContext` callback in VisibleDeclConsumer.
* add a simple unittest for sema code completion (only for visited contexts at the moment).
Reviewers: ilya-biryukov
Reviewed By: ilya-biryukov
Subscribers: mgorny, bkramer, cfe-commits
Differential Revision: https://reviews.llvm.org/D42071
llvm-svn: 322661
Diffstat (limited to 'clang/unittests/Sema/CodeCompleteTest.cpp')
| -rw-r--r-- | clang/unittests/Sema/CodeCompleteTest.cpp | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/clang/unittests/Sema/CodeCompleteTest.cpp b/clang/unittests/Sema/CodeCompleteTest.cpp new file mode 100644 index 00000000000..8e888cbe528 --- /dev/null +++ b/clang/unittests/Sema/CodeCompleteTest.cpp @@ -0,0 +1,134 @@ +//=== 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()); +} + +} // namespace |

