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