1 //=== unittests/Sema/CodeCompleteTest.cpp - Code Complete tests ==============//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "clang/Frontend/CompilerInstance.h"
11 #include "clang/Frontend/FrontendActions.h"
12 #include "clang/Lex/Preprocessor.h"
13 #include "clang/Parse/ParseAST.h"
14 #include "clang/Sema/Sema.h"
15 #include "clang/Sema/SemaDiagnostic.h"
16 #include "clang/Tooling/Tooling.h"
17 #include "gtest/gtest.h"
18 #include "gmock/gmock.h"
19 
20 namespace {
21 
22 using namespace clang;
23 using namespace clang::tooling;
24 using ::testing::UnorderedElementsAre;
25 
26 const char TestCCName[] = "test.cc";
27 using VisitedContextResults = std::vector<std::string>;
28 
29 class VisitedContextFinder: public CodeCompleteConsumer {
30 public:
31   VisitedContextFinder(VisitedContextResults &Results)
32       : CodeCompleteConsumer(/*CodeCompleteOpts=*/{},
33                              /*CodeCompleteConsumer*/ false),
34         VCResults(Results),
35         CCTUInfo(std::make_shared<GlobalCodeCompletionAllocator>()) {}
36 
37   void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
38                                   CodeCompletionResult *Results,
39                                   unsigned NumResults) override {
40     VisitedContexts = Context.getVisitedContexts();
41     VCResults = getVisitedNamespace();
42   }
43 
44   CodeCompletionAllocator &getAllocator() override {
45     return CCTUInfo.getAllocator();
46   }
47 
48   CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
49 
50   std::vector<std::string> getVisitedNamespace() const {
51     std::vector<std::string> NSNames;
52     for (const auto *Context : VisitedContexts)
53       if (const auto *NS = llvm::dyn_cast<NamespaceDecl>(Context))
54         NSNames.push_back(NS->getQualifiedNameAsString());
55     return NSNames;
56   }
57 
58 private:
59   VisitedContextResults& VCResults;
60   CodeCompletionTUInfo CCTUInfo;
61   CodeCompletionContext::VisitedContextSet VisitedContexts;
62 };
63 
64 class CodeCompleteAction : public SyntaxOnlyAction {
65 public:
66   CodeCompleteAction(ParsedSourceLocation P, VisitedContextResults &Results)
67       : CompletePosition(std::move(P)), VCResults(Results) {}
68 
69   bool BeginInvocation(CompilerInstance &CI) override {
70     CI.getFrontendOpts().CodeCompletionAt = CompletePosition;
71     CI.setCodeCompletionConsumer(new VisitedContextFinder(VCResults));
72     return true;
73   }
74 
75 private:
76   // 1-based code complete position <Line, Col>;
77   ParsedSourceLocation CompletePosition;
78   VisitedContextResults& VCResults;
79 };
80 
81 ParsedSourceLocation offsetToPosition(llvm::StringRef Code, size_t Offset) {
82   Offset = std::min(Code.size(), Offset);
83   StringRef Before = Code.substr(0, Offset);
84   int Lines = Before.count('\n');
85   size_t PrevNL = Before.rfind('\n');
86   size_t StartOfLine = (PrevNL == StringRef::npos) ? 0 : (PrevNL + 1);
87   return {TestCCName, static_cast<unsigned>(Lines + 1),
88           static_cast<unsigned>(Offset - StartOfLine + 1)};
89 }
90 
91 VisitedContextResults runCodeCompleteOnCode(StringRef Code) {
92   VisitedContextResults Results;
93   auto TokenOffset = Code.find('^');
94   assert(TokenOffset != StringRef::npos &&
95          "Completion token ^ wasn't found in Code.");
96   std::string WithoutToken = Code.take_front(TokenOffset);
97   WithoutToken += Code.drop_front(WithoutToken.size() + 1);
98   assert(StringRef(WithoutToken).find('^') == StringRef::npos &&
99          "expected exactly one completion token ^ inside the code");
100 
101   auto Action = llvm::make_unique<CodeCompleteAction>(
102       offsetToPosition(WithoutToken, TokenOffset), Results);
103   clang::tooling::runToolOnCodeWithArgs(Action.release(), Code, {"-std=c++11"},
104                                         TestCCName);
105   return Results;
106 }
107 
108 TEST(SemaCodeCompleteTest, VisitedNSForValidQualifiedId) {
109   auto VisitedNS = runCodeCompleteOnCode(R"cpp(
110      namespace ns1 {}
111      namespace ns2 {}
112      namespace ns3 {}
113      namespace ns3 { namespace nns3 {} }
114 
115      namespace foo {
116      using namespace ns1;
117      namespace ns4 {} // not visited
118      namespace { using namespace ns2; }
119      inline namespace bar { using namespace ns3::nns3; }
120      } // foo
121      namespace ns { foo::^ }
122   )cpp");
123   EXPECT_THAT(VisitedNS, UnorderedElementsAre("foo", "ns1", "ns2", "ns3::nns3",
124                                               "foo::(anonymous)"));
125 }
126 
127 TEST(SemaCodeCompleteTest, VisitedNSForInvalideQualifiedId) {
128   auto VisitedNS = runCodeCompleteOnCode(R"cpp(
129      namespace ns { foo::^ }
130   )cpp");
131   EXPECT_TRUE(VisitedNS.empty());
132 }
133 
134 } // namespace
135