|
@@ -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
|