blob: 04cb14b971ec744b67f0dbcb7a5e390819395631 [file] [log] [blame]
//=== 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