Skip to content

Commit 98abbee

Browse files
committed
[cling] Fix broken autocomplete
This also moves closer to clang-repl autocomplete machinery: `ClingCodeCompleteConsumer` is now almost identical to `clang::ReplCodeCompleter` Autocomplete in `cling` was broken after 9cdffe0 since cling handles autocomplete a bit differently from `clang-repl`
1 parent 05299c5 commit 98abbee

4 files changed

Lines changed: 223 additions & 111 deletions

File tree

interpreter/cling/include/cling/Interpreter/ClingCodeCompleteConsumer.h

Lines changed: 11 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@
55
// This file is dual-licensed: you can choose to license it under the University
66
// of Illinois Open Source License or the GNU Lesser General Public License. See
77
// LICENSE.TXT for details.
8-
//------------------------------------------------------------------------------
8+
//
9+
//===----------------------------------------------------------------------===//
10+
//
11+
// This file follows the same structure/logic (duplicate) as:
12+
// clang/Interpreter/CodeCompletion.h
13+
//
14+
//===----------------------------------------------------------------------===//
915

1016
#ifndef CLING_CODE_COMPLETE_CONSUMER
1117
#define CLING_CODE_COMPLETE_CONSUMER
@@ -19,42 +25,6 @@ namespace clang {
1925
}
2026

2127
namespace cling {
22-
/// \brief Create a new printing code-completion consumer that prints its
23-
/// results to the given raw output stream.
24-
class ClingCodeCompleteConsumer : public CodeCompleteConsumer {
25-
CodeCompletionTUInfo m_CCTUInfo;
26-
/// \ brief Results of the completer to be printed by the text interface.
27-
std::vector<std::string> &m_Completions;
28-
29-
public:
30-
ClingCodeCompleteConsumer(const CodeCompleteOptions &CodeComplOpts,
31-
std::vector<std::string> &completions)
32-
: CodeCompleteConsumer(CodeComplOpts),
33-
m_CCTUInfo(std::make_shared<GlobalCodeCompletionAllocator>()),
34-
m_Completions(completions) {}
35-
~ClingCodeCompleteConsumer() {}
36-
37-
/// \brief Prints the finalized code-completion results.
38-
void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
39-
CodeCompletionResult *Results,
40-
unsigned NumResults) override;
41-
42-
CodeCompletionAllocator &getAllocator() override {
43-
return m_CCTUInfo.getAllocator();
44-
}
45-
46-
CodeCompletionTUInfo &getCodeCompletionTUInfo() override {
47-
return m_CCTUInfo;
48-
}
49-
50-
bool isResultFilteredOut(StringRef Filter,
51-
CodeCompletionResult Results) override;
52-
53-
void getCompletions(std::vector<std::string>& completions) {
54-
completions = m_Completions;
55-
}
56-
};
57-
5828
struct ClingCodeCompleter {
5929
ClingCodeCompleter() = default;
6030
std::string Prefix;
@@ -67,10 +37,11 @@ namespace cling {
6737
/// \param[in] ParentCI The running interpreter compiler instance that
6838
/// provides ASTContexts.
6939
/// \param[out] CCResults The completion results.
70-
void codeComplete(CompilerInstance* InterpCI, llvm::StringRef Content,
71-
unsigned Line, unsigned Col, CompilerInstance* ParentCI,
40+
void codeComplete(clang::CompilerInstance* InterpCI,
41+
llvm::StringRef Content, unsigned Line, unsigned Col,
42+
clang::CompilerInstance* ParentCI,
7243
std::vector<std::string>& CCResults);
7344
};
74-
}
45+
} // namespace cling
7546

7647
#endif

interpreter/cling/lib/Interpreter/ClingCodeCompleteConsumer.cpp

Lines changed: 186 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -5,83 +5,206 @@
55
// This file is dual-licensed: you can choose to license it under the University
66
// of Illinois Open Source License or the GNU Lesser General Public License. See
77
// LICENSE.TXT for details.
8-
//------------------------------------------------------------------------------
8+
//
9+
//===----------------------------------------------------------------------===//
10+
//
11+
// This is mostly a copy of clang/lib/Interpreter/CodeCompletion.cpp
12+
// TODO: Replace with ReplCodeCompleter
13+
//
14+
//===----------------------------------------------------------------------===//
915

1016
#include "cling/Interpreter/ClingCodeCompleteConsumer.h"
1117

1218
#include "clang/AST/ASTImporter.h"
1319
#include "clang/AST/DeclLookups.h"
14-
#include "clang/Basic/Diagnostic.h"
20+
#include "clang/AST/DeclarationName.h"
21+
#include "clang/AST/ExternalASTSource.h"
22+
#include "clang/Basic/IdentifierTable.h"
1523
#include "clang/Frontend/ASTUnit.h"
1624
#include "clang/Frontend/CompilerInstance.h"
1725
#include "clang/Frontend/FrontendActions.h"
18-
#include "clang/Lex/Preprocessor.h"
26+
#include "clang/Lex/PreprocessorOptions.h"
27+
#include "clang/Sema/CodeCompleteConsumer.h"
28+
#include "clang/Sema/CodeCompleteOptions.h"
1929
#include "clang/Sema/Sema.h"
2030

2131
namespace cling {
22-
void ClingCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &SemaRef,
23-
CodeCompletionContext Context,
24-
CodeCompletionResult *Results,
25-
unsigned NumResults) {
26-
std::stable_sort(Results, Results + NumResults);
27-
28-
StringRef Filter = SemaRef.getPreprocessor().getCodeCompletionFilter();
29-
30-
for (unsigned I = 0; I != NumResults; ++I) {
31-
if (!Filter.empty() && isResultFilteredOut(Filter, Results[I]))
32-
continue;
33-
switch (Results[I].Kind) {
34-
case CodeCompletionResult::RK_Declaration:
35-
if (CodeCompletionString *CCS
36-
= Results[I].CreateCodeCompletionString(SemaRef, Context,
37-
getAllocator(),
38-
m_CCTUInfo,
39-
includeBriefComments())) {
40-
m_Completions.push_back(CCS->getAsString());
32+
const std::string CodeCompletionFileName = "input_line_[Completion]";
33+
34+
clang::CodeCompleteOptions getClangCompleteOpts() {
35+
clang::CodeCompleteOptions Opts;
36+
Opts.IncludeCodePatterns = true;
37+
Opts.IncludeMacros = true;
38+
Opts.IncludeGlobals = true;
39+
Opts.IncludeBriefComments = true;
40+
return Opts;
41+
}
42+
43+
/// Create a new printing code-completion consumer that prints its
44+
/// results to the given raw output stream.
45+
class ClingCompletionConsumer : public clang::CodeCompleteConsumer {
46+
public:
47+
ClingCompletionConsumer(std::vector<std::string>& Results,
48+
ClingCodeCompleter& CC)
49+
: CodeCompleteConsumer(getClangCompleteOpts()),
50+
CCAllocator(std::make_shared<GlobalCodeCompletionAllocator>()),
51+
CCTUInfo(CCAllocator), Results(Results), CC(CC) {}
52+
53+
// The entry of handling code completion. When the function is called, we
54+
// create a `Context`-based handler (see classes defined below) to handle
55+
// each completion result.
56+
void ProcessCodeCompleteResults(class Sema& S,
57+
CodeCompletionContext Context,
58+
CodeCompletionResult* InResults,
59+
unsigned NumResults) final;
60+
61+
CodeCompletionAllocator& getAllocator() override { return *CCAllocator; }
62+
63+
CodeCompletionTUInfo& getCodeCompletionTUInfo() override {
64+
return CCTUInfo;
65+
}
66+
67+
private:
68+
std::shared_ptr<GlobalCodeCompletionAllocator> CCAllocator;
69+
CodeCompletionTUInfo CCTUInfo;
70+
std::vector<std::string>& Results;
71+
ClingCodeCompleter& CC;
72+
};
73+
74+
/// The class CompletionContextHandler contains four interfaces, each of
75+
/// which handles one type of completion result.
76+
/// Its derived classes are used to create concrete handlers based on
77+
/// \c CodeCompletionContext.
78+
class CompletionContextHandler {
79+
protected:
80+
CodeCompletionContext CCC;
81+
std::vector<std::string>& Results;
82+
83+
private:
84+
Sema& S;
85+
86+
public:
87+
CompletionContextHandler(Sema& S, CodeCompletionContext CCC,
88+
std::vector<std::string>& Results)
89+
: CCC(CCC), Results(Results), S(S) {}
90+
91+
virtual ~CompletionContextHandler() = default;
92+
/// Converts a Declaration completion result to a completion string, and
93+
/// then stores it in Results.
94+
virtual void handleDeclaration(const CodeCompletionResult& Result) {
95+
auto PreferredType = CCC.getPreferredType();
96+
if (PreferredType.isNull()) {
97+
Results.push_back(Result.Declaration->getName().str());
98+
return;
99+
}
100+
101+
if (auto* VD = dyn_cast<VarDecl>(Result.Declaration)) {
102+
auto ArgumentType = VD->getType();
103+
if (PreferredType->isReferenceType()) {
104+
QualType RT =
105+
PreferredType->castAs<ReferenceType>()->getPointeeType();
106+
Sema::ReferenceConversions RefConv;
107+
Sema::ReferenceCompareResult RefRelationship =
108+
S.CompareReferenceRelationship(SourceLocation(), RT, ArgumentType,
109+
&RefConv);
110+
switch (RefRelationship) {
111+
case Sema::Ref_Compatible:
112+
case Sema::Ref_Related:
113+
Results.push_back(VD->getName().str());
114+
break;
115+
case Sema::Ref_Incompatible: break;
41116
}
42-
break;
117+
} else if (S.Context.hasSameType(ArgumentType, PreferredType)) {
118+
Results.push_back(VD->getName().str());
119+
}
120+
}
121+
}
43122

44-
case CodeCompletionResult::RK_Keyword:
45-
m_Completions.push_back(Results[I].Keyword);
46-
break;
123+
/// Converts a Keyword completion result to a completion string, and then
124+
/// stores it in Results.
125+
virtual void handleKeyword(const CodeCompletionResult& Result) {
126+
auto Prefix = S.getPreprocessor().getCodeCompletionFilter();
127+
// Add keyword to the completion results only if we are in a type-aware
128+
// situation.
129+
if (!CCC.getBaseType().isNull() || !CCC.getPreferredType().isNull())
130+
return;
131+
if (StringRef(Result.Keyword).starts_with(Prefix))
132+
Results.push_back(Result.Keyword);
133+
}
47134

48-
case CodeCompletionResult::RK_Macro:
49-
if (CodeCompletionString *CCS
50-
= Results[I].CreateCodeCompletionString(SemaRef, Context,
51-
getAllocator(),
52-
m_CCTUInfo,
53-
includeBriefComments())) {
54-
m_Completions.push_back(CCS->getAsString());
135+
/// Converts a Pattern completion result to a completion string, and then
136+
/// stores it in Results.
137+
virtual void handlePattern(const CodeCompletionResult& Result) {}
138+
139+
/// Converts a Macro completion result to a completion string, and then
140+
/// stores it in Results.
141+
virtual void handleMacro(const CodeCompletionResult& Result) {}
142+
};
143+
144+
class DotMemberAccessHandler : public CompletionContextHandler {
145+
public:
146+
DotMemberAccessHandler(Sema& S, CodeCompletionContext CCC,
147+
std::vector<std::string>& Results)
148+
: CompletionContextHandler(S, CCC, Results) {}
149+
void handleDeclaration(const CodeCompletionResult& Result) override {
150+
auto* ID = Result.Declaration->getIdentifier();
151+
if (!ID)
152+
return;
153+
if (!isa<CXXMethodDecl>(Result.Declaration))
154+
return;
155+
const auto* Fun = cast<CXXMethodDecl>(Result.Declaration);
156+
if (Fun->getParent()->getCanonicalDecl() ==
157+
CCC.getBaseType()->getAsCXXRecordDecl()->getCanonicalDecl()) {
158+
Results.push_back(ID->getName().str());
159+
}
160+
}
161+
162+
void handleKeyword(const CodeCompletionResult& Result) override {}
163+
};
164+
165+
void ClingCompletionConsumer::ProcessCodeCompleteResults(
166+
class Sema& S, CodeCompletionContext Context,
167+
CodeCompletionResult* InResults, unsigned NumResults) {
168+
169+
auto Prefix = S.getPreprocessor().getCodeCompletionFilter();
170+
CC.Prefix = Prefix;
171+
172+
std::unique_ptr<CompletionContextHandler> CCH;
173+
174+
// initialize fine-grained code completion handler based on the code
175+
// completion context.
176+
switch (Context.getKind()) {
177+
case CodeCompletionContext::CCC_DotMemberAccess:
178+
CCH.reset(new DotMemberAccessHandler(S, Context, this->Results));
179+
break;
180+
default:
181+
CCH.reset(new CompletionContextHandler(S, Context, this->Results));
182+
};
183+
184+
for (unsigned I = 0; I < NumResults; I++) {
185+
auto& Result = InResults[I];
186+
switch (Result.Kind) {
187+
case CodeCompletionResult::RK_Declaration:
188+
if (Result.Hidden) {
189+
break;
190+
}
191+
if (!Result.Declaration->getDeclName().isIdentifier() ||
192+
!Result.Declaration->getName().starts_with(Prefix)) {
193+
break;
55194
}
195+
CCH->handleDeclaration(Result);
56196
break;
57-
197+
case CodeCompletionResult::RK_Keyword:
198+
CCH->handleKeyword(Result);
199+
break;
200+
case CodeCompletionResult::RK_Macro: CCH->handleMacro(Result); break;
58201
case CodeCompletionResult::RK_Pattern:
59-
m_Completions.push_back(Results[I].Pattern->getAsString());
202+
CCH->handlePattern(Result);
60203
break;
61204
}
62205
}
63-
}
64206

65-
bool ClingCodeCompleteConsumer::isResultFilteredOut(StringRef Filter,
66-
CodeCompletionResult Result) {
67-
switch (Result.Kind) {
68-
case CodeCompletionResult::RK_Declaration: {
69-
return !(
70-
Result.Declaration->getIdentifier() &&
71-
Result.Declaration->getIdentifier()->getName().starts_with(Filter));
72-
}
73-
case CodeCompletionResult::RK_Keyword: {
74-
return !((StringRef(Result.Keyword)).starts_with(Filter));
75-
}
76-
case CodeCompletionResult::RK_Macro: {
77-
return !(Result.Macro->getName().starts_with(Filter));
78-
}
79-
case CodeCompletionResult::RK_Pattern: {
80-
return !(
81-
StringRef((Result.Pattern->getAsString())).starts_with(Filter));
82-
}
83-
default: llvm_unreachable("Unknown code completion result Kind.");
84-
}
207+
std::sort(Results.begin(), Results.end());
85208
}
86209

87210
// Code copied from: clang/lib/Interpreter/CodeCompletion.cpp
@@ -235,23 +358,18 @@ namespace cling {
235358
std::vector<std::string>& CCResults) {
236359
const std::string CodeCompletionFileName = "input_line_[Completion]";
237360
auto DiagOpts = DiagnosticOptions();
238-
auto consumer =
239-
ClingCodeCompleteConsumer(ParentCI->getFrontendOpts().CodeCompleteOpts,
240-
CCResults);
241-
;
361+
auto consumer = ClingCompletionConsumer(CCResults, *this);
242362

243363
auto diag = InterpCI->getDiagnosticsPtr();
244-
std::unique_ptr<clang::ASTUnit> AU(
245-
clang::ASTUnit::LoadFromCompilerInvocationAction(
246-
InterpCI->getInvocationPtr(),
247-
std::make_shared<PCHContainerOperations>(), diag));
364+
std::unique_ptr<ASTUnit> AU(ASTUnit::LoadFromCompilerInvocationAction(
365+
InterpCI->getInvocationPtr(),
366+
std::make_shared<PCHContainerOperations>(), diag));
248367
llvm::SmallVector<clang::StoredDiagnostic, 8> sd = {};
249368
llvm::SmallVector<const llvm::MemoryBuffer*, 1> tb = {};
250369
InterpCI->getFrontendOpts().Inputs[0] =
251370
FrontendInputFile(CodeCompletionFileName, Language::CXX,
252371
InputKind::Source);
253-
auto Act = std::unique_ptr<IncrementalSyntaxOnlyAction>(
254-
new IncrementalSyntaxOnlyAction(ParentCI));
372+
auto Act = std::make_unique<IncrementalSyntaxOnlyAction>(ParentCI);
255373
std::unique_ptr<llvm::MemoryBuffer> MB =
256374
llvm::MemoryBuffer::getMemBufferCopy(Content, CodeCompletionFileName);
257375
llvm::SmallVector<ASTUnit::RemappedFile, 4> RemappedFiles;
@@ -263,7 +381,7 @@ namespace cling {
263381
AU->CodeComplete(CodeCompletionFileName, 1, Col, RemappedFiles, false,
264382
false, false, consumer,
265383
std::make_shared<clang::PCHContainerOperations>(), *diag,
266-
InterpCI->getLangOpts(), InterpCI->getSourceManager(),
267-
InterpCI->getFileManager(), sd, tb, std::move(Act));
384+
InterpCI->getLangOpts(), AU->getSourceManager(),
385+
AU->getFileManager(), sd, tb, std::move(Act));
268386
}
269387
}

0 commit comments

Comments
 (0)