OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library engine.resolver.element_resolver; | |
6 | |
7 import 'dart:collection'; | |
8 | |
9 import 'ast.dart'; | |
10 import 'element.dart'; | |
11 import 'engine.dart'; | |
12 import 'error.dart'; | |
13 import 'resolver.dart'; | |
14 import 'scanner.dart' as sc; | |
15 import 'utilities_dart.dart'; | |
16 | |
17 /** | |
18 * An object used by instances of [ResolverVisitor] to resolve references within | |
19 * the AST structure to the elements being referenced. The requirements for the | |
20 * element resolver are: | |
21 * | |
22 * 1. Every [SimpleIdentifier] should be resolved to the element to which it | |
23 * refers. Specifically: | |
24 * * An identifier within the declaration of that name should resolve to the | |
25 * element being declared. | |
26 * * An identifier denoting a prefix should resolve to the element | |
27 * representing the import that defines the prefix (an [ImportElement]). | |
28 * * An identifier denoting a variable should resolve to the element | |
29 * representing the variable (a [VariableElement]). | |
30 * * An identifier denoting a parameter should resolve to the element | |
31 * representing the parameter (a [ParameterElement]). | |
32 * * An identifier denoting a field should resolve to the element | |
33 * representing the getter or setter being invoked (a | |
34 * [PropertyAccessorElement]). | |
35 * * An identifier denoting the name of a method or function being invoked | |
36 * should resolve to the element representing the method or function (an | |
37 * [ExecutableElement]). | |
38 * * An identifier denoting a label should resolve to the element | |
39 * representing the label (a [LabelElement]). | |
40 * The identifiers within directives are exceptions to this rule and are | |
41 * covered below. | |
42 * 2. Every node containing a token representing an operator that can be | |
43 * overridden ( [BinaryExpression], [PrefixExpression], [PostfixExpression]) | |
44 * should resolve to the element representing the method invoked by that | |
45 * operator (a [MethodElement]). | |
46 * 3. Every [FunctionExpressionInvocation] should resolve to the element | |
47 * representing the function being invoked (a [FunctionElement]). This will | |
48 * be the same element as that to which the name is resolved if the function | |
49 * has a name, but is provided for those cases where an unnamed function is | |
50 * being invoked. | |
51 * 4. Every [LibraryDirective] and [PartOfDirective] should resolve to the | |
52 * element representing the library being specified by the directive (a | |
53 * [LibraryElement]) unless, in the case of a part-of directive, the | |
54 * specified library does not exist. | |
55 * 5. Every [ImportDirective] and [ExportDirective] should resolve to the | |
56 * element representing the library being specified by the directive unless | |
57 * the specified library does not exist (an [ImportElement] or | |
58 * [ExportElement]). | |
59 * 6. The identifier representing the prefix in an [ImportDirective] should | |
60 * resolve to the element representing the prefix (a [PrefixElement]). | |
61 * 7. The identifiers in the hide and show combinators in [ImportDirective]s | |
62 * and [ExportDirective]s should resolve to the elements that are being | |
63 * hidden or shown, respectively, unless those names are not defined in the | |
64 * specified library (or the specified library does not exist). | |
65 * 8. Every [PartDirective] should resolve to the element representing the | |
66 * compilation unit being specified by the string unless the specified | |
67 * compilation unit does not exist (a [CompilationUnitElement]). | |
68 * | |
69 * Note that AST nodes that would represent elements that are not defined are | |
70 * not resolved to anything. This includes such things as references to | |
71 * undeclared variables (which is an error) and names in hide and show | |
72 * combinators that are not defined in the imported library (which is not an | |
73 * error). | |
74 */ | |
75 class ElementResolver extends SimpleAstVisitor<Object> { | |
76 /** | |
77 * The resolver driving this participant. | |
78 */ | |
79 final ResolverVisitor _resolver; | |
80 | |
81 /** | |
82 * The element for the library containing the compilation unit being visited. | |
83 */ | |
84 LibraryElement _definingLibrary; | |
85 | |
86 /** | |
87 * A flag indicating whether we should generate hints. | |
88 */ | |
89 bool _enableHints = false; | |
90 | |
91 /** | |
92 * A flag indicating whether we should strictly follow the specification when | |
93 * generating warnings on "call" methods (fixes dartbug.com/21938). | |
94 */ | |
95 bool _enableStrictCallChecks = false; | |
96 | |
97 /** | |
98 * The type representing the type 'dynamic'. | |
99 */ | |
100 DartType _dynamicType; | |
101 | |
102 /** | |
103 * The type representing the type 'type'. | |
104 */ | |
105 DartType _typeType; | |
106 | |
107 /** | |
108 * A utility class for the resolver to answer the question of "what are my | |
109 * subtypes?". | |
110 */ | |
111 SubtypeManager _subtypeManager; | |
112 | |
113 /** | |
114 * The object keeping track of which elements have had their types promoted. | |
115 */ | |
116 TypePromotionManager _promoteManager; | |
117 | |
118 /** | |
119 * Initialize a newly created visitor to work for the given [_resolver] to | |
120 * resolve the nodes in a compilation unit. | |
121 */ | |
122 ElementResolver(this._resolver) { | |
123 this._definingLibrary = _resolver.definingLibrary; | |
124 AnalysisOptions options = _definingLibrary.context.analysisOptions; | |
125 _enableHints = options.hint; | |
126 _enableStrictCallChecks = options.enableStrictCallChecks; | |
127 _dynamicType = _resolver.typeProvider.dynamicType; | |
128 _typeType = _resolver.typeProvider.typeType; | |
129 _subtypeManager = new SubtypeManager(); | |
130 _promoteManager = _resolver.promoteManager; | |
131 } | |
132 | |
133 /** | |
134 * Return `true` iff the current enclosing function is a constant constructor | |
135 * declaration. | |
136 */ | |
137 bool get isInConstConstructor { | |
138 ExecutableElement function = _resolver.enclosingFunction; | |
139 if (function is ConstructorElement) { | |
140 return function.isConst; | |
141 } | |
142 return false; | |
143 } | |
144 | |
145 @override | |
146 Object visitAssignmentExpression(AssignmentExpression node) { | |
147 sc.Token operator = node.operator; | |
148 sc.TokenType operatorType = operator.type; | |
149 if (operatorType != sc.TokenType.EQ && | |
150 operatorType != sc.TokenType.QUESTION_QUESTION_EQ) { | |
151 operatorType = _operatorFromCompoundAssignment(operatorType); | |
152 Expression leftHandSide = node.leftHandSide; | |
153 if (leftHandSide != null) { | |
154 String methodName = operatorType.lexeme; | |
155 DartType staticType = _getStaticType(leftHandSide); | |
156 MethodElement staticMethod = | |
157 _lookUpMethod(leftHandSide, staticType, methodName); | |
158 node.staticElement = staticMethod; | |
159 DartType propagatedType = _getPropagatedType(leftHandSide); | |
160 MethodElement propagatedMethod = | |
161 _lookUpMethod(leftHandSide, propagatedType, methodName); | |
162 node.propagatedElement = propagatedMethod; | |
163 if (_shouldReportMissingMember(staticType, staticMethod)) { | |
164 _recordUndefinedToken(staticType.element, | |
165 StaticTypeWarningCode.UNDEFINED_METHOD, operator, [ | |
166 methodName, | |
167 staticType.displayName | |
168 ]); | |
169 } else if (_enableHints && | |
170 _shouldReportMissingMember(propagatedType, propagatedMethod) && | |
171 !_memberFoundInSubclass( | |
172 propagatedType.element, methodName, true, false)) { | |
173 _recordUndefinedToken(propagatedType.element, | |
174 HintCode.UNDEFINED_METHOD, operator, [ | |
175 methodName, | |
176 propagatedType.displayName | |
177 ]); | |
178 } | |
179 } | |
180 } | |
181 return null; | |
182 } | |
183 | |
184 @override | |
185 Object visitBinaryExpression(BinaryExpression node) { | |
186 sc.Token operator = node.operator; | |
187 if (operator.isUserDefinableOperator) { | |
188 _resolveBinaryExpression(node, operator.lexeme); | |
189 } else if (operator.type == sc.TokenType.BANG_EQ) { | |
190 _resolveBinaryExpression(node, sc.TokenType.EQ_EQ.lexeme); | |
191 } | |
192 return null; | |
193 } | |
194 | |
195 @override | |
196 Object visitBreakStatement(BreakStatement node) { | |
197 node.target = _lookupBreakOrContinueTarget(node, node.label, false); | |
198 return null; | |
199 } | |
200 | |
201 @override | |
202 Object visitClassDeclaration(ClassDeclaration node) { | |
203 setMetadata(node.element, node); | |
204 return null; | |
205 } | |
206 @override | |
207 Object visitClassTypeAlias(ClassTypeAlias node) { | |
208 setMetadata(node.element, node); | |
209 return null; | |
210 } | |
211 | |
212 @override | |
213 Object visitCommentReference(CommentReference node) { | |
214 Identifier identifier = node.identifier; | |
215 if (identifier is SimpleIdentifier) { | |
216 SimpleIdentifier simpleIdentifier = identifier; | |
217 Element element = _resolveSimpleIdentifier(simpleIdentifier); | |
218 if (element == null) { | |
219 // | |
220 // This might be a reference to an imported name that is missing the | |
221 // prefix. | |
222 // | |
223 element = _findImportWithoutPrefix(simpleIdentifier); | |
224 if (element is MultiplyDefinedElement) { | |
225 // TODO(brianwilkerson) Report this error? | |
226 element = null; | |
227 } | |
228 } | |
229 if (element == null) { | |
230 // TODO(brianwilkerson) Report this error? | |
231 // resolver.reportError( | |
232 // StaticWarningCode.UNDEFINED_IDENTIFIER, | |
233 // simpleIdentifier, | |
234 // simpleIdentifier.getName()); | |
235 } else { | |
236 if (element.library == null || element.library != _definingLibrary) { | |
237 // TODO(brianwilkerson) Report this error? | |
238 } | |
239 simpleIdentifier.staticElement = element; | |
240 if (node.newKeyword != null) { | |
241 if (element is ClassElement) { | |
242 ConstructorElement constructor = element.unnamedConstructor; | |
243 if (constructor == null) { | |
244 // TODO(brianwilkerson) Report this error. | |
245 } else { | |
246 simpleIdentifier.staticElement = constructor; | |
247 } | |
248 } else { | |
249 // TODO(brianwilkerson) Report this error. | |
250 } | |
251 } | |
252 } | |
253 } else if (identifier is PrefixedIdentifier) { | |
254 PrefixedIdentifier prefixedIdentifier = identifier; | |
255 SimpleIdentifier prefix = prefixedIdentifier.prefix; | |
256 SimpleIdentifier name = prefixedIdentifier.identifier; | |
257 Element element = _resolveSimpleIdentifier(prefix); | |
258 if (element == null) { | |
259 // resolver.reportError(StaticWarningCode.UNDEFINED_IDENTIFIER, prefix, p
refix.getName()); | |
260 } else { | |
261 if (element is PrefixElement) { | |
262 prefix.staticElement = element; | |
263 // TODO(brianwilkerson) Report this error? | |
264 element = _resolver.nameScope.lookup(identifier, _definingLibrary); | |
265 name.staticElement = element; | |
266 return null; | |
267 } | |
268 LibraryElement library = element.library; | |
269 if (library == null) { | |
270 // TODO(brianwilkerson) We need to understand how the library could | |
271 // ever be null. | |
272 AnalysisEngine.instance.logger | |
273 .logError("Found element with null library: ${element.name}"); | |
274 } else if (library != _definingLibrary) { | |
275 // TODO(brianwilkerson) Report this error. | |
276 } | |
277 name.staticElement = element; | |
278 if (node.newKeyword == null) { | |
279 if (element is ClassElement) { | |
280 Element memberElement = | |
281 _lookupGetterOrMethod(element.type, name.name); | |
282 if (memberElement == null) { | |
283 memberElement = element.getNamedConstructor(name.name); | |
284 if (memberElement == null) { | |
285 memberElement = _lookUpSetter(prefix, element.type, name.name); | |
286 } | |
287 } | |
288 if (memberElement == null) { | |
289 // reportGetterOrSetterNotFound(prefixedIdentifier, name, element.g
etDisplayName()); | |
290 } else { | |
291 name.staticElement = memberElement; | |
292 } | |
293 } else { | |
294 // TODO(brianwilkerson) Report this error. | |
295 } | |
296 } else { | |
297 if (element is ClassElement) { | |
298 ConstructorElement constructor = | |
299 element.getNamedConstructor(name.name); | |
300 if (constructor == null) { | |
301 // TODO(brianwilkerson) Report this error. | |
302 } else { | |
303 name.staticElement = constructor; | |
304 } | |
305 } else { | |
306 // TODO(brianwilkerson) Report this error. | |
307 } | |
308 } | |
309 } | |
310 } | |
311 return null; | |
312 } | |
313 | |
314 @override | |
315 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
316 super.visitConstructorDeclaration(node); | |
317 ConstructorElement element = node.element; | |
318 if (element is ConstructorElementImpl) { | |
319 ConstructorElementImpl constructorElement = element; | |
320 ConstructorName redirectedNode = node.redirectedConstructor; | |
321 if (redirectedNode != null) { | |
322 // set redirected factory constructor | |
323 ConstructorElement redirectedElement = redirectedNode.staticElement; | |
324 constructorElement.redirectedConstructor = redirectedElement; | |
325 } else { | |
326 // set redirected generative constructor | |
327 for (ConstructorInitializer initializer in node.initializers) { | |
328 if (initializer is RedirectingConstructorInvocation) { | |
329 ConstructorElement redirectedElement = initializer.staticElement; | |
330 constructorElement.redirectedConstructor = redirectedElement; | |
331 } | |
332 } | |
333 } | |
334 setMetadata(constructorElement, node); | |
335 } | |
336 return null; | |
337 } | |
338 | |
339 @override | |
340 Object visitConstructorFieldInitializer(ConstructorFieldInitializer node) { | |
341 SimpleIdentifier fieldName = node.fieldName; | |
342 ClassElement enclosingClass = _resolver.enclosingClass; | |
343 FieldElement fieldElement = enclosingClass.getField(fieldName.name); | |
344 fieldName.staticElement = fieldElement; | |
345 return null; | |
346 } | |
347 | |
348 @override | |
349 Object visitConstructorName(ConstructorName node) { | |
350 DartType type = node.type.type; | |
351 if (type != null && type.isDynamic) { | |
352 return null; | |
353 } else if (type is! InterfaceType) { | |
354 // TODO(brianwilkerson) Report these errors. | |
355 // ASTNode parent = node.getParent(); | |
356 // if (parent instanceof InstanceCreationExpression) { | |
357 // if (((InstanceCreationExpression) parent).isConst()) { | |
358 // // CompileTimeErrorCode.CONST_WITH_NON_TYPE | |
359 // } else { | |
360 // // StaticWarningCode.NEW_WITH_NON_TYPE | |
361 // } | |
362 // } else { | |
363 // // This is part of a redirecting factory constructor; not sure which e
rror code to use | |
364 // } | |
365 return null; | |
366 } | |
367 // look up ConstructorElement | |
368 ConstructorElement constructor; | |
369 SimpleIdentifier name = node.name; | |
370 InterfaceType interfaceType = type as InterfaceType; | |
371 if (name == null) { | |
372 constructor = interfaceType.lookUpConstructor(null, _definingLibrary); | |
373 } else { | |
374 constructor = | |
375 interfaceType.lookUpConstructor(name.name, _definingLibrary); | |
376 name.staticElement = constructor; | |
377 } | |
378 node.staticElement = constructor; | |
379 return null; | |
380 } | |
381 | |
382 @override | |
383 Object visitContinueStatement(ContinueStatement node) { | |
384 node.target = _lookupBreakOrContinueTarget(node, node.label, true); | |
385 return null; | |
386 } | |
387 | |
388 @override | |
389 Object visitDeclaredIdentifier(DeclaredIdentifier node) { | |
390 setMetadata(node.element, node); | |
391 return null; | |
392 } | |
393 | |
394 @override | |
395 Object visitEnumDeclaration(EnumDeclaration node) { | |
396 setMetadata(node.element, node); | |
397 return null; | |
398 } | |
399 | |
400 @override | |
401 Object visitExportDirective(ExportDirective node) { | |
402 ExportElement exportElement = node.element; | |
403 if (exportElement != null) { | |
404 // The element is null when the URI is invalid | |
405 // TODO(brianwilkerson) Figure out whether the element can ever be | |
406 // something other than an ExportElement | |
407 _resolveCombinators(exportElement.exportedLibrary, node.combinators); | |
408 setMetadata(exportElement, node); | |
409 } | |
410 return null; | |
411 } | |
412 | |
413 @override | |
414 Object visitFieldFormalParameter(FieldFormalParameter node) { | |
415 _setMetadataForParameter(node.element, node); | |
416 return super.visitFieldFormalParameter(node); | |
417 } | |
418 | |
419 @override | |
420 Object visitFunctionDeclaration(FunctionDeclaration node) { | |
421 setMetadata(node.element, node); | |
422 return null; | |
423 } | |
424 | |
425 @override | |
426 Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { | |
427 // TODO(brianwilkerson) Can we ever resolve the function being invoked? | |
428 Expression expression = node.function; | |
429 if (expression is FunctionExpression) { | |
430 FunctionExpression functionExpression = expression; | |
431 ExecutableElement functionElement = functionExpression.element; | |
432 ArgumentList argumentList = node.argumentList; | |
433 List<ParameterElement> parameters = | |
434 _resolveArgumentsToFunction(false, argumentList, functionElement); | |
435 if (parameters != null) { | |
436 argumentList.correspondingStaticParameters = parameters; | |
437 } | |
438 } | |
439 return null; | |
440 } | |
441 | |
442 @override | |
443 Object visitFunctionTypeAlias(FunctionTypeAlias node) { | |
444 setMetadata(node.element, node); | |
445 return null; | |
446 } | |
447 | |
448 @override | |
449 Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { | |
450 _setMetadataForParameter(node.element, node); | |
451 return null; | |
452 } | |
453 | |
454 @override | |
455 Object visitImportDirective(ImportDirective node) { | |
456 SimpleIdentifier prefixNode = node.prefix; | |
457 if (prefixNode != null) { | |
458 String prefixName = prefixNode.name; | |
459 for (PrefixElement prefixElement in _definingLibrary.prefixes) { | |
460 if (prefixElement.displayName == prefixName) { | |
461 prefixNode.staticElement = prefixElement; | |
462 break; | |
463 } | |
464 } | |
465 } | |
466 ImportElement importElement = node.element; | |
467 if (importElement != null) { | |
468 // The element is null when the URI is invalid | |
469 LibraryElement library = importElement.importedLibrary; | |
470 if (library != null) { | |
471 _resolveCombinators(library, node.combinators); | |
472 } | |
473 setMetadata(importElement, node); | |
474 } | |
475 return null; | |
476 } | |
477 | |
478 @override | |
479 Object visitIndexExpression(IndexExpression node) { | |
480 Expression target = node.realTarget; | |
481 DartType staticType = _getStaticType(target); | |
482 DartType propagatedType = _getPropagatedType(target); | |
483 String getterMethodName = sc.TokenType.INDEX.lexeme; | |
484 String setterMethodName = sc.TokenType.INDEX_EQ.lexeme; | |
485 bool isInGetterContext = node.inGetterContext(); | |
486 bool isInSetterContext = node.inSetterContext(); | |
487 if (isInGetterContext && isInSetterContext) { | |
488 // lookup setter | |
489 MethodElement setterStaticMethod = | |
490 _lookUpMethod(target, staticType, setterMethodName); | |
491 MethodElement setterPropagatedMethod = | |
492 _lookUpMethod(target, propagatedType, setterMethodName); | |
493 // set setter element | |
494 node.staticElement = setterStaticMethod; | |
495 node.propagatedElement = setterPropagatedMethod; | |
496 // generate undefined method warning | |
497 _checkForUndefinedIndexOperator(node, target, getterMethodName, | |
498 setterStaticMethod, setterPropagatedMethod, staticType, | |
499 propagatedType); | |
500 // lookup getter method | |
501 MethodElement getterStaticMethod = | |
502 _lookUpMethod(target, staticType, getterMethodName); | |
503 MethodElement getterPropagatedMethod = | |
504 _lookUpMethod(target, propagatedType, getterMethodName); | |
505 // set getter element | |
506 AuxiliaryElements auxiliaryElements = | |
507 new AuxiliaryElements(getterStaticMethod, getterPropagatedMethod); | |
508 node.auxiliaryElements = auxiliaryElements; | |
509 // generate undefined method warning | |
510 _checkForUndefinedIndexOperator(node, target, getterMethodName, | |
511 getterStaticMethod, getterPropagatedMethod, staticType, | |
512 propagatedType); | |
513 } else if (isInGetterContext) { | |
514 // lookup getter method | |
515 MethodElement staticMethod = | |
516 _lookUpMethod(target, staticType, getterMethodName); | |
517 MethodElement propagatedMethod = | |
518 _lookUpMethod(target, propagatedType, getterMethodName); | |
519 // set getter element | |
520 node.staticElement = staticMethod; | |
521 node.propagatedElement = propagatedMethod; | |
522 // generate undefined method warning | |
523 _checkForUndefinedIndexOperator(node, target, getterMethodName, | |
524 staticMethod, propagatedMethod, staticType, propagatedType); | |
525 } else if (isInSetterContext) { | |
526 // lookup setter method | |
527 MethodElement staticMethod = | |
528 _lookUpMethod(target, staticType, setterMethodName); | |
529 MethodElement propagatedMethod = | |
530 _lookUpMethod(target, propagatedType, setterMethodName); | |
531 // set setter element | |
532 node.staticElement = staticMethod; | |
533 node.propagatedElement = propagatedMethod; | |
534 // generate undefined method warning | |
535 _checkForUndefinedIndexOperator(node, target, setterMethodName, | |
536 staticMethod, propagatedMethod, staticType, propagatedType); | |
537 } | |
538 return null; | |
539 } | |
540 | |
541 @override | |
542 Object visitInstanceCreationExpression(InstanceCreationExpression node) { | |
543 ConstructorElement invokedConstructor = node.constructorName.staticElement; | |
544 node.staticElement = invokedConstructor; | |
545 ArgumentList argumentList = node.argumentList; | |
546 List<ParameterElement> parameters = _resolveArgumentsToFunction( | |
547 node.isConst, argumentList, invokedConstructor); | |
548 if (parameters != null) { | |
549 argumentList.correspondingStaticParameters = parameters; | |
550 } | |
551 return null; | |
552 } | |
553 | |
554 @override | |
555 Object visitLibraryDirective(LibraryDirective node) { | |
556 setMetadata(node.element, node); | |
557 return null; | |
558 } | |
559 | |
560 @override | |
561 Object visitMethodDeclaration(MethodDeclaration node) { | |
562 setMetadata(node.element, node); | |
563 return null; | |
564 } | |
565 | |
566 @override | |
567 Object visitMethodInvocation(MethodInvocation node) { | |
568 SimpleIdentifier methodName = node.methodName; | |
569 // | |
570 // Synthetic identifiers have been already reported during parsing. | |
571 // | |
572 if (methodName.isSynthetic) { | |
573 return null; | |
574 } | |
575 // | |
576 // We have a method invocation of one of two forms: 'e.m(a1, ..., an)' or | |
577 // 'm(a1, ..., an)'. The first step is to figure out which executable is | |
578 // being invoked, using both the static and the propagated type information. | |
579 // | |
580 Expression target = node.realTarget; | |
581 if (target is SuperExpression && !_isSuperInValidContext(target)) { | |
582 return null; | |
583 } | |
584 Element staticElement; | |
585 Element propagatedElement; | |
586 DartType staticType = null; | |
587 DartType propagatedType = null; | |
588 if (target == null) { | |
589 staticElement = _resolveInvokedElement(methodName); | |
590 propagatedElement = null; | |
591 } else if (methodName.name == FunctionElement.LOAD_LIBRARY_NAME && | |
592 _isDeferredPrefix(target)) { | |
593 if (node.operator.type == sc.TokenType.QUESTION_PERIOD) { | |
594 _resolver.reportErrorForNode( | |
595 CompileTimeErrorCode.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT, target, | |
596 [(target as SimpleIdentifier).name]); | |
597 } | |
598 LibraryElement importedLibrary = _getImportedLibrary(target); | |
599 methodName.staticElement = importedLibrary.loadLibraryFunction; | |
600 return null; | |
601 } else { | |
602 staticType = _getStaticType(target); | |
603 propagatedType = _getPropagatedType(target); | |
604 // | |
605 // If this method invocation is of the form 'C.m' where 'C' is a class, | |
606 // then we don't call resolveInvokedElement(...) which walks up the class | |
607 // hierarchy, instead we just look for the member in the type only. This | |
608 // does not apply to conditional method invocation (i.e. 'C?.m(...)'). | |
609 // | |
610 bool isConditional = node.operator.type == sc.TokenType.QUESTION_PERIOD; | |
611 ClassElementImpl typeReference = getTypeReference(target, isConditional); | |
612 if (typeReference != null) { | |
613 staticElement = | |
614 propagatedElement = _resolveElement(typeReference, methodName); | |
615 } else { | |
616 staticElement = _resolveInvokedElementWithTarget( | |
617 target, staticType, methodName, isConditional); | |
618 propagatedElement = _resolveInvokedElementWithTarget( | |
619 target, propagatedType, methodName, isConditional); | |
620 } | |
621 } | |
622 staticElement = _convertSetterToGetter(staticElement); | |
623 propagatedElement = _convertSetterToGetter(propagatedElement); | |
624 // | |
625 // Record the results. | |
626 // | |
627 methodName.staticElement = staticElement; | |
628 methodName.propagatedElement = propagatedElement; | |
629 ArgumentList argumentList = node.argumentList; | |
630 if (staticElement != null) { | |
631 List<ParameterElement> parameters = | |
632 _computeCorrespondingParameters(argumentList, staticElement); | |
633 if (parameters != null) { | |
634 argumentList.correspondingStaticParameters = parameters; | |
635 } | |
636 } | |
637 if (propagatedElement != null) { | |
638 List<ParameterElement> parameters = | |
639 _computeCorrespondingParameters(argumentList, propagatedElement); | |
640 if (parameters != null) { | |
641 argumentList.correspondingPropagatedParameters = parameters; | |
642 } | |
643 } | |
644 // | |
645 // Then check for error conditions. | |
646 // | |
647 ErrorCode errorCode = _checkForInvocationError(target, true, staticElement); | |
648 bool generatedWithTypePropagation = false; | |
649 if (_enableHints && errorCode == null && staticElement == null) { | |
650 // The method lookup may have failed because there were multiple | |
651 // incompatible choices. In this case we don't want to generate a hint. | |
652 errorCode = _checkForInvocationError(target, false, propagatedElement); | |
653 if (identical(errorCode, StaticTypeWarningCode.UNDEFINED_METHOD)) { | |
654 ClassElement classElementContext = null; | |
655 if (target == null) { | |
656 classElementContext = _resolver.enclosingClass; | |
657 } else { | |
658 DartType type = _getBestType(target); | |
659 if (type != null) { | |
660 if (type.element is ClassElement) { | |
661 classElementContext = type.element as ClassElement; | |
662 } | |
663 } | |
664 } | |
665 if (classElementContext != null) { | |
666 _subtypeManager.ensureLibraryVisited(_definingLibrary); | |
667 HashSet<ClassElement> subtypeElements = | |
668 _subtypeManager.computeAllSubtypes(classElementContext); | |
669 for (ClassElement subtypeElement in subtypeElements) { | |
670 if (subtypeElement.getMethod(methodName.name) != null) { | |
671 errorCode = null; | |
672 } | |
673 } | |
674 } | |
675 } | |
676 generatedWithTypePropagation = true; | |
677 } | |
678 if (errorCode == null) { | |
679 return null; | |
680 } | |
681 if (identical( | |
682 errorCode, StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION) || | |
683 identical(errorCode, | |
684 CompileTimeErrorCode.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT) || | |
685 identical(errorCode, StaticTypeWarningCode.UNDEFINED_FUNCTION)) { | |
686 _resolver.reportErrorForNode(errorCode, methodName, [methodName.name]); | |
687 } else if (identical(errorCode, StaticTypeWarningCode.UNDEFINED_METHOD)) { | |
688 String targetTypeName; | |
689 if (target == null) { | |
690 ClassElement enclosingClass = _resolver.enclosingClass; | |
691 targetTypeName = enclosingClass.displayName; | |
692 ErrorCode proxyErrorCode = (generatedWithTypePropagation | |
693 ? HintCode.UNDEFINED_METHOD | |
694 : StaticTypeWarningCode.UNDEFINED_METHOD); | |
695 _recordUndefinedNode(_resolver.enclosingClass, proxyErrorCode, | |
696 methodName, [methodName.name, targetTypeName]); | |
697 } else { | |
698 // ignore Function "call" | |
699 // (if we are about to create a hint using type propagation, | |
700 // then we can use type propagation here as well) | |
701 DartType targetType = null; | |
702 if (!generatedWithTypePropagation) { | |
703 targetType = _getStaticType(target); | |
704 } else { | |
705 // choose the best type | |
706 targetType = _getPropagatedType(target); | |
707 if (targetType == null) { | |
708 targetType = _getStaticType(target); | |
709 } | |
710 } | |
711 if (!_enableStrictCallChecks && | |
712 targetType != null && | |
713 targetType.isDartCoreFunction && | |
714 methodName.name == FunctionElement.CALL_METHOD_NAME) { | |
715 // TODO(brianwilkerson) Can we ever resolve the function being | |
716 // invoked? | |
717 // resolveArgumentsToParameters(node.getArgumentList(), invokedFunction
); | |
718 return null; | |
719 } | |
720 targetTypeName = targetType == null ? null : targetType.displayName; | |
721 ErrorCode proxyErrorCode = (generatedWithTypePropagation | |
722 ? HintCode.UNDEFINED_METHOD | |
723 : StaticTypeWarningCode.UNDEFINED_METHOD); | |
724 _recordUndefinedNode(targetType.element, proxyErrorCode, methodName, [ | |
725 methodName.name, | |
726 targetTypeName | |
727 ]); | |
728 } | |
729 } else if (identical( | |
730 errorCode, StaticTypeWarningCode.UNDEFINED_SUPER_METHOD)) { | |
731 // Generate the type name. | |
732 // The error code will never be generated via type propagation | |
733 DartType targetType = _getStaticType(target); | |
734 if (targetType is InterfaceType && !targetType.isObject) { | |
735 targetType = (targetType as InterfaceType).superclass; | |
736 } | |
737 String targetTypeName = targetType == null ? null : targetType.name; | |
738 _resolver.reportErrorForNode(StaticTypeWarningCode.UNDEFINED_SUPER_METHOD, | |
739 methodName, [methodName.name, targetTypeName]); | |
740 } | |
741 return null; | |
742 } | |
743 | |
744 @override | |
745 Object visitPartDirective(PartDirective node) { | |
746 setMetadata(node.element, node); | |
747 return null; | |
748 } | |
749 | |
750 @override | |
751 Object visitPartOfDirective(PartOfDirective node) { | |
752 setMetadata(node.element, node); | |
753 return null; | |
754 } | |
755 | |
756 @override | |
757 Object visitPostfixExpression(PostfixExpression node) { | |
758 Expression operand = node.operand; | |
759 String methodName = _getPostfixOperator(node); | |
760 DartType staticType = _getStaticType(operand); | |
761 MethodElement staticMethod = _lookUpMethod(operand, staticType, methodName); | |
762 node.staticElement = staticMethod; | |
763 DartType propagatedType = _getPropagatedType(operand); | |
764 MethodElement propagatedMethod = | |
765 _lookUpMethod(operand, propagatedType, methodName); | |
766 node.propagatedElement = propagatedMethod; | |
767 if (_shouldReportMissingMember(staticType, staticMethod)) { | |
768 if (operand is SuperExpression) { | |
769 _recordUndefinedToken(staticType.element, | |
770 StaticTypeWarningCode.UNDEFINED_SUPER_OPERATOR, node.operator, [ | |
771 methodName, | |
772 staticType.displayName | |
773 ]); | |
774 } else { | |
775 _recordUndefinedToken(staticType.element, | |
776 StaticTypeWarningCode.UNDEFINED_OPERATOR, node.operator, [ | |
777 methodName, | |
778 staticType.displayName | |
779 ]); | |
780 } | |
781 } else if (_enableHints && | |
782 _shouldReportMissingMember(propagatedType, propagatedMethod) && | |
783 !_memberFoundInSubclass( | |
784 propagatedType.element, methodName, true, false)) { | |
785 _recordUndefinedToken(propagatedType.element, HintCode.UNDEFINED_OPERATOR, | |
786 node.operator, [methodName, propagatedType.displayName]); | |
787 } | |
788 return null; | |
789 } | |
790 | |
791 @override | |
792 Object visitPrefixedIdentifier(PrefixedIdentifier node) { | |
793 SimpleIdentifier prefix = node.prefix; | |
794 SimpleIdentifier identifier = node.identifier; | |
795 // | |
796 // First, check the "lib.loadLibrary" case | |
797 // | |
798 if (identifier.name == FunctionElement.LOAD_LIBRARY_NAME && | |
799 _isDeferredPrefix(prefix)) { | |
800 LibraryElement importedLibrary = _getImportedLibrary(prefix); | |
801 identifier.staticElement = importedLibrary.loadLibraryFunction; | |
802 return null; | |
803 } | |
804 // | |
805 // Check to see whether the prefix is really a prefix. | |
806 // | |
807 Element prefixElement = prefix.staticElement; | |
808 if (prefixElement is PrefixElement) { | |
809 Element element = _resolver.nameScope.lookup(node, _definingLibrary); | |
810 if (element == null && identifier.inSetterContext()) { | |
811 element = _resolver.nameScope.lookup( | |
812 new SyntheticIdentifier("${node.name}=", node), _definingLibrary); | |
813 } | |
814 if (element == null) { | |
815 if (identifier.inSetterContext()) { | |
816 _resolver.reportErrorForNode(StaticWarningCode.UNDEFINED_SETTER, | |
817 identifier, [identifier.name, prefixElement.name]); | |
818 } else if (node.parent is Annotation) { | |
819 Annotation annotation = node.parent as Annotation; | |
820 _resolver.reportErrorForNode( | |
821 CompileTimeErrorCode.INVALID_ANNOTATION, annotation); | |
822 return null; | |
823 } else { | |
824 _resolver.reportErrorForNode(StaticWarningCode.UNDEFINED_GETTER, | |
825 identifier, [identifier.name, prefixElement.name]); | |
826 } | |
827 return null; | |
828 } | |
829 if (element is PropertyAccessorElement && identifier.inSetterContext()) { | |
830 PropertyInducingElement variable = | |
831 (element as PropertyAccessorElement).variable; | |
832 if (variable != null) { | |
833 PropertyAccessorElement setter = variable.setter; | |
834 if (setter != null) { | |
835 element = setter; | |
836 } | |
837 } | |
838 } | |
839 // TODO(brianwilkerson) The prefix needs to be resolved to the element for | |
840 // the import that defines the prefix, not the prefix's element. | |
841 identifier.staticElement = element; | |
842 // Validate annotation element. | |
843 if (node.parent is Annotation) { | |
844 Annotation annotation = node.parent as Annotation; | |
845 _resolveAnnotationElement(annotation); | |
846 return null; | |
847 } | |
848 return null; | |
849 } | |
850 // May be annotation, resolve invocation of "const" constructor. | |
851 if (node.parent is Annotation) { | |
852 Annotation annotation = node.parent as Annotation; | |
853 _resolveAnnotationElement(annotation); | |
854 } | |
855 // | |
856 // Otherwise, the prefix is really an expression that happens to be a simple | |
857 // identifier and this is really equivalent to a property access node. | |
858 // | |
859 _resolvePropertyAccess(prefix, identifier, false); | |
860 return null; | |
861 } | |
862 | |
863 @override | |
864 Object visitPrefixExpression(PrefixExpression node) { | |
865 sc.Token operator = node.operator; | |
866 sc.TokenType operatorType = operator.type; | |
867 if (operatorType.isUserDefinableOperator || | |
868 operatorType == sc.TokenType.PLUS_PLUS || | |
869 operatorType == sc.TokenType.MINUS_MINUS) { | |
870 Expression operand = node.operand; | |
871 String methodName = _getPrefixOperator(node); | |
872 DartType staticType = _getStaticType(operand); | |
873 MethodElement staticMethod = | |
874 _lookUpMethod(operand, staticType, methodName); | |
875 node.staticElement = staticMethod; | |
876 DartType propagatedType = _getPropagatedType(operand); | |
877 MethodElement propagatedMethod = | |
878 _lookUpMethod(operand, propagatedType, methodName); | |
879 node.propagatedElement = propagatedMethod; | |
880 if (_shouldReportMissingMember(staticType, staticMethod)) { | |
881 if (operand is SuperExpression) { | |
882 _recordUndefinedToken(staticType.element, | |
883 StaticTypeWarningCode.UNDEFINED_SUPER_OPERATOR, operator, [ | |
884 methodName, | |
885 staticType.displayName | |
886 ]); | |
887 } else { | |
888 _recordUndefinedToken(staticType.element, | |
889 StaticTypeWarningCode.UNDEFINED_OPERATOR, operator, [ | |
890 methodName, | |
891 staticType.displayName | |
892 ]); | |
893 } | |
894 } else if (_enableHints && | |
895 _shouldReportMissingMember(propagatedType, propagatedMethod) && | |
896 !_memberFoundInSubclass( | |
897 propagatedType.element, methodName, true, false)) { | |
898 _recordUndefinedToken(propagatedType.element, | |
899 HintCode.UNDEFINED_OPERATOR, operator, [ | |
900 methodName, | |
901 propagatedType.displayName | |
902 ]); | |
903 } | |
904 } | |
905 return null; | |
906 } | |
907 | |
908 @override | |
909 Object visitPropertyAccess(PropertyAccess node) { | |
910 Expression target = node.realTarget; | |
911 if (target is SuperExpression && !_isSuperInValidContext(target)) { | |
912 return null; | |
913 } | |
914 SimpleIdentifier propertyName = node.propertyName; | |
915 _resolvePropertyAccess(target, propertyName, | |
916 node.operator.type == sc.TokenType.QUESTION_PERIOD); | |
917 return null; | |
918 } | |
919 | |
920 @override | |
921 Object visitRedirectingConstructorInvocation( | |
922 RedirectingConstructorInvocation node) { | |
923 ClassElement enclosingClass = _resolver.enclosingClass; | |
924 if (enclosingClass == null) { | |
925 // TODO(brianwilkerson) Report this error. | |
926 return null; | |
927 } | |
928 SimpleIdentifier name = node.constructorName; | |
929 ConstructorElement element; | |
930 if (name == null) { | |
931 element = enclosingClass.unnamedConstructor; | |
932 } else { | |
933 element = enclosingClass.getNamedConstructor(name.name); | |
934 } | |
935 if (element == null) { | |
936 // TODO(brianwilkerson) Report this error and decide what element to | |
937 // associate with the node. | |
938 return null; | |
939 } | |
940 if (name != null) { | |
941 name.staticElement = element; | |
942 } | |
943 node.staticElement = element; | |
944 ArgumentList argumentList = node.argumentList; | |
945 List<ParameterElement> parameters = | |
946 _resolveArgumentsToFunction(false, argumentList, element); | |
947 if (parameters != null) { | |
948 argumentList.correspondingStaticParameters = parameters; | |
949 } | |
950 return null; | |
951 } | |
952 | |
953 @override | |
954 Object visitSimpleFormalParameter(SimpleFormalParameter node) { | |
955 _setMetadataForParameter(node.element, node); | |
956 return null; | |
957 } | |
958 | |
959 @override | |
960 Object visitSimpleIdentifier(SimpleIdentifier node) { | |
961 // | |
962 // Synthetic identifiers have been already reported during parsing. | |
963 // | |
964 if (node.isSynthetic) { | |
965 return null; | |
966 } | |
967 // | |
968 // We ignore identifiers that have already been resolved, such as | |
969 // identifiers representing the name in a declaration. | |
970 // | |
971 if (node.staticElement != null) { | |
972 return null; | |
973 } | |
974 // | |
975 // The name dynamic denotes a Type object even though dynamic is not a | |
976 // class. | |
977 // | |
978 if (node.name == _dynamicType.name) { | |
979 node.staticElement = _dynamicType.element; | |
980 node.staticType = _typeType; | |
981 return null; | |
982 } | |
983 // | |
984 // Otherwise, the node should be resolved. | |
985 // | |
986 Element element = _resolveSimpleIdentifier(node); | |
987 ClassElement enclosingClass = _resolver.enclosingClass; | |
988 if (_isFactoryConstructorReturnType(node) && | |
989 !identical(element, enclosingClass)) { | |
990 _resolver.reportErrorForNode( | |
991 CompileTimeErrorCode.INVALID_FACTORY_NAME_NOT_A_CLASS, node); | |
992 } else if (_isConstructorReturnType(node) && | |
993 !identical(element, enclosingClass)) { | |
994 _resolver.reportErrorForNode( | |
995 CompileTimeErrorCode.INVALID_CONSTRUCTOR_NAME, node); | |
996 element = null; | |
997 } else if (element == null || | |
998 (element is PrefixElement && !_isValidAsPrefix(node))) { | |
999 // TODO(brianwilkerson) Recover from this error. | |
1000 if (_isConstructorReturnType(node)) { | |
1001 _resolver.reportErrorForNode( | |
1002 CompileTimeErrorCode.INVALID_CONSTRUCTOR_NAME, node); | |
1003 } else if (node.parent is Annotation) { | |
1004 Annotation annotation = node.parent as Annotation; | |
1005 _resolver.reportErrorForNode( | |
1006 CompileTimeErrorCode.INVALID_ANNOTATION, annotation); | |
1007 } else if (element is PrefixElement) { | |
1008 _resolver.reportErrorForNode( | |
1009 CompileTimeErrorCode.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT, node, | |
1010 [element.name]); | |
1011 } else { | |
1012 _recordUndefinedNode(_resolver.enclosingClass, | |
1013 StaticWarningCode.UNDEFINED_IDENTIFIER, node, [node.name]); | |
1014 } | |
1015 } | |
1016 node.staticElement = element; | |
1017 if (node.inSetterContext() && | |
1018 node.inGetterContext() && | |
1019 enclosingClass != null) { | |
1020 InterfaceType enclosingType = enclosingClass.type; | |
1021 AuxiliaryElements auxiliaryElements = new AuxiliaryElements( | |
1022 _lookUpGetter(null, enclosingType, node.name), null); | |
1023 node.auxiliaryElements = auxiliaryElements; | |
1024 } | |
1025 // | |
1026 // Validate annotation element. | |
1027 // | |
1028 if (node.parent is Annotation) { | |
1029 Annotation annotation = node.parent as Annotation; | |
1030 _resolveAnnotationElement(annotation); | |
1031 } | |
1032 return null; | |
1033 } | |
1034 | |
1035 @override | |
1036 Object visitSuperConstructorInvocation(SuperConstructorInvocation node) { | |
1037 ClassElementImpl enclosingClass = _resolver.enclosingClass; | |
1038 if (enclosingClass == null) { | |
1039 // TODO(brianwilkerson) Report this error. | |
1040 return null; | |
1041 } | |
1042 InterfaceType superType = enclosingClass.supertype; | |
1043 if (superType == null) { | |
1044 // TODO(brianwilkerson) Report this error. | |
1045 return null; | |
1046 } | |
1047 SimpleIdentifier name = node.constructorName; | |
1048 String superName = name != null ? name.name : null; | |
1049 ConstructorElement element = | |
1050 superType.lookUpConstructor(superName, _definingLibrary); | |
1051 if (element == null || | |
1052 (!enclosingClass.doesMixinLackConstructors && | |
1053 !enclosingClass.isSuperConstructorAccessible(element))) { | |
1054 if (name != null) { | |
1055 _resolver.reportErrorForNode( | |
1056 CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER, node, [ | |
1057 superType.displayName, | |
1058 name | |
1059 ]); | |
1060 } else { | |
1061 _resolver.reportErrorForNode( | |
1062 CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT, | |
1063 node, [superType.displayName]); | |
1064 } | |
1065 return null; | |
1066 } else { | |
1067 if (element.isFactory) { | |
1068 _resolver.reportErrorForNode( | |
1069 CompileTimeErrorCode.NON_GENERATIVE_CONSTRUCTOR, node, [element]); | |
1070 } | |
1071 } | |
1072 if (name != null) { | |
1073 name.staticElement = element; | |
1074 } | |
1075 node.staticElement = element; | |
1076 ArgumentList argumentList = node.argumentList; | |
1077 List<ParameterElement> parameters = _resolveArgumentsToFunction( | |
1078 isInConstConstructor, argumentList, element); | |
1079 if (parameters != null) { | |
1080 argumentList.correspondingStaticParameters = parameters; | |
1081 } | |
1082 return null; | |
1083 } | |
1084 | |
1085 @override | |
1086 Object visitSuperExpression(SuperExpression node) { | |
1087 if (!_isSuperInValidContext(node)) { | |
1088 _resolver.reportErrorForNode( | |
1089 CompileTimeErrorCode.SUPER_IN_INVALID_CONTEXT, node); | |
1090 } | |
1091 return super.visitSuperExpression(node); | |
1092 } | |
1093 | |
1094 @override | |
1095 Object visitTypeParameter(TypeParameter node) { | |
1096 setMetadata(node.element, node); | |
1097 return null; | |
1098 } | |
1099 | |
1100 @override | |
1101 Object visitVariableDeclaration(VariableDeclaration node) { | |
1102 setMetadata(node.element, node); | |
1103 return null; | |
1104 } | |
1105 | |
1106 /** | |
1107 * Given that we have found code to invoke the given [element], return the | |
1108 * error code that should be reported, or `null` if no error should be | |
1109 * reported. The [target] is the target of the invocation, or `null` if there | |
1110 * was no target. The flag [useStaticContext] should be `true` if the | |
1111 * invocation is in a static constant (does not have access to instance state. | |
1112 */ | |
1113 ErrorCode _checkForInvocationError( | |
1114 Expression target, bool useStaticContext, Element element) { | |
1115 // Prefix is not declared, instead "prefix.id" are declared. | |
1116 if (element is PrefixElement) { | |
1117 return CompileTimeErrorCode.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT; | |
1118 } | |
1119 if (element is PropertyAccessorElement) { | |
1120 // | |
1121 // This is really a function expression invocation. | |
1122 // | |
1123 // TODO(brianwilkerson) Consider the possibility of re-writing the AST. | |
1124 FunctionType getterType = element.type; | |
1125 if (getterType != null) { | |
1126 DartType returnType = getterType.returnType; | |
1127 if (!_isExecutableType(returnType)) { | |
1128 return StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION; | |
1129 } | |
1130 } | |
1131 } else if (element is ExecutableElement) { | |
1132 return null; | |
1133 } else if (element is MultiplyDefinedElement) { | |
1134 // The error has already been reported | |
1135 return null; | |
1136 } else if (element == null && target is SuperExpression) { | |
1137 // TODO(jwren) We should split the UNDEFINED_METHOD into two error codes, | |
1138 // this one, and a code that describes the situation where the method was | |
1139 // found, but it was not accessible from the current library. | |
1140 return StaticTypeWarningCode.UNDEFINED_SUPER_METHOD; | |
1141 } else { | |
1142 // | |
1143 // This is really a function expression invocation. | |
1144 // | |
1145 // TODO(brianwilkerson) Consider the possibility of re-writing the AST. | |
1146 if (element is PropertyInducingElement) { | |
1147 PropertyAccessorElement getter = element.getter; | |
1148 FunctionType getterType = getter.type; | |
1149 if (getterType != null) { | |
1150 DartType returnType = getterType.returnType; | |
1151 if (!_isExecutableType(returnType)) { | |
1152 return StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION; | |
1153 } | |
1154 } | |
1155 } else if (element is VariableElement) { | |
1156 DartType variableType = element.type; | |
1157 if (!_isExecutableType(variableType)) { | |
1158 return StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION; | |
1159 } | |
1160 } else { | |
1161 if (target == null) { | |
1162 ClassElement enclosingClass = _resolver.enclosingClass; | |
1163 if (enclosingClass == null) { | |
1164 return StaticTypeWarningCode.UNDEFINED_FUNCTION; | |
1165 } else if (element == null) { | |
1166 // Proxy-conditional warning, based on state of | |
1167 // resolver.getEnclosingClass() | |
1168 return StaticTypeWarningCode.UNDEFINED_METHOD; | |
1169 } else { | |
1170 return StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION; | |
1171 } | |
1172 } else { | |
1173 DartType targetType; | |
1174 if (useStaticContext) { | |
1175 targetType = _getStaticType(target); | |
1176 } else { | |
1177 // Compute and use the propagated type, if it is null, then it may | |
1178 // be the case that static type is some type, in which the static | |
1179 // type should be used. | |
1180 targetType = _getBestType(target); | |
1181 } | |
1182 if (targetType == null) { | |
1183 return StaticTypeWarningCode.UNDEFINED_FUNCTION; | |
1184 } else if (!targetType.isDynamic && !targetType.isBottom) { | |
1185 // Proxy-conditional warning, based on state of | |
1186 // targetType.getElement() | |
1187 return StaticTypeWarningCode.UNDEFINED_METHOD; | |
1188 } | |
1189 } | |
1190 } | |
1191 } | |
1192 return null; | |
1193 } | |
1194 | |
1195 /** | |
1196 * Check that the given index [expression] was resolved, otherwise a | |
1197 * [StaticTypeWarningCode.UNDEFINED_OPERATOR] is generated. The [target] is | |
1198 * the target of the expression. The [methodName] is the name of the operator | |
1199 * associated with the context of using of the given index expression. | |
1200 */ | |
1201 bool _checkForUndefinedIndexOperator(IndexExpression expression, | |
1202 Expression target, String methodName, MethodElement staticMethod, | |
1203 MethodElement propagatedMethod, DartType staticType, | |
1204 DartType propagatedType) { | |
1205 bool shouldReportMissingMember_static = | |
1206 _shouldReportMissingMember(staticType, staticMethod); | |
1207 bool shouldReportMissingMember_propagated = | |
1208 !shouldReportMissingMember_static && | |
1209 _enableHints && | |
1210 _shouldReportMissingMember(propagatedType, propagatedMethod) && | |
1211 !_memberFoundInSubclass( | |
1212 propagatedType.element, methodName, true, false); | |
1213 if (shouldReportMissingMember_static || | |
1214 shouldReportMissingMember_propagated) { | |
1215 sc.Token leftBracket = expression.leftBracket; | |
1216 sc.Token rightBracket = expression.rightBracket; | |
1217 ErrorCode errorCode; | |
1218 if (shouldReportMissingMember_static) { | |
1219 if (target is SuperExpression) { | |
1220 errorCode = StaticTypeWarningCode.UNDEFINED_SUPER_OPERATOR; | |
1221 } else { | |
1222 errorCode = StaticTypeWarningCode.UNDEFINED_OPERATOR; | |
1223 } | |
1224 } else { | |
1225 errorCode = HintCode.UNDEFINED_OPERATOR; | |
1226 } | |
1227 DartType type = | |
1228 shouldReportMissingMember_static ? staticType : propagatedType; | |
1229 if (leftBracket == null || rightBracket == null) { | |
1230 _recordUndefinedNode(type.element, errorCode, expression, [ | |
1231 methodName, | |
1232 type.displayName | |
1233 ]); | |
1234 } else { | |
1235 int offset = leftBracket.offset; | |
1236 int length = rightBracket.offset - offset + 1; | |
1237 _recordUndefinedOffset(type.element, errorCode, offset, length, [ | |
1238 methodName, | |
1239 type.displayName | |
1240 ]); | |
1241 } | |
1242 return true; | |
1243 } | |
1244 return false; | |
1245 } | |
1246 | |
1247 /** | |
1248 * Given an [argumentList] and the executable [element] that will be invoked | |
1249 * using those arguments, compute the list of parameters that correspond to | |
1250 * the list of arguments. Return the parameters that correspond to the | |
1251 * arguments, or `null` if no correspondence could be computed. | |
1252 */ | |
1253 List<ParameterElement> _computeCorrespondingParameters( | |
1254 ArgumentList argumentList, Element element) { | |
1255 if (element is PropertyAccessorElement) { | |
1256 // | |
1257 // This is an invocation of the call method defined on the value returned | |
1258 // by the getter. | |
1259 // | |
1260 FunctionType getterType = element.type; | |
1261 if (getterType != null) { | |
1262 DartType getterReturnType = getterType.returnType; | |
1263 if (getterReturnType is InterfaceType) { | |
1264 MethodElement callMethod = getterReturnType.lookUpMethod( | |
1265 FunctionElement.CALL_METHOD_NAME, _definingLibrary); | |
1266 if (callMethod != null) { | |
1267 return _resolveArgumentsToFunction(false, argumentList, callMethod); | |
1268 } | |
1269 } else if (getterReturnType is FunctionType) { | |
1270 List<ParameterElement> parameters = getterReturnType.parameters; | |
1271 return _resolveArgumentsToParameters(false, argumentList, parameters); | |
1272 } | |
1273 } | |
1274 } else if (element is ExecutableElement) { | |
1275 return _resolveArgumentsToFunction(false, argumentList, element); | |
1276 } else if (element is VariableElement) { | |
1277 VariableElement variable = element; | |
1278 DartType type = _promoteManager.getStaticType(variable); | |
1279 if (type is FunctionType) { | |
1280 FunctionType functionType = type; | |
1281 List<ParameterElement> parameters = functionType.parameters; | |
1282 return _resolveArgumentsToParameters(false, argumentList, parameters); | |
1283 } else if (type is InterfaceType) { | |
1284 // "call" invocation | |
1285 MethodElement callMethod = type.lookUpMethod( | |
1286 FunctionElement.CALL_METHOD_NAME, _definingLibrary); | |
1287 if (callMethod != null) { | |
1288 List<ParameterElement> parameters = callMethod.parameters; | |
1289 return _resolveArgumentsToParameters(false, argumentList, parameters); | |
1290 } | |
1291 } | |
1292 } | |
1293 return null; | |
1294 } | |
1295 | |
1296 /** | |
1297 * If the given [element] is a setter, return the getter associated with it. | |
1298 * Otherwise, return the element unchanged. | |
1299 */ | |
1300 Element _convertSetterToGetter(Element element) { | |
1301 // TODO(brianwilkerson) Determine whether and why the element could ever be | |
1302 // a setter. | |
1303 if (element is PropertyAccessorElement) { | |
1304 return element.variable.getter; | |
1305 } | |
1306 return element; | |
1307 } | |
1308 | |
1309 /** | |
1310 * Return `true` if the given [element] is not a proxy. See | |
1311 * [ClassElement.isOrInheritsProxy]. | |
1312 */ | |
1313 bool _doesntHaveProxy(Element element) => | |
1314 !(element is ClassElement && element.isOrInheritsProxy); | |
1315 | |
1316 /** | |
1317 * Look for any declarations of the given [identifier] that are imported using | |
1318 * a prefix. Return the element that was found, or `null` if the name is not | |
1319 * imported using a prefix. | |
1320 */ | |
1321 Element _findImportWithoutPrefix(SimpleIdentifier identifier) { | |
1322 Element element = null; | |
1323 Scope nameScope = _resolver.nameScope; | |
1324 for (ImportElement importElement in _definingLibrary.imports) { | |
1325 PrefixElement prefixElement = importElement.prefix; | |
1326 if (prefixElement != null) { | |
1327 Identifier prefixedIdentifier = new SyntheticIdentifier( | |
1328 "${prefixElement.name}.${identifier.name}", identifier); | |
1329 Element importedElement = | |
1330 nameScope.lookup(prefixedIdentifier, _definingLibrary); | |
1331 if (importedElement != null) { | |
1332 if (element == null) { | |
1333 element = importedElement; | |
1334 } else { | |
1335 element = MultiplyDefinedElementImpl.fromElements( | |
1336 _definingLibrary.context, element, importedElement); | |
1337 } | |
1338 } | |
1339 } | |
1340 } | |
1341 return element; | |
1342 } | |
1343 | |
1344 /** | |
1345 * Return the best type of the given [expression] that is to be used for | |
1346 * type analysis. | |
1347 */ | |
1348 DartType _getBestType(Expression expression) { | |
1349 DartType bestType = _resolveTypeParameter(expression.bestType); | |
1350 if (bestType is FunctionType) { | |
1351 // | |
1352 // All function types are subtypes of 'Function', which is itself a | |
1353 // subclass of 'Object'. | |
1354 // | |
1355 bestType = _resolver.typeProvider.functionType; | |
1356 } | |
1357 return bestType; | |
1358 } | |
1359 | |
1360 /** | |
1361 * Assuming that the given [expression] is a prefix for a deferred import, | |
1362 * return the library that is being imported. | |
1363 */ | |
1364 LibraryElement _getImportedLibrary(Expression expression) { | |
1365 PrefixElement prefixElement = | |
1366 (expression as SimpleIdentifier).staticElement as PrefixElement; | |
1367 List<ImportElement> imports = | |
1368 prefixElement.enclosingElement.getImportsWithPrefix(prefixElement); | |
1369 return imports[0].importedLibrary; | |
1370 } | |
1371 | |
1372 /** | |
1373 * Return the name of the method invoked by the given postfix [expression]. | |
1374 */ | |
1375 String _getPostfixOperator(PostfixExpression expression) => | |
1376 (expression.operator.type == sc.TokenType.PLUS_PLUS) | |
1377 ? sc.TokenType.PLUS.lexeme | |
1378 : sc.TokenType.MINUS.lexeme; | |
1379 | |
1380 /** | |
1381 * Return the name of the method invoked by the given postfix [expression]. | |
1382 */ | |
1383 String _getPrefixOperator(PrefixExpression expression) { | |
1384 sc.Token operator = expression.operator; | |
1385 sc.TokenType operatorType = operator.type; | |
1386 if (operatorType == sc.TokenType.PLUS_PLUS) { | |
1387 return sc.TokenType.PLUS.lexeme; | |
1388 } else if (operatorType == sc.TokenType.MINUS_MINUS) { | |
1389 return sc.TokenType.MINUS.lexeme; | |
1390 } else if (operatorType == sc.TokenType.MINUS) { | |
1391 return "unary-"; | |
1392 } else { | |
1393 return operator.lexeme; | |
1394 } | |
1395 } | |
1396 | |
1397 /** | |
1398 * Return the propagated type of the given [expression] that is to be used for | |
1399 * type analysis. | |
1400 */ | |
1401 DartType _getPropagatedType(Expression expression) { | |
1402 DartType propagatedType = _resolveTypeParameter(expression.propagatedType); | |
1403 if (propagatedType is FunctionType) { | |
1404 // | |
1405 // All function types are subtypes of 'Function', which is itself a | |
1406 // subclass of 'Object'. | |
1407 // | |
1408 propagatedType = _resolver.typeProvider.functionType; | |
1409 } | |
1410 return propagatedType; | |
1411 } | |
1412 | |
1413 /** | |
1414 * Return the static type of the given [expression] that is to be used for | |
1415 * type analysis. | |
1416 */ | |
1417 DartType _getStaticType(Expression expression) { | |
1418 if (expression is NullLiteral) { | |
1419 return _resolver.typeProvider.bottomType; | |
1420 } | |
1421 DartType staticType = _resolveTypeParameter(expression.staticType); | |
1422 if (staticType is FunctionType) { | |
1423 // | |
1424 // All function types are subtypes of 'Function', which is itself a | |
1425 // subclass of 'Object'. | |
1426 // | |
1427 staticType = _resolver.typeProvider.functionType; | |
1428 } | |
1429 return staticType; | |
1430 } | |
1431 | |
1432 /** | |
1433 * Return `true` if the given [expression] is a prefix for a deferred import. | |
1434 */ | |
1435 bool _isDeferredPrefix(Expression expression) { | |
1436 if (expression is! SimpleIdentifier) { | |
1437 return false; | |
1438 } | |
1439 Element element = (expression as SimpleIdentifier).staticElement; | |
1440 if (element is! PrefixElement) { | |
1441 return false; | |
1442 } | |
1443 PrefixElement prefixElement = element as PrefixElement; | |
1444 List<ImportElement> imports = | |
1445 prefixElement.enclosingElement.getImportsWithPrefix(prefixElement); | |
1446 if (imports.length != 1) { | |
1447 return false; | |
1448 } | |
1449 return imports[0].isDeferred; | |
1450 } | |
1451 | |
1452 /** | |
1453 * Return `true` if the given [type] represents an object that could be | |
1454 * invoked using the call operator '()'. | |
1455 */ | |
1456 bool _isExecutableType(DartType type) { | |
1457 if (type.isDynamic || type is FunctionType) { | |
1458 return true; | |
1459 } else if (!_enableStrictCallChecks && | |
1460 (type.isDartCoreFunction || type.isObject)) { | |
1461 return true; | |
1462 } else if (type is InterfaceType) { | |
1463 ClassElement classElement = type.element; | |
1464 // 16078 from Gilad: If the type is a Functor with the @proxy annotation, | |
1465 // treat it as an executable type. | |
1466 // example code: NonErrorResolverTest. | |
1467 // test_invocationOfNonFunction_proxyOnFunctionClass() | |
1468 if (classElement.isProxy && | |
1469 type.isSubtypeOf(_resolver.typeProvider.functionType)) { | |
1470 return true; | |
1471 } | |
1472 MethodElement methodElement = classElement.lookUpMethod( | |
1473 FunctionElement.CALL_METHOD_NAME, _definingLibrary); | |
1474 return methodElement != null; | |
1475 } | |
1476 return false; | |
1477 } | |
1478 | |
1479 /** | |
1480 * Return `true` if the given [element] is a static element. | |
1481 */ | |
1482 bool _isStatic(Element element) { | |
1483 if (element is ExecutableElement) { | |
1484 return element.isStatic; | |
1485 } else if (element is PropertyInducingElement) { | |
1486 return element.isStatic; | |
1487 } | |
1488 return false; | |
1489 } | |
1490 | |
1491 /** | |
1492 * Return `true` if the given [node] can validly be resolved to a prefix: | |
1493 * * it is the prefix in an import directive, or | |
1494 * * it is the prefix in a prefixed identifier. | |
1495 */ | |
1496 bool _isValidAsPrefix(SimpleIdentifier node) { | |
1497 AstNode parent = node.parent; | |
1498 if (parent is ImportDirective) { | |
1499 return identical(parent.prefix, node); | |
1500 } else if (parent is PrefixedIdentifier) { | |
1501 return true; | |
1502 } else if (parent is MethodInvocation) { | |
1503 return identical(parent.target, node); | |
1504 } | |
1505 return false; | |
1506 } | |
1507 | |
1508 /** | |
1509 * Return the target of a break or continue statement, and update the static | |
1510 * element of its label (if any). The [parentNode] is the AST node of the | |
1511 * break or continue statement. The [labelNode] is the label contained in that | |
1512 * statement (if any). The flag [isContinue] is `true` if the node being | |
1513 * visited is a continue statement. | |
1514 */ | |
1515 AstNode _lookupBreakOrContinueTarget( | |
1516 AstNode parentNode, SimpleIdentifier labelNode, bool isContinue) { | |
1517 if (labelNode == null) { | |
1518 return _resolver.implicitLabelScope.getTarget(isContinue); | |
1519 } else { | |
1520 LabelScope labelScope = _resolver.labelScope; | |
1521 if (labelScope == null) { | |
1522 // There are no labels in scope, so by definition the label is | |
1523 // undefined. | |
1524 _resolver.reportErrorForNode( | |
1525 CompileTimeErrorCode.LABEL_UNDEFINED, labelNode, [labelNode.name]); | |
1526 return null; | |
1527 } | |
1528 LabelScope definingScope = labelScope.lookup(labelNode.name); | |
1529 if (definingScope == null) { | |
1530 // No definition of the given label name could be found in any | |
1531 // enclosing scope. | |
1532 _resolver.reportErrorForNode( | |
1533 CompileTimeErrorCode.LABEL_UNDEFINED, labelNode, [labelNode.name]); | |
1534 return null; | |
1535 } | |
1536 // The target has been found. | |
1537 labelNode.staticElement = definingScope.element; | |
1538 ExecutableElement labelContainer = definingScope.element | |
1539 .getAncestor((element) => element is ExecutableElement); | |
1540 if (!identical(labelContainer, _resolver.enclosingFunction)) { | |
1541 _resolver.reportErrorForNode(CompileTimeErrorCode.LABEL_IN_OUTER_SCOPE, | |
1542 labelNode, [labelNode.name]); | |
1543 } | |
1544 return definingScope.node; | |
1545 } | |
1546 } | |
1547 | |
1548 /** | |
1549 * Look up the getter with the given [getterName] in the given [type]. Return | |
1550 * the element representing the getter that was found, or `null` if there is | |
1551 * no getter with the given name. The [target] is the target of the | |
1552 * invocation, or `null` if there is no target. | |
1553 */ | |
1554 PropertyAccessorElement _lookUpGetter( | |
1555 Expression target, DartType type, String getterName) { | |
1556 type = _resolveTypeParameter(type); | |
1557 if (type is InterfaceType) { | |
1558 InterfaceType interfaceType = type; | |
1559 PropertyAccessorElement accessor; | |
1560 if (target is SuperExpression) { | |
1561 accessor = interfaceType.lookUpGetterInSuperclass( | |
1562 getterName, _definingLibrary); | |
1563 } else { | |
1564 accessor = interfaceType.lookUpGetter(getterName, _definingLibrary); | |
1565 } | |
1566 if (accessor != null) { | |
1567 return accessor; | |
1568 } | |
1569 return _lookUpGetterInInterfaces( | |
1570 interfaceType, false, getterName, new HashSet<ClassElement>()); | |
1571 } | |
1572 return null; | |
1573 } | |
1574 | |
1575 /** | |
1576 * Look up the getter with the given [getterName] in the interfaces | |
1577 * implemented by the given [targetType], either directly or indirectly. | |
1578 * Return the element representing the getter that was found, or `null` if | |
1579 * there is no getter with the given name. The flag [includeTargetType] should | |
1580 * be `true` if the search should include the target type. The | |
1581 * [visitedInterfaces] is a set containing all of the interfaces that have | |
1582 * been examined, used to prevent infinite recursion and to optimize the | |
1583 * search. | |
1584 */ | |
1585 PropertyAccessorElement _lookUpGetterInInterfaces(InterfaceType targetType, | |
1586 bool includeTargetType, String getterName, | |
1587 HashSet<ClassElement> visitedInterfaces) { | |
1588 // TODO(brianwilkerson) This isn't correct. Section 8.1.1 of the | |
1589 // specification (titled "Inheritance and Overriding" under "Interfaces") | |
1590 // describes a much more complex scheme for finding the inherited member. | |
1591 // We need to follow that scheme. The code below should cover the 80% case. | |
1592 ClassElement targetClass = targetType.element; | |
1593 if (visitedInterfaces.contains(targetClass)) { | |
1594 return null; | |
1595 } | |
1596 visitedInterfaces.add(targetClass); | |
1597 if (includeTargetType) { | |
1598 PropertyAccessorElement getter = targetType.getGetter(getterName); | |
1599 if (getter != null && getter.isAccessibleIn(_definingLibrary)) { | |
1600 return getter; | |
1601 } | |
1602 } | |
1603 for (InterfaceType interfaceType in targetType.interfaces) { | |
1604 PropertyAccessorElement getter = _lookUpGetterInInterfaces( | |
1605 interfaceType, true, getterName, visitedInterfaces); | |
1606 if (getter != null) { | |
1607 return getter; | |
1608 } | |
1609 } | |
1610 for (InterfaceType mixinType in targetType.mixins.reversed) { | |
1611 PropertyAccessorElement getter = _lookUpGetterInInterfaces( | |
1612 mixinType, true, getterName, visitedInterfaces); | |
1613 if (getter != null) { | |
1614 return getter; | |
1615 } | |
1616 } | |
1617 InterfaceType superclass = targetType.superclass; | |
1618 if (superclass == null) { | |
1619 return null; | |
1620 } | |
1621 return _lookUpGetterInInterfaces( | |
1622 superclass, true, getterName, visitedInterfaces); | |
1623 } | |
1624 | |
1625 /** | |
1626 * Look up the method or getter with the given [memberName] in the given | |
1627 * [type]. Return the element representing the method or getter that was | |
1628 * found, or `null` if there is no method or getter with the given name. | |
1629 */ | |
1630 ExecutableElement _lookupGetterOrMethod(DartType type, String memberName) { | |
1631 type = _resolveTypeParameter(type); | |
1632 if (type is InterfaceType) { | |
1633 InterfaceType interfaceType = type; | |
1634 ExecutableElement member = | |
1635 interfaceType.lookUpMethod(memberName, _definingLibrary); | |
1636 if (member != null) { | |
1637 return member; | |
1638 } | |
1639 member = interfaceType.lookUpGetter(memberName, _definingLibrary); | |
1640 if (member != null) { | |
1641 return member; | |
1642 } | |
1643 return _lookUpGetterOrMethodInInterfaces( | |
1644 interfaceType, false, memberName, new HashSet<ClassElement>()); | |
1645 } | |
1646 return null; | |
1647 } | |
1648 | |
1649 /** | |
1650 * Look up the method or getter with the given [memberName] in the interfaces | |
1651 * implemented by the given [targetType], either directly or indirectly. | |
1652 * Return the element representing the method or getter that was found, or | |
1653 * `null` if there is no method or getter with the given name. The flag | |
1654 * [includeTargetType] should be `true` if the search should include the | |
1655 * target type. The [visitedInterfaces] is a set containing all of the | |
1656 * interfaces that have been examined, used to prevent infinite recursion and | |
1657 * to optimize the search. | |
1658 */ | |
1659 ExecutableElement _lookUpGetterOrMethodInInterfaces(InterfaceType targetType, | |
1660 bool includeTargetType, String memberName, | |
1661 HashSet<ClassElement> visitedInterfaces) { | |
1662 // TODO(brianwilkerson) This isn't correct. Section 8.1.1 of the | |
1663 // specification (titled "Inheritance and Overriding" under "Interfaces") | |
1664 // describes a much more complex scheme for finding the inherited member. | |
1665 // We need to follow that scheme. The code below should cover the 80% case. | |
1666 ClassElement targetClass = targetType.element; | |
1667 if (visitedInterfaces.contains(targetClass)) { | |
1668 return null; | |
1669 } | |
1670 visitedInterfaces.add(targetClass); | |
1671 if (includeTargetType) { | |
1672 ExecutableElement member = targetType.getMethod(memberName); | |
1673 if (member != null) { | |
1674 return member; | |
1675 } | |
1676 member = targetType.getGetter(memberName); | |
1677 if (member != null) { | |
1678 return member; | |
1679 } | |
1680 } | |
1681 for (InterfaceType interfaceType in targetType.interfaces) { | |
1682 ExecutableElement member = _lookUpGetterOrMethodInInterfaces( | |
1683 interfaceType, true, memberName, visitedInterfaces); | |
1684 if (member != null) { | |
1685 return member; | |
1686 } | |
1687 } | |
1688 for (InterfaceType mixinType in targetType.mixins.reversed) { | |
1689 ExecutableElement member = _lookUpGetterOrMethodInInterfaces( | |
1690 mixinType, true, memberName, visitedInterfaces); | |
1691 if (member != null) { | |
1692 return member; | |
1693 } | |
1694 } | |
1695 InterfaceType superclass = targetType.superclass; | |
1696 if (superclass == null) { | |
1697 return null; | |
1698 } | |
1699 return _lookUpGetterOrMethodInInterfaces( | |
1700 superclass, true, memberName, visitedInterfaces); | |
1701 } | |
1702 | |
1703 /** | |
1704 * Look up the method with the given [methodName] in the given [type]. Return | |
1705 * the element representing the method that was found, or `null` if there is | |
1706 * no method with the given name. The [target] is the target of the | |
1707 * invocation, or `null` if there is no target. | |
1708 */ | |
1709 MethodElement _lookUpMethod( | |
1710 Expression target, DartType type, String methodName) { | |
1711 type = _resolveTypeParameter(type); | |
1712 if (type is InterfaceType) { | |
1713 InterfaceType interfaceType = type; | |
1714 MethodElement method; | |
1715 if (target is SuperExpression) { | |
1716 method = interfaceType.lookUpMethodInSuperclass( | |
1717 methodName, _definingLibrary); | |
1718 } else { | |
1719 method = interfaceType.lookUpMethod(methodName, _definingLibrary); | |
1720 } | |
1721 if (method != null) { | |
1722 return method; | |
1723 } | |
1724 return _lookUpMethodInInterfaces( | |
1725 interfaceType, false, methodName, new HashSet<ClassElement>()); | |
1726 } | |
1727 return null; | |
1728 } | |
1729 | |
1730 /** | |
1731 * Look up the method with the given [methodName] in the interfaces | |
1732 * implemented by the given [targetType], either directly or indirectly. | |
1733 * Return the element representing the method that was found, or `null` if | |
1734 * there is no method with the given name. The flag [includeTargetType] should | |
1735 * be `true` if the search should include the target type. The | |
1736 * [visitedInterfaces] is a set containing all of the interfaces that have | |
1737 * been examined, used to prevent infinite recursion and to optimize the | |
1738 * search. | |
1739 */ | |
1740 MethodElement _lookUpMethodInInterfaces(InterfaceType targetType, | |
1741 bool includeTargetType, String methodName, | |
1742 HashSet<ClassElement> visitedInterfaces) { | |
1743 // TODO(brianwilkerson) This isn't correct. Section 8.1.1 of the | |
1744 // specification (titled "Inheritance and Overriding" under "Interfaces") | |
1745 // describes a much more complex scheme for finding the inherited member. | |
1746 // We need to follow that scheme. The code below should cover the 80% case. | |
1747 ClassElement targetClass = targetType.element; | |
1748 if (visitedInterfaces.contains(targetClass)) { | |
1749 return null; | |
1750 } | |
1751 visitedInterfaces.add(targetClass); | |
1752 if (includeTargetType) { | |
1753 MethodElement method = targetType.getMethod(methodName); | |
1754 if (method != null && method.isAccessibleIn(_definingLibrary)) { | |
1755 return method; | |
1756 } | |
1757 } | |
1758 for (InterfaceType interfaceType in targetType.interfaces) { | |
1759 MethodElement method = _lookUpMethodInInterfaces( | |
1760 interfaceType, true, methodName, visitedInterfaces); | |
1761 if (method != null) { | |
1762 return method; | |
1763 } | |
1764 } | |
1765 for (InterfaceType mixinType in targetType.mixins.reversed) { | |
1766 MethodElement method = _lookUpMethodInInterfaces( | |
1767 mixinType, true, methodName, visitedInterfaces); | |
1768 if (method != null) { | |
1769 return method; | |
1770 } | |
1771 } | |
1772 InterfaceType superclass = targetType.superclass; | |
1773 if (superclass == null) { | |
1774 return null; | |
1775 } | |
1776 return _lookUpMethodInInterfaces( | |
1777 superclass, true, methodName, visitedInterfaces); | |
1778 } | |
1779 | |
1780 /** | |
1781 * Look up the setter with the given [setterName] in the given [type]. Return | |
1782 * the element representing the setter that was found, or `null` if there is | |
1783 * no setter with the given name. The [target] is the target of the | |
1784 * invocation, or `null` if there is no target. | |
1785 */ | |
1786 PropertyAccessorElement _lookUpSetter( | |
1787 Expression target, DartType type, String setterName) { | |
1788 type = _resolveTypeParameter(type); | |
1789 if (type is InterfaceType) { | |
1790 InterfaceType interfaceType = type; | |
1791 PropertyAccessorElement accessor; | |
1792 if (target is SuperExpression) { | |
1793 accessor = interfaceType.lookUpSetterInSuperclass( | |
1794 setterName, _definingLibrary); | |
1795 } else { | |
1796 accessor = interfaceType.lookUpSetter(setterName, _definingLibrary); | |
1797 } | |
1798 if (accessor != null) { | |
1799 return accessor; | |
1800 } | |
1801 return _lookUpSetterInInterfaces( | |
1802 interfaceType, false, setterName, new HashSet<ClassElement>()); | |
1803 } | |
1804 return null; | |
1805 } | |
1806 | |
1807 /** | |
1808 * Look up the setter with the given [setterName] in the interfaces | |
1809 * implemented by the given [targetType], either directly or indirectly. | |
1810 * Return the element representing the setter that was found, or `null` if | |
1811 * there is no setter with the given name. The [targetType] is the type in | |
1812 * which the setter might be defined. The flag [includeTargetType] should be | |
1813 * `true` if the search should include the target type. The | |
1814 * [visitedInterfaces] is a set containing all of the interfaces that have | |
1815 * been examined, used to prevent infinite recursion and to optimize the | |
1816 * search. | |
1817 */ | |
1818 PropertyAccessorElement _lookUpSetterInInterfaces(InterfaceType targetType, | |
1819 bool includeTargetType, String setterName, | |
1820 HashSet<ClassElement> visitedInterfaces) { | |
1821 // TODO(brianwilkerson) This isn't correct. Section 8.1.1 of the | |
1822 // specification (titled "Inheritance and Overriding" under "Interfaces") | |
1823 // describes a much more complex scheme for finding the inherited member. | |
1824 // We need to follow that scheme. The code below should cover the 80% case. | |
1825 ClassElement targetClass = targetType.element; | |
1826 if (visitedInterfaces.contains(targetClass)) { | |
1827 return null; | |
1828 } | |
1829 visitedInterfaces.add(targetClass); | |
1830 if (includeTargetType) { | |
1831 PropertyAccessorElement setter = targetType.getSetter(setterName); | |
1832 if (setter != null && setter.isAccessibleIn(_definingLibrary)) { | |
1833 return setter; | |
1834 } | |
1835 } | |
1836 for (InterfaceType interfaceType in targetType.interfaces) { | |
1837 PropertyAccessorElement setter = _lookUpSetterInInterfaces( | |
1838 interfaceType, true, setterName, visitedInterfaces); | |
1839 if (setter != null) { | |
1840 return setter; | |
1841 } | |
1842 } | |
1843 for (InterfaceType mixinType in targetType.mixins.reversed) { | |
1844 PropertyAccessorElement setter = _lookUpSetterInInterfaces( | |
1845 mixinType, true, setterName, visitedInterfaces); | |
1846 if (setter != null) { | |
1847 return setter; | |
1848 } | |
1849 } | |
1850 InterfaceType superclass = targetType.superclass; | |
1851 if (superclass == null) { | |
1852 return null; | |
1853 } | |
1854 return _lookUpSetterInInterfaces( | |
1855 superclass, true, setterName, visitedInterfaces); | |
1856 } | |
1857 | |
1858 /** | |
1859 * Given some class [element], this method uses [_subtypeManager] to find the | |
1860 * set of all subtypes; the subtypes are then searched for a member (method, | |
1861 * getter, or setter), that has the given [memberName]. The flag [asMethod] | |
1862 * should be `true` if the methods should be searched for in the subtypes. The | |
1863 * flag [asAccessor] should be `true` if the accessors (getters and setters) | |
1864 * should be searched for in the subtypes. | |
1865 */ | |
1866 bool _memberFoundInSubclass( | |
1867 Element element, String memberName, bool asMethod, bool asAccessor) { | |
1868 if (element is ClassElement) { | |
1869 _subtypeManager.ensureLibraryVisited(_definingLibrary); | |
1870 HashSet<ClassElement> subtypeElements = | |
1871 _subtypeManager.computeAllSubtypes(element); | |
1872 for (ClassElement subtypeElement in subtypeElements) { | |
1873 if (asMethod && subtypeElement.getMethod(memberName) != null) { | |
1874 return true; | |
1875 } else if (asAccessor && | |
1876 (subtypeElement.getGetter(memberName) != null || | |
1877 subtypeElement.getSetter(memberName) != null)) { | |
1878 return true; | |
1879 } | |
1880 } | |
1881 } | |
1882 return false; | |
1883 } | |
1884 | |
1885 /** | |
1886 * Return the binary operator that is invoked by the given compound assignment | |
1887 * [operator]. | |
1888 */ | |
1889 sc.TokenType _operatorFromCompoundAssignment(sc.TokenType operator) { | |
1890 while (true) { | |
1891 if (operator == sc.TokenType.AMPERSAND_EQ) { | |
1892 return sc.TokenType.AMPERSAND; | |
1893 } else if (operator == sc.TokenType.BAR_EQ) { | |
1894 return sc.TokenType.BAR; | |
1895 } else if (operator == sc.TokenType.CARET_EQ) { | |
1896 return sc.TokenType.CARET; | |
1897 } else if (operator == sc.TokenType.GT_GT_EQ) { | |
1898 return sc.TokenType.GT_GT; | |
1899 } else if (operator == sc.TokenType.LT_LT_EQ) { | |
1900 return sc.TokenType.LT_LT; | |
1901 } else if (operator == sc.TokenType.MINUS_EQ) { | |
1902 return sc.TokenType.MINUS; | |
1903 } else if (operator == sc.TokenType.PERCENT_EQ) { | |
1904 return sc.TokenType.PERCENT; | |
1905 } else if (operator == sc.TokenType.PLUS_EQ) { | |
1906 return sc.TokenType.PLUS; | |
1907 } else if (operator == sc.TokenType.SLASH_EQ) { | |
1908 return sc.TokenType.SLASH; | |
1909 } else if (operator == sc.TokenType.STAR_EQ) { | |
1910 return sc.TokenType.STAR; | |
1911 } else if (operator == sc.TokenType.TILDE_SLASH_EQ) { | |
1912 return sc.TokenType.TILDE_SLASH; | |
1913 } else { | |
1914 // Internal error: Unmapped assignment operator. | |
1915 AnalysisEngine.instance.logger.logError( | |
1916 "Failed to map ${operator.lexeme} to it's corresponding operator"); | |
1917 return operator; | |
1918 } | |
1919 break; | |
1920 } | |
1921 } | |
1922 | |
1923 /** | |
1924 * Record that the given [node] is undefined, causing an error to be reported | |
1925 * if appropriate. The [declaringElement] is the element inside which no | |
1926 * declaration was found. If this element is a proxy, no error will be | |
1927 * reported. If null, then an error will always be reported. The [errorCode] | |
1928 * is the error code to report. The [arguments] are the arguments to the error | |
1929 * message. | |
1930 */ | |
1931 void _recordUndefinedNode(Element declaringElement, ErrorCode errorCode, | |
1932 AstNode node, List<Object> arguments) { | |
1933 if (_doesntHaveProxy(declaringElement)) { | |
1934 _resolver.reportErrorForNode(errorCode, node, arguments); | |
1935 } | |
1936 } | |
1937 | |
1938 /** | |
1939 * Record that the given [offset]/[length] is undefined, causing an error to | |
1940 * be reported if appropriate. The [declaringElement] is the element inside | |
1941 * which no declaration was found. If this element is a proxy, no error will | |
1942 * be reported. If null, then an error will always be reported. The | |
1943 * [errorCode] is the error code to report. The [arguments] are arguments to | |
1944 * the error message. | |
1945 */ | |
1946 void _recordUndefinedOffset(Element declaringElement, ErrorCode errorCode, | |
1947 int offset, int length, List<Object> arguments) { | |
1948 if (_doesntHaveProxy(declaringElement)) { | |
1949 _resolver.reportErrorForOffset(errorCode, offset, length, arguments); | |
1950 } | |
1951 } | |
1952 | |
1953 /** | |
1954 * Record that the given [token] is undefined, causing an error to be reported | |
1955 * if appropriate. The [declaringElement] is the element inside which no | |
1956 * declaration was found. If this element is a proxy, no error will be | |
1957 * reported. If null, then an error will always be reported. The [errorCode] | |
1958 * is the error code to report. The [arguments] are arguments to the error | |
1959 * message. | |
1960 */ | |
1961 void _recordUndefinedToken(Element declaringElement, ErrorCode errorCode, | |
1962 sc.Token token, List<Object> arguments) { | |
1963 if (_doesntHaveProxy(declaringElement)) { | |
1964 _resolver.reportErrorForToken(errorCode, token, arguments); | |
1965 } | |
1966 } | |
1967 | |
1968 void _resolveAnnotationConstructorInvocationArguments( | |
1969 Annotation annotation, ConstructorElement constructor) { | |
1970 ArgumentList argumentList = annotation.arguments; | |
1971 // error will be reported in ConstantVerifier | |
1972 if (argumentList == null) { | |
1973 return; | |
1974 } | |
1975 // resolve arguments to parameters | |
1976 List<ParameterElement> parameters = | |
1977 _resolveArgumentsToFunction(true, argumentList, constructor); | |
1978 if (parameters != null) { | |
1979 argumentList.correspondingStaticParameters = parameters; | |
1980 } | |
1981 } | |
1982 | |
1983 /** | |
1984 * Continues resolution of the given [annotation]. | |
1985 */ | |
1986 void _resolveAnnotationElement(Annotation annotation) { | |
1987 SimpleIdentifier nameNode1; | |
1988 SimpleIdentifier nameNode2; | |
1989 { | |
1990 Identifier annName = annotation.name; | |
1991 if (annName is PrefixedIdentifier) { | |
1992 PrefixedIdentifier prefixed = annName; | |
1993 nameNode1 = prefixed.prefix; | |
1994 nameNode2 = prefixed.identifier; | |
1995 } else { | |
1996 nameNode1 = annName as SimpleIdentifier; | |
1997 nameNode2 = null; | |
1998 } | |
1999 } | |
2000 SimpleIdentifier nameNode3 = annotation.constructorName; | |
2001 ConstructorElement constructor = null; | |
2002 // | |
2003 // CONST or Class(args) | |
2004 // | |
2005 if (nameNode1 != null && nameNode2 == null && nameNode3 == null) { | |
2006 Element element1 = nameNode1.staticElement; | |
2007 // CONST | |
2008 if (element1 is PropertyAccessorElement) { | |
2009 _resolveAnnotationElementGetter(annotation, element1); | |
2010 return; | |
2011 } | |
2012 // Class(args) | |
2013 if (element1 is ClassElement) { | |
2014 ClassElement classElement = element1; | |
2015 constructor = new InterfaceTypeImpl(classElement).lookUpConstructor( | |
2016 null, _definingLibrary); | |
2017 } | |
2018 } | |
2019 // | |
2020 // prefix.CONST or prefix.Class() or Class.CONST or Class.constructor(args) | |
2021 // | |
2022 if (nameNode1 != null && nameNode2 != null && nameNode3 == null) { | |
2023 Element element1 = nameNode1.staticElement; | |
2024 Element element2 = nameNode2.staticElement; | |
2025 // Class.CONST - not resolved yet | |
2026 if (element1 is ClassElement) { | |
2027 ClassElement classElement = element1; | |
2028 element2 = classElement.lookUpGetter(nameNode2.name, _definingLibrary); | |
2029 } | |
2030 // prefix.CONST or Class.CONST | |
2031 if (element2 is PropertyAccessorElement) { | |
2032 nameNode2.staticElement = element2; | |
2033 annotation.element = element2; | |
2034 _resolveAnnotationElementGetter(annotation, element2); | |
2035 return; | |
2036 } | |
2037 // prefix.Class() | |
2038 if (element2 is ClassElement) { | |
2039 constructor = element2.unnamedConstructor; | |
2040 } | |
2041 // Class.constructor(args) | |
2042 if (element1 is ClassElement) { | |
2043 ClassElement classElement = element1; | |
2044 constructor = new InterfaceTypeImpl(classElement).lookUpConstructor( | |
2045 nameNode2.name, _definingLibrary); | |
2046 nameNode2.staticElement = constructor; | |
2047 } | |
2048 } | |
2049 // | |
2050 // prefix.Class.CONST or prefix.Class.constructor(args) | |
2051 // | |
2052 if (nameNode1 != null && nameNode2 != null && nameNode3 != null) { | |
2053 Element element2 = nameNode2.staticElement; | |
2054 // element2 should be ClassElement | |
2055 if (element2 is ClassElement) { | |
2056 ClassElement classElement = element2; | |
2057 String name3 = nameNode3.name; | |
2058 // prefix.Class.CONST | |
2059 PropertyAccessorElement getter = | |
2060 classElement.lookUpGetter(name3, _definingLibrary); | |
2061 if (getter != null) { | |
2062 nameNode3.staticElement = getter; | |
2063 annotation.element = element2; | |
2064 _resolveAnnotationElementGetter(annotation, getter); | |
2065 return; | |
2066 } | |
2067 // prefix.Class.constructor(args) | |
2068 constructor = new InterfaceTypeImpl(classElement).lookUpConstructor( | |
2069 name3, _definingLibrary); | |
2070 nameNode3.staticElement = constructor; | |
2071 } | |
2072 } | |
2073 // we need constructor | |
2074 if (constructor == null) { | |
2075 _resolver.reportErrorForNode( | |
2076 CompileTimeErrorCode.INVALID_ANNOTATION, annotation); | |
2077 return; | |
2078 } | |
2079 // record element | |
2080 annotation.element = constructor; | |
2081 // resolve arguments | |
2082 _resolveAnnotationConstructorInvocationArguments(annotation, constructor); | |
2083 } | |
2084 | |
2085 void _resolveAnnotationElementGetter( | |
2086 Annotation annotation, PropertyAccessorElement accessorElement) { | |
2087 // accessor should be synthetic | |
2088 if (!accessorElement.isSynthetic) { | |
2089 _resolver.reportErrorForNode( | |
2090 CompileTimeErrorCode.INVALID_ANNOTATION, annotation); | |
2091 return; | |
2092 } | |
2093 // variable should be constant | |
2094 VariableElement variableElement = accessorElement.variable; | |
2095 if (!variableElement.isConst) { | |
2096 _resolver.reportErrorForNode( | |
2097 CompileTimeErrorCode.INVALID_ANNOTATION, annotation); | |
2098 } | |
2099 // OK | |
2100 return; | |
2101 } | |
2102 | |
2103 /** | |
2104 * Given an [argumentList] and the [executableElement] that will be invoked | |
2105 * using those argument, compute the list of parameters that correspond to the | |
2106 * list of arguments. An error will be reported if any of the arguments cannot | |
2107 * be matched to a parameter. The flag [reportError] should be `true` if a | |
2108 * compile-time error should be reported; or `false` if a compile-time warning | |
2109 * should be reported. Return the parameters that correspond to the arguments, | |
2110 * or `null` if no correspondence could be computed. | |
2111 */ | |
2112 List<ParameterElement> _resolveArgumentsToFunction(bool reportError, | |
2113 ArgumentList argumentList, ExecutableElement executableElement) { | |
2114 if (executableElement == null) { | |
2115 return null; | |
2116 } | |
2117 List<ParameterElement> parameters = executableElement.parameters; | |
2118 return _resolveArgumentsToParameters(reportError, argumentList, parameters); | |
2119 } | |
2120 | |
2121 /** | |
2122 * Given an [argumentList] and the [parameters] related to the element that | |
2123 * will be invoked using those arguments, compute the list of parameters that | |
2124 * correspond to the list of arguments. An error will be reported if any of | |
2125 * the arguments cannot be matched to a parameter. The flag [reportError] | |
2126 * should be `true` if a compile-time error should be reported; or `false` if | |
2127 * a compile-time warning should be reported. Return the parameters that | |
2128 * correspond to the arguments. | |
2129 */ | |
2130 List<ParameterElement> _resolveArgumentsToParameters(bool reportError, | |
2131 ArgumentList argumentList, List<ParameterElement> parameters) { | |
2132 List<ParameterElement> requiredParameters = new List<ParameterElement>(); | |
2133 List<ParameterElement> positionalParameters = new List<ParameterElement>(); | |
2134 HashMap<String, ParameterElement> namedParameters = | |
2135 new HashMap<String, ParameterElement>(); | |
2136 for (ParameterElement parameter in parameters) { | |
2137 ParameterKind kind = parameter.parameterKind; | |
2138 if (kind == ParameterKind.REQUIRED) { | |
2139 requiredParameters.add(parameter); | |
2140 } else if (kind == ParameterKind.POSITIONAL) { | |
2141 positionalParameters.add(parameter); | |
2142 } else { | |
2143 namedParameters[parameter.name] = parameter; | |
2144 } | |
2145 } | |
2146 List<ParameterElement> unnamedParameters = | |
2147 new List<ParameterElement>.from(requiredParameters); | |
2148 unnamedParameters.addAll(positionalParameters); | |
2149 int unnamedParameterCount = unnamedParameters.length; | |
2150 int unnamedIndex = 0; | |
2151 NodeList<Expression> arguments = argumentList.arguments; | |
2152 int argumentCount = arguments.length; | |
2153 List<ParameterElement> resolvedParameters = | |
2154 new List<ParameterElement>(argumentCount); | |
2155 int positionalArgumentCount = 0; | |
2156 HashSet<String> usedNames = new HashSet<String>(); | |
2157 bool noBlankArguments = true; | |
2158 for (int i = 0; i < argumentCount; i++) { | |
2159 Expression argument = arguments[i]; | |
2160 if (argument is NamedExpression) { | |
2161 SimpleIdentifier nameNode = argument.name.label; | |
2162 String name = nameNode.name; | |
2163 ParameterElement element = namedParameters[name]; | |
2164 if (element == null) { | |
2165 ErrorCode errorCode = (reportError | |
2166 ? CompileTimeErrorCode.UNDEFINED_NAMED_PARAMETER | |
2167 : StaticWarningCode.UNDEFINED_NAMED_PARAMETER); | |
2168 _resolver.reportErrorForNode(errorCode, nameNode, [name]); | |
2169 } else { | |
2170 resolvedParameters[i] = element; | |
2171 nameNode.staticElement = element; | |
2172 } | |
2173 if (!usedNames.add(name)) { | |
2174 _resolver.reportErrorForNode( | |
2175 CompileTimeErrorCode.DUPLICATE_NAMED_ARGUMENT, nameNode, [name]); | |
2176 } | |
2177 } else { | |
2178 if (argument is SimpleIdentifier && argument.name.isEmpty) { | |
2179 noBlankArguments = false; | |
2180 } | |
2181 positionalArgumentCount++; | |
2182 if (unnamedIndex < unnamedParameterCount) { | |
2183 resolvedParameters[i] = unnamedParameters[unnamedIndex++]; | |
2184 } | |
2185 } | |
2186 } | |
2187 if (positionalArgumentCount < requiredParameters.length && | |
2188 noBlankArguments) { | |
2189 ErrorCode errorCode = (reportError | |
2190 ? CompileTimeErrorCode.NOT_ENOUGH_REQUIRED_ARGUMENTS | |
2191 : StaticWarningCode.NOT_ENOUGH_REQUIRED_ARGUMENTS); | |
2192 _resolver.reportErrorForNode(errorCode, argumentList, [ | |
2193 requiredParameters.length, | |
2194 positionalArgumentCount | |
2195 ]); | |
2196 } else if (positionalArgumentCount > unnamedParameterCount && | |
2197 noBlankArguments) { | |
2198 ErrorCode errorCode = (reportError | |
2199 ? CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS | |
2200 : StaticWarningCode.EXTRA_POSITIONAL_ARGUMENTS); | |
2201 _resolver.reportErrorForNode(errorCode, argumentList, [ | |
2202 unnamedParameterCount, | |
2203 positionalArgumentCount | |
2204 ]); | |
2205 } | |
2206 return resolvedParameters; | |
2207 } | |
2208 | |
2209 void _resolveBinaryExpression(BinaryExpression node, String methodName) { | |
2210 Expression leftOperand = node.leftOperand; | |
2211 if (leftOperand != null) { | |
2212 DartType staticType = _getStaticType(leftOperand); | |
2213 MethodElement staticMethod = | |
2214 _lookUpMethod(leftOperand, staticType, methodName); | |
2215 node.staticElement = staticMethod; | |
2216 DartType propagatedType = _getPropagatedType(leftOperand); | |
2217 MethodElement propagatedMethod = | |
2218 _lookUpMethod(leftOperand, propagatedType, methodName); | |
2219 node.propagatedElement = propagatedMethod; | |
2220 if (_shouldReportMissingMember(staticType, staticMethod)) { | |
2221 if (leftOperand is SuperExpression) { | |
2222 _recordUndefinedToken(staticType.element, | |
2223 StaticTypeWarningCode.UNDEFINED_SUPER_OPERATOR, node.operator, [ | |
2224 methodName, | |
2225 staticType.displayName | |
2226 ]); | |
2227 } else { | |
2228 _recordUndefinedToken(staticType.element, | |
2229 StaticTypeWarningCode.UNDEFINED_OPERATOR, node.operator, [ | |
2230 methodName, | |
2231 staticType.displayName | |
2232 ]); | |
2233 } | |
2234 } else if (_enableHints && | |
2235 _shouldReportMissingMember(propagatedType, propagatedMethod) && | |
2236 !_memberFoundInSubclass( | |
2237 propagatedType.element, methodName, true, false)) { | |
2238 _recordUndefinedToken(propagatedType.element, | |
2239 HintCode.UNDEFINED_OPERATOR, node.operator, [ | |
2240 methodName, | |
2241 propagatedType.displayName | |
2242 ]); | |
2243 } | |
2244 } | |
2245 } | |
2246 | |
2247 /** | |
2248 * Resolve the names in the given [combinators] in the scope of the given | |
2249 * [library]. | |
2250 */ | |
2251 void _resolveCombinators( | |
2252 LibraryElement library, NodeList<Combinator> combinators) { | |
2253 if (library == null) { | |
2254 // | |
2255 // The library will be null if the directive containing the combinators | |
2256 // has a URI that is not valid. | |
2257 // | |
2258 return; | |
2259 } | |
2260 Namespace namespace = | |
2261 new NamespaceBuilder().createExportNamespaceForLibrary(library); | |
2262 for (Combinator combinator in combinators) { | |
2263 NodeList<SimpleIdentifier> names; | |
2264 if (combinator is HideCombinator) { | |
2265 names = combinator.hiddenNames; | |
2266 } else { | |
2267 names = (combinator as ShowCombinator).shownNames; | |
2268 } | |
2269 for (SimpleIdentifier name in names) { | |
2270 String nameStr = name.name; | |
2271 Element element = namespace.get(nameStr); | |
2272 if (element == null) { | |
2273 element = namespace.get("$nameStr="); | |
2274 } | |
2275 if (element != null) { | |
2276 // Ensure that the name always resolves to a top-level variable | |
2277 // rather than a getter or setter | |
2278 if (element is PropertyAccessorElement) { | |
2279 element = (element as PropertyAccessorElement).variable; | |
2280 } | |
2281 name.staticElement = element; | |
2282 } | |
2283 } | |
2284 } | |
2285 } | |
2286 | |
2287 /** | |
2288 * Given that we are accessing a property of the given [classElement] with the | |
2289 * given [propertyName], return the element that represents the property. | |
2290 */ | |
2291 Element _resolveElement( | |
2292 ClassElementImpl classElement, SimpleIdentifier propertyName) { | |
2293 String name = propertyName.name; | |
2294 Element element = null; | |
2295 if (propertyName.inSetterContext()) { | |
2296 element = classElement.getSetter(name); | |
2297 } | |
2298 if (element == null) { | |
2299 element = classElement.getGetter(name); | |
2300 } | |
2301 if (element == null) { | |
2302 element = classElement.getMethod(name); | |
2303 } | |
2304 if (element != null && element.isAccessibleIn(_definingLibrary)) { | |
2305 return element; | |
2306 } | |
2307 return null; | |
2308 } | |
2309 | |
2310 /** | |
2311 * Given an invocation of the form 'm(a1, ..., an)', resolve 'm' to the | |
2312 * element being invoked. If the returned element is a method, then the method | |
2313 * will be invoked. If the returned element is a getter, the getter will be | |
2314 * invoked without arguments and the result of that invocation will then be | |
2315 * invoked with the arguments. The [methodName] is the name of the method | |
2316 * being invoked ('m'). | |
2317 */ | |
2318 Element _resolveInvokedElement(SimpleIdentifier methodName) { | |
2319 // | |
2320 // Look first in the lexical scope. | |
2321 // | |
2322 Element element = _resolver.nameScope.lookup(methodName, _definingLibrary); | |
2323 if (element == null) { | |
2324 // | |
2325 // If it isn't defined in the lexical scope, and the invocation is within | |
2326 // a class, then look in the inheritance scope. | |
2327 // | |
2328 ClassElement enclosingClass = _resolver.enclosingClass; | |
2329 if (enclosingClass != null) { | |
2330 InterfaceType enclosingType = enclosingClass.type; | |
2331 element = _lookUpMethod(null, enclosingType, methodName.name); | |
2332 if (element == null) { | |
2333 // | |
2334 // If there's no method, then it's possible that 'm' is a getter that | |
2335 // returns a function. | |
2336 // | |
2337 element = _lookUpGetter(null, enclosingType, methodName.name); | |
2338 } | |
2339 } | |
2340 } | |
2341 // TODO(brianwilkerson) Report this error. | |
2342 return element; | |
2343 } | |
2344 | |
2345 /** | |
2346 * Given an invocation of the form 'e.m(a1, ..., an)', resolve 'e.m' to the | |
2347 * element being invoked. If the returned element is a method, then the method | |
2348 * will be invoked. If the returned element is a getter, the getter will be | |
2349 * invoked without arguments and the result of that invocation will then be | |
2350 * invoked with the arguments. The [target] is the target of the invocation | |
2351 * ('e'). The [targetType] is the type of the target. The [methodName] is th | |
2352 * name of the method being invoked ('m'). [isConditional] indicates | |
2353 * whether the invocatoin uses a '?.' operator. | |
2354 */ | |
2355 Element _resolveInvokedElementWithTarget(Expression target, | |
2356 DartType targetType, SimpleIdentifier methodName, bool isConditional) { | |
2357 if (targetType is InterfaceType) { | |
2358 Element element = _lookUpMethod(target, targetType, methodName.name); | |
2359 if (element == null) { | |
2360 // | |
2361 // If there's no method, then it's possible that 'm' is a getter that | |
2362 // returns a function. | |
2363 // | |
2364 // TODO (collinsn): need to add union type support here too, in the | |
2365 // style of [lookUpMethod]. | |
2366 element = _lookUpGetter(target, targetType, methodName.name); | |
2367 } | |
2368 return element; | |
2369 } else if (target is SimpleIdentifier) { | |
2370 Element targetElement = target.staticElement; | |
2371 if (targetElement is PrefixElement) { | |
2372 if (isConditional) { | |
2373 _resolver.reportErrorForNode( | |
2374 CompileTimeErrorCode.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT, | |
2375 target, [target.name]); | |
2376 } | |
2377 // | |
2378 // Look to see whether the name of the method is really part of a | |
2379 // prefixed identifier for an imported top-level function or top-level | |
2380 // getter that returns a function. | |
2381 // | |
2382 String name = "${target.name}.$methodName"; | |
2383 Identifier functionName = new SyntheticIdentifier(name, methodName); | |
2384 Element element = | |
2385 _resolver.nameScope.lookup(functionName, _definingLibrary); | |
2386 if (element != null) { | |
2387 // TODO(brianwilkerson) This isn't a method invocation, it's a | |
2388 // function invocation where the function name is a prefixed | |
2389 // identifier. Consider re-writing the AST. | |
2390 return element; | |
2391 } | |
2392 } | |
2393 } | |
2394 // TODO(brianwilkerson) Report this error. | |
2395 return null; | |
2396 } | |
2397 | |
2398 /** | |
2399 * Given that we are accessing a property of the given [targetType] with the | |
2400 * given [propertyName], return the element that represents the property. The | |
2401 * [target] is the target of the invocation ('e'). | |
2402 */ | |
2403 ExecutableElement _resolveProperty( | |
2404 Expression target, DartType targetType, SimpleIdentifier propertyName) { | |
2405 ExecutableElement memberElement = null; | |
2406 if (propertyName.inSetterContext()) { | |
2407 memberElement = _lookUpSetter(target, targetType, propertyName.name); | |
2408 } | |
2409 if (memberElement == null) { | |
2410 memberElement = _lookUpGetter(target, targetType, propertyName.name); | |
2411 } | |
2412 if (memberElement == null) { | |
2413 memberElement = _lookUpMethod(target, targetType, propertyName.name); | |
2414 } | |
2415 return memberElement; | |
2416 } | |
2417 | |
2418 void _resolvePropertyAccess( | |
2419 Expression target, SimpleIdentifier propertyName, bool isConditional) { | |
2420 DartType staticType = _getStaticType(target); | |
2421 DartType propagatedType = _getPropagatedType(target); | |
2422 Element staticElement = null; | |
2423 Element propagatedElement = null; | |
2424 // | |
2425 // If this property access is of the form 'C.m' where 'C' is a class, | |
2426 // then we don't call resolveProperty(...) which walks up the class | |
2427 // hierarchy, instead we just look for the member in the type only. This | |
2428 // does not apply to conditional property accesses (i.e. 'C?.m'). | |
2429 // | |
2430 ClassElementImpl typeReference = getTypeReference(target, isConditional); | |
2431 if (typeReference != null) { | |
2432 // TODO(brianwilkerson) Why are we setting the propagated element here? | |
2433 // It looks wrong. | |
2434 staticElement = | |
2435 propagatedElement = _resolveElement(typeReference, propertyName); | |
2436 } else { | |
2437 staticElement = _resolveProperty(target, staticType, propertyName); | |
2438 propagatedElement = | |
2439 _resolveProperty(target, propagatedType, propertyName); | |
2440 } | |
2441 // May be part of annotation, record property element only if exists. | |
2442 // Error was already reported in validateAnnotationElement(). | |
2443 if (target.parent.parent is Annotation) { | |
2444 if (staticElement != null) { | |
2445 propertyName.staticElement = staticElement; | |
2446 } | |
2447 return; | |
2448 } | |
2449 propertyName.staticElement = staticElement; | |
2450 propertyName.propagatedElement = propagatedElement; | |
2451 bool shouldReportMissingMember_static = | |
2452 _shouldReportMissingMember(staticType, staticElement); | |
2453 bool shouldReportMissingMember_propagated = | |
2454 !shouldReportMissingMember_static && | |
2455 _enableHints && | |
2456 _shouldReportMissingMember(propagatedType, propagatedElement) && | |
2457 !_memberFoundInSubclass( | |
2458 propagatedType.element, propertyName.name, false, true); | |
2459 if (shouldReportMissingMember_static || | |
2460 shouldReportMissingMember_propagated) { | |
2461 DartType staticOrPropagatedType = | |
2462 shouldReportMissingMember_static ? staticType : propagatedType; | |
2463 Element staticOrPropagatedEnclosingElt = staticOrPropagatedType.element; | |
2464 bool isStaticProperty = _isStatic(staticOrPropagatedEnclosingElt); | |
2465 DartType displayType = staticOrPropagatedType != null | |
2466 ? staticOrPropagatedType | |
2467 : propagatedType != null ? propagatedType : staticType; | |
2468 // Special getter cases. | |
2469 if (propertyName.inGetterContext()) { | |
2470 if (!isStaticProperty && | |
2471 staticOrPropagatedEnclosingElt is ClassElement) { | |
2472 ClassElement classElement = staticOrPropagatedEnclosingElt; | |
2473 InterfaceType targetType = classElement.type; | |
2474 if (!_enableStrictCallChecks && | |
2475 targetType != null && | |
2476 targetType.isDartCoreFunction && | |
2477 propertyName.name == FunctionElement.CALL_METHOD_NAME) { | |
2478 // TODO(brianwilkerson) Can we ever resolve the function being | |
2479 // invoked? | |
2480 // resolveArgumentsToParameters(node.getArgumentList(), invokedFuncti
on); | |
2481 return; | |
2482 } else if (classElement.isEnum && propertyName.name == "_name") { | |
2483 _resolver.reportErrorForNode( | |
2484 CompileTimeErrorCode.ACCESS_PRIVATE_ENUM_FIELD, propertyName, | |
2485 [propertyName.name]); | |
2486 return; | |
2487 } | |
2488 } | |
2489 } | |
2490 Element declaringElement = | |
2491 staticType.isVoid ? null : staticOrPropagatedEnclosingElt; | |
2492 if (propertyName.inSetterContext()) { | |
2493 ErrorCode errorCode; | |
2494 if (shouldReportMissingMember_static) { | |
2495 if (target is SuperExpression) { | |
2496 if (isStaticProperty && !staticType.isVoid) { | |
2497 errorCode = StaticWarningCode.UNDEFINED_SUPER_SETTER; | |
2498 } else { | |
2499 errorCode = StaticTypeWarningCode.UNDEFINED_SUPER_SETTER; | |
2500 } | |
2501 } else { | |
2502 if (isStaticProperty && !staticType.isVoid) { | |
2503 errorCode = StaticWarningCode.UNDEFINED_SETTER; | |
2504 } else { | |
2505 errorCode = StaticTypeWarningCode.UNDEFINED_SETTER; | |
2506 } | |
2507 } | |
2508 } else { | |
2509 errorCode = HintCode.UNDEFINED_SETTER; | |
2510 } | |
2511 _recordUndefinedNode(declaringElement, errorCode, propertyName, [ | |
2512 propertyName.name, | |
2513 displayType.displayName | |
2514 ]); | |
2515 } else if (propertyName.inGetterContext()) { | |
2516 ErrorCode errorCode; | |
2517 if (shouldReportMissingMember_static) { | |
2518 if (target is SuperExpression) { | |
2519 if (isStaticProperty && !staticType.isVoid) { | |
2520 errorCode = StaticWarningCode.UNDEFINED_SUPER_GETTER; | |
2521 } else { | |
2522 errorCode = StaticTypeWarningCode.UNDEFINED_SUPER_GETTER; | |
2523 } | |
2524 } else { | |
2525 if (isStaticProperty && !staticType.isVoid) { | |
2526 errorCode = StaticWarningCode.UNDEFINED_GETTER; | |
2527 } else { | |
2528 errorCode = StaticTypeWarningCode.UNDEFINED_GETTER; | |
2529 } | |
2530 } | |
2531 } else { | |
2532 errorCode = HintCode.UNDEFINED_GETTER; | |
2533 } | |
2534 _recordUndefinedNode(declaringElement, errorCode, propertyName, [ | |
2535 propertyName.name, | |
2536 displayType.displayName | |
2537 ]); | |
2538 } else { | |
2539 _recordUndefinedNode(declaringElement, | |
2540 StaticWarningCode.UNDEFINED_IDENTIFIER, propertyName, | |
2541 [propertyName.name]); | |
2542 } | |
2543 } | |
2544 } | |
2545 | |
2546 /** | |
2547 * Resolve the given simple [identifier] if possible. Return the element to | |
2548 * which it could be resolved, or `null` if it could not be resolved. This | |
2549 * does not record the results of the resolution. | |
2550 */ | |
2551 Element _resolveSimpleIdentifier(SimpleIdentifier identifier) { | |
2552 Element element = _resolver.nameScope.lookup(identifier, _definingLibrary); | |
2553 if (element is PropertyAccessorElement && identifier.inSetterContext()) { | |
2554 PropertyInducingElement variable = | |
2555 (element as PropertyAccessorElement).variable; | |
2556 if (variable != null) { | |
2557 PropertyAccessorElement setter = variable.setter; | |
2558 if (setter == null) { | |
2559 // | |
2560 // Check to see whether there might be a locally defined getter and | |
2561 // an inherited setter. | |
2562 // | |
2563 ClassElement enclosingClass = _resolver.enclosingClass; | |
2564 if (enclosingClass != null) { | |
2565 setter = _lookUpSetter(null, enclosingClass.type, identifier.name); | |
2566 } | |
2567 } | |
2568 if (setter != null) { | |
2569 element = setter; | |
2570 } | |
2571 } | |
2572 } else if (element == null && | |
2573 (identifier.inSetterContext() || | |
2574 identifier.parent is CommentReference)) { | |
2575 element = _resolver.nameScope.lookup( | |
2576 new SyntheticIdentifier("${identifier.name}=", identifier), | |
2577 _definingLibrary); | |
2578 } | |
2579 ClassElement enclosingClass = _resolver.enclosingClass; | |
2580 if (element == null && enclosingClass != null) { | |
2581 InterfaceType enclosingType = enclosingClass.type; | |
2582 if (element == null && | |
2583 (identifier.inSetterContext() || | |
2584 identifier.parent is CommentReference)) { | |
2585 element = _lookUpSetter(null, enclosingType, identifier.name); | |
2586 } | |
2587 if (element == null && identifier.inGetterContext()) { | |
2588 element = _lookUpGetter(null, enclosingType, identifier.name); | |
2589 } | |
2590 if (element == null) { | |
2591 element = _lookUpMethod(null, enclosingType, identifier.name); | |
2592 } | |
2593 } | |
2594 return element; | |
2595 } | |
2596 | |
2597 /** | |
2598 * If the given [type] is a type parameter, resolve it to the type that should | |
2599 * be used when looking up members. Otherwise, return the original type. | |
2600 */ | |
2601 DartType _resolveTypeParameter(DartType type) { | |
2602 if (type is TypeParameterType) { | |
2603 DartType bound = type.element.bound; | |
2604 if (bound == null) { | |
2605 return _resolver.typeProvider.objectType; | |
2606 } | |
2607 return bound; | |
2608 } | |
2609 return type; | |
2610 } | |
2611 | |
2612 /** | |
2613 * Given a [node] that can have annotations associated with it and the | |
2614 * [element] to which that node has been resolved, create the annotations in | |
2615 * the element model representing the annotations on the node. | |
2616 */ | |
2617 void _setMetadataForParameter(Element element, NormalFormalParameter node) { | |
2618 if (element is! ElementImpl) { | |
2619 return; | |
2620 } | |
2621 List<ElementAnnotationImpl> annotationList = | |
2622 new List<ElementAnnotationImpl>(); | |
2623 _addAnnotations(annotationList, node.metadata); | |
2624 if (!annotationList.isEmpty) { | |
2625 (element as ElementImpl).metadata = annotationList; | |
2626 } | |
2627 } | |
2628 | |
2629 /** | |
2630 * Return `true` if we should report an error as a result of looking up a | |
2631 * [member] in the given [type] and not finding any member. | |
2632 */ | |
2633 bool _shouldReportMissingMember(DartType type, Element member) { | |
2634 if (member != null || type == null || type.isDynamic || type.isBottom) { | |
2635 return false; | |
2636 } | |
2637 return true; | |
2638 } | |
2639 | |
2640 /** | |
2641 * Checks whether the given [expression] is a reference to a class. If it is | |
2642 * then the element representing the class is returned, otherwise `null` is | |
2643 * returned. [isConditional] indicates whether [expression] is to the left | |
2644 * of a '?.' opertator. | |
2645 */ | |
2646 static ClassElementImpl getTypeReference( | |
2647 Expression expression, bool isConditional) { | |
2648 if (!isConditional && expression is Identifier) { | |
2649 Element staticElement = expression.staticElement; | |
2650 if (staticElement is ClassElementImpl) { | |
2651 return staticElement; | |
2652 } | |
2653 } | |
2654 return null; | |
2655 } | |
2656 | |
2657 /** | |
2658 * Given a [node] that can have annotations associated with it and the | |
2659 * [element] to which that node has been resolved, create the annotations in | |
2660 * the element model representing the annotations on the node. | |
2661 */ | |
2662 static void setMetadata(Element element, AnnotatedNode node) { | |
2663 if (element is! ElementImpl) { | |
2664 return; | |
2665 } | |
2666 List<ElementAnnotationImpl> annotationList = <ElementAnnotationImpl>[]; | |
2667 _addAnnotations(annotationList, node.metadata); | |
2668 if (node is VariableDeclaration && node.parent is VariableDeclarationList) { | |
2669 VariableDeclarationList list = node.parent as VariableDeclarationList; | |
2670 _addAnnotations(annotationList, list.metadata); | |
2671 if (list.parent is FieldDeclaration) { | |
2672 FieldDeclaration fieldDeclaration = list.parent as FieldDeclaration; | |
2673 _addAnnotations(annotationList, fieldDeclaration.metadata); | |
2674 } else if (list.parent is TopLevelVariableDeclaration) { | |
2675 TopLevelVariableDeclaration variableDeclaration = | |
2676 list.parent as TopLevelVariableDeclaration; | |
2677 _addAnnotations(annotationList, variableDeclaration.metadata); | |
2678 } | |
2679 } | |
2680 if (!annotationList.isEmpty) { | |
2681 (element as ElementImpl).metadata = annotationList; | |
2682 } | |
2683 } | |
2684 | |
2685 /** | |
2686 * Generate annotation elements for each of the annotations in the | |
2687 * [annotationList] and add them to the given list of [annotations]. | |
2688 */ | |
2689 static void _addAnnotations(List<ElementAnnotationImpl> annotationList, | |
2690 NodeList<Annotation> annotations) { | |
2691 int annotationCount = annotations.length; | |
2692 for (int i = 0; i < annotationCount; i++) { | |
2693 Annotation annotation = annotations[i]; | |
2694 Element resolvedElement = annotation.element; | |
2695 if (resolvedElement != null) { | |
2696 ElementAnnotationImpl elementAnnotation = | |
2697 new ElementAnnotationImpl(resolvedElement); | |
2698 annotation.elementAnnotation = elementAnnotation; | |
2699 annotationList.add(elementAnnotation); | |
2700 } | |
2701 } | |
2702 } | |
2703 | |
2704 /** | |
2705 * Return `true` if the given [identifier] is the return type of a constructor | |
2706 * declaration. | |
2707 */ | |
2708 static bool _isConstructorReturnType(SimpleIdentifier identifier) { | |
2709 AstNode parent = identifier.parent; | |
2710 if (parent is ConstructorDeclaration) { | |
2711 return identical(parent.returnType, identifier); | |
2712 } | |
2713 return false; | |
2714 } | |
2715 | |
2716 /** | |
2717 * Return `true` if the given [identifier] is the return type of a factory | |
2718 * constructor. | |
2719 */ | |
2720 static bool _isFactoryConstructorReturnType(SimpleIdentifier identifier) { | |
2721 AstNode parent = identifier.parent; | |
2722 if (parent is ConstructorDeclaration) { | |
2723 ConstructorDeclaration constructor = parent; | |
2724 return identical(constructor.returnType, identifier) && | |
2725 constructor.factoryKeyword != null; | |
2726 } | |
2727 return false; | |
2728 } | |
2729 | |
2730 /** | |
2731 * Return `true` if the given 'super' [expression] is used in a valid context. | |
2732 */ | |
2733 static bool _isSuperInValidContext(SuperExpression expression) { | |
2734 for (AstNode node = expression; node != null; node = node.parent) { | |
2735 if (node is CompilationUnit) { | |
2736 return false; | |
2737 } | |
2738 if (node is ConstructorDeclaration) { | |
2739 return node.factoryKeyword == null; | |
2740 } | |
2741 if (node is ConstructorFieldInitializer) { | |
2742 return false; | |
2743 } | |
2744 if (node is MethodDeclaration) { | |
2745 return !node.isStatic; | |
2746 } | |
2747 } | |
2748 return false; | |
2749 } | |
2750 } | |
2751 | |
2752 /** | |
2753 * An identifier that can be used to look up names in the lexical scope when | |
2754 * there is no identifier in the AST structure. There is no identifier in the | |
2755 * AST when the parser could not distinguish between a method invocation and an | |
2756 * invocation of a top-level function imported with a prefix. | |
2757 */ | |
2758 class SyntheticIdentifier extends Identifier { | |
2759 /** | |
2760 * The name of the synthetic identifier. | |
2761 */ | |
2762 final String name; | |
2763 | |
2764 /** | |
2765 * The identifier to be highlighted in case of an error | |
2766 */ | |
2767 final Identifier targetIdentifier; | |
2768 | |
2769 /** | |
2770 * Initialize a newly created synthetic identifier to have the given [name] | |
2771 * and [targetIdentifier]. | |
2772 */ | |
2773 SyntheticIdentifier(this.name, this.targetIdentifier); | |
2774 | |
2775 @override | |
2776 sc.Token get beginToken => null; | |
2777 | |
2778 @override | |
2779 Element get bestElement => null; | |
2780 | |
2781 @override | |
2782 Iterable get childEntities { | |
2783 // Should never be called, since a SyntheticIdentifier never appears in the | |
2784 // AST--it is just used for lookup. | |
2785 assert(false); | |
2786 return new ChildEntities(); | |
2787 } | |
2788 | |
2789 @override | |
2790 sc.Token get endToken => null; | |
2791 | |
2792 @override | |
2793 int get length => targetIdentifier.length; | |
2794 | |
2795 @override | |
2796 int get offset => targetIdentifier.offset; | |
2797 | |
2798 @override | |
2799 int get precedence => 16; | |
2800 | |
2801 @override | |
2802 Element get propagatedElement => null; | |
2803 | |
2804 @override | |
2805 Element get staticElement => null; | |
2806 | |
2807 @override | |
2808 accept(AstVisitor visitor) => null; | |
2809 | |
2810 @override | |
2811 void visitChildren(AstVisitor visitor) {} | |
2812 } | |
OLD | NEW |