12
12
13
13
#include " clang/Interpreter/CodeCompletion.h"
14
14
#include " clang/AST/ASTImporter.h"
15
+ #include " clang/AST/DeclLookups.h"
15
16
#include " clang/AST/DeclarationName.h"
16
17
#include " clang/AST/ExternalASTSource.h"
17
18
#include " clang/Basic/IdentifierTable.h"
23
24
#include " clang/Sema/CodeCompleteConsumer.h"
24
25
#include " clang/Sema/CodeCompleteOptions.h"
25
26
#include " clang/Sema/Sema.h"
27
+ #include " llvm/Support/Debug.h"
28
+ #define DEBUG_TYPE " REPLCC"
26
29
27
30
namespace clang {
28
31
@@ -39,11 +42,15 @@ clang::CodeCompleteOptions getClangCompleteOpts() {
39
42
40
43
class ReplCompletionConsumer : public CodeCompleteConsumer {
41
44
public:
42
- ReplCompletionConsumer (std::vector<std::string> &Results)
45
+ ReplCompletionConsumer (std::vector<std::string> &Results,
46
+ ReplCodeCompleter &CC)
43
47
: CodeCompleteConsumer(getClangCompleteOpts()),
44
48
CCAllocator (std::make_shared<GlobalCodeCompletionAllocator>()),
45
- CCTUInfo(CCAllocator), Results(Results){};
49
+ CCTUInfo(CCAllocator), Results(Results), CC(CC) {}
46
50
51
+ // The entry of handling code completion. When the function is called, we
52
+ // create a `Context`-based handler (see classes defined below) to handle each
53
+ // completion result.
47
54
void ProcessCodeCompleteResults (class Sema &S, CodeCompletionContext Context,
48
55
CodeCompletionResult *InResults,
49
56
unsigned NumResults) final ;
@@ -56,26 +63,146 @@ class ReplCompletionConsumer : public CodeCompleteConsumer {
56
63
std::shared_ptr<GlobalCodeCompletionAllocator> CCAllocator;
57
64
CodeCompletionTUInfo CCTUInfo;
58
65
std::vector<std::string> &Results;
66
+ ReplCodeCompleter &CC;
67
+ };
68
+
69
+ // / The class CompletionContextHandler contains four interfaces, each of
70
+ // / which handles one type of completion result.
71
+ // / Its derived classes are used to create concrete handlers based on
72
+ // / \c CodeCompletionContext.
73
+ class CompletionContextHandler {
74
+ protected:
75
+ CodeCompletionContext CCC;
76
+ std::vector<std::string> &Results;
77
+
78
+ private:
79
+ Sema &S;
80
+
81
+ public:
82
+ CompletionContextHandler (Sema &S, CodeCompletionContext CCC,
83
+ std::vector<std::string> &Results)
84
+ : CCC(CCC), Results(Results), S(S) {}
85
+
86
+ // / Converts a Declaration completion result to a completion string, and then
87
+ // / stores it in Results.
88
+ virtual void handleDeclaration (const CodeCompletionResult &Result) {
89
+ auto PreferredType = CCC.getPreferredType ();
90
+ if (PreferredType.isNull ()) {
91
+ Results.push_back (Result.Declaration ->getName ().str ());
92
+ return ;
93
+ }
94
+
95
+ if (auto *VD = dyn_cast<VarDecl>(Result.Declaration )) {
96
+ auto ArgumentType = VD->getType ();
97
+ if (PreferredType->isReferenceType ()) {
98
+ QualType RT = PreferredType->castAs <ReferenceType>()->getPointeeType ();
99
+ Sema::ReferenceConversions RefConv;
100
+ Sema::ReferenceCompareResult RefRelationship =
101
+ S.CompareReferenceRelationship (SourceLocation (), RT, ArgumentType,
102
+ &RefConv);
103
+ switch (RefRelationship) {
104
+ case Sema::Ref_Compatible:
105
+ case Sema::Ref_Related:
106
+ Results.push_back (VD->getName ().str ());
107
+ break ;
108
+ case Sema::Ref_Incompatible:
109
+ break ;
110
+ }
111
+ } else if (S.Context .hasSameType (ArgumentType, PreferredType)) {
112
+ Results.push_back (VD->getName ().str ());
113
+ }
114
+ }
115
+ }
116
+
117
+ // / Converts a Keyword completion result to a completion string, and then
118
+ // / stores it in Results.
119
+ virtual void handleKeyword (const CodeCompletionResult &Result) {
120
+ auto Prefix = S.getPreprocessor ().getCodeCompletionFilter ();
121
+ // Add keyword to the completion results only if we are in a type-aware
122
+ // situation.
123
+ if (!CCC.getBaseType ().isNull () || !CCC.getPreferredType ().isNull ())
124
+ return ;
125
+ if (StringRef (Result.Keyword ).startswith (Prefix))
126
+ Results.push_back (Result.Keyword );
127
+ }
128
+
129
+ // / Converts a Pattern completion result to a completion string, and then
130
+ // / stores it in Results.
131
+ virtual void handlePattern (const CodeCompletionResult &Result) {}
132
+
133
+ // / Converts a Macro completion result to a completion string, and then stores
134
+ // / it in Results.
135
+ virtual void handleMacro (const CodeCompletionResult &Result) {}
136
+ };
137
+
138
+ class DotMemberAccessHandler : public CompletionContextHandler {
139
+ public:
140
+ DotMemberAccessHandler (Sema &S, CodeCompletionContext CCC,
141
+ std::vector<std::string> &Results)
142
+ : CompletionContextHandler(S, CCC, Results) {}
143
+ void handleDeclaration (const CodeCompletionResult &Result) override {
144
+ auto *ID = Result.Declaration ->getIdentifier ();
145
+ if (!ID)
146
+ return ;
147
+ if (!isa<CXXMethodDecl>(Result.Declaration ))
148
+ return ;
149
+ const auto *Fun = cast<CXXMethodDecl>(Result.Declaration );
150
+ if (Fun->getParent ()->getCanonicalDecl () ==
151
+ CCC.getBaseType ()->getAsCXXRecordDecl ()->getCanonicalDecl ()) {
152
+ LLVM_DEBUG (llvm::dbgs () << " [In HandleCodeCompleteDOT] Name : "
153
+ << ID->getName () << " \n " );
154
+ Results.push_back (ID->getName ().str ());
155
+ }
156
+ }
157
+
158
+ void handleKeyword (const CodeCompletionResult &Result) override {}
59
159
};
60
160
61
161
void ReplCompletionConsumer::ProcessCodeCompleteResults (
62
162
class Sema &S, CodeCompletionContext Context,
63
163
CodeCompletionResult *InResults, unsigned NumResults) {
64
- for (unsigned I = 0 ; I < NumResults; ++I) {
164
+
165
+ auto Prefix = S.getPreprocessor ().getCodeCompletionFilter ();
166
+ CC.Prefix = Prefix;
167
+
168
+ std::unique_ptr<CompletionContextHandler> CCH;
169
+
170
+ // initialize fine-grained code completion handler based on the code
171
+ // completion context.
172
+ switch (Context.getKind ()) {
173
+ case CodeCompletionContext::CCC_DotMemberAccess:
174
+ CCH.reset (new DotMemberAccessHandler (S, Context, this ->Results ));
175
+ break ;
176
+ default :
177
+ CCH.reset (new CompletionContextHandler (S, Context, this ->Results ));
178
+ };
179
+
180
+ for (unsigned I = 0 ; I < NumResults; I++) {
65
181
auto &Result = InResults[I];
66
182
switch (Result.Kind ) {
67
183
case CodeCompletionResult::RK_Declaration:
68
- if (auto *ID = Result.Declaration ->getIdentifier ()) {
69
- Results.push_back (ID->getName ().str ());
184
+ if (Result.Hidden ) {
185
+ break ;
186
+ }
187
+ if (!Result.Declaration ->getDeclName ().isIdentifier () ||
188
+ !Result.Declaration ->getName ().startswith (Prefix)) {
189
+ break ;
70
190
}
191
+ CCH->handleDeclaration (Result);
71
192
break ;
72
193
case CodeCompletionResult::RK_Keyword:
73
- Results. push_back (Result. Keyword );
194
+ CCH-> handleKeyword (Result);
74
195
break ;
75
- default :
196
+ case CodeCompletionResult::RK_Macro:
197
+ CCH->handleMacro (Result);
198
+ break ;
199
+ case CodeCompletionResult::RK_Pattern:
200
+ CCH->handlePattern (Result);
76
201
break ;
77
202
}
78
203
}
204
+
205
+ std::sort (Results.begin (), Results.end ());
79
206
}
80
207
81
208
class IncrementalSyntaxOnlyAction : public SyntaxOnlyAction {
@@ -118,6 +245,16 @@ void IncrementalSyntaxOnlyAction::ExecuteAction() {
118
245
CI.getASTContext ().getTranslationUnitDecl ()->setHasExternalVisibleStorage (
119
246
true );
120
247
248
+ // Load all external decls into current context. Under the hood, it calls
249
+ // ExternalSource::completeVisibleDeclsMap, which make all decls on the redecl
250
+ // chain visible.
251
+ //
252
+ // This is crucial to code completion on dot members, since a bound variable
253
+ // before "." would be otherwise treated out-of-scope.
254
+ //
255
+ // clang-repl> Foo f1;
256
+ // clang-repl> f1.<tab>
257
+ CI.getASTContext ().getTranslationUnitDecl ()->lookups ();
121
258
SyntaxOnlyAction::ExecuteAction ();
122
259
}
123
260
@@ -134,6 +271,7 @@ ExternalSource::ExternalSource(ASTContext &ChildASTCtxt, FileManager &ChildFM,
134
271
135
272
bool ExternalSource::FindExternalVisibleDeclsByName (const DeclContext *DC,
136
273
DeclarationName Name) {
274
+
137
275
IdentifierTable &ParentIdTable = ParentASTCtxt.Idents ;
138
276
139
277
auto ParentDeclName =
@@ -159,29 +297,67 @@ void ExternalSource::completeVisibleDeclsMap(
159
297
for (auto *DeclCtxt = ParentTUDeclCtxt; DeclCtxt != nullptr ;
160
298
DeclCtxt = DeclCtxt->getPreviousDecl ()) {
161
299
for (auto &IDeclContext : DeclCtxt->decls ()) {
162
- if (NamedDecl *Decl = llvm::dyn_cast<NamedDecl>(IDeclContext)) {
163
- if (auto DeclOrErr = Importer->Import (Decl)) {
164
- if (NamedDecl *importedNamedDecl =
165
- llvm::dyn_cast<NamedDecl>(*DeclOrErr)) {
166
- SetExternalVisibleDeclsForName (ChildDeclContext,
167
- importedNamedDecl->getDeclName (),
168
- importedNamedDecl);
169
- }
170
-
171
- } else {
172
- llvm::consumeError (DeclOrErr.takeError ());
173
- }
300
+ if (!llvm::isa<NamedDecl>(IDeclContext))
301
+ continue ;
302
+
303
+ NamedDecl *Decl = llvm::cast<NamedDecl>(IDeclContext);
304
+
305
+ auto DeclOrErr = Importer->Import (Decl);
306
+ if (!DeclOrErr) {
307
+ // if an error happens, it usually means the decl has already been
308
+ // imported or the decl is a result of a failed import. But in our
309
+ // case, every import is fresh each time code completion is
310
+ // triggered. So Import usually doesn't fail. If it does, it just means
311
+ // the related decl can't be used in code completion and we can safely
312
+ // drop it.
313
+ llvm::consumeError (DeclOrErr.takeError ());
314
+ continue ;
174
315
}
316
+
317
+ if (!llvm::isa<NamedDecl>(*DeclOrErr))
318
+ continue ;
319
+
320
+ NamedDecl *importedNamedDecl = llvm::cast<NamedDecl>(*DeclOrErr);
321
+
322
+ SetExternalVisibleDeclsForName (ChildDeclContext,
323
+ importedNamedDecl->getDeclName (),
324
+ importedNamedDecl);
325
+
326
+ if (!llvm::isa<CXXRecordDecl>(importedNamedDecl))
327
+ continue ;
328
+
329
+ auto *Record = llvm::cast<CXXRecordDecl>(importedNamedDecl);
330
+
331
+ if (auto Err = Importer->ImportDefinition (Decl)) {
332
+ // the same as above
333
+ consumeError (std::move (Err));
334
+ continue ;
335
+ }
336
+
337
+ Record->setHasLoadedFieldsFromExternalStorage (true );
338
+ LLVM_DEBUG (llvm::dbgs ()
339
+ << " \n CXXRecrod : " << Record->getName () << " size(methods): "
340
+ << std::distance (Record->method_begin (), Record->method_end ())
341
+ << " has def?: " << Record->hasDefinition ()
342
+ << " # (methods): "
343
+ << std::distance (Record->getDefinition ()->method_begin (),
344
+ Record->getDefinition ()->method_end ())
345
+ << " \n " );
346
+ for (auto *Meth : Record->methods ())
347
+ SetExternalVisibleDeclsForName (ChildDeclContext, Meth->getDeclName (),
348
+ Meth);
175
349
}
176
350
ChildDeclContext->setHasExternalLexicalStorage (false );
177
351
}
178
352
}
179
353
180
- void codeComplete (CompilerInstance *InterpCI, llvm::StringRef Content,
181
- unsigned Line, unsigned Col, const CompilerInstance *ParentCI,
182
- std::vector<std::string> &CCResults) {
354
+ void ReplCodeCompleter::codeComplete (CompilerInstance *InterpCI,
355
+ llvm::StringRef Content, unsigned Line,
356
+ unsigned Col,
357
+ const CompilerInstance *ParentCI,
358
+ std::vector<std::string> &CCResults) {
183
359
auto DiagOpts = DiagnosticOptions ();
184
- auto consumer = ReplCompletionConsumer (CCResults);
360
+ auto consumer = ReplCompletionConsumer (CCResults, * this );
185
361
186
362
auto diag = InterpCI->getDiagnosticsPtr ();
187
363
std::unique_ptr<ASTUnit> AU (ASTUnit::LoadFromCompilerInvocationAction (
0 commit comments