summaryrefslogtreecommitdiffstats
path: root/clang/unittests/Sema/CodeCompleteTest.cpp
blob: 8e888cbe528a1c43f0a88a3fd38d05e26423c86e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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
OpenPOWER on IntegriCloud