blob: 7464bc1f88ff4fd8c1b815dd26891a7550837ec4 [file] [log] [blame]
Douglas Gregor81b747b2009-09-17 21:32:03 +00001//===---- CodeCompleteConsumer.h - Code Completion Interface ----*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file implements the CodeCompleteConsumer class.
11//
12//===----------------------------------------------------------------------===//
13#include "clang/Sema/CodeCompleteConsumer.h"
14#include "clang/Lex/Preprocessor.h"
15#include "Sema.h"
16#include "llvm/ADT/STLExtras.h"
17#include "llvm/Support/Compiler.h"
18#include "llvm/Support/raw_ostream.h"
19#include <algorithm>
20#include <string.h>
21using namespace clang;
22
23CodeCompleteConsumer::CodeCompleteConsumer(Sema &S) : SemaRef(S) {
24 SemaRef.setCodeCompleteConsumer(this);
25}
26
27CodeCompleteConsumer::~CodeCompleteConsumer() {
28 SemaRef.setCodeCompleteConsumer(0);
29}
30
31void
32CodeCompleteConsumer::CodeCompleteMemberReferenceExpr(Scope *S,
33 QualType BaseType,
34 bool IsArrow) {
35 if (IsArrow) {
36 if (const PointerType *Ptr = BaseType->getAs<PointerType>())
37 BaseType = Ptr->getPointeeType();
38 else if (BaseType->isObjCObjectPointerType())
39 /*Do nothing*/ ;
40 else
41 return;
42 }
43
44 ResultSet Results;
45 unsigned NextRank = 0;
46
47 if (const RecordType *Record = BaseType->getAs<RecordType>()) {
48 NextRank = CollectMemberResults(Record->getDecl(), NextRank, Results);
49
50 if (getSema().getLangOptions().CPlusPlus) {
51 if (!Results.empty())
52 // The "template" keyword can follow "->" or "." in the grammar.
53 Results.MaybeAddResult(Result("template", NextRank++));
54
55 // FIXME: For C++, we also need to look into the current scope, since
56 // we could have the start of a nested-name-specifier.
57 }
58
59 // Hand off the results found for code completion.
60 ProcessCodeCompleteResults(Results.data(), Results.size());
61
62 // We're done!
63 return;
64 }
65}
66
67void
68CodeCompleteConsumer::CodeCompleteQualifiedId(Scope *S,
69 NestedNameSpecifier *NNS,
70 bool EnteringContext) {
71 CXXScopeSpec SS;
72 SS.setScopeRep(NNS);
73 DeclContext *Ctx = getSema().computeDeclContext(SS, EnteringContext);
74 if (!Ctx)
75 return;
76
77 ResultSet Results;
78 unsigned NextRank = CollectMemberResults(Ctx, 0, Results);
79
80 // The "template" keyword can follow "::" in the grammar
81 if (!Results.empty())
82 Results.MaybeAddResult(Result("template", NextRank));
83
84 ProcessCodeCompleteResults(Results.data(), Results.size());
85}
86
87void CodeCompleteConsumer::ResultSet::MaybeAddResult(Result R) {
88 if (R.Kind != Result::RK_Declaration) {
89 // For non-declaration results, just add the result.
90 Results.push_back(R);
91 return;
92 }
93
94 // FIXME: Using declarations
95
96 Decl *CanonDecl = R.Declaration->getCanonicalDecl();
97 unsigned IDNS = CanonDecl->getIdentifierNamespace();
98
99 // Friend declarations and declarations introduced due to friends are never
100 // added as results.
101 if (isa<FriendDecl>(CanonDecl) ||
102 (IDNS & (Decl::IDNS_OrdinaryFriend | Decl::IDNS_TagFriend)))
103 return;
104
105 ShadowMap &SMap = ShadowMaps.back();
106 ShadowMap::iterator I, IEnd;
107 for (llvm::tie(I, IEnd) = SMap.equal_range(R.Declaration->getDeclName());
108 I != IEnd; ++I) {
109 NamedDecl *ND = I->second.first;
110 unsigned Index = I->second.second;
111 if (ND->getCanonicalDecl() == CanonDecl) {
112 // This is a redeclaration. Always pick the newer declaration.
113 I->second.first = R.Declaration;
114 Results[Index].Declaration = R.Declaration;
115
116 // Pick the best rank of the two.
117 Results[Index].Rank = std::min(Results[Index].Rank, R.Rank);
118
119 // We're done.
120 return;
121 }
122 }
123
124 // This is a new declaration in this scope. However, check whether this
125 // declaration name is hidden by a similarly-named declaration in an outer
126 // scope.
127 std::list<ShadowMap>::iterator SM, SMEnd = ShadowMaps.end();
128 --SMEnd;
129 for (SM = ShadowMaps.begin(); SM != SMEnd; ++SM) {
130 for (llvm::tie(I, IEnd) = SM->equal_range(R.Declaration->getDeclName());
131 I != IEnd; ++I) {
132 // A tag declaration does not hide a non-tag declaration.
133 if (I->second.first->getIdentifierNamespace() == Decl::IDNS_Tag &&
134 (IDNS & (Decl::IDNS_Member | Decl::IDNS_Ordinary |
135 Decl::IDNS_ObjCProtocol)))
136 continue;
137
138 // Protocols are in distinct namespaces from everything else.
139 if (((I->second.first->getIdentifierNamespace() & Decl::IDNS_ObjCProtocol)
140 || (IDNS & Decl::IDNS_ObjCProtocol)) &&
141 I->second.first->getIdentifierNamespace() != IDNS)
142 continue;
143
144 // The newly-added result is hidden by an entry in the shadow map.
145 R.Hidden = true;
146 break;
147 }
148 }
149
150 // Insert this result into the set of results and into the current shadow
151 // map.
152 SMap.insert(std::make_pair(R.Declaration->getDeclName(),
153 std::make_pair(R.Declaration, Results.size())));
154 Results.push_back(R);
155}
156
157/// \brief Enter into a new scope.
158void CodeCompleteConsumer::ResultSet::EnterNewScope() {
159 ShadowMaps.push_back(ShadowMap());
160}
161
162/// \brief Exit from the current scope.
163void CodeCompleteConsumer::ResultSet::ExitScope() {
164 ShadowMaps.pop_back();
165}
166
167/// \brief Collect the results of searching for members within the given
168/// declaration context.
169///
170/// \param Ctx the declaration context from which we will gather results.
171///
172/// \param InitialRank the initial rank given to results in this tag
173/// declaration. Larger rank values will be used for, e.g., members found
174/// in base classes.
175///
176/// \param Results the result set that will be extended with any results
177/// found within this declaration context (and, for a C++ class, its bases).
178///
179/// \returns the next higher rank value, after considering all of the
180/// names within this declaration context.
181unsigned CodeCompleteConsumer::CollectMemberResults(DeclContext *Ctx,
182 unsigned InitialRank,
183 ResultSet &Results) {
184 // Enumerate all of the results in this context.
185 Results.EnterNewScope();
186 for (DeclContext *CurCtx = Ctx->getPrimaryContext(); CurCtx;
187 CurCtx = CurCtx->getNextContext()) {
188 for (DeclContext::decl_iterator D = CurCtx->decls_begin(),
189 DEnd = CurCtx->decls_end();
190 D != DEnd; ++D) {
191 if (NamedDecl *ND = dyn_cast<NamedDecl>(*D)) {
192 // FIXME: Apply a filter to the results
193 Results.MaybeAddResult(Result(ND, InitialRank));
194 }
195 }
196 }
197
198 // Traverse the contexts of inherited classes.
199 unsigned NextRank = InitialRank;
200 if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(Ctx)) {
201 for (CXXRecordDecl::base_class_iterator B = Record->bases_begin(),
202 BEnd = Record->bases_end();
203 B != BEnd; ++B) {
204 QualType BaseType = B->getType();
205
206 // Don't look into dependent bases, because name lookup can't look
207 // there anyway.
208 if (BaseType->isDependentType())
209 continue;
210
211 const RecordType *Record = BaseType->getAs<RecordType>();
212 if (!Record)
213 continue;
214
215 // FIXME: We should keep track of the virtual bases we visit, so
216 // that we don't visit them more than once.
217
218 // FIXME: It would be nice to be able to determine whether referencing
219 // a particular member would be ambiguous. For example, given
220 //
221 // struct A { int member; };
222 // struct B { int member; };
223 // struct C : A, B { };
224 //
225 // void f(C *c) { c->### }
226 // accessing 'member' would result in an ambiguity. However, code
227 // completion could be smart enough to qualify the member with the
228 // base class, e.g.,
229 //
230 // c->B::member
231 //
232 // or
233 //
234 // c->A::member
235
236 // Collect results from this base class (and its bases).
237 NextRank = std::max(NextRank,
238 CollectMemberResults(Record->getDecl(),
239 InitialRank + 1,
240 Results));
241 }
242 }
243
244 // FIXME: Look into base classes in Objective-C!
245
246 Results.ExitScope();
247 return NextRank;
248}
249
250namespace {
251 struct VISIBILITY_HIDDEN SortCodeCompleteResult {
252 typedef CodeCompleteConsumer::Result Result;
253
254 bool operator()(const Result &X, const Result &Y) const {
255 // Sort first by rank.
256 if (X.Rank < Y.Rank)
257 return true;
258 else if (X.Rank > Y.Rank)
259 return false;
260
261 // Result kinds are ordered by decreasing importance.
262 if (X.Kind < Y.Kind)
263 return true;
264 else if (X.Kind > Y.Kind)
265 return false;
266
267 // Non-hidden names precede hidden names.
268 if (X.Hidden != Y.Hidden)
269 return !X.Hidden;
270
271 // Ordering depends on the kind of result.
272 switch (X.Kind) {
273 case Result::RK_Declaration:
274 // Order based on the declaration names.
275 return X.Declaration->getDeclName() < Y.Declaration->getDeclName();
276
277 case Result::RK_Keyword:
278 return strcmp(X.Keyword, Y.Keyword) == -1;
279 }
280
281 // If only our C++ compiler did control-flow warnings properly.
282 return false;
283 }
284 };
285}
286
287void
288PrintingCodeCompleteConsumer::ProcessCodeCompleteResults(Result *Results,
289 unsigned NumResults) {
290 // Sort the results by rank/kind/etc.
291 std::stable_sort(Results, Results + NumResults, SortCodeCompleteResult());
292
293 // Print the results.
294 for (unsigned I = 0; I != NumResults; ++I) {
295 switch (Results[I].Kind) {
296 case Result::RK_Declaration:
297 OS << Results[I].Declaration->getNameAsString() << " : "
298 << Results[I].Rank;
299 if (Results[I].Hidden)
300 OS << " (Hidden)";
301 OS << '\n';
302 break;
303
304 case Result::RK_Keyword:
305 OS << Results[I].Keyword << " : " << Results[I].Rank << '\n';
306 break;
307 }
308 }
309
310 // Once we've printed the code-completion results, suppress remaining
311 // diagnostics.
312 // FIXME: Move this somewhere else!
313 getSema().PP.getDiagnostics().setSuppressAllDiagnostics();
314}