summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/include/clang/Sema/CodeCompleteConsumer.h16
-rw-r--r--clang/include/clang/Sema/Lookup.h6
-rw-r--r--clang/lib/Sema/SemaCodeComplete.cpp11
-rw-r--r--clang/lib/Sema/SemaLookup.cpp2
-rw-r--r--clang/unittests/Sema/CMakeLists.txt1
-rw-r--r--clang/unittests/Sema/CodeCompleteTest.cpp134
6 files changed, 169 insertions, 1 deletions
diff --git a/clang/include/clang/Sema/CodeCompleteConsumer.h b/clang/include/clang/Sema/CodeCompleteConsumer.h
index e8db3ec237c..2fea262a44f 100644
--- a/clang/include/clang/Sema/CodeCompleteConsumer.h
+++ b/clang/include/clang/Sema/CodeCompleteConsumer.h
@@ -268,6 +268,8 @@ public:
CCC_Recovery
};
+ using VisitedContextSet = llvm::SmallPtrSet<DeclContext*, 8>;
+
private:
enum Kind Kind;
@@ -285,6 +287,10 @@ private:
/// "a::b::"
llvm::Optional<CXXScopeSpec> ScopeSpecifier;
+ /// \brief A set of declaration contexts visited by Sema when doing lookup for
+ /// code completion.
+ VisitedContextSet VisitedContexts;
+
public:
/// \brief Construct a new code-completion context of the given kind.
CodeCompletionContext(enum Kind Kind) : Kind(Kind), SelIdents(None) { }
@@ -328,6 +334,16 @@ public:
this->ScopeSpecifier = std::move(SS);
}
+ /// \brief Adds a visited context.
+ void addVisitedContext(DeclContext* Ctx) {
+ VisitedContexts.insert(Ctx);
+ }
+
+ /// \brief Retrieves all visited contexts.
+ const VisitedContextSet &getVisitedContexts() const {
+ return VisitedContexts;
+ }
+
llvm::Optional<const CXXScopeSpec *> getCXXScopeSpecifier() {
if (ScopeSpecifier)
return ScopeSpecifier.getPointer();
diff --git a/clang/include/clang/Sema/Lookup.h b/clang/include/clang/Sema/Lookup.h
index 546df8842a3..0e8dcada742 100644
--- a/clang/include/clang/Sema/Lookup.h
+++ b/clang/include/clang/Sema/Lookup.h
@@ -784,6 +784,12 @@ public:
/// class of the context we searched.
virtual void FoundDecl(NamedDecl *ND, NamedDecl *Hiding, DeclContext *Ctx,
bool InBaseClass) = 0;
+
+ /// \brief Callback to inform the client that Sema entered into a new context
+ /// to find a visible declaration.
+ //
+ /// \param Ctx the context which Sema entered.
+ virtual void EnteredContext(DeclContext *Ctx) {}
};
/// \brief A class for storing results from argument-dependent lookup.
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index f7adaf4dcd0..ac318a65fce 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -318,6 +318,11 @@ namespace {
/// \brief Ignore this declaration, if it is seen again.
void Ignore(const Decl *D) { AllDeclsFound.insert(D->getCanonicalDecl()); }
+ /// \brief Add a visited context.
+ void addVisitedContext(DeclContext *Ctx) {
+ CompletionContext.addVisitedContext(Ctx);
+ }
+
/// \name Name lookup predicates
///
/// These predicates can be passed to the name lookup functions to filter the
@@ -1280,7 +1285,7 @@ namespace {
class CodeCompletionDeclConsumer : public VisibleDeclConsumer {
ResultBuilder &Results;
DeclContext *CurContext;
-
+
public:
CodeCompletionDeclConsumer(ResultBuilder &Results, DeclContext *CurContext)
: Results(Results), CurContext(CurContext) { }
@@ -1295,6 +1300,10 @@ namespace {
false, Accessible);
Results.AddResult(Result, CurContext, Hiding, InBaseClass);
}
+
+ void EnteredContext(DeclContext* Ctx) override {
+ Results.addVisitedContext(Ctx);
+ }
};
}
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index 157d090490b..7e7eac38c1f 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -3507,6 +3507,8 @@ static void LookupVisibleDecls(DeclContext *Ctx, LookupResult &Result,
if (Visited.visitedContext(Ctx->getPrimaryContext()))
return;
+ Consumer.EnteredContext(Ctx);
+
// Outside C++, lookup results for the TU live on identifiers.
if (isa<TranslationUnitDecl>(Ctx) &&
!Result.getSema().getLangOpts().CPlusPlus) {
diff --git a/clang/unittests/Sema/CMakeLists.txt b/clang/unittests/Sema/CMakeLists.txt
index 16fae820dfe..45460f1e0f6 100644
--- a/clang/unittests/Sema/CMakeLists.txt
+++ b/clang/unittests/Sema/CMakeLists.txt
@@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
add_clang_unittest(SemaTests
ExternalSemaSourceTest.cpp
+ CodeCompleteTest.cpp
)
target_link_libraries(SemaTests
diff --git a/clang/unittests/Sema/CodeCompleteTest.cpp b/clang/unittests/Sema/CodeCompleteTest.cpp
new file mode 100644
index 00000000000..8e888cbe528
--- /dev/null
+++ b/clang/unittests/Sema/CodeCompleteTest.cpp
@@ -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
OpenPOWER on IntegriCloud