Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(49)

Side by Side Diff: pkg/analysis_server/lib/src/services/completion/suggestion_builder.dart

Issue 829173003: Handle generic parameters correctly in invocation completions. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library services.completion.suggestion.builder; 5 library services.completion.suggestion.builder;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:collection';
8 9
9 import 'package:analysis_server/src/protocol_server.dart' as protocol; 10 import 'package:analysis_server/src/protocol_server.dart' as protocol;
10 import 'package:analysis_server/src/protocol_server.dart' hide Element, 11 import 'package:analysis_server/src/protocol_server.dart' hide Element,
11 ElementKind; 12 ElementKind;
12 import 'package:analysis_server/src/services/completion/dart_completion_manager. dart'; 13 import 'package:analysis_server/src/services/completion/dart_completion_manager. dart';
13 import 'package:analyzer/src/generated/ast.dart'; 14 import 'package:analyzer/src/generated/ast.dart';
14 import 'package:analyzer/src/generated/element.dart'; 15 import 'package:analyzer/src/generated/element.dart';
15 import 'package:analyzer/src/generated/utilities_dart.dart'; 16 import 'package:analyzer/src/generated/utilities_dart.dart';
16 17
17 const String DYNAMIC = 'dynamic'; 18 const String DYNAMIC = 'dynamic';
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
57 bool isDeprecated = element.isDeprecated; 58 bool isDeprecated = element.isDeprecated;
58 CompletionSuggestion suggestion = new CompletionSuggestion( 59 CompletionSuggestion suggestion = new CompletionSuggestion(
59 kind, 60 kind,
60 isDeprecated ? COMPLETION_RELEVANCE_LOW : relevance, 61 isDeprecated ? COMPLETION_RELEVANCE_LOW : relevance,
61 completion, 62 completion,
62 completion.length, 63 completion.length,
63 0, 64 0,
64 isDeprecated, 65 isDeprecated,
65 false); 66 false);
66 suggestion.element = protocol.newElement_fromEngine(element); 67 suggestion.element = protocol.newElement_fromEngine(element);
67 if (element is ClassMemberElement) { 68 Element enclosingElement = element.enclosingElement;
68 ClassElement enclosingElement = element.enclosingElement; 69 if (enclosingElement is ClassElement) {
69 if (enclosingElement != null) { 70 suggestion.declaringType = enclosingElement.displayName;
70 suggestion.declaringType = enclosingElement.displayName;
71 }
72 } 71 }
73 suggestion.returnType = returnType; 72 suggestion.returnType = returnType;
74 if (element is ExecutableElement && element is! PropertyAccessorElement) { 73 if (element is ExecutableElement && element is! PropertyAccessorElement) {
75 suggestion.parameterNames = element.parameters.map( 74 suggestion.parameterNames = element.parameters.map(
76 (ParameterElement parameter) => parameter.name).toList(); 75 (ParameterElement parameter) => parameter.name).toList();
77 suggestion.parameterTypes = element.parameters.map( 76 suggestion.parameterTypes = element.parameters.map(
78 (ParameterElement parameter) => parameter.type.displayName).toList(); 77 (ParameterElement parameter) => parameter.type.displayName).toList();
79 suggestion.requiredParameterCount = element.parameters.where( 78 suggestion.requiredParameterCount = element.parameters.where(
80 (ParameterElement parameter) => 79 (ParameterElement parameter) =>
81 parameter.parameterKind == ParameterKind.REQUIRED).length; 80 parameter.parameterKind == ParameterKind.REQUIRED).length;
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
158 todo.add(classNode); 157 todo.add(classNode);
159 } else { 158 } else {
160 imported(name); 159 imported(name);
161 } 160 }
162 } 161 }
163 }); 162 });
164 } 163 }
165 } 164 }
166 165
167 /** 166 /**
168 * This class visits elements in a class and provides suggestions based upon
169 * the visible members in that class. Clients should call
170 * [ClassElementSuggestionBuilder.suggestionsFor].
171 */
172 class ClassElementSuggestionBuilder extends GeneralizingElementVisitor with
173 ElementSuggestionBuilder {
174 final bool staticOnly;
175 final DartCompletionRequest request;
176
177 ClassElementSuggestionBuilder(this.request, bool staticOnly)
178 : this.staticOnly = staticOnly;
179
180 @override
181 CompletionSuggestionKind get kind => CompletionSuggestionKind.INVOCATION;
182
183 @override
184 visitClassElement(ClassElement element) {
185 element.visitChildren(this);
186 element.allSupertypes.forEach((InterfaceType type) {
187 type.element.visitChildren(this);
188 });
189 }
190
191 @override
192 visitElement(Element element) {
193 // ignored
194 }
195
196 @override
197 visitFieldElement(FieldElement element) {
198 if (staticOnly && !element.isStatic) {
199 return;
200 }
201 addSuggestion(element);
202 }
203
204 @override
205 visitMethodElement(MethodElement element) {
206 if (staticOnly && !element.isStatic) {
207 return;
208 }
209 if (element.isOperator) {
210 return;
211 }
212 addSuggestion(element);
213 }
214
215 @override
216 visitPropertyAccessorElement(PropertyAccessorElement element) {
217 if (staticOnly && !element.isStatic) {
218 return;
219 }
220 addSuggestion(element);
221 }
222
223 /**
224 * Add suggestions for the visible members in the given class
225 */
226 static void suggestionsFor(DartCompletionRequest request, Element element,
227 {bool staticOnly: false}) {
228 if (element == DynamicElementImpl.instance) {
229 element = request.cache.objectClassElement;
230 }
231 if (element is ClassElement) {
232 return element.accept(
233 new ClassElementSuggestionBuilder(request, staticOnly));
234 }
235 }
236 }
237
238 /**
239 * Common mixin for sharing behavior 167 * Common mixin for sharing behavior
240 */ 168 */
241 abstract class ElementSuggestionBuilder { 169 abstract class ElementSuggestionBuilder {
242 170
243 /** 171 /**
244 * Internal collection of completions to prevent duplicate completions. 172 * Internal collection of completions to prevent duplicate completions.
245 */ 173 */
246 final Set<String> _completions = new Set<String>(); 174 final Set<String> _completions = new Set<String>();
247 175
248 /** 176 /**
(...skipping 29 matching lines...) Expand all
278 return; 206 return;
279 } 207 }
280 CompletionSuggestion suggestion = createSuggestion(element, kind: kind); 208 CompletionSuggestion suggestion = createSuggestion(element, kind: kind);
281 if (suggestion != null) { 209 if (suggestion != null) {
282 request.suggestions.add(suggestion); 210 request.suggestions.add(suggestion);
283 } 211 }
284 } 212 }
285 } 213 }
286 214
287 /** 215 /**
216 * This class provides suggestions based upon the visible instance members in
217 * an interface type. Clients should call
218 * [InterfaceTypeSuggestionBuilder.suggestionsFor].
219 */
220 class InterfaceTypeSuggestionBuilder {
221 /**
222 * Enumerated value indicating that we have not generated any completions for
223 * a given identifier yet.
224 */
225 static const int _COMPLETION_TYPE_NONE = 0;
226
227 /**
228 * Enumerated value indicating that we have generated a completion for a
229 * getter.
230 */
231 static const int _COMPLETION_TYPE_GETTER = 1;
232
233 /**
234 * Enumerated value indicating that we have generated a completion for a
235 * setter.
236 */
237 static const int _COMPLETION_TYPE_SETTER = 2;
238
239 /**
240 * Enumerated value indicating that we have generated a completion for a
241 * field, a method, or a getter/setter pair.
242 */
243 static const int _COMPLETION_TYPE_FIELD_OR_METHOD_OR_GETSET = 3;
244
245 final DartCompletionRequest request;
246
247 /**
248 * Map indicating, for each possible completion identifier, whether we have
249 * already generated completions for a getter, setter, or both. The "both"
250 * case also handles the case where have generated a completion for a method
251 * or a field.
252 *
253 * Note: the enumerated values stored in this map are intended to be bitwise
254 * compared.
255 */
256 Map<String, int> _completionTypesGenerated = new HashMap<String, int>();
257
258 InterfaceTypeSuggestionBuilder(this.request);
259
260 @override
danrubel 2015/01/15 02:53:25 Remove override annotation?
Paul Berry 2015/01/21 15:10:33 Done.
261 CompletionSuggestionKind get kind => CompletionSuggestionKind.INVOCATION;
262
263 /**
264 * Add a suggestion based upon the given element, provided that it is not
265 * shadowed by a previously added suggestion.
266 */
267 void addSuggestion(Element element) {
268 if (element.isPrivate) {
269 LibraryElement elementLibrary = element.library;
270 LibraryElement unitLibrary = request.unit.element.library;
271 if (elementLibrary != unitLibrary) {
272 return;
273 }
274 }
275 String identifier = element.displayName;
276 int alreadyGenerated =
277 _completionTypesGenerated.putIfAbsent(identifier, () => _COMPLETION_TYPE _NONE);
278 if (element is MethodElement) {
279 // Anything shadows a method.
280 if (alreadyGenerated != _COMPLETION_TYPE_NONE) {
281 return;
282 }
283 _completionTypesGenerated[identifier] =
284 _COMPLETION_TYPE_FIELD_OR_METHOD_OR_GETSET;
285 } else if (element is PropertyAccessorElement) {
286 if (element.isGetter) {
287 // Getters, fields, and methods shadow a getter.
288 if ((alreadyGenerated & _COMPLETION_TYPE_GETTER) != 0) {
289 return;
290 }
291 _completionTypesGenerated[identifier] |= _COMPLETION_TYPE_GETTER;
292 } else {
293 // Setters, fields, and methods shadow a setter.
294 if ((alreadyGenerated & _COMPLETION_TYPE_SETTER) != 0) {
295 return;
296 }
297 _completionTypesGenerated[identifier] |= _COMPLETION_TYPE_SETTER;
298 }
299 } else if (element is FieldElement) {
300 // Fields and methods shadow a field. A getter/setter pair shadows a
301 // field, but a getter or setter by itself doesn't.
302 if (alreadyGenerated == _COMPLETION_TYPE_FIELD_OR_METHOD_OR_GETSET) {
303 return;
304 }
305 _completionTypesGenerated[identifier] =
306 _COMPLETION_TYPE_FIELD_OR_METHOD_OR_GETSET;
307 } else {
308 // Unexpected element type; skip it.
309 assert(false);
310 return;
311 }
312 CompletionSuggestion suggestion = createSuggestion(element, kind: kind);
313 if (suggestion != null) {
314 request.suggestions.add(suggestion);
315 }
316 }
317
318 void _buildSuggestions(InterfaceType type, LibraryElement library) {
319 // Visit all of the types in the class hierarchy, collecting possible
320 // completions. If multiple elements are found that complete to the same
321 // identifier, addSuggestion will discard all but the first (with a few
322 // exceptions to handle getter/setter pairs).
323 for (InterfaceType targetType in _getTypeOrdering(type)) {
324 for (MethodElement method in targetType.methods) {
325 addSuggestion(method);
326 }
327 for (PropertyAccessorElement propertyAccessor in targetType.accessors) {
328 if (propertyAccessor.isSynthetic) {
329 // Avoid visiting a field twice
330 if (propertyAccessor.isGetter) {
danrubel 2015/01/15 02:53:25 A synthetic getter indicates an non-synthetic fiel
Paul Berry 2015/01/21 15:10:33 That's true, but the only way InterfaceType provid
331 addSuggestion(propertyAccessor.variable);
332 }
333 } else {
334 addSuggestion(propertyAccessor);
335 }
336 }
337 }
338 }
339
340 /**
341 * Get a list of [InterfaceType]s that should be searched to find the
342 * possible completions for an object having type [type].
343 */
344 List<InterfaceType> _getTypeOrdering(InterfaceType type) {
345 // Candidate completions can come from [type] as well as any types above it
346 // in the class hierarchy (including mixins, superclasses, and interfaces).
347 // If a given completion identifier shows up in multiple types, we should
348 // use the element that is nearest in the superclass chain, so we will
349 // visit [type] first, then its mixins, then its superclass, then its
350 // superclass's mixins, etc., and only afterwards visit interfaces.
351 //
352 // We short-circuit loops in the class hierarchy by keeping track of the
353 // classes seen (not the interfaces) so that we won't be fooled by nonsense
354 // like "class C<T> extends C<List<T>> {}"
355 List<InterfaceType> result = <InterfaceType>[];
356 Set<ClassElement> classesSeen = new HashSet<ClassElement>();
357 List<InterfaceType> typesToVisit = <InterfaceType>[type];
358 while (typesToVisit.isNotEmpty) {
359 InterfaceType nextType = typesToVisit.removeLast();
360 if (!classesSeen.add(nextType.element)) {
361 // Class had already been seen, so ignore this type.
362 continue;
363 }
364 result.add(nextType);
365 // typesToVisit is a stack, so push on the interfaces first, then the
366 // superclass, then the mixins. This will ensure that they are visited
367 // in the reverse order.
368 typesToVisit.addAll(nextType.interfaces);
369 if (nextType.superclass != null) {
370 typesToVisit.add(nextType.superclass);
371 }
372 typesToVisit.addAll(nextType.mixins);
373 }
374 return result;
375 }
376
377 /**
378 * Add suggestions for the visible members in the given interface
379 */
380 static void suggestionsFor(DartCompletionRequest request, DartType type) {
381 CompilationUnit compilationUnit =
382 request.node.getAncestor((AstNode node) => node is CompilationUnit);
383 LibraryElement library = compilationUnit.element.library;
384 if (type is DynamicTypeImpl) {
385 type = request.cache.objectClassElement.type;
386 }
387 if (type is InterfaceType) {
388 return new InterfaceTypeSuggestionBuilder(
389 request)._buildSuggestions(type, library);
390 }
391 }
392 }
393
394 /**
288 * This class visits elements in a library and provides suggestions based upon 395 * This class visits elements in a library and provides suggestions based upon
289 * the visible members in that library. Clients should call 396 * the visible members in that library. Clients should call
290 * [LibraryElementSuggestionBuilder.suggestionsFor]. 397 * [LibraryElementSuggestionBuilder.suggestionsFor].
291 */ 398 */
292 class LibraryElementSuggestionBuilder extends GeneralizingElementVisitor with 399 class LibraryElementSuggestionBuilder extends GeneralizingElementVisitor with
293 ElementSuggestionBuilder { 400 ElementSuggestionBuilder {
294 final DartCompletionRequest request; 401 final DartCompletionRequest request;
295 final CompletionSuggestionKind kind; 402 final CompletionSuggestionKind kind;
296 403
297 LibraryElementSuggestionBuilder(this.request, this.kind); 404 LibraryElementSuggestionBuilder(this.request, this.kind);
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
385 addSuggestion(element); 492 addSuggestion(element);
386 } 493 }
387 494
388 @override 495 @override
389 visitElement(Element element) { 496 visitElement(Element element) {
390 // ignored 497 // ignored
391 } 498 }
392 } 499 }
393 500
394 /** 501 /**
502 * This class visits elements in a class and provides suggestions based upon
503 * the visible static members in that class. Clients should call
504 * [StaticClassElementSuggestionBuilder.suggestionsFor].
505 */
506 class StaticClassElementSuggestionBuilder extends GeneralizingElementVisitor
507 with ElementSuggestionBuilder {
508 final DartCompletionRequest request;
509
510 StaticClassElementSuggestionBuilder(this.request);
511
512 @override
513 CompletionSuggestionKind get kind => CompletionSuggestionKind.INVOCATION;
514
515 @override
516 visitClassElement(ClassElement element) {
517 element.visitChildren(this);
518 element.allSupertypes.forEach((InterfaceType type) {
519 type.element.visitChildren(this);
520 });
521 }
522
523 @override
524 visitElement(Element element) {
525 // ignored
526 }
527
528 @override
529 visitFieldElement(FieldElement element) {
530 if (!element.isStatic) {
531 return;
532 }
533 addSuggestion(element);
534 }
535
536 @override
537 visitMethodElement(MethodElement element) {
538 if (!element.isStatic) {
539 return;
540 }
541 if (element.isOperator) {
542 return;
543 }
544 addSuggestion(element);
545 }
546
547 @override
548 visitPropertyAccessorElement(PropertyAccessorElement element) {
549 if (!element.isStatic) {
550 return;
551 }
552 addSuggestion(element);
553 }
554
555 /**
556 * Add suggestions for the visible members in the given class
557 */
558 static void suggestionsFor(DartCompletionRequest request, Element element) {
559 if (element == DynamicElementImpl.instance) {
560 element = request.cache.objectClassElement;
561 }
562 if (element is ClassElement) {
563 return element.accept(new StaticClassElementSuggestionBuilder(request));
564 }
565 }
566 }
567
568 /**
395 * Common interface implemented by suggestion builders. 569 * Common interface implemented by suggestion builders.
396 */ 570 */
397 abstract class SuggestionBuilder { 571 abstract class SuggestionBuilder {
398 /** 572 /**
399 * Compute suggestions and return `true` if building is complete, 573 * Compute suggestions and return `true` if building is complete,
400 * or `false` if [computeFull] should be called. 574 * or `false` if [computeFull] should be called.
401 */ 575 */
402 bool computeFast(AstNode node); 576 bool computeFast(AstNode node);
403 577
404 /** 578 /**
405 * Return a future that computes the suggestions given a fully resolved AST. 579 * Return a future that computes the suggestions given a fully resolved AST.
406 * The future returns `true` if suggestions were added, else `false`. 580 * The future returns `true` if suggestions were added, else `false`.
407 */ 581 */
408 Future<bool> computeFull(AstNode node); 582 Future<bool> computeFull(AstNode node);
409 } 583 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698