diff options
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 000000000000..04cb14b971ec --- /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 | 
