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

Side by Side Diff: packages/analyzer/lib/src/dart/resolver/inheritance_manager.dart

Issue 2990843002: Removed fixed dependencies (Closed)
Patch Set: Created 3 years, 4 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
OLDNEW
(Empty)
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
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.
4
5 import 'dart:collection';
6
7 import 'package:analyzer/dart/ast/ast.dart';
8 import 'package:analyzer/dart/ast/token.dart';
9 import 'package:analyzer/dart/element/element.dart';
10 import 'package:analyzer/dart/element/type.dart';
11 import 'package:analyzer/error/error.dart';
12 import 'package:analyzer/exception/exception.dart';
13 import 'package:analyzer/src/dart/ast/token.dart';
14 import 'package:analyzer/src/dart/element/element.dart';
15 import 'package:analyzer/src/dart/element/member.dart';
16 import 'package:analyzer/src/dart/element/type.dart';
17 import 'package:analyzer/src/error/codes.dart';
18 import 'package:analyzer/src/generated/type_system.dart';
19 import 'package:analyzer/src/generated/utilities_dart.dart';
20
21 /**
22 * Instances of the class `InheritanceManager` manage the knowledge of where cla ss members
23 * (methods, getters & setters) are inherited from.
24 */
25 class InheritanceManager {
26 /**
27 * The [LibraryElement] that is managed by this manager.
28 */
29 LibraryElement _library;
30
31 /**
32 * A flag indicating whether abstract methods should be included when looking
33 * up the superclass chain.
34 */
35 bool _includeAbstractFromSuperclasses;
36
37 /**
38 * This is a mapping between each [ClassElement] and a map between the [String ] member
39 * names and the associated [ExecutableElement] in the mixin and superclass ch ain.
40 */
41 Map<ClassElement, Map<String, ExecutableElement>> _classLookup;
42
43 /**
44 * This is a mapping between each [ClassElement] and a map between the [String ] member
45 * names and the associated [ExecutableElement] in the interface set.
46 */
47 Map<ClassElement, Map<String, ExecutableElement>> _interfaceLookup;
48
49 /**
50 * A map between each visited [ClassElement] and the set of [AnalysisError]s f ound on
51 * the class element.
52 */
53 Map<ClassElement, Set<AnalysisError>> _errorsInClassElement =
54 new HashMap<ClassElement, Set<AnalysisError>>();
55
56 /**
57 * Initialize a newly created inheritance manager.
58 *
59 * @param library the library element context that the inheritance mappings ar e being generated
60 */
61 InheritanceManager(LibraryElement library,
62 {bool includeAbstractFromSuperclasses: false}) {
63 this._library = library;
64 _includeAbstractFromSuperclasses = includeAbstractFromSuperclasses;
65 _classLookup = new HashMap<ClassElement, Map<String, ExecutableElement>>();
66 _interfaceLookup =
67 new HashMap<ClassElement, Map<String, ExecutableElement>>();
68 }
69
70 /**
71 * Set the new library element context.
72 *
73 * @param library the new library element
74 */
75 void set libraryElement(LibraryElement library) {
76 this._library = library;
77 }
78
79 /**
80 * Return the set of [AnalysisError]s found on the passed [ClassElement], or
81 * `null` if there are none.
82 *
83 * @param classElt the class element to query
84 * @return the set of [AnalysisError]s found on the passed [ClassElement], or
85 * `null` if there are none
86 */
87 Set<AnalysisError> getErrors(ClassElement classElt) =>
88 _errorsInClassElement[classElt];
89
90 /**
91 * Get and return a mapping between the set of all string names of the members inherited from the
92 * passed [ClassElement] superclass hierarchy, and the associated [ExecutableE lement].
93 *
94 * @param classElt the class element to query
95 * @return a mapping between the set of all members inherited from the passed [ClassElement]
96 * superclass hierarchy, and the associated [ExecutableElement]
97 */
98 @deprecated
99 MemberMap getMapOfMembersInheritedFromClasses(ClassElement classElt) =>
100 new MemberMap.fromMap(
101 _computeClassChainLookupMap(classElt, new HashSet<ClassElement>()));
102
103 /**
104 * Get and return a mapping between the set of all string names of the members inherited from the
105 * passed [ClassElement] interface hierarchy, and the associated [ExecutableEl ement].
106 *
107 * @param classElt the class element to query
108 * @return a mapping between the set of all string names of the members inheri ted from the passed
109 * [ClassElement] interface hierarchy, and the associated [ExecutableE lement].
110 */
111 @deprecated
112 MemberMap getMapOfMembersInheritedFromInterfaces(ClassElement classElt) =>
113 new MemberMap.fromMap(
114 _computeInterfaceLookupMap(classElt, new HashSet<ClassElement>()));
115
116 /**
117 * Return a table mapping the string names of the members inherited from the
118 * passed [ClassElement]'s superclass hierarchy, and the associated executable
119 * element.
120 */
121 Map<String, ExecutableElement> getMembersInheritedFromClasses(
122 ClassElement classElt) =>
123 _computeClassChainLookupMap(classElt, new HashSet<ClassElement>());
124
125 /**
126 * Return a table mapping the string names of the members inherited from the
127 * passed [ClassElement]'s interface hierarchy, and the associated executable
128 * element.
129 */
130 Map<String, ExecutableElement> getMembersInheritedFromInterfaces(
131 ClassElement classElt) =>
132 _computeInterfaceLookupMap(classElt, new HashSet<ClassElement>());
133
134 /**
135 * Given some [ClassElement] and some member name, this returns the
136 * [ExecutableElement] that the class inherits from the mixins,
137 * superclasses or interfaces, that has the member name, if no member is inher ited `null` is
138 * returned.
139 *
140 * @param classElt the class element to query
141 * @param memberName the name of the executable element to find and return
142 * @return the inherited executable element with the member name, or `null` if no such
143 * member exists
144 */
145 ExecutableElement lookupInheritance(
146 ClassElement classElt, String memberName) {
147 if (memberName == null || memberName.isEmpty) {
148 return null;
149 }
150 ExecutableElement executable = _computeClassChainLookupMap(
151 classElt, new HashSet<ClassElement>())[memberName];
152 if (executable == null) {
153 return _computeInterfaceLookupMap(classElt, new HashSet<ClassElement>())[
154 memberName];
155 }
156 return executable;
157 }
158
159 /**
160 * Given some [ClassElement] and some member name, this returns the
161 * [ExecutableElement] that the class either declares itself, or
162 * inherits, that has the member name, if no member is inherited `null` is ret urned.
163 *
164 * @param classElt the class element to query
165 * @param memberName the name of the executable element to find and return
166 * @return the inherited executable element with the member name, or `null` if no such
167 * member exists
168 */
169 ExecutableElement lookupMember(ClassElement classElt, String memberName) {
170 ExecutableElement element = _lookupMemberInClass(classElt, memberName);
171 if (element != null) {
172 return element;
173 }
174 return lookupInheritance(classElt, memberName);
175 }
176
177 /**
178 * Determine the set of methods which is overridden by the given class member. If no member is
179 * inherited, an empty list is returned. If one of the inherited members is a
180 * [MultiplyInheritedExecutableElement], then it is expanded into its constitu ent inherited
181 * elements.
182 *
183 * @param classElt the class to query
184 * @param memberName the name of the class member to query
185 * @return a list of overridden methods
186 */
187 List<ExecutableElement> lookupOverrides(
188 ClassElement classElt, String memberName) {
189 List<ExecutableElement> result = new List<ExecutableElement>();
190 if (memberName == null || memberName.isEmpty) {
191 return result;
192 }
193 List<Map<String, ExecutableElement>> interfaceMaps =
194 _gatherInterfaceLookupMaps(classElt, new HashSet<ClassElement>());
195 if (interfaceMaps != null) {
196 for (Map<String, ExecutableElement> interfaceMap in interfaceMaps) {
197 ExecutableElement overriddenElement = interfaceMap[memberName];
198 if (overriddenElement != null) {
199 if (overriddenElement is MultiplyInheritedExecutableElement) {
200 for (ExecutableElement element
201 in overriddenElement.inheritedElements) {
202 result.add(element);
203 }
204 } else {
205 result.add(overriddenElement);
206 }
207 }
208 }
209 }
210 return result;
211 }
212
213 /**
214 * This method takes some inherited [FunctionType], and resolves all the param eterized types
215 * in the function type, dependent on the class in which it is being overridde n.
216 *
217 * @param baseFunctionType the function type that is being overridden
218 * @param memberName the name of the member, this is used to lookup the inheri tance path of the
219 * override
220 * @param definingType the type that is overriding the member
221 * @return the passed function type with any parameterized types substituted
222 */
223 // TODO(jmesserly): investigate why this is needed in ErrorVerifier's override
224 // checking. There seems to be some rare cases where we get partially
225 // substituted type arguments, and the function types don't compare equally.
226 FunctionType substituteTypeArgumentsInMemberFromInheritance(
227 FunctionType baseFunctionType,
228 String memberName,
229 InterfaceType definingType) {
230 // if the baseFunctionType is null, or does not have any parameters,
231 // return it.
232 if (baseFunctionType == null ||
233 baseFunctionType.typeArguments.length == 0) {
234 return baseFunctionType;
235 }
236 // First, generate the path from the defining type to the overridden member
237 Queue<InterfaceType> inheritancePath = new Queue<InterfaceType>();
238 _computeInheritancePath(inheritancePath, definingType, memberName);
239 if (inheritancePath == null || inheritancePath.isEmpty) {
240 // TODO(jwren) log analysis engine error
241 return baseFunctionType;
242 }
243 FunctionType functionTypeToReturn = baseFunctionType;
244 // loop backward through the list substituting as we go:
245 while (!inheritancePath.isEmpty) {
246 InterfaceType lastType = inheritancePath.removeLast();
247 List<DartType> parameterTypes = lastType.element.type.typeArguments;
248 List<DartType> argumentTypes = lastType.typeArguments;
249 functionTypeToReturn =
250 functionTypeToReturn.substitute2(argumentTypes, parameterTypes);
251 }
252 return functionTypeToReturn;
253 }
254
255 /**
256 * Compute and return a mapping between the set of all string names of the mem bers inherited from
257 * the passed [ClassElement] superclass hierarchy, and the associated
258 * [ExecutableElement].
259 *
260 * @param classElt the class element to query
261 * @param visitedClasses a set of visited classes passed back into this method when it calls
262 * itself recursively
263 * @return a mapping between the set of all string names of the members inheri ted from the passed
264 * [ClassElement] superclass hierarchy, and the associated [Executable Element]
265 */
266 Map<String, ExecutableElement> _computeClassChainLookupMap(
267 ClassElement classElt, Set<ClassElement> visitedClasses) {
268 Map<String, ExecutableElement> resultMap = _classLookup[classElt];
269 if (resultMap != null) {
270 return resultMap;
271 } else {
272 resultMap = new Map<String, ExecutableElement>();
273 }
274 InterfaceType supertype = classElt.supertype;
275 if (supertype == null) {
276 // classElt is Object
277 _classLookup[classElt] = resultMap;
278 return resultMap;
279 }
280 ClassElement superclassElt = supertype.element;
281 if (superclassElt != null) {
282 if (!visitedClasses.contains(superclassElt)) {
283 visitedClasses.add(superclassElt);
284 try {
285 resultMap = new Map<String, ExecutableElement>.from(
286 _computeClassChainLookupMap(superclassElt, visitedClasses));
287 //
288 // Substitute the super types down the hierarchy.
289 //
290 _substituteTypeParametersDownHierarchy(supertype, resultMap);
291 //
292 // Include the members from the superclass in the resultMap.
293 //
294 _recordMapWithClassMembers(
295 resultMap, supertype, _includeAbstractFromSuperclasses);
296 } finally {
297 visitedClasses.remove(superclassElt);
298 }
299 } else {
300 // This case happens only when the superclass was previously visited and
301 // not in the lookup, meaning this is meant to shorten the compute for
302 // recursive cases.
303 _classLookup[superclassElt] = resultMap;
304 return resultMap;
305 }
306 }
307 //
308 // Include the members from the mixins in the resultMap. If there are
309 // multiple mixins, visit them in the order listed so that methods in later
310 // mixins will overwrite identically-named methods in earlier mixins.
311 //
312 List<InterfaceType> mixins = classElt.mixins;
313 for (InterfaceType mixin in mixins) {
314 ClassElement mixinElement = mixin.element;
315 if (mixinElement != null) {
316 if (!visitedClasses.contains(mixinElement)) {
317 visitedClasses.add(mixinElement);
318 try {
319 Map<String, ExecutableElement> map =
320 new Map<String, ExecutableElement>();
321 //
322 // Include the members from the mixin in the resultMap.
323 //
324 _recordMapWithClassMembers(
325 map, mixin, _includeAbstractFromSuperclasses);
326 //
327 // Add the members from map into result map.
328 //
329 for (String memberName in map.keys) {
330 ExecutableElement value = map[memberName];
331 ClassElement definingClass = value
332 .getAncestor((Element element) => element is ClassElement);
333 if (!definingClass.type.isObject) {
334 ExecutableElement existingValue = resultMap[memberName];
335 if (existingValue == null ||
336 (existingValue != null && !_isAbstract(value))) {
337 resultMap[memberName] = value;
338 }
339 }
340 }
341 } finally {
342 visitedClasses.remove(mixinElement);
343 }
344 } else {
345 // This case happens only when the superclass was previously visited
346 // and not in the lookup, meaning this is meant to shorten the compute
347 // for recursive cases.
348 _classLookup[mixinElement] = resultMap;
349 return resultMap;
350 }
351 }
352 }
353 _classLookup[classElt] = resultMap;
354 return resultMap;
355 }
356
357 /**
358 * Compute and return the inheritance path given the context of a type and a m ember that is
359 * overridden in the inheritance path (for which the type is in the path).
360 *
361 * @param chain the inheritance path that is built up as this method calls its elf recursively,
362 * when this method is called an empty [LinkedList] should be provide d
363 * @param currentType the current type in the inheritance path
364 * @param memberName the name of the member that is being looked up the inheri tance path
365 */
366 void _computeInheritancePath(Queue<InterfaceType> chain,
367 InterfaceType currentType, String memberName) {
368 // TODO (jwren) create a public version of this method which doesn't require
369 // the initial chain to be provided, then provided tests for this
370 // functionality in InheritanceManagerTest
371 chain.add(currentType);
372 ClassElement classElt = currentType.element;
373 InterfaceType supertype = classElt.supertype;
374 // Base case- reached Object
375 if (supertype == null) {
376 // Looked up the chain all the way to Object, return null.
377 // This should never happen.
378 return;
379 }
380 // If we are done, return the chain
381 // We are not done if this is the first recursive call on this method.
382 if (chain.length != 1) {
383 // We are done however if the member is in this classElt
384 if (_lookupMemberInClass(classElt, memberName) != null) {
385 return;
386 }
387 }
388 // Mixins- note that mixins call lookupMemberInClass, not lookupMember
389 List<InterfaceType> mixins = classElt.mixins;
390 for (int i = mixins.length - 1; i >= 0; i--) {
391 ClassElement mixinElement = mixins[i].element;
392 if (mixinElement != null) {
393 ExecutableElement elt = _lookupMemberInClass(mixinElement, memberName);
394 if (elt != null) {
395 // this is equivalent (but faster than) calling this method
396 // recursively
397 // (return computeInheritancePath(chain, mixins[i], memberName);)
398 chain.add(mixins[i]);
399 return;
400 }
401 }
402 }
403 // Superclass
404 ClassElement superclassElt = supertype.element;
405 if (lookupMember(superclassElt, memberName) != null) {
406 _computeInheritancePath(chain, supertype, memberName);
407 return;
408 }
409 // Interfaces
410 List<InterfaceType> interfaces = classElt.interfaces;
411 for (InterfaceType interfaceType in interfaces) {
412 ClassElement interfaceElement = interfaceType.element;
413 if (interfaceElement != null &&
414 lookupMember(interfaceElement, memberName) != null) {
415 _computeInheritancePath(chain, interfaceType, memberName);
416 return;
417 }
418 }
419 }
420
421 /**
422 * Compute and return a mapping between the set of all string names of the mem bers inherited from
423 * the passed [ClassElement] interface hierarchy, and the associated
424 * [ExecutableElement].
425 *
426 * @param classElt the class element to query
427 * @param visitedInterfaces a set of visited classes passed back into this met hod when it calls
428 * itself recursively
429 * @return a mapping between the set of all string names of the members inheri ted from the passed
430 * [ClassElement] interface hierarchy, and the associated [ExecutableE lement]
431 */
432 Map<String, ExecutableElement> _computeInterfaceLookupMap(
433 ClassElement classElt, HashSet<ClassElement> visitedInterfaces) {
434 Map<String, ExecutableElement> resultMap = _interfaceLookup[classElt];
435 if (resultMap != null) {
436 return resultMap;
437 }
438 List<Map<String, ExecutableElement>> lookupMaps =
439 _gatherInterfaceLookupMaps(classElt, visitedInterfaces);
440 if (lookupMaps == null) {
441 resultMap = new Map<String, ExecutableElement>();
442 } else {
443 HashMap<String, List<ExecutableElement>> unionMap =
444 _unionInterfaceLookupMaps(lookupMaps);
445 resultMap = _resolveInheritanceLookup(classElt, unionMap);
446 }
447 _interfaceLookup[classElt] = resultMap;
448 return resultMap;
449 }
450
451 /**
452 * Collect a list of interface lookup maps whose elements correspond to all of the classes
453 * directly above [classElt] in the class hierarchy (the direct superclass if any, all
454 * mixins, and all direct superinterfaces). Each item in the list is the inter face lookup map
455 * returned by [computeInterfaceLookupMap] for the corresponding super, except with type
456 * parameters appropriately substituted.
457 *
458 * @param classElt the class element to query
459 * @param visitedInterfaces a set of visited classes passed back into this met hod when it calls
460 * itself recursively
461 * @return `null` if there was a problem (such as a loop in the class hierarch y) or if there
462 * are no classes above this one in the class hierarchy. Otherwise, a list of interface
463 * lookup maps.
464 */
465 List<Map<String, ExecutableElement>> _gatherInterfaceLookupMaps(
466 ClassElement classElt, HashSet<ClassElement> visitedInterfaces) {
467 InterfaceType supertype = classElt.supertype;
468 ClassElement superclassElement = supertype?.element;
469 List<InterfaceType> mixins = classElt.mixins;
470 List<InterfaceType> interfaces = classElt.interfaces;
471 // Recursively collect the list of mappings from all of the interface types
472 List<Map<String, ExecutableElement>> lookupMaps =
473 new List<Map<String, ExecutableElement>>();
474 //
475 // Superclass element
476 //
477 if (superclassElement != null) {
478 if (!visitedInterfaces.contains(superclassElement)) {
479 try {
480 visitedInterfaces.add(superclassElement);
481 //
482 // Recursively compute the map for the super type.
483 //
484 Map<String, ExecutableElement> map =
485 _computeInterfaceLookupMap(superclassElement, visitedInterfaces);
486 map = new Map<String, ExecutableElement>.from(map);
487 //
488 // Substitute the super type down the hierarchy.
489 //
490 _substituteTypeParametersDownHierarchy(supertype, map);
491 //
492 // Add any members from the super type into the map as well.
493 //
494 _recordMapWithClassMembers(map, supertype, true);
495 lookupMaps.add(map);
496 } finally {
497 visitedInterfaces.remove(superclassElement);
498 }
499 } else {
500 return null;
501 }
502 }
503 //
504 // Mixin elements
505 //
506 for (int i = mixins.length - 1; i >= 0; i--) {
507 InterfaceType mixinType = mixins[i];
508 ClassElement mixinElement = mixinType.element;
509 if (mixinElement != null) {
510 if (!visitedInterfaces.contains(mixinElement)) {
511 try {
512 visitedInterfaces.add(mixinElement);
513 //
514 // Recursively compute the map for the mixin.
515 //
516 Map<String, ExecutableElement> map =
517 _computeInterfaceLookupMap(mixinElement, visitedInterfaces);
518 map = new Map<String, ExecutableElement>.from(map);
519 //
520 // Substitute the mixin type down the hierarchy.
521 //
522 _substituteTypeParametersDownHierarchy(mixinType, map);
523 //
524 // Add any members from the mixin type into the map as well.
525 //
526 _recordMapWithClassMembers(map, mixinType, true);
527 lookupMaps.add(map);
528 } finally {
529 visitedInterfaces.remove(mixinElement);
530 }
531 } else {
532 return null;
533 }
534 }
535 }
536 //
537 // Interface elements
538 //
539 int interfaceLength = interfaces.length;
540 for (int i = 0; i < interfaceLength; i++) {
541 InterfaceType interfaceType = interfaces[i];
542 ClassElement interfaceElement = interfaceType.element;
543 if (interfaceElement != null) {
544 if (!visitedInterfaces.contains(interfaceElement)) {
545 try {
546 visitedInterfaces.add(interfaceElement);
547 //
548 // Recursively compute the map for the interfaces.
549 //
550 Map<String, ExecutableElement> map =
551 _computeInterfaceLookupMap(interfaceElement, visitedInterfaces);
552 map = new Map<String, ExecutableElement>.from(map);
553 //
554 // Substitute the supertypes down the hierarchy
555 //
556 _substituteTypeParametersDownHierarchy(interfaceType, map);
557 //
558 // And add any members from the interface into the map as well.
559 //
560 _recordMapWithClassMembers(map, interfaceType, true);
561 lookupMaps.add(map);
562 } finally {
563 visitedInterfaces.remove(interfaceElement);
564 }
565 } else {
566 return null;
567 }
568 }
569 }
570 if (lookupMaps.length == 0) {
571 return null;
572 }
573 return lookupMaps;
574 }
575
576 /**
577 * Given some [classElement], this method finds and returns the executable
578 * element with the given [memberName] in the class element. Static members,
579 * members in super types and members not accessible from the current library
580 * are not considered.
581 */
582 ExecutableElement _lookupMemberInClass(
583 ClassElement classElement, String memberName) {
584 List<MethodElement> methods = classElement.methods;
585 int methodLength = methods.length;
586 for (int i = 0; i < methodLength; i++) {
587 MethodElement method = methods[i];
588 if (memberName == method.name &&
589 method.isAccessibleIn(_library) &&
590 !method.isStatic) {
591 return method;
592 }
593 }
594 List<PropertyAccessorElement> accessors = classElement.accessors;
595 int accessorLength = accessors.length;
596 for (int i = 0; i < accessorLength; i++) {
597 PropertyAccessorElement accessor = accessors[i];
598 if (memberName == accessor.name &&
599 accessor.isAccessibleIn(_library) &&
600 !accessor.isStatic) {
601 return accessor;
602 }
603 }
604 return null;
605 }
606
607 /**
608 * Record the passed map with the set of all members (methods, getters and set ters) in the type
609 * into the passed map.
610 *
611 * @param map some non-`null` map to put the methods and accessors from the pa ssed
612 * [ClassElement] into
613 * @param type the type that will be recorded into the passed map
614 * @param doIncludeAbstract `true` if abstract members will be put into the ma p
615 */
616 void _recordMapWithClassMembers(Map<String, ExecutableElement> map,
617 InterfaceType type, bool doIncludeAbstract) {
618 Set<InterfaceType> seenTypes = new HashSet<InterfaceType>();
619 while (type.element.isMixinApplication) {
620 List<InterfaceType> mixins = type.mixins;
621 if (!seenTypes.add(type) || mixins.isEmpty) {
622 // In the case of a circularity in the type hierarchy, just don't add
623 // any members to the map.
624 return;
625 }
626 type = mixins.last;
627 }
628 List<MethodElement> methods = type.methods;
629 for (MethodElement method in methods) {
630 if (method.isAccessibleIn(_library) &&
631 !method.isStatic &&
632 (doIncludeAbstract || !method.isAbstract)) {
633 map[method.name] = method;
634 }
635 }
636 List<PropertyAccessorElement> accessors = type.accessors;
637 for (PropertyAccessorElement accessor in accessors) {
638 if (accessor.isAccessibleIn(_library) &&
639 !accessor.isStatic &&
640 (doIncludeAbstract || !accessor.isAbstract)) {
641 map[accessor.name] = accessor;
642 }
643 }
644 }
645
646 /**
647 * This method is used to report errors on when they are found computing inher itance information.
648 * See [ErrorVerifier.checkForInconsistentMethodInheritance] to see where thes e generated
649 * error codes are reported back into the analysis engine.
650 *
651 * @param classElt the location of the source for which the exception occurred
652 * @param offset the offset of the location of the error
653 * @param length the length of the location of the error
654 * @param errorCode the error code to be associated with this error
655 * @param arguments the arguments used to build the error message
656 */
657 void _reportError(ClassElement classElt, int offset, int length,
658 ErrorCode errorCode, List<Object> arguments) {
659 HashSet<AnalysisError> errorSet = _errorsInClassElement[classElt];
660 if (errorSet == null) {
661 errorSet = new HashSet<AnalysisError>();
662 _errorsInClassElement[classElt] = errorSet;
663 }
664 errorSet.add(new AnalysisError(
665 classElt.source, offset, length, errorCode, arguments));
666 }
667
668 /**
669 * Given the set of methods defined by classes above [classElt] in the class h ierarchy,
670 * apply the appropriate inheritance rules to determine those methods inherite d by or overridden
671 * by [classElt]. Also report static warnings
672 * [StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE] and
673 * [StaticWarningCode.INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD] if ap propriate.
674 *
675 * @param classElt the class element to query.
676 * @param unionMap a mapping from method name to the set of unique (in terms o f signature) methods
677 * defined in superclasses of [classElt].
678 * @return the inheritance lookup map for [classElt].
679 */
680 Map<String, ExecutableElement> _resolveInheritanceLookup(
681 ClassElement classElt, Map<String, List<ExecutableElement>> unionMap) {
682 Map<String, ExecutableElement> resultMap =
683 new Map<String, ExecutableElement>();
684 unionMap.forEach((String key, List<ExecutableElement> list) {
685 int numOfEltsWithMatchingNames = list.length;
686 if (numOfEltsWithMatchingNames == 1) {
687 //
688 // Example: class A inherits only 1 method named 'm'.
689 // Since it is the only such method, it is inherited.
690 // Another example: class A inherits 2 methods named 'm' from 2
691 // different interfaces, but they both have the same signature, so it is
692 // the method inherited.
693 //
694 resultMap[key] = list[0];
695 } else {
696 //
697 // Then numOfEltsWithMatchingNames > 1, check for the warning cases.
698 //
699 bool allMethods = true;
700 bool allSetters = true;
701 bool allGetters = true;
702 for (ExecutableElement executableElement in list) {
703 if (executableElement is PropertyAccessorElement) {
704 allMethods = false;
705 if (executableElement.isSetter) {
706 allGetters = false;
707 } else {
708 allSetters = false;
709 }
710 } else {
711 allGetters = false;
712 allSetters = false;
713 }
714 }
715 //
716 // If there isn't a mixture of methods with getters, then continue,
717 // otherwise create a warning.
718 //
719 if (allMethods || allGetters || allSetters) {
720 //
721 // Compute the element whose type is the subtype of all of the other
722 // types.
723 //
724 List<ExecutableElement> elements = new List.from(list);
725 List<FunctionType> executableElementTypes =
726 new List<FunctionType>(numOfEltsWithMatchingNames);
727 for (int i = 0; i < numOfEltsWithMatchingNames; i++) {
728 executableElementTypes[i] = elements[i].type;
729 }
730 List<int> subtypesOfAllOtherTypesIndexes = new List<int>();
731 for (int i = 0; i < numOfEltsWithMatchingNames; i++) {
732 FunctionType subtype = executableElementTypes[i];
733 if (subtype == null) {
734 continue;
735 }
736 bool subtypeOfAllTypes = true;
737 TypeSystem typeSystem = _library.context.typeSystem;
738 for (int j = 0;
739 j < numOfEltsWithMatchingNames && subtypeOfAllTypes;
740 j++) {
741 if (i != j) {
742 if (!typeSystem.isSubtypeOf(
743 subtype, executableElementTypes[j])) {
744 subtypeOfAllTypes = false;
745 break;
746 }
747 }
748 }
749 if (subtypeOfAllTypes) {
750 subtypesOfAllOtherTypesIndexes.add(i);
751 }
752 }
753 //
754 // The following is split into three cases determined by the number of
755 // elements in subtypesOfAllOtherTypes
756 //
757 if (subtypesOfAllOtherTypesIndexes.length == 1) {
758 //
759 // Example: class A inherited only 2 method named 'm'.
760 // One has the function type '() -> dynamic' and one has the
761 // function type '([int]) -> dynamic'. Since the second method is a
762 // subtype of all the others, it is the inherited method.
763 // Tests: InheritanceManagerTest.
764 // test_getMapOfMembersInheritedFromInterfaces_union_oneSubtype_*
765 //
766 resultMap[key] = elements[subtypesOfAllOtherTypesIndexes[0]];
767 } else {
768 if (subtypesOfAllOtherTypesIndexes.isEmpty) {
769 //
770 // Determine if the current class has a method or accessor with
771 // the member name, if it does then then this class does not
772 // "inherit" from any of the supertypes. See issue 16134.
773 //
774 bool classHasMember = false;
775 if (allMethods) {
776 classHasMember = classElt.getMethod(key) != null;
777 } else {
778 List<PropertyAccessorElement> accessors = classElt.accessors;
779 for (int i = 0; i < accessors.length; i++) {
780 if (accessors[i].name == key) {
781 classHasMember = true;
782 }
783 }
784 }
785 //
786 // Example: class A inherited only 2 method named 'm'.
787 // One has the function type '() -> int' and one has the function
788 // type '() -> String'. Since neither is a subtype of the other,
789 // we create a warning, and have this class inherit nothing.
790 //
791 if (!classHasMember) {
792 String firstTwoFuntionTypesStr =
793 "${executableElementTypes[0]}, ${executableElementTypes[1]}" ;
794 _reportError(
795 classElt,
796 classElt.nameOffset,
797 classElt.nameLength,
798 StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE,
799 [key, firstTwoFuntionTypesStr]);
800 }
801 } else {
802 //
803 // Example: class A inherits 2 methods named 'm'.
804 // One has the function type '(int) -> dynamic' and one has the
805 // function type '(num) -> dynamic'. Since they are both a subtype
806 // of the other, a synthetic function '(dynamic) -> dynamic' is
807 // inherited.
808 // Tests: test_getMapOfMembersInheritedFromInterfaces_
809 // union_multipleSubtypes_*
810 //
811 List<ExecutableElement> elementArrayToMerge =
812 new List<ExecutableElement>(
813 subtypesOfAllOtherTypesIndexes.length);
814 for (int i = 0; i < elementArrayToMerge.length; i++) {
815 elementArrayToMerge[i] =
816 elements[subtypesOfAllOtherTypesIndexes[i]];
817 }
818 ExecutableElement mergedExecutableElement =
819 _computeMergedExecutableElement(elementArrayToMerge);
820 resultMap[key] = mergedExecutableElement;
821 }
822 }
823 } else {
824 _reportError(
825 classElt,
826 classElt.nameOffset,
827 classElt.nameLength,
828 StaticWarningCode
829 .INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD,
830 [key]);
831 }
832 }
833 });
834 return resultMap;
835 }
836
837 /**
838 * Loop through all of the members in the given [map], performing type
839 * parameter substitutions using a passed [supertype].
840 */
841 void _substituteTypeParametersDownHierarchy(
842 InterfaceType superType, Map<String, ExecutableElement> map) {
843 for (String memberName in map.keys) {
844 ExecutableElement executableElement = map[memberName];
845 if (executableElement is MethodMember) {
846 map[memberName] = MethodMember.from(executableElement, superType);
847 } else if (executableElement is PropertyAccessorMember) {
848 map[memberName] =
849 PropertyAccessorMember.from(executableElement, superType);
850 }
851 }
852 }
853
854 /**
855 * Union all of the [lookupMaps] together into a single map, grouping the Exec utableElements
856 * into a list where none of the elements are equal where equality is determin ed by having equal
857 * function types. (We also take note too of the kind of the element: ()->int and () -> int may
858 * not be equal if one is a getter and the other is a method.)
859 *
860 * @param lookupMaps the maps to be unioned together.
861 * @return the resulting union map.
862 */
863 HashMap<String, List<ExecutableElement>> _unionInterfaceLookupMaps(
864 List<Map<String, ExecutableElement>> lookupMaps) {
865 HashMap<String, List<ExecutableElement>> unionMap =
866 new HashMap<String, List<ExecutableElement>>();
867 for (Map<String, ExecutableElement> lookupMap in lookupMaps) {
868 for (String memberName in lookupMap.keys) {
869 // Get the list value out of the unionMap
870 List<ExecutableElement> list = unionMap.putIfAbsent(
871 memberName, () => new List<ExecutableElement>());
872 // Fetch the entry out of this lookupMap
873 ExecutableElement newExecutableElementEntry = lookupMap[memberName];
874 if (list.isEmpty) {
875 // If the list is empty, just the new value
876 list.add(newExecutableElementEntry);
877 } else {
878 // Otherwise, only add the newExecutableElementEntry if it isn't
879 // already in the list, this covers situation where a class inherits
880 // two methods (or two getters) that are identical.
881 bool alreadyInList = false;
882 bool isMethod1 = newExecutableElementEntry is MethodElement;
883 for (ExecutableElement executableElementInList in list) {
884 bool isMethod2 = executableElementInList is MethodElement;
885 if (isMethod1 == isMethod2 &&
886 executableElementInList.type ==
887 newExecutableElementEntry.type) {
888 alreadyInList = true;
889 break;
890 }
891 }
892 if (!alreadyInList) {
893 list.add(newExecutableElementEntry);
894 }
895 }
896 }
897 }
898 return unionMap;
899 }
900
901 /**
902 * Given some array of [ExecutableElement]s, this method creates a synthetic e lement as
903 * described in 8.1.1:
904 *
905 * Let <i>numberOfPositionals</i>(<i>f</i>) denote the number of positional pa rameters of a
906 * function <i>f</i>, and let <i>numberOfRequiredParams</i>(<i>f</i>) denote t he number of
907 * required parameters of a function <i>f</i>. Furthermore, let <i>s</i> denot e the set of all
908 * named parameters of the <i>m<sub>1</sub>, &hellip;, m<sub>k</sub></i>. Then let
909 * * <i>h = max(numberOfPositionals(m<sub>i</sub>)),</i>
910 * * <i>r = min(numberOfRequiredParams(m<sub>i</sub>)), for all <i>i</i>, 1 <= i <= k.</i>
911 * Then <i>I</i> has a method named <i>n</i>, with <i>r</i> required parameter s of type
912 * <b>dynamic</b>, <i>h</i> positional parameters of type <b>dynamic</b>, name d parameters
913 * <i>s</i> of type <b>dynamic</b> and return type <b>dynamic</b>.
914 *
915 */
916 static ExecutableElement _computeMergedExecutableElement(
917 List<ExecutableElement> elementArrayToMerge) {
918 int h = _getNumOfPositionalParameters(elementArrayToMerge[0]);
919 int r = _getNumOfRequiredParameters(elementArrayToMerge[0]);
920 Set<String> namedParametersList = new HashSet<String>();
921 for (int i = 1; i < elementArrayToMerge.length; i++) {
922 ExecutableElement element = elementArrayToMerge[i];
923 int numOfPositionalParams = _getNumOfPositionalParameters(element);
924 if (h < numOfPositionalParams) {
925 h = numOfPositionalParams;
926 }
927 int numOfRequiredParams = _getNumOfRequiredParameters(element);
928 if (r > numOfRequiredParams) {
929 r = numOfRequiredParams;
930 }
931 namedParametersList.addAll(_getNamedParameterNames(element));
932 }
933 return _createSyntheticExecutableElement(
934 elementArrayToMerge,
935 elementArrayToMerge[0].displayName,
936 r,
937 h - r,
938 new List.from(namedParametersList));
939 }
940
941 /**
942 * Used by [computeMergedExecutableElement] to actually create the
943 * synthetic element.
944 *
945 * @param elementArrayToMerge the array used to create the synthetic element
946 * @param name the name of the method, getter or setter
947 * @param numOfRequiredParameters the number of required parameters
948 * @param numOfPositionalParameters the number of positional parameters
949 * @param namedParameters the list of [String]s that are the named parameters
950 * @return the created synthetic element
951 */
952 static ExecutableElement _createSyntheticExecutableElement(
953 List<ExecutableElement> elementArrayToMerge,
954 String name,
955 int numOfRequiredParameters,
956 int numOfPositionalParameters,
957 List<String> namedParameters) {
958 DynamicTypeImpl dynamicType = DynamicTypeImpl.instance;
959 SimpleIdentifier nameIdentifier =
960 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, 0));
961 ExecutableElementImpl executable;
962 ExecutableElement elementToMerge = elementArrayToMerge[0];
963 if (elementToMerge is MethodElement) {
964 MultiplyInheritedMethodElementImpl unionedMethod =
965 new MultiplyInheritedMethodElementImpl(nameIdentifier);
966 unionedMethod.inheritedElements = elementArrayToMerge;
967 executable = unionedMethod;
968 } else if (elementToMerge is PropertyAccessorElement) {
969 MultiplyInheritedPropertyAccessorElementImpl unionedPropertyAccessor =
970 new MultiplyInheritedPropertyAccessorElementImpl(nameIdentifier);
971 unionedPropertyAccessor.getter = elementToMerge.isGetter;
972 unionedPropertyAccessor.setter = elementToMerge.isSetter;
973 unionedPropertyAccessor.inheritedElements = elementArrayToMerge;
974 executable = unionedPropertyAccessor;
975 } else {
976 throw new AnalysisException(
977 'Invalid class of element in merge: ${elementToMerge.runtimeType}');
978 }
979 int numOfParameters = numOfRequiredParameters +
980 numOfPositionalParameters +
981 namedParameters.length;
982 List<ParameterElement> parameters =
983 new List<ParameterElement>(numOfParameters);
984 int i = 0;
985 for (int j = 0; j < numOfRequiredParameters; j++, i++) {
986 ParameterElementImpl parameter = new ParameterElementImpl("", 0);
987 parameter.type = dynamicType;
988 parameter.parameterKind = ParameterKind.REQUIRED;
989 parameters[i] = parameter;
990 }
991 for (int k = 0; k < numOfPositionalParameters; k++, i++) {
992 ParameterElementImpl parameter = new ParameterElementImpl("", 0);
993 parameter.type = dynamicType;
994 parameter.parameterKind = ParameterKind.POSITIONAL;
995 parameters[i] = parameter;
996 }
997 for (int m = 0; m < namedParameters.length; m++, i++) {
998 ParameterElementImpl parameter =
999 new ParameterElementImpl(namedParameters[m], 0);
1000 parameter.type = dynamicType;
1001 parameter.parameterKind = ParameterKind.NAMED;
1002 parameters[i] = parameter;
1003 }
1004 executable.returnType = dynamicType;
1005 executable.parameters = parameters;
1006 FunctionTypeImpl methodType = new FunctionTypeImpl(executable);
1007 executable.type = methodType;
1008 return executable;
1009 }
1010
1011 /**
1012 * Given some [ExecutableElement], return the list of named parameters.
1013 */
1014 static List<String> _getNamedParameterNames(
1015 ExecutableElement executableElement) {
1016 List<String> namedParameterNames = new List<String>();
1017 List<ParameterElement> parameters = executableElement.parameters;
1018 for (int i = 0; i < parameters.length; i++) {
1019 ParameterElement parameterElement = parameters[i];
1020 if (parameterElement.parameterKind == ParameterKind.NAMED) {
1021 namedParameterNames.add(parameterElement.name);
1022 }
1023 }
1024 return namedParameterNames;
1025 }
1026
1027 /**
1028 * Given some [ExecutableElement] return the number of parameters of the speci fied kind.
1029 */
1030 static int _getNumOfParameters(
1031 ExecutableElement executableElement, ParameterKind parameterKind) {
1032 int parameterCount = 0;
1033 List<ParameterElement> parameters = executableElement.parameters;
1034 for (int i = 0; i < parameters.length; i++) {
1035 ParameterElement parameterElement = parameters[i];
1036 if (parameterElement.parameterKind == parameterKind) {
1037 parameterCount++;
1038 }
1039 }
1040 return parameterCount;
1041 }
1042
1043 /**
1044 * Given some [ExecutableElement] return the number of positional parameters.
1045 *
1046 * Note: by positional we mean [ParameterKind.REQUIRED] or [ParameterKind.POSI TIONAL].
1047 */
1048 static int _getNumOfPositionalParameters(
1049 ExecutableElement executableElement) =>
1050 _getNumOfParameters(executableElement, ParameterKind.REQUIRED) +
1051 _getNumOfParameters(executableElement, ParameterKind.POSITIONAL);
1052
1053 /**
1054 * Given some [ExecutableElement] return the number of required parameters.
1055 */
1056 static int _getNumOfRequiredParameters(ExecutableElement executableElement) =>
1057 _getNumOfParameters(executableElement, ParameterKind.REQUIRED);
1058
1059 /**
1060 * Given some [ExecutableElement] returns `true` if it is an abstract member o f a
1061 * class.
1062 *
1063 * @param executableElement some [ExecutableElement] to evaluate
1064 * @return `true` if the given element is an abstract member of a class
1065 */
1066 static bool _isAbstract(ExecutableElement executableElement) {
1067 if (executableElement is MethodElement) {
1068 return executableElement.isAbstract;
1069 } else if (executableElement is PropertyAccessorElement) {
1070 return executableElement.isAbstract;
1071 }
1072 return false;
1073 }
1074 }
1075
1076 /**
1077 * This class is used to replace uses of `HashMap<String, ExecutableElement>`
1078 * which are not as performant as this class.
1079 */
1080 @deprecated
1081 class MemberMap {
1082 /**
1083 * The current size of this map.
1084 */
1085 int _size = 0;
1086
1087 /**
1088 * The array of keys.
1089 */
1090 List<String> _keys;
1091
1092 /**
1093 * The array of ExecutableElement values.
1094 */
1095 List<ExecutableElement> _values;
1096
1097 /**
1098 * Initialize a newly created member map to have the given [initialCapacity].
1099 * The map will grow if needed.
1100 */
1101 MemberMap([int initialCapacity = 10]) {
1102 _initArrays(initialCapacity);
1103 }
1104
1105 /**
1106 * Initialize a newly created member map to contain the same members as the
1107 * given [memberMap].
1108 */
1109 MemberMap.from(MemberMap memberMap) {
1110 _initArrays(memberMap._size + 5);
1111 for (int i = 0; i < memberMap._size; i++) {
1112 _keys[i] = memberMap._keys[i];
1113 _values[i] = memberMap._values[i];
1114 }
1115 _size = memberMap._size;
1116 }
1117
1118 /**
1119 * Initialize a newly created member map to contain the same members as the
1120 * given [map].
1121 */
1122 MemberMap.fromMap(Map<String, ExecutableElement> map) {
1123 _size = map.length;
1124 _initArrays(_size + 5);
1125 int index = 0;
1126 map.forEach((String memberName, ExecutableElement element) {
1127 _keys[index] = memberName;
1128 _values[index] = element;
1129 index++;
1130 });
1131 }
1132
1133 /**
1134 * The size of the map.
1135 *
1136 * @return the size of the map.
1137 */
1138 int get size => _size;
1139
1140 /**
1141 * Given some key, return the ExecutableElement value from the map, if the key does not exist in
1142 * the map, `null` is returned.
1143 *
1144 * @param key some key to look up in the map
1145 * @return the associated ExecutableElement value from the map, if the key doe s not exist in the
1146 * map, `null` is returned
1147 */
1148 ExecutableElement get(String key) {
1149 for (int i = 0; i < _size; i++) {
1150 if (_keys[i] != null && _keys[i] == key) {
1151 return _values[i];
1152 }
1153 }
1154 return null;
1155 }
1156
1157 /**
1158 * Get and return the key at the specified location. If the key/value pair has been removed from
1159 * the set, then `null` is returned.
1160 *
1161 * @param i some non-zero value less than size
1162 * @return the key at the passed index
1163 * @throw ArrayIndexOutOfBoundsException this exception is thrown if the passe d index is less than
1164 * zero or greater than or equal to the capacity of the arrays
1165 */
1166 String getKey(int i) => _keys[i];
1167
1168 /**
1169 * Get and return the ExecutableElement at the specified location. If the key/ value pair has been
1170 * removed from the set, then then `null` is returned.
1171 *
1172 * @param i some non-zero value less than size
1173 * @return the key at the passed index
1174 * @throw ArrayIndexOutOfBoundsException this exception is thrown if the passe d index is less than
1175 * zero or greater than or equal to the capacity of the arrays
1176 */
1177 ExecutableElement getValue(int i) => _values[i];
1178
1179 /**
1180 * Given some key/value pair, store the pair in the map. If the key exists alr eady, then the new
1181 * value overrides the old value.
1182 *
1183 * @param key the key to store in the map
1184 * @param value the ExecutableElement value to store in the map
1185 */
1186 void put(String key, ExecutableElement value) {
1187 // If we already have a value with this key, override the value
1188 for (int i = 0; i < _size; i++) {
1189 if (_keys[i] != null && _keys[i] == key) {
1190 _values[i] = value;
1191 return;
1192 }
1193 }
1194 // If needed, double the size of our arrays and copy values over in both
1195 // arrays
1196 if (_size == _keys.length) {
1197 int newArrayLength = _size * 2;
1198 List<String> keys_new_array = new List<String>(newArrayLength);
1199 List<ExecutableElement> values_new_array =
1200 new List<ExecutableElement>(newArrayLength);
1201 for (int i = 0; i < _size; i++) {
1202 keys_new_array[i] = _keys[i];
1203 }
1204 for (int i = 0; i < _size; i++) {
1205 values_new_array[i] = _values[i];
1206 }
1207 _keys = keys_new_array;
1208 _values = values_new_array;
1209 }
1210 // Put new value at end of array
1211 _keys[_size] = key;
1212 _values[_size] = value;
1213 _size++;
1214 }
1215
1216 /**
1217 * Given some [String] key, this method replaces the associated key and value pair with
1218 * `null`. The size is not decremented with this call, instead it is expected that the users
1219 * check for `null`.
1220 *
1221 * @param key the key of the key/value pair to remove from the map
1222 */
1223 void remove(String key) {
1224 for (int i = 0; i < _size; i++) {
1225 if (_keys[i] == key) {
1226 _keys[i] = null;
1227 _values[i] = null;
1228 return;
1229 }
1230 }
1231 }
1232
1233 /**
1234 * Sets the ExecutableElement at the specified location.
1235 *
1236 * @param i some non-zero value less than size
1237 * @param value the ExecutableElement value to store in the map
1238 */
1239 void setValue(int i, ExecutableElement value) {
1240 _values[i] = value;
1241 }
1242
1243 /**
1244 * Initializes [keys] and [values].
1245 */
1246 void _initArrays(int initialCapacity) {
1247 _keys = new List<String>(initialCapacity);
1248 _values = new List<ExecutableElement>(initialCapacity);
1249 }
1250 }
OLDNEW
« no previous file with comments | « packages/analyzer/lib/src/dart/error/syntactic_errors.dart ('k') | packages/analyzer/lib/src/dart/resolver/scope.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698