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); |
| 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); |
| 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 return null; |
| 917 } |
| 918 |
| 919 @override |
| 920 Object visitRedirectingConstructorInvocation( |
| 921 RedirectingConstructorInvocation node) { |
| 922 ClassElement enclosingClass = _resolver.enclosingClass; |
| 923 if (enclosingClass == null) { |
| 924 // TODO(brianwilkerson) Report this error. |
| 925 return null; |
| 926 } |
| 927 SimpleIdentifier name = node.constructorName; |
| 928 ConstructorElement element; |
| 929 if (name == null) { |
| 930 element = enclosingClass.unnamedConstructor; |
| 931 } else { |
| 932 element = enclosingClass.getNamedConstructor(name.name); |
| 933 } |
| 934 if (element == null) { |
| 935 // TODO(brianwilkerson) Report this error and decide what element to |
| 936 // associate with the node. |
| 937 return null; |
| 938 } |
| 939 if (name != null) { |
| 940 name.staticElement = element; |
| 941 } |
| 942 node.staticElement = element; |
| 943 ArgumentList argumentList = node.argumentList; |
| 944 List<ParameterElement> parameters = |
| 945 _resolveArgumentsToFunction(false, argumentList, element); |
| 946 if (parameters != null) { |
| 947 argumentList.correspondingStaticParameters = parameters; |
| 948 } |
| 949 return null; |
| 950 } |
| 951 |
| 952 @override |
| 953 Object visitSimpleFormalParameter(SimpleFormalParameter node) { |
| 954 _setMetadataForParameter(node.element, node); |
| 955 return null; |
| 956 } |
| 957 |
| 958 @override |
| 959 Object visitSimpleIdentifier(SimpleIdentifier node) { |
| 960 // |
| 961 // Synthetic identifiers have been already reported during parsing. |
| 962 // |
| 963 if (node.isSynthetic) { |
| 964 return null; |
| 965 } |
| 966 // |
| 967 // We ignore identifiers that have already been resolved, such as |
| 968 // identifiers representing the name in a declaration. |
| 969 // |
| 970 if (node.staticElement != null) { |
| 971 return null; |
| 972 } |
| 973 // |
| 974 // The name dynamic denotes a Type object even though dynamic is not a |
| 975 // class. |
| 976 // |
| 977 if (node.name == _dynamicType.name) { |
| 978 node.staticElement = _dynamicType.element; |
| 979 node.staticType = _typeType; |
| 980 return null; |
| 981 } |
| 982 // |
| 983 // Otherwise, the node should be resolved. |
| 984 // |
| 985 Element element = _resolveSimpleIdentifier(node); |
| 986 ClassElement enclosingClass = _resolver.enclosingClass; |
| 987 if (_isFactoryConstructorReturnType(node) && |
| 988 !identical(element, enclosingClass)) { |
| 989 _resolver.reportErrorForNode( |
| 990 CompileTimeErrorCode.INVALID_FACTORY_NAME_NOT_A_CLASS, node); |
| 991 } else if (_isConstructorReturnType(node) && |
| 992 !identical(element, enclosingClass)) { |
| 993 _resolver.reportErrorForNode( |
| 994 CompileTimeErrorCode.INVALID_CONSTRUCTOR_NAME, node); |
| 995 element = null; |
| 996 } else if (element == null || |
| 997 (element is PrefixElement && !_isValidAsPrefix(node))) { |
| 998 // TODO(brianwilkerson) Recover from this error. |
| 999 if (_isConstructorReturnType(node)) { |
| 1000 _resolver.reportErrorForNode( |
| 1001 CompileTimeErrorCode.INVALID_CONSTRUCTOR_NAME, node); |
| 1002 } else if (node.parent is Annotation) { |
| 1003 Annotation annotation = node.parent as Annotation; |
| 1004 _resolver.reportErrorForNode( |
| 1005 CompileTimeErrorCode.INVALID_ANNOTATION, annotation); |
| 1006 } else if (element is PrefixElement) { |
| 1007 _resolver.reportErrorForNode( |
| 1008 CompileTimeErrorCode.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT, node, |
| 1009 [element.name]); |
| 1010 } else { |
| 1011 _recordUndefinedNode(_resolver.enclosingClass, |
| 1012 StaticWarningCode.UNDEFINED_IDENTIFIER, node, [node.name]); |
| 1013 } |
| 1014 } |
| 1015 node.staticElement = element; |
| 1016 if (node.inSetterContext() && |
| 1017 node.inGetterContext() && |
| 1018 enclosingClass != null) { |
| 1019 InterfaceType enclosingType = enclosingClass.type; |
| 1020 AuxiliaryElements auxiliaryElements = new AuxiliaryElements( |
| 1021 _lookUpGetter(null, enclosingType, node.name), null); |
| 1022 node.auxiliaryElements = auxiliaryElements; |
| 1023 } |
| 1024 // |
| 1025 // Validate annotation element. |
| 1026 // |
| 1027 if (node.parent is Annotation) { |
| 1028 Annotation annotation = node.parent as Annotation; |
| 1029 _resolveAnnotationElement(annotation); |
| 1030 } |
| 1031 return null; |
| 1032 } |
| 1033 |
| 1034 @override |
| 1035 Object visitSuperConstructorInvocation(SuperConstructorInvocation node) { |
| 1036 ClassElementImpl enclosingClass = _resolver.enclosingClass; |
| 1037 if (enclosingClass == null) { |
| 1038 // TODO(brianwilkerson) Report this error. |
| 1039 return null; |
| 1040 } |
| 1041 InterfaceType superType = enclosingClass.supertype; |
| 1042 if (superType == null) { |
| 1043 // TODO(brianwilkerson) Report this error. |
| 1044 return null; |
| 1045 } |
| 1046 SimpleIdentifier name = node.constructorName; |
| 1047 String superName = name != null ? name.name : null; |
| 1048 ConstructorElement element = |
| 1049 superType.lookUpConstructor(superName, _definingLibrary); |
| 1050 if (element == null || |
| 1051 (!enclosingClass.doesMixinLackConstructors && |
| 1052 !enclosingClass.isSuperConstructorAccessible(element))) { |
| 1053 if (name != null) { |
| 1054 _resolver.reportErrorForNode( |
| 1055 CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER, node, [ |
| 1056 superType.displayName, |
| 1057 name |
| 1058 ]); |
| 1059 } else { |
| 1060 _resolver.reportErrorForNode( |
| 1061 CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT, |
| 1062 node, [superType.displayName]); |
| 1063 } |
| 1064 return null; |
| 1065 } else { |
| 1066 if (element.isFactory) { |
| 1067 _resolver.reportErrorForNode( |
| 1068 CompileTimeErrorCode.NON_GENERATIVE_CONSTRUCTOR, node, [element]); |
| 1069 } |
| 1070 } |
| 1071 if (name != null) { |
| 1072 name.staticElement = element; |
| 1073 } |
| 1074 node.staticElement = element; |
| 1075 ArgumentList argumentList = node.argumentList; |
| 1076 List<ParameterElement> parameters = _resolveArgumentsToFunction( |
| 1077 isInConstConstructor, argumentList, element); |
| 1078 if (parameters != null) { |
| 1079 argumentList.correspondingStaticParameters = parameters; |
| 1080 } |
| 1081 return null; |
| 1082 } |
| 1083 |
| 1084 @override |
| 1085 Object visitSuperExpression(SuperExpression node) { |
| 1086 if (!_isSuperInValidContext(node)) { |
| 1087 _resolver.reportErrorForNode( |
| 1088 CompileTimeErrorCode.SUPER_IN_INVALID_CONTEXT, node); |
| 1089 } |
| 1090 return super.visitSuperExpression(node); |
| 1091 } |
| 1092 |
| 1093 @override |
| 1094 Object visitTypeParameter(TypeParameter node) { |
| 1095 setMetadata(node.element, node); |
| 1096 return null; |
| 1097 } |
| 1098 |
| 1099 @override |
| 1100 Object visitVariableDeclaration(VariableDeclaration node) { |
| 1101 setMetadata(node.element, node); |
| 1102 return null; |
| 1103 } |
| 1104 |
| 1105 /** |
| 1106 * Given that we have found code to invoke the given [element], return the |
| 1107 * error code that should be reported, or `null` if no error should be |
| 1108 * reported. The [target] is the target of the invocation, or `null` if there |
| 1109 * was no target. The flag [useStaticContext] should be `true` if the |
| 1110 * invocation is in a static constant (does not have access to instance state. |
| 1111 */ |
| 1112 ErrorCode _checkForInvocationError( |
| 1113 Expression target, bool useStaticContext, Element element) { |
| 1114 // Prefix is not declared, instead "prefix.id" are declared. |
| 1115 if (element is PrefixElement) { |
| 1116 return CompileTimeErrorCode.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT; |
| 1117 } |
| 1118 if (element is PropertyAccessorElement) { |
| 1119 // |
| 1120 // This is really a function expression invocation. |
| 1121 // |
| 1122 // TODO(brianwilkerson) Consider the possibility of re-writing the AST. |
| 1123 FunctionType getterType = element.type; |
| 1124 if (getterType != null) { |
| 1125 DartType returnType = getterType.returnType; |
| 1126 if (!_isExecutableType(returnType)) { |
| 1127 return StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION; |
| 1128 } |
| 1129 } |
| 1130 } else if (element is ExecutableElement) { |
| 1131 return null; |
| 1132 } else if (element is MultiplyDefinedElement) { |
| 1133 // The error has already been reported |
| 1134 return null; |
| 1135 } else if (element == null && target is SuperExpression) { |
| 1136 // TODO(jwren) We should split the UNDEFINED_METHOD into two error codes, |
| 1137 // this one, and a code that describes the situation where the method was |
| 1138 // found, but it was not accessible from the current library. |
| 1139 return StaticTypeWarningCode.UNDEFINED_SUPER_METHOD; |
| 1140 } else { |
| 1141 // |
| 1142 // This is really a function expression invocation. |
| 1143 // |
| 1144 // TODO(brianwilkerson) Consider the possibility of re-writing the AST. |
| 1145 if (element is PropertyInducingElement) { |
| 1146 PropertyAccessorElement getter = element.getter; |
| 1147 FunctionType getterType = getter.type; |
| 1148 if (getterType != null) { |
| 1149 DartType returnType = getterType.returnType; |
| 1150 if (!_isExecutableType(returnType)) { |
| 1151 return StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION; |
| 1152 } |
| 1153 } |
| 1154 } else if (element is VariableElement) { |
| 1155 DartType variableType = element.type; |
| 1156 if (!_isExecutableType(variableType)) { |
| 1157 return StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION; |
| 1158 } |
| 1159 } else { |
| 1160 if (target == null) { |
| 1161 ClassElement enclosingClass = _resolver.enclosingClass; |
| 1162 if (enclosingClass == null) { |
| 1163 return StaticTypeWarningCode.UNDEFINED_FUNCTION; |
| 1164 } else if (element == null) { |
| 1165 // Proxy-conditional warning, based on state of |
| 1166 // resolver.getEnclosingClass() |
| 1167 return StaticTypeWarningCode.UNDEFINED_METHOD; |
| 1168 } else { |
| 1169 return StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION; |
| 1170 } |
| 1171 } else { |
| 1172 DartType targetType; |
| 1173 if (useStaticContext) { |
| 1174 targetType = _getStaticType(target); |
| 1175 } else { |
| 1176 // Compute and use the propagated type, if it is null, then it may |
| 1177 // be the case that static type is some type, in which the static |
| 1178 // type should be used. |
| 1179 targetType = _getBestType(target); |
| 1180 } |
| 1181 if (targetType == null) { |
| 1182 return StaticTypeWarningCode.UNDEFINED_FUNCTION; |
| 1183 } else if (!targetType.isDynamic && !targetType.isBottom) { |
| 1184 // Proxy-conditional warning, based on state of |
| 1185 // targetType.getElement() |
| 1186 return StaticTypeWarningCode.UNDEFINED_METHOD; |
| 1187 } |
| 1188 } |
| 1189 } |
| 1190 } |
| 1191 return null; |
| 1192 } |
| 1193 |
| 1194 /** |
| 1195 * Check that the given index [expression] was resolved, otherwise a |
| 1196 * [StaticTypeWarningCode.UNDEFINED_OPERATOR] is generated. The [target] is |
| 1197 * the target of the expression. The [methodName] is the name of the operator |
| 1198 * associated with the context of using of the given index expression. |
| 1199 */ |
| 1200 bool _checkForUndefinedIndexOperator(IndexExpression expression, |
| 1201 Expression target, String methodName, MethodElement staticMethod, |
| 1202 MethodElement propagatedMethod, DartType staticType, |
| 1203 DartType propagatedType) { |
| 1204 bool shouldReportMissingMember_static = |
| 1205 _shouldReportMissingMember(staticType, staticMethod); |
| 1206 bool shouldReportMissingMember_propagated = |
| 1207 !shouldReportMissingMember_static && |
| 1208 _enableHints && |
| 1209 _shouldReportMissingMember(propagatedType, propagatedMethod) && |
| 1210 !_memberFoundInSubclass( |
| 1211 propagatedType.element, methodName, true, false); |
| 1212 if (shouldReportMissingMember_static || |
| 1213 shouldReportMissingMember_propagated) { |
| 1214 sc.Token leftBracket = expression.leftBracket; |
| 1215 sc.Token rightBracket = expression.rightBracket; |
| 1216 ErrorCode errorCode; |
| 1217 if (shouldReportMissingMember_static) { |
| 1218 if (target is SuperExpression) { |
| 1219 errorCode = StaticTypeWarningCode.UNDEFINED_SUPER_OPERATOR; |
| 1220 } else { |
| 1221 errorCode = StaticTypeWarningCode.UNDEFINED_OPERATOR; |
| 1222 } |
| 1223 } else { |
| 1224 errorCode = HintCode.UNDEFINED_OPERATOR; |
| 1225 } |
| 1226 DartType type = |
| 1227 shouldReportMissingMember_static ? staticType : propagatedType; |
| 1228 if (leftBracket == null || rightBracket == null) { |
| 1229 _recordUndefinedNode(type.element, errorCode, expression, [ |
| 1230 methodName, |
| 1231 type.displayName |
| 1232 ]); |
| 1233 } else { |
| 1234 int offset = leftBracket.offset; |
| 1235 int length = rightBracket.offset - offset + 1; |
| 1236 _recordUndefinedOffset(type.element, errorCode, offset, length, [ |
| 1237 methodName, |
| 1238 type.displayName |
| 1239 ]); |
| 1240 } |
| 1241 return true; |
| 1242 } |
| 1243 return false; |
| 1244 } |
| 1245 |
| 1246 /** |
| 1247 * Given an [argumentList] and the executable [element] that will be invoked |
| 1248 * using those arguments, compute the list of parameters that correspond to |
| 1249 * the list of arguments. Return the parameters that correspond to the |
| 1250 * arguments, or `null` if no correspondence could be computed. |
| 1251 */ |
| 1252 List<ParameterElement> _computeCorrespondingParameters( |
| 1253 ArgumentList argumentList, Element element) { |
| 1254 if (element is PropertyAccessorElement) { |
| 1255 // |
| 1256 // This is an invocation of the call method defined on the value returned |
| 1257 // by the getter. |
| 1258 // |
| 1259 FunctionType getterType = element.type; |
| 1260 if (getterType != null) { |
| 1261 DartType getterReturnType = getterType.returnType; |
| 1262 if (getterReturnType is InterfaceType) { |
| 1263 MethodElement callMethod = getterReturnType.lookUpMethod( |
| 1264 FunctionElement.CALL_METHOD_NAME, _definingLibrary); |
| 1265 if (callMethod != null) { |
| 1266 return _resolveArgumentsToFunction(false, argumentList, callMethod); |
| 1267 } |
| 1268 } else if (getterReturnType is FunctionType) { |
| 1269 List<ParameterElement> parameters = getterReturnType.parameters; |
| 1270 return _resolveArgumentsToParameters(false, argumentList, parameters); |
| 1271 } |
| 1272 } |
| 1273 } else if (element is ExecutableElement) { |
| 1274 return _resolveArgumentsToFunction(false, argumentList, element); |
| 1275 } else if (element is VariableElement) { |
| 1276 VariableElement variable = element; |
| 1277 DartType type = _promoteManager.getStaticType(variable); |
| 1278 if (type is FunctionType) { |
| 1279 FunctionType functionType = type; |
| 1280 List<ParameterElement> parameters = functionType.parameters; |
| 1281 return _resolveArgumentsToParameters(false, argumentList, parameters); |
| 1282 } else if (type is InterfaceType) { |
| 1283 // "call" invocation |
| 1284 MethodElement callMethod = type.lookUpMethod( |
| 1285 FunctionElement.CALL_METHOD_NAME, _definingLibrary); |
| 1286 if (callMethod != null) { |
| 1287 List<ParameterElement> parameters = callMethod.parameters; |
| 1288 return _resolveArgumentsToParameters(false, argumentList, parameters); |
| 1289 } |
| 1290 } |
| 1291 } |
| 1292 return null; |
| 1293 } |
| 1294 |
| 1295 /** |
| 1296 * If the given [element] is a setter, return the getter associated with it. |
| 1297 * Otherwise, return the element unchanged. |
| 1298 */ |
| 1299 Element _convertSetterToGetter(Element element) { |
| 1300 // TODO(brianwilkerson) Determine whether and why the element could ever be |
| 1301 // a setter. |
| 1302 if (element is PropertyAccessorElement) { |
| 1303 return element.variable.getter; |
| 1304 } |
| 1305 return element; |
| 1306 } |
| 1307 |
| 1308 /** |
| 1309 * Return `true` if the given [element] is not a proxy. See |
| 1310 * [ClassElement.isOrInheritsProxy]. |
| 1311 */ |
| 1312 bool _doesntHaveProxy(Element element) => |
| 1313 !(element is ClassElement && element.isOrInheritsProxy); |
| 1314 |
| 1315 /** |
| 1316 * Look for any declarations of the given [identifier] that are imported using |
| 1317 * a prefix. Return the element that was found, or `null` if the name is not |
| 1318 * imported using a prefix. |
| 1319 */ |
| 1320 Element _findImportWithoutPrefix(SimpleIdentifier identifier) { |
| 1321 Element element = null; |
| 1322 Scope nameScope = _resolver.nameScope; |
| 1323 for (ImportElement importElement in _definingLibrary.imports) { |
| 1324 PrefixElement prefixElement = importElement.prefix; |
| 1325 if (prefixElement != null) { |
| 1326 Identifier prefixedIdentifier = new SyntheticIdentifier( |
| 1327 "${prefixElement.name}.${identifier.name}", identifier); |
| 1328 Element importedElement = |
| 1329 nameScope.lookup(prefixedIdentifier, _definingLibrary); |
| 1330 if (importedElement != null) { |
| 1331 if (element == null) { |
| 1332 element = importedElement; |
| 1333 } else { |
| 1334 element = MultiplyDefinedElementImpl.fromElements( |
| 1335 _definingLibrary.context, element, importedElement); |
| 1336 } |
| 1337 } |
| 1338 } |
| 1339 } |
| 1340 return element; |
| 1341 } |
| 1342 |
| 1343 /** |
| 1344 * Return the best type of the given [expression] that is to be used for |
| 1345 * type analysis. |
| 1346 */ |
| 1347 DartType _getBestType(Expression expression) { |
| 1348 DartType bestType = _resolveTypeParameter(expression.bestType); |
| 1349 if (bestType is FunctionType) { |
| 1350 // |
| 1351 // All function types are subtypes of 'Function', which is itself a |
| 1352 // subclass of 'Object'. |
| 1353 // |
| 1354 bestType = _resolver.typeProvider.functionType; |
| 1355 } |
| 1356 return bestType; |
| 1357 } |
| 1358 |
| 1359 /** |
| 1360 * Assuming that the given [expression] is a prefix for a deferred import, |
| 1361 * return the library that is being imported. |
| 1362 */ |
| 1363 LibraryElement _getImportedLibrary(Expression expression) { |
| 1364 PrefixElement prefixElement = |
| 1365 (expression as SimpleIdentifier).staticElement as PrefixElement; |
| 1366 List<ImportElement> imports = |
| 1367 prefixElement.enclosingElement.getImportsWithPrefix(prefixElement); |
| 1368 return imports[0].importedLibrary; |
| 1369 } |
| 1370 |
| 1371 /** |
| 1372 * Return the name of the method invoked by the given postfix [expression]. |
| 1373 */ |
| 1374 String _getPostfixOperator(PostfixExpression expression) => |
| 1375 (expression.operator.type == sc.TokenType.PLUS_PLUS) |
| 1376 ? sc.TokenType.PLUS.lexeme |
| 1377 : sc.TokenType.MINUS.lexeme; |
| 1378 |
| 1379 /** |
| 1380 * Return the name of the method invoked by the given postfix [expression]. |
| 1381 */ |
| 1382 String _getPrefixOperator(PrefixExpression expression) { |
| 1383 sc.Token operator = expression.operator; |
| 1384 sc.TokenType operatorType = operator.type; |
| 1385 if (operatorType == sc.TokenType.PLUS_PLUS) { |
| 1386 return sc.TokenType.PLUS.lexeme; |
| 1387 } else if (operatorType == sc.TokenType.MINUS_MINUS) { |
| 1388 return sc.TokenType.MINUS.lexeme; |
| 1389 } else if (operatorType == sc.TokenType.MINUS) { |
| 1390 return "unary-"; |
| 1391 } else { |
| 1392 return operator.lexeme; |
| 1393 } |
| 1394 } |
| 1395 |
| 1396 /** |
| 1397 * Return the propagated type of the given [expression] that is to be used for |
| 1398 * type analysis. |
| 1399 */ |
| 1400 DartType _getPropagatedType(Expression expression) { |
| 1401 DartType propagatedType = _resolveTypeParameter(expression.propagatedType); |
| 1402 if (propagatedType is FunctionType) { |
| 1403 // |
| 1404 // All function types are subtypes of 'Function', which is itself a |
| 1405 // subclass of 'Object'. |
| 1406 // |
| 1407 propagatedType = _resolver.typeProvider.functionType; |
| 1408 } |
| 1409 return propagatedType; |
| 1410 } |
| 1411 |
| 1412 /** |
| 1413 * Return the static type of the given [expression] that is to be used for |
| 1414 * type analysis. |
| 1415 */ |
| 1416 DartType _getStaticType(Expression expression) { |
| 1417 if (expression is NullLiteral) { |
| 1418 return _resolver.typeProvider.bottomType; |
| 1419 } |
| 1420 DartType staticType = _resolveTypeParameter(expression.staticType); |
| 1421 if (staticType is FunctionType) { |
| 1422 // |
| 1423 // All function types are subtypes of 'Function', which is itself a |
| 1424 // subclass of 'Object'. |
| 1425 // |
| 1426 staticType = _resolver.typeProvider.functionType; |
| 1427 } |
| 1428 return staticType; |
| 1429 } |
| 1430 |
| 1431 /** |
| 1432 * Return `true` if the given [expression] is a prefix for a deferred import. |
| 1433 */ |
| 1434 bool _isDeferredPrefix(Expression expression) { |
| 1435 if (expression is! SimpleIdentifier) { |
| 1436 return false; |
| 1437 } |
| 1438 Element element = (expression as SimpleIdentifier).staticElement; |
| 1439 if (element is! PrefixElement) { |
| 1440 return false; |
| 1441 } |
| 1442 PrefixElement prefixElement = element as PrefixElement; |
| 1443 List<ImportElement> imports = |
| 1444 prefixElement.enclosingElement.getImportsWithPrefix(prefixElement); |
| 1445 if (imports.length != 1) { |
| 1446 return false; |
| 1447 } |
| 1448 return imports[0].isDeferred; |
| 1449 } |
| 1450 |
| 1451 /** |
| 1452 * Return `true` if the given [type] represents an object that could be |
| 1453 * invoked using the call operator '()'. |
| 1454 */ |
| 1455 bool _isExecutableType(DartType type) { |
| 1456 if (type.isDynamic || type is FunctionType) { |
| 1457 return true; |
| 1458 } else if (!_enableStrictCallChecks && |
| 1459 (type.isDartCoreFunction || type.isObject)) { |
| 1460 return true; |
| 1461 } else if (type is InterfaceType) { |
| 1462 ClassElement classElement = type.element; |
| 1463 // 16078 from Gilad: If the type is a Functor with the @proxy annotation, |
| 1464 // treat it as an executable type. |
| 1465 // example code: NonErrorResolverTest. |
| 1466 // test_invocationOfNonFunction_proxyOnFunctionClass() |
| 1467 if (classElement.isProxy && |
| 1468 type.isSubtypeOf(_resolver.typeProvider.functionType)) { |
| 1469 return true; |
| 1470 } |
| 1471 MethodElement methodElement = classElement.lookUpMethod( |
| 1472 FunctionElement.CALL_METHOD_NAME, _definingLibrary); |
| 1473 return methodElement != null; |
| 1474 } |
| 1475 return false; |
| 1476 } |
| 1477 |
| 1478 /** |
| 1479 * Return `true` if the given [element] is a static element. |
| 1480 */ |
| 1481 bool _isStatic(Element element) { |
| 1482 if (element is ExecutableElement) { |
| 1483 return element.isStatic; |
| 1484 } else if (element is PropertyInducingElement) { |
| 1485 return element.isStatic; |
| 1486 } |
| 1487 return false; |
| 1488 } |
| 1489 |
| 1490 /** |
| 1491 * Return `true` if the given [node] can validly be resolved to a prefix: |
| 1492 * * it is the prefix in an import directive, or |
| 1493 * * it is the prefix in a prefixed identifier. |
| 1494 */ |
| 1495 bool _isValidAsPrefix(SimpleIdentifier node) { |
| 1496 AstNode parent = node.parent; |
| 1497 if (parent is ImportDirective) { |
| 1498 return identical(parent.prefix, node); |
| 1499 } else if (parent is PrefixedIdentifier) { |
| 1500 return true; |
| 1501 } else if (parent is MethodInvocation) { |
| 1502 return identical(parent.target, node); |
| 1503 } |
| 1504 return false; |
| 1505 } |
| 1506 |
| 1507 /** |
| 1508 * Return the target of a break or continue statement, and update the static |
| 1509 * element of its label (if any). The [parentNode] is the AST node of the |
| 1510 * break or continue statement. The [labelNode] is the label contained in that |
| 1511 * statement (if any). The flag [isContinue] is `true` if the node being |
| 1512 * visited is a continue statement. |
| 1513 */ |
| 1514 AstNode _lookupBreakOrContinueTarget( |
| 1515 AstNode parentNode, SimpleIdentifier labelNode, bool isContinue) { |
| 1516 if (labelNode == null) { |
| 1517 return _resolver.implicitLabelScope.getTarget(isContinue); |
| 1518 } else { |
| 1519 LabelScope labelScope = _resolver.labelScope; |
| 1520 if (labelScope == null) { |
| 1521 // There are no labels in scope, so by definition the label is |
| 1522 // undefined. |
| 1523 _resolver.reportErrorForNode( |
| 1524 CompileTimeErrorCode.LABEL_UNDEFINED, labelNode, [labelNode.name]); |
| 1525 return null; |
| 1526 } |
| 1527 LabelScope definingScope = labelScope.lookup(labelNode.name); |
| 1528 if (definingScope == null) { |
| 1529 // No definition of the given label name could be found in any |
| 1530 // enclosing scope. |
| 1531 _resolver.reportErrorForNode( |
| 1532 CompileTimeErrorCode.LABEL_UNDEFINED, labelNode, [labelNode.name]); |
| 1533 return null; |
| 1534 } |
| 1535 // The target has been found. |
| 1536 labelNode.staticElement = definingScope.element; |
| 1537 ExecutableElement labelContainer = definingScope.element |
| 1538 .getAncestor((element) => element is ExecutableElement); |
| 1539 if (!identical(labelContainer, _resolver.enclosingFunction)) { |
| 1540 _resolver.reportErrorForNode(CompileTimeErrorCode.LABEL_IN_OUTER_SCOPE, |
| 1541 labelNode, [labelNode.name]); |
| 1542 } |
| 1543 return definingScope.node; |
| 1544 } |
| 1545 } |
| 1546 |
| 1547 /** |
| 1548 * Look up the getter with the given [getterName] in the given [type]. Return |
| 1549 * the element representing the getter that was found, or `null` if there is |
| 1550 * no getter with the given name. The [target] is the target of the |
| 1551 * invocation, or `null` if there is no target. |
| 1552 */ |
| 1553 PropertyAccessorElement _lookUpGetter( |
| 1554 Expression target, DartType type, String getterName) { |
| 1555 type = _resolveTypeParameter(type); |
| 1556 if (type is InterfaceType) { |
| 1557 InterfaceType interfaceType = type; |
| 1558 PropertyAccessorElement accessor; |
| 1559 if (target is SuperExpression) { |
| 1560 accessor = interfaceType.lookUpGetterInSuperclass( |
| 1561 getterName, _definingLibrary); |
| 1562 } else { |
| 1563 accessor = interfaceType.lookUpGetter(getterName, _definingLibrary); |
| 1564 } |
| 1565 if (accessor != null) { |
| 1566 return accessor; |
| 1567 } |
| 1568 return _lookUpGetterInInterfaces( |
| 1569 interfaceType, false, getterName, new HashSet<ClassElement>()); |
| 1570 } |
| 1571 return null; |
| 1572 } |
| 1573 |
| 1574 /** |
| 1575 * Look up the getter with the given [getterName] in the interfaces |
| 1576 * implemented by the given [targetType], either directly or indirectly. |
| 1577 * Return the element representing the getter that was found, or `null` if |
| 1578 * there is no getter with the given name. The flag [includeTargetType] should |
| 1579 * be `true` if the search should include the target type. The |
| 1580 * [visitedInterfaces] is a set containing all of the interfaces that have |
| 1581 * been examined, used to prevent infinite recursion and to optimize the |
| 1582 * search. |
| 1583 */ |
| 1584 PropertyAccessorElement _lookUpGetterInInterfaces(InterfaceType targetType, |
| 1585 bool includeTargetType, String getterName, |
| 1586 HashSet<ClassElement> visitedInterfaces) { |
| 1587 // TODO(brianwilkerson) This isn't correct. Section 8.1.1 of the |
| 1588 // specification (titled "Inheritance and Overriding" under "Interfaces") |
| 1589 // describes a much more complex scheme for finding the inherited member. |
| 1590 // We need to follow that scheme. The code below should cover the 80% case. |
| 1591 ClassElement targetClass = targetType.element; |
| 1592 if (visitedInterfaces.contains(targetClass)) { |
| 1593 return null; |
| 1594 } |
| 1595 visitedInterfaces.add(targetClass); |
| 1596 if (includeTargetType) { |
| 1597 PropertyAccessorElement getter = targetType.getGetter(getterName); |
| 1598 if (getter != null && getter.isAccessibleIn(_definingLibrary)) { |
| 1599 return getter; |
| 1600 } |
| 1601 } |
| 1602 for (InterfaceType interfaceType in targetType.interfaces) { |
| 1603 PropertyAccessorElement getter = _lookUpGetterInInterfaces( |
| 1604 interfaceType, true, getterName, visitedInterfaces); |
| 1605 if (getter != null) { |
| 1606 return getter; |
| 1607 } |
| 1608 } |
| 1609 for (InterfaceType mixinType in targetType.mixins.reversed) { |
| 1610 PropertyAccessorElement getter = _lookUpGetterInInterfaces( |
| 1611 mixinType, true, getterName, visitedInterfaces); |
| 1612 if (getter != null) { |
| 1613 return getter; |
| 1614 } |
| 1615 } |
| 1616 InterfaceType superclass = targetType.superclass; |
| 1617 if (superclass == null) { |
| 1618 return null; |
| 1619 } |
| 1620 return _lookUpGetterInInterfaces( |
| 1621 superclass, true, getterName, visitedInterfaces); |
| 1622 } |
| 1623 |
| 1624 /** |
| 1625 * Look up the method or getter with the given [memberName] in the given |
| 1626 * [type]. Return the element representing the method or getter that was |
| 1627 * found, or `null` if there is no method or getter with the given name. |
| 1628 */ |
| 1629 ExecutableElement _lookupGetterOrMethod(DartType type, String memberName) { |
| 1630 type = _resolveTypeParameter(type); |
| 1631 if (type is InterfaceType) { |
| 1632 InterfaceType interfaceType = type; |
| 1633 ExecutableElement member = |
| 1634 interfaceType.lookUpMethod(memberName, _definingLibrary); |
| 1635 if (member != null) { |
| 1636 return member; |
| 1637 } |
| 1638 member = interfaceType.lookUpGetter(memberName, _definingLibrary); |
| 1639 if (member != null) { |
| 1640 return member; |
| 1641 } |
| 1642 return _lookUpGetterOrMethodInInterfaces( |
| 1643 interfaceType, false, memberName, new HashSet<ClassElement>()); |
| 1644 } |
| 1645 return null; |
| 1646 } |
| 1647 |
| 1648 /** |
| 1649 * Look up the method or getter with the given [memberName] in the interfaces |
| 1650 * implemented by the given [targetType], either directly or indirectly. |
| 1651 * Return the element representing the method or getter that was found, or |
| 1652 * `null` if there is no method or getter with the given name. The flag |
| 1653 * [includeTargetType] should be `true` if the search should include the |
| 1654 * target type. The [visitedInterfaces] is a set containing all of the |
| 1655 * interfaces that have been examined, used to prevent infinite recursion and |
| 1656 * to optimize the search. |
| 1657 */ |
| 1658 ExecutableElement _lookUpGetterOrMethodInInterfaces(InterfaceType targetType, |
| 1659 bool includeTargetType, String memberName, |
| 1660 HashSet<ClassElement> visitedInterfaces) { |
| 1661 // TODO(brianwilkerson) This isn't correct. Section 8.1.1 of the |
| 1662 // specification (titled "Inheritance and Overriding" under "Interfaces") |
| 1663 // describes a much more complex scheme for finding the inherited member. |
| 1664 // We need to follow that scheme. The code below should cover the 80% case. |
| 1665 ClassElement targetClass = targetType.element; |
| 1666 if (visitedInterfaces.contains(targetClass)) { |
| 1667 return null; |
| 1668 } |
| 1669 visitedInterfaces.add(targetClass); |
| 1670 if (includeTargetType) { |
| 1671 ExecutableElement member = targetType.getMethod(memberName); |
| 1672 if (member != null) { |
| 1673 return member; |
| 1674 } |
| 1675 member = targetType.getGetter(memberName); |
| 1676 if (member != null) { |
| 1677 return member; |
| 1678 } |
| 1679 } |
| 1680 for (InterfaceType interfaceType in targetType.interfaces) { |
| 1681 ExecutableElement member = _lookUpGetterOrMethodInInterfaces( |
| 1682 interfaceType, true, memberName, visitedInterfaces); |
| 1683 if (member != null) { |
| 1684 return member; |
| 1685 } |
| 1686 } |
| 1687 for (InterfaceType mixinType in targetType.mixins.reversed) { |
| 1688 ExecutableElement member = _lookUpGetterOrMethodInInterfaces( |
| 1689 mixinType, true, memberName, visitedInterfaces); |
| 1690 if (member != null) { |
| 1691 return member; |
| 1692 } |
| 1693 } |
| 1694 InterfaceType superclass = targetType.superclass; |
| 1695 if (superclass == null) { |
| 1696 return null; |
| 1697 } |
| 1698 return _lookUpGetterOrMethodInInterfaces( |
| 1699 superclass, true, memberName, visitedInterfaces); |
| 1700 } |
| 1701 |
| 1702 /** |
| 1703 * Look up the method with the given [methodName] in the given [type]. Return |
| 1704 * the element representing the method that was found, or `null` if there is |
| 1705 * no method with the given name. The [target] is the target of the |
| 1706 * invocation, or `null` if there is no target. |
| 1707 */ |
| 1708 MethodElement _lookUpMethod( |
| 1709 Expression target, DartType type, String methodName) { |
| 1710 type = _resolveTypeParameter(type); |
| 1711 if (type is InterfaceType) { |
| 1712 InterfaceType interfaceType = type; |
| 1713 MethodElement method; |
| 1714 if (target is SuperExpression) { |
| 1715 method = interfaceType.lookUpMethodInSuperclass( |
| 1716 methodName, _definingLibrary); |
| 1717 } else { |
| 1718 method = interfaceType.lookUpMethod(methodName, _definingLibrary); |
| 1719 } |
| 1720 if (method != null) { |
| 1721 return method; |
| 1722 } |
| 1723 return _lookUpMethodInInterfaces( |
| 1724 interfaceType, false, methodName, new HashSet<ClassElement>()); |
| 1725 } |
| 1726 return null; |
| 1727 } |
| 1728 |
| 1729 /** |
| 1730 * Look up the method with the given [methodName] in the interfaces |
| 1731 * implemented by the given [targetType], either directly or indirectly. |
| 1732 * Return the element representing the method that was found, or `null` if |
| 1733 * there is no method with the given name. The flag [includeTargetType] should |
| 1734 * be `true` if the search should include the target type. The |
| 1735 * [visitedInterfaces] is a set containing all of the interfaces that have |
| 1736 * been examined, used to prevent infinite recursion and to optimize the |
| 1737 * search. |
| 1738 */ |
| 1739 MethodElement _lookUpMethodInInterfaces(InterfaceType targetType, |
| 1740 bool includeTargetType, String methodName, |
| 1741 HashSet<ClassElement> visitedInterfaces) { |
| 1742 // TODO(brianwilkerson) This isn't correct. Section 8.1.1 of the |
| 1743 // specification (titled "Inheritance and Overriding" under "Interfaces") |
| 1744 // describes a much more complex scheme for finding the inherited member. |
| 1745 // We need to follow that scheme. The code below should cover the 80% case. |
| 1746 ClassElement targetClass = targetType.element; |
| 1747 if (visitedInterfaces.contains(targetClass)) { |
| 1748 return null; |
| 1749 } |
| 1750 visitedInterfaces.add(targetClass); |
| 1751 if (includeTargetType) { |
| 1752 MethodElement method = targetType.getMethod(methodName); |
| 1753 if (method != null && method.isAccessibleIn(_definingLibrary)) { |
| 1754 return method; |
| 1755 } |
| 1756 } |
| 1757 for (InterfaceType interfaceType in targetType.interfaces) { |
| 1758 MethodElement method = _lookUpMethodInInterfaces( |
| 1759 interfaceType, true, methodName, visitedInterfaces); |
| 1760 if (method != null) { |
| 1761 return method; |
| 1762 } |
| 1763 } |
| 1764 for (InterfaceType mixinType in targetType.mixins.reversed) { |
| 1765 MethodElement method = _lookUpMethodInInterfaces( |
| 1766 mixinType, true, methodName, visitedInterfaces); |
| 1767 if (method != null) { |
| 1768 return method; |
| 1769 } |
| 1770 } |
| 1771 InterfaceType superclass = targetType.superclass; |
| 1772 if (superclass == null) { |
| 1773 return null; |
| 1774 } |
| 1775 return _lookUpMethodInInterfaces( |
| 1776 superclass, true, methodName, visitedInterfaces); |
| 1777 } |
| 1778 |
| 1779 /** |
| 1780 * Look up the setter with the given [setterName] in the given [type]. Return |
| 1781 * the element representing the setter that was found, or `null` if there is |
| 1782 * no setter with the given name. The [target] is the target of the |
| 1783 * invocation, or `null` if there is no target. |
| 1784 */ |
| 1785 PropertyAccessorElement _lookUpSetter( |
| 1786 Expression target, DartType type, String setterName) { |
| 1787 type = _resolveTypeParameter(type); |
| 1788 if (type is InterfaceType) { |
| 1789 InterfaceType interfaceType = type; |
| 1790 PropertyAccessorElement accessor; |
| 1791 if (target is SuperExpression) { |
| 1792 accessor = interfaceType.lookUpSetterInSuperclass( |
| 1793 setterName, _definingLibrary); |
| 1794 } else { |
| 1795 accessor = interfaceType.lookUpSetter(setterName, _definingLibrary); |
| 1796 } |
| 1797 if (accessor != null) { |
| 1798 return accessor; |
| 1799 } |
| 1800 return _lookUpSetterInInterfaces( |
| 1801 interfaceType, false, setterName, new HashSet<ClassElement>()); |
| 1802 } |
| 1803 return null; |
| 1804 } |
| 1805 |
| 1806 /** |
| 1807 * Look up the setter with the given [setterName] in the interfaces |
| 1808 * implemented by the given [targetType], either directly or indirectly. |
| 1809 * Return the element representing the setter that was found, or `null` if |
| 1810 * there is no setter with the given name. The [targetType] is the type in |
| 1811 * which the setter might be defined. The flag [includeTargetType] should be |
| 1812 * `true` if the search should include the target type. The |
| 1813 * [visitedInterfaces] is a set containing all of the interfaces that have |
| 1814 * been examined, used to prevent infinite recursion and to optimize the |
| 1815 * search. |
| 1816 */ |
| 1817 PropertyAccessorElement _lookUpSetterInInterfaces(InterfaceType targetType, |
| 1818 bool includeTargetType, String setterName, |
| 1819 HashSet<ClassElement> visitedInterfaces) { |
| 1820 // TODO(brianwilkerson) This isn't correct. Section 8.1.1 of the |
| 1821 // specification (titled "Inheritance and Overriding" under "Interfaces") |
| 1822 // describes a much more complex scheme for finding the inherited member. |
| 1823 // We need to follow that scheme. The code below should cover the 80% case. |
| 1824 ClassElement targetClass = targetType.element; |
| 1825 if (visitedInterfaces.contains(targetClass)) { |
| 1826 return null; |
| 1827 } |
| 1828 visitedInterfaces.add(targetClass); |
| 1829 if (includeTargetType) { |
| 1830 PropertyAccessorElement setter = targetType.getSetter(setterName); |
| 1831 if (setter != null && setter.isAccessibleIn(_definingLibrary)) { |
| 1832 return setter; |
| 1833 } |
| 1834 } |
| 1835 for (InterfaceType interfaceType in targetType.interfaces) { |
| 1836 PropertyAccessorElement setter = _lookUpSetterInInterfaces( |
| 1837 interfaceType, true, setterName, visitedInterfaces); |
| 1838 if (setter != null) { |
| 1839 return setter; |
| 1840 } |
| 1841 } |
| 1842 for (InterfaceType mixinType in targetType.mixins.reversed) { |
| 1843 PropertyAccessorElement setter = _lookUpSetterInInterfaces( |
| 1844 mixinType, true, setterName, visitedInterfaces); |
| 1845 if (setter != null) { |
| 1846 return setter; |
| 1847 } |
| 1848 } |
| 1849 InterfaceType superclass = targetType.superclass; |
| 1850 if (superclass == null) { |
| 1851 return null; |
| 1852 } |
| 1853 return _lookUpSetterInInterfaces( |
| 1854 superclass, true, setterName, visitedInterfaces); |
| 1855 } |
| 1856 |
| 1857 /** |
| 1858 * Given some class [element], this method uses [_subtypeManager] to find the |
| 1859 * set of all subtypes; the subtypes are then searched for a member (method, |
| 1860 * getter, or setter), that has the given [memberName]. The flag [asMethod] |
| 1861 * should be `true` if the methods should be searched for in the subtypes. The |
| 1862 * flag [asAccessor] should be `true` if the accessors (getters and setters) |
| 1863 * should be searched for in the subtypes. |
| 1864 */ |
| 1865 bool _memberFoundInSubclass( |
| 1866 Element element, String memberName, bool asMethod, bool asAccessor) { |
| 1867 if (element is ClassElement) { |
| 1868 _subtypeManager.ensureLibraryVisited(_definingLibrary); |
| 1869 HashSet<ClassElement> subtypeElements = |
| 1870 _subtypeManager.computeAllSubtypes(element); |
| 1871 for (ClassElement subtypeElement in subtypeElements) { |
| 1872 if (asMethod && subtypeElement.getMethod(memberName) != null) { |
| 1873 return true; |
| 1874 } else if (asAccessor && |
| 1875 (subtypeElement.getGetter(memberName) != null || |
| 1876 subtypeElement.getSetter(memberName) != null)) { |
| 1877 return true; |
| 1878 } |
| 1879 } |
| 1880 } |
| 1881 return false; |
| 1882 } |
| 1883 |
| 1884 /** |
| 1885 * Return the binary operator that is invoked by the given compound assignment |
| 1886 * [operator]. |
| 1887 */ |
| 1888 sc.TokenType _operatorFromCompoundAssignment(sc.TokenType operator) { |
| 1889 while (true) { |
| 1890 if (operator == sc.TokenType.AMPERSAND_EQ) { |
| 1891 return sc.TokenType.AMPERSAND; |
| 1892 } else if (operator == sc.TokenType.BAR_EQ) { |
| 1893 return sc.TokenType.BAR; |
| 1894 } else if (operator == sc.TokenType.CARET_EQ) { |
| 1895 return sc.TokenType.CARET; |
| 1896 } else if (operator == sc.TokenType.GT_GT_EQ) { |
| 1897 return sc.TokenType.GT_GT; |
| 1898 } else if (operator == sc.TokenType.LT_LT_EQ) { |
| 1899 return sc.TokenType.LT_LT; |
| 1900 } else if (operator == sc.TokenType.MINUS_EQ) { |
| 1901 return sc.TokenType.MINUS; |
| 1902 } else if (operator == sc.TokenType.PERCENT_EQ) { |
| 1903 return sc.TokenType.PERCENT; |
| 1904 } else if (operator == sc.TokenType.PLUS_EQ) { |
| 1905 return sc.TokenType.PLUS; |
| 1906 } else if (operator == sc.TokenType.SLASH_EQ) { |
| 1907 return sc.TokenType.SLASH; |
| 1908 } else if (operator == sc.TokenType.STAR_EQ) { |
| 1909 return sc.TokenType.STAR; |
| 1910 } else if (operator == sc.TokenType.TILDE_SLASH_EQ) { |
| 1911 return sc.TokenType.TILDE_SLASH; |
| 1912 } else { |
| 1913 // Internal error: Unmapped assignment operator. |
| 1914 AnalysisEngine.instance.logger.logError( |
| 1915 "Failed to map ${operator.lexeme} to it's corresponding operator"); |
| 1916 return operator; |
| 1917 } |
| 1918 break; |
| 1919 } |
| 1920 } |
| 1921 |
| 1922 /** |
| 1923 * Record that the given [node] is undefined, causing an error to be reported |
| 1924 * if appropriate. The [declaringElement] is the element inside which no |
| 1925 * declaration was found. If this element is a proxy, no error will be |
| 1926 * reported. If null, then an error will always be reported. The [errorCode] |
| 1927 * is the error code to report. The [arguments] are the arguments to the error |
| 1928 * message. |
| 1929 */ |
| 1930 void _recordUndefinedNode(Element declaringElement, ErrorCode errorCode, |
| 1931 AstNode node, List<Object> arguments) { |
| 1932 if (_doesntHaveProxy(declaringElement)) { |
| 1933 _resolver.reportErrorForNode(errorCode, node, arguments); |
| 1934 } |
| 1935 } |
| 1936 |
| 1937 /** |
| 1938 * Record that the given [offset]/[length] is undefined, causing an error to |
| 1939 * be reported if appropriate. The [declaringElement] is the element inside |
| 1940 * which no declaration was found. If this element is a proxy, no error will |
| 1941 * be reported. If null, then an error will always be reported. The |
| 1942 * [errorCode] is the error code to report. The [arguments] are arguments to |
| 1943 * the error message. |
| 1944 */ |
| 1945 void _recordUndefinedOffset(Element declaringElement, ErrorCode errorCode, |
| 1946 int offset, int length, List<Object> arguments) { |
| 1947 if (_doesntHaveProxy(declaringElement)) { |
| 1948 _resolver.reportErrorForOffset(errorCode, offset, length, arguments); |
| 1949 } |
| 1950 } |
| 1951 |
| 1952 /** |
| 1953 * Record that the given [token] is undefined, causing an error to be reported |
| 1954 * if appropriate. The [declaringElement] is the element inside which no |
| 1955 * declaration was found. If this element is a proxy, no error will be |
| 1956 * reported. If null, then an error will always be reported. The [errorCode] |
| 1957 * is the error code to report. The [arguments] are arguments to the error |
| 1958 * message. |
| 1959 */ |
| 1960 void _recordUndefinedToken(Element declaringElement, ErrorCode errorCode, |
| 1961 sc.Token token, List<Object> arguments) { |
| 1962 if (_doesntHaveProxy(declaringElement)) { |
| 1963 _resolver.reportErrorForToken(errorCode, token, arguments); |
| 1964 } |
| 1965 } |
| 1966 |
| 1967 void _resolveAnnotationConstructorInvocationArguments( |
| 1968 Annotation annotation, ConstructorElement constructor) { |
| 1969 ArgumentList argumentList = annotation.arguments; |
| 1970 // error will be reported in ConstantVerifier |
| 1971 if (argumentList == null) { |
| 1972 return; |
| 1973 } |
| 1974 // resolve arguments to parameters |
| 1975 List<ParameterElement> parameters = |
| 1976 _resolveArgumentsToFunction(true, argumentList, constructor); |
| 1977 if (parameters != null) { |
| 1978 argumentList.correspondingStaticParameters = parameters; |
| 1979 } |
| 1980 } |
| 1981 |
| 1982 /** |
| 1983 * Continues resolution of the given [annotation]. |
| 1984 */ |
| 1985 void _resolveAnnotationElement(Annotation annotation) { |
| 1986 SimpleIdentifier nameNode1; |
| 1987 SimpleIdentifier nameNode2; |
| 1988 { |
| 1989 Identifier annName = annotation.name; |
| 1990 if (annName is PrefixedIdentifier) { |
| 1991 PrefixedIdentifier prefixed = annName; |
| 1992 nameNode1 = prefixed.prefix; |
| 1993 nameNode2 = prefixed.identifier; |
| 1994 } else { |
| 1995 nameNode1 = annName as SimpleIdentifier; |
| 1996 nameNode2 = null; |
| 1997 } |
| 1998 } |
| 1999 SimpleIdentifier nameNode3 = annotation.constructorName; |
| 2000 ConstructorElement constructor = null; |
| 2001 // |
| 2002 // CONST or Class(args) |
| 2003 // |
| 2004 if (nameNode1 != null && nameNode2 == null && nameNode3 == null) { |
| 2005 Element element1 = nameNode1.staticElement; |
| 2006 // CONST |
| 2007 if (element1 is PropertyAccessorElement) { |
| 2008 _resolveAnnotationElementGetter(annotation, element1); |
| 2009 return; |
| 2010 } |
| 2011 // Class(args) |
| 2012 if (element1 is ClassElement) { |
| 2013 ClassElement classElement = element1; |
| 2014 constructor = new InterfaceTypeImpl(classElement).lookUpConstructor( |
| 2015 null, _definingLibrary); |
| 2016 } |
| 2017 } |
| 2018 // |
| 2019 // prefix.CONST or prefix.Class() or Class.CONST or Class.constructor(args) |
| 2020 // |
| 2021 if (nameNode1 != null && nameNode2 != null && nameNode3 == null) { |
| 2022 Element element1 = nameNode1.staticElement; |
| 2023 Element element2 = nameNode2.staticElement; |
| 2024 // Class.CONST - not resolved yet |
| 2025 if (element1 is ClassElement) { |
| 2026 ClassElement classElement = element1; |
| 2027 element2 = classElement.lookUpGetter(nameNode2.name, _definingLibrary); |
| 2028 } |
| 2029 // prefix.CONST or Class.CONST |
| 2030 if (element2 is PropertyAccessorElement) { |
| 2031 nameNode2.staticElement = element2; |
| 2032 annotation.element = element2; |
| 2033 _resolveAnnotationElementGetter(annotation, element2); |
| 2034 return; |
| 2035 } |
| 2036 // prefix.Class() |
| 2037 if (element2 is ClassElement) { |
| 2038 constructor = element2.unnamedConstructor; |
| 2039 } |
| 2040 // Class.constructor(args) |
| 2041 if (element1 is ClassElement) { |
| 2042 ClassElement classElement = element1; |
| 2043 constructor = new InterfaceTypeImpl(classElement).lookUpConstructor( |
| 2044 nameNode2.name, _definingLibrary); |
| 2045 nameNode2.staticElement = constructor; |
| 2046 } |
| 2047 } |
| 2048 // |
| 2049 // prefix.Class.CONST or prefix.Class.constructor(args) |
| 2050 // |
| 2051 if (nameNode1 != null && nameNode2 != null && nameNode3 != null) { |
| 2052 Element element2 = nameNode2.staticElement; |
| 2053 // element2 should be ClassElement |
| 2054 if (element2 is ClassElement) { |
| 2055 ClassElement classElement = element2; |
| 2056 String name3 = nameNode3.name; |
| 2057 // prefix.Class.CONST |
| 2058 PropertyAccessorElement getter = |
| 2059 classElement.lookUpGetter(name3, _definingLibrary); |
| 2060 if (getter != null) { |
| 2061 nameNode3.staticElement = getter; |
| 2062 annotation.element = element2; |
| 2063 _resolveAnnotationElementGetter(annotation, getter); |
| 2064 return; |
| 2065 } |
| 2066 // prefix.Class.constructor(args) |
| 2067 constructor = new InterfaceTypeImpl(classElement).lookUpConstructor( |
| 2068 name3, _definingLibrary); |
| 2069 nameNode3.staticElement = constructor; |
| 2070 } |
| 2071 } |
| 2072 // we need constructor |
| 2073 if (constructor == null) { |
| 2074 _resolver.reportErrorForNode( |
| 2075 CompileTimeErrorCode.INVALID_ANNOTATION, annotation); |
| 2076 return; |
| 2077 } |
| 2078 // record element |
| 2079 annotation.element = constructor; |
| 2080 // resolve arguments |
| 2081 _resolveAnnotationConstructorInvocationArguments(annotation, constructor); |
| 2082 } |
| 2083 |
| 2084 void _resolveAnnotationElementGetter( |
| 2085 Annotation annotation, PropertyAccessorElement accessorElement) { |
| 2086 // accessor should be synthetic |
| 2087 if (!accessorElement.isSynthetic) { |
| 2088 _resolver.reportErrorForNode( |
| 2089 CompileTimeErrorCode.INVALID_ANNOTATION, annotation); |
| 2090 return; |
| 2091 } |
| 2092 // variable should be constant |
| 2093 VariableElement variableElement = accessorElement.variable; |
| 2094 if (!variableElement.isConst) { |
| 2095 _resolver.reportErrorForNode( |
| 2096 CompileTimeErrorCode.INVALID_ANNOTATION, annotation); |
| 2097 } |
| 2098 // OK |
| 2099 return; |
| 2100 } |
| 2101 |
| 2102 /** |
| 2103 * Given an [argumentList] and the [executableElement] that will be invoked |
| 2104 * using those argument, compute the list of parameters that correspond to the |
| 2105 * list of arguments. An error will be reported if any of the arguments cannot |
| 2106 * be matched to a parameter. The flag [reportError] should be `true` if a |
| 2107 * compile-time error should be reported; or `false` if a compile-time warning |
| 2108 * should be reported. Return the parameters that correspond to the arguments, |
| 2109 * or `null` if no correspondence could be computed. |
| 2110 */ |
| 2111 List<ParameterElement> _resolveArgumentsToFunction(bool reportError, |
| 2112 ArgumentList argumentList, ExecutableElement executableElement) { |
| 2113 if (executableElement == null) { |
| 2114 return null; |
| 2115 } |
| 2116 List<ParameterElement> parameters = executableElement.parameters; |
| 2117 return _resolveArgumentsToParameters(reportError, argumentList, parameters); |
| 2118 } |
| 2119 |
| 2120 /** |
| 2121 * Given an [argumentList] and the [parameters] related to the element that |
| 2122 * will be invoked using those arguments, compute the list of parameters that |
| 2123 * correspond to the list of arguments. An error will be reported if any of |
| 2124 * the arguments cannot be matched to a parameter. The flag [reportError] |
| 2125 * should be `true` if a compile-time error should be reported; or `false` if |
| 2126 * a compile-time warning should be reported. Return the parameters that |
| 2127 * correspond to the arguments. |
| 2128 */ |
| 2129 List<ParameterElement> _resolveArgumentsToParameters(bool reportError, |
| 2130 ArgumentList argumentList, List<ParameterElement> parameters) { |
| 2131 List<ParameterElement> requiredParameters = new List<ParameterElement>(); |
| 2132 List<ParameterElement> positionalParameters = new List<ParameterElement>(); |
| 2133 HashMap<String, ParameterElement> namedParameters = |
| 2134 new HashMap<String, ParameterElement>(); |
| 2135 for (ParameterElement parameter in parameters) { |
| 2136 ParameterKind kind = parameter.parameterKind; |
| 2137 if (kind == ParameterKind.REQUIRED) { |
| 2138 requiredParameters.add(parameter); |
| 2139 } else if (kind == ParameterKind.POSITIONAL) { |
| 2140 positionalParameters.add(parameter); |
| 2141 } else { |
| 2142 namedParameters[parameter.name] = parameter; |
| 2143 } |
| 2144 } |
| 2145 List<ParameterElement> unnamedParameters = |
| 2146 new List<ParameterElement>.from(requiredParameters); |
| 2147 unnamedParameters.addAll(positionalParameters); |
| 2148 int unnamedParameterCount = unnamedParameters.length; |
| 2149 int unnamedIndex = 0; |
| 2150 NodeList<Expression> arguments = argumentList.arguments; |
| 2151 int argumentCount = arguments.length; |
| 2152 List<ParameterElement> resolvedParameters = |
| 2153 new List<ParameterElement>(argumentCount); |
| 2154 int positionalArgumentCount = 0; |
| 2155 HashSet<String> usedNames = new HashSet<String>(); |
| 2156 bool noBlankArguments = true; |
| 2157 for (int i = 0; i < argumentCount; i++) { |
| 2158 Expression argument = arguments[i]; |
| 2159 if (argument is NamedExpression) { |
| 2160 SimpleIdentifier nameNode = argument.name.label; |
| 2161 String name = nameNode.name; |
| 2162 ParameterElement element = namedParameters[name]; |
| 2163 if (element == null) { |
| 2164 ErrorCode errorCode = (reportError |
| 2165 ? CompileTimeErrorCode.UNDEFINED_NAMED_PARAMETER |
| 2166 : StaticWarningCode.UNDEFINED_NAMED_PARAMETER); |
| 2167 _resolver.reportErrorForNode(errorCode, nameNode, [name]); |
| 2168 } else { |
| 2169 resolvedParameters[i] = element; |
| 2170 nameNode.staticElement = element; |
| 2171 } |
| 2172 if (!usedNames.add(name)) { |
| 2173 _resolver.reportErrorForNode( |
| 2174 CompileTimeErrorCode.DUPLICATE_NAMED_ARGUMENT, nameNode, [name]); |
| 2175 } |
| 2176 } else { |
| 2177 if (argument is SimpleIdentifier && argument.name.isEmpty) { |
| 2178 noBlankArguments = false; |
| 2179 } |
| 2180 positionalArgumentCount++; |
| 2181 if (unnamedIndex < unnamedParameterCount) { |
| 2182 resolvedParameters[i] = unnamedParameters[unnamedIndex++]; |
| 2183 } |
| 2184 } |
| 2185 } |
| 2186 if (positionalArgumentCount < requiredParameters.length && |
| 2187 noBlankArguments) { |
| 2188 ErrorCode errorCode = (reportError |
| 2189 ? CompileTimeErrorCode.NOT_ENOUGH_REQUIRED_ARGUMENTS |
| 2190 : StaticWarningCode.NOT_ENOUGH_REQUIRED_ARGUMENTS); |
| 2191 _resolver.reportErrorForNode(errorCode, argumentList, [ |
| 2192 requiredParameters.length, |
| 2193 positionalArgumentCount |
| 2194 ]); |
| 2195 } else if (positionalArgumentCount > unnamedParameterCount && |
| 2196 noBlankArguments) { |
| 2197 ErrorCode errorCode = (reportError |
| 2198 ? CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS |
| 2199 : StaticWarningCode.EXTRA_POSITIONAL_ARGUMENTS); |
| 2200 _resolver.reportErrorForNode(errorCode, argumentList, [ |
| 2201 unnamedParameterCount, |
| 2202 positionalArgumentCount |
| 2203 ]); |
| 2204 } |
| 2205 return resolvedParameters; |
| 2206 } |
| 2207 |
| 2208 void _resolveBinaryExpression(BinaryExpression node, String methodName) { |
| 2209 Expression leftOperand = node.leftOperand; |
| 2210 if (leftOperand != null) { |
| 2211 DartType staticType = _getStaticType(leftOperand); |
| 2212 MethodElement staticMethod = |
| 2213 _lookUpMethod(leftOperand, staticType, methodName); |
| 2214 node.staticElement = staticMethod; |
| 2215 DartType propagatedType = _getPropagatedType(leftOperand); |
| 2216 MethodElement propagatedMethod = |
| 2217 _lookUpMethod(leftOperand, propagatedType, methodName); |
| 2218 node.propagatedElement = propagatedMethod; |
| 2219 if (_shouldReportMissingMember(staticType, staticMethod)) { |
| 2220 if (leftOperand is SuperExpression) { |
| 2221 _recordUndefinedToken(staticType.element, |
| 2222 StaticTypeWarningCode.UNDEFINED_SUPER_OPERATOR, node.operator, [ |
| 2223 methodName, |
| 2224 staticType.displayName |
| 2225 ]); |
| 2226 } else { |
| 2227 _recordUndefinedToken(staticType.element, |
| 2228 StaticTypeWarningCode.UNDEFINED_OPERATOR, node.operator, [ |
| 2229 methodName, |
| 2230 staticType.displayName |
| 2231 ]); |
| 2232 } |
| 2233 } else if (_enableHints && |
| 2234 _shouldReportMissingMember(propagatedType, propagatedMethod) && |
| 2235 !_memberFoundInSubclass( |
| 2236 propagatedType.element, methodName, true, false)) { |
| 2237 _recordUndefinedToken(propagatedType.element, |
| 2238 HintCode.UNDEFINED_OPERATOR, node.operator, [ |
| 2239 methodName, |
| 2240 propagatedType.displayName |
| 2241 ]); |
| 2242 } |
| 2243 } |
| 2244 } |
| 2245 |
| 2246 /** |
| 2247 * Resolve the names in the given [combinators] in the scope of the given |
| 2248 * [library]. |
| 2249 */ |
| 2250 void _resolveCombinators( |
| 2251 LibraryElement library, NodeList<Combinator> combinators) { |
| 2252 if (library == null) { |
| 2253 // |
| 2254 // The library will be null if the directive containing the combinators |
| 2255 // has a URI that is not valid. |
| 2256 // |
| 2257 return; |
| 2258 } |
| 2259 Namespace namespace = |
| 2260 new NamespaceBuilder().createExportNamespaceForLibrary(library); |
| 2261 for (Combinator combinator in combinators) { |
| 2262 NodeList<SimpleIdentifier> names; |
| 2263 if (combinator is HideCombinator) { |
| 2264 names = combinator.hiddenNames; |
| 2265 } else { |
| 2266 names = (combinator as ShowCombinator).shownNames; |
| 2267 } |
| 2268 for (SimpleIdentifier name in names) { |
| 2269 String nameStr = name.name; |
| 2270 Element element = namespace.get(nameStr); |
| 2271 if (element == null) { |
| 2272 element = namespace.get("$nameStr="); |
| 2273 } |
| 2274 if (element != null) { |
| 2275 // Ensure that the name always resolves to a top-level variable |
| 2276 // rather than a getter or setter |
| 2277 if (element is PropertyAccessorElement) { |
| 2278 element = (element as PropertyAccessorElement).variable; |
| 2279 } |
| 2280 name.staticElement = element; |
| 2281 } |
| 2282 } |
| 2283 } |
| 2284 } |
| 2285 |
| 2286 /** |
| 2287 * Given that we are accessing a property of the given [classElement] with the |
| 2288 * given [propertyName], return the element that represents the property. |
| 2289 */ |
| 2290 Element _resolveElement( |
| 2291 ClassElementImpl classElement, SimpleIdentifier propertyName) { |
| 2292 String name = propertyName.name; |
| 2293 Element element = null; |
| 2294 if (propertyName.inSetterContext()) { |
| 2295 element = classElement.getSetter(name); |
| 2296 } |
| 2297 if (element == null) { |
| 2298 element = classElement.getGetter(name); |
| 2299 } |
| 2300 if (element == null) { |
| 2301 element = classElement.getMethod(name); |
| 2302 } |
| 2303 if (element != null && element.isAccessibleIn(_definingLibrary)) { |
| 2304 return element; |
| 2305 } |
| 2306 return null; |
| 2307 } |
| 2308 |
| 2309 /** |
| 2310 * Given an invocation of the form 'm(a1, ..., an)', resolve 'm' to the |
| 2311 * element being invoked. If the returned element is a method, then the method |
| 2312 * will be invoked. If the returned element is a getter, the getter will be |
| 2313 * invoked without arguments and the result of that invocation will then be |
| 2314 * invoked with the arguments. The [methodName] is the name of the method |
| 2315 * being invoked ('m'). |
| 2316 */ |
| 2317 Element _resolveInvokedElement(SimpleIdentifier methodName) { |
| 2318 // |
| 2319 // Look first in the lexical scope. |
| 2320 // |
| 2321 Element element = _resolver.nameScope.lookup(methodName, _definingLibrary); |
| 2322 if (element == null) { |
| 2323 // |
| 2324 // If it isn't defined in the lexical scope, and the invocation is within |
| 2325 // a class, then look in the inheritance scope. |
| 2326 // |
| 2327 ClassElement enclosingClass = _resolver.enclosingClass; |
| 2328 if (enclosingClass != null) { |
| 2329 InterfaceType enclosingType = enclosingClass.type; |
| 2330 element = _lookUpMethod(null, enclosingType, methodName.name); |
| 2331 if (element == null) { |
| 2332 // |
| 2333 // If there's no method, then it's possible that 'm' is a getter that |
| 2334 // returns a function. |
| 2335 // |
| 2336 element = _lookUpGetter(null, enclosingType, methodName.name); |
| 2337 } |
| 2338 } |
| 2339 } |
| 2340 // TODO(brianwilkerson) Report this error. |
| 2341 return element; |
| 2342 } |
| 2343 |
| 2344 /** |
| 2345 * Given an invocation of the form 'e.m(a1, ..., an)', resolve 'e.m' to the |
| 2346 * element being invoked. If the returned element is a method, then the method |
| 2347 * will be invoked. If the returned element is a getter, the getter will be |
| 2348 * invoked without arguments and the result of that invocation will then be |
| 2349 * invoked with the arguments. The [target] is the target of the invocation |
| 2350 * ('e'). The [targetType] is the type of the target. The [methodName] is th |
| 2351 * name of the method being invoked ('m'). [isConditional] indicates |
| 2352 * whether the invocatoin uses a '?.' operator. |
| 2353 */ |
| 2354 Element _resolveInvokedElementWithTarget(Expression target, |
| 2355 DartType targetType, SimpleIdentifier methodName, bool isConditional) { |
| 2356 if (targetType is InterfaceType) { |
| 2357 Element element = _lookUpMethod(target, targetType, methodName.name); |
| 2358 if (element == null) { |
| 2359 // |
| 2360 // If there's no method, then it's possible that 'm' is a getter that |
| 2361 // returns a function. |
| 2362 // |
| 2363 // TODO (collinsn): need to add union type support here too, in the |
| 2364 // style of [lookUpMethod]. |
| 2365 element = _lookUpGetter(target, targetType, methodName.name); |
| 2366 } |
| 2367 return element; |
| 2368 } else if (target is SimpleIdentifier) { |
| 2369 Element targetElement = target.staticElement; |
| 2370 if (targetElement is PrefixElement) { |
| 2371 if (isConditional) { |
| 2372 _resolver.reportErrorForNode( |
| 2373 CompileTimeErrorCode.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT, |
| 2374 target, [target.name]); |
| 2375 } |
| 2376 // |
| 2377 // Look to see whether the name of the method is really part of a |
| 2378 // prefixed identifier for an imported top-level function or top-level |
| 2379 // getter that returns a function. |
| 2380 // |
| 2381 String name = "${target.name}.$methodName"; |
| 2382 Identifier functionName = new SyntheticIdentifier(name, methodName); |
| 2383 Element element = |
| 2384 _resolver.nameScope.lookup(functionName, _definingLibrary); |
| 2385 if (element != null) { |
| 2386 // TODO(brianwilkerson) This isn't a method invocation, it's a |
| 2387 // function invocation where the function name is a prefixed |
| 2388 // identifier. Consider re-writing the AST. |
| 2389 return element; |
| 2390 } |
| 2391 } |
| 2392 } |
| 2393 // TODO(brianwilkerson) Report this error. |
| 2394 return null; |
| 2395 } |
| 2396 |
| 2397 /** |
| 2398 * Given that we are accessing a property of the given [targetType] with the |
| 2399 * given [propertyName], return the element that represents the property. The |
| 2400 * [target] is the target of the invocation ('e'). |
| 2401 */ |
| 2402 ExecutableElement _resolveProperty( |
| 2403 Expression target, DartType targetType, SimpleIdentifier propertyName) { |
| 2404 ExecutableElement memberElement = null; |
| 2405 if (propertyName.inSetterContext()) { |
| 2406 memberElement = _lookUpSetter(target, targetType, propertyName.name); |
| 2407 } |
| 2408 if (memberElement == null) { |
| 2409 memberElement = _lookUpGetter(target, targetType, propertyName.name); |
| 2410 } |
| 2411 if (memberElement == null) { |
| 2412 memberElement = _lookUpMethod(target, targetType, propertyName.name); |
| 2413 } |
| 2414 return memberElement; |
| 2415 } |
| 2416 |
| 2417 void _resolvePropertyAccess( |
| 2418 Expression target, SimpleIdentifier propertyName) { |
| 2419 DartType staticType = _getStaticType(target); |
| 2420 DartType propagatedType = _getPropagatedType(target); |
| 2421 Element staticElement = null; |
| 2422 Element propagatedElement = null; |
| 2423 // |
| 2424 // If this property access is of the form 'C.m' where 'C' is a class, |
| 2425 // then we don't call resolveProperty(...) which walks up the class |
| 2426 // hierarchy, instead we just look for the member in the type only. This |
| 2427 // does not apply to conditional property accesses (i.e. 'C?.m'). |
| 2428 // |
| 2429 ClassElementImpl typeReference = getTypeReference(target); |
| 2430 if (typeReference != null) { |
| 2431 // TODO(brianwilkerson) Why are we setting the propagated element here? |
| 2432 // It looks wrong. |
| 2433 staticElement = |
| 2434 propagatedElement = _resolveElement(typeReference, propertyName); |
| 2435 } else { |
| 2436 staticElement = _resolveProperty(target, staticType, propertyName); |
| 2437 propagatedElement = |
| 2438 _resolveProperty(target, propagatedType, propertyName); |
| 2439 } |
| 2440 // May be part of annotation, record property element only if exists. |
| 2441 // Error was already reported in validateAnnotationElement(). |
| 2442 if (target.parent.parent is Annotation) { |
| 2443 if (staticElement != null) { |
| 2444 propertyName.staticElement = staticElement; |
| 2445 } |
| 2446 return; |
| 2447 } |
| 2448 propertyName.staticElement = staticElement; |
| 2449 propertyName.propagatedElement = propagatedElement; |
| 2450 bool shouldReportMissingMember_static = |
| 2451 _shouldReportMissingMember(staticType, staticElement); |
| 2452 bool shouldReportMissingMember_propagated = |
| 2453 !shouldReportMissingMember_static && |
| 2454 _enableHints && |
| 2455 _shouldReportMissingMember(propagatedType, propagatedElement) && |
| 2456 !_memberFoundInSubclass( |
| 2457 propagatedType.element, propertyName.name, false, true); |
| 2458 if (shouldReportMissingMember_static || |
| 2459 shouldReportMissingMember_propagated) { |
| 2460 DartType staticOrPropagatedType = |
| 2461 shouldReportMissingMember_static ? staticType : propagatedType; |
| 2462 Element staticOrPropagatedEnclosingElt = staticOrPropagatedType.element; |
| 2463 bool isStaticProperty = _isStatic(staticOrPropagatedEnclosingElt); |
| 2464 DartType displayType = staticOrPropagatedType != null |
| 2465 ? staticOrPropagatedType |
| 2466 : propagatedType != null ? propagatedType : staticType; |
| 2467 // Special getter cases. |
| 2468 if (propertyName.inGetterContext()) { |
| 2469 if (!isStaticProperty && |
| 2470 staticOrPropagatedEnclosingElt is ClassElement) { |
| 2471 ClassElement classElement = staticOrPropagatedEnclosingElt; |
| 2472 InterfaceType targetType = classElement.type; |
| 2473 if (!_enableStrictCallChecks && |
| 2474 targetType != null && |
| 2475 targetType.isDartCoreFunction && |
| 2476 propertyName.name == FunctionElement.CALL_METHOD_NAME) { |
| 2477 // TODO(brianwilkerson) Can we ever resolve the function being |
| 2478 // invoked? |
| 2479 // resolveArgumentsToParameters(node.getArgumentList(), invokedFuncti
on); |
| 2480 return; |
| 2481 } else if (classElement.isEnum && propertyName.name == "_name") { |
| 2482 _resolver.reportErrorForNode( |
| 2483 CompileTimeErrorCode.ACCESS_PRIVATE_ENUM_FIELD, propertyName, |
| 2484 [propertyName.name]); |
| 2485 return; |
| 2486 } |
| 2487 } |
| 2488 } |
| 2489 Element declaringElement = |
| 2490 staticType.isVoid ? null : staticOrPropagatedEnclosingElt; |
| 2491 if (propertyName.inSetterContext()) { |
| 2492 ErrorCode errorCode; |
| 2493 if (shouldReportMissingMember_static) { |
| 2494 if (target is SuperExpression) { |
| 2495 if (isStaticProperty && !staticType.isVoid) { |
| 2496 errorCode = StaticWarningCode.UNDEFINED_SUPER_SETTER; |
| 2497 } else { |
| 2498 errorCode = StaticTypeWarningCode.UNDEFINED_SUPER_SETTER; |
| 2499 } |
| 2500 } else { |
| 2501 if (isStaticProperty && !staticType.isVoid) { |
| 2502 errorCode = StaticWarningCode.UNDEFINED_SETTER; |
| 2503 } else { |
| 2504 errorCode = StaticTypeWarningCode.UNDEFINED_SETTER; |
| 2505 } |
| 2506 } |
| 2507 } else { |
| 2508 errorCode = HintCode.UNDEFINED_SETTER; |
| 2509 } |
| 2510 _recordUndefinedNode(declaringElement, errorCode, propertyName, [ |
| 2511 propertyName.name, |
| 2512 displayType.displayName |
| 2513 ]); |
| 2514 } else if (propertyName.inGetterContext()) { |
| 2515 ErrorCode errorCode; |
| 2516 if (shouldReportMissingMember_static) { |
| 2517 if (target is SuperExpression) { |
| 2518 if (isStaticProperty && !staticType.isVoid) { |
| 2519 errorCode = StaticWarningCode.UNDEFINED_SUPER_GETTER; |
| 2520 } else { |
| 2521 errorCode = StaticTypeWarningCode.UNDEFINED_SUPER_GETTER; |
| 2522 } |
| 2523 } else { |
| 2524 if (isStaticProperty && !staticType.isVoid) { |
| 2525 errorCode = StaticWarningCode.UNDEFINED_GETTER; |
| 2526 } else { |
| 2527 errorCode = StaticTypeWarningCode.UNDEFINED_GETTER; |
| 2528 } |
| 2529 } |
| 2530 } else { |
| 2531 errorCode = HintCode.UNDEFINED_GETTER; |
| 2532 } |
| 2533 _recordUndefinedNode(declaringElement, errorCode, propertyName, [ |
| 2534 propertyName.name, |
| 2535 displayType.displayName |
| 2536 ]); |
| 2537 } else { |
| 2538 _recordUndefinedNode(declaringElement, |
| 2539 StaticWarningCode.UNDEFINED_IDENTIFIER, propertyName, |
| 2540 [propertyName.name]); |
| 2541 } |
| 2542 } |
| 2543 } |
| 2544 |
| 2545 /** |
| 2546 * Resolve the given simple [identifier] if possible. Return the element to |
| 2547 * which it could be resolved, or `null` if it could not be resolved. This |
| 2548 * does not record the results of the resolution. |
| 2549 */ |
| 2550 Element _resolveSimpleIdentifier(SimpleIdentifier identifier) { |
| 2551 Element element = _resolver.nameScope.lookup(identifier, _definingLibrary); |
| 2552 if (element is PropertyAccessorElement && identifier.inSetterContext()) { |
| 2553 PropertyInducingElement variable = |
| 2554 (element as PropertyAccessorElement).variable; |
| 2555 if (variable != null) { |
| 2556 PropertyAccessorElement setter = variable.setter; |
| 2557 if (setter == null) { |
| 2558 // |
| 2559 // Check to see whether there might be a locally defined getter and |
| 2560 // an inherited setter. |
| 2561 // |
| 2562 ClassElement enclosingClass = _resolver.enclosingClass; |
| 2563 if (enclosingClass != null) { |
| 2564 setter = _lookUpSetter(null, enclosingClass.type, identifier.name); |
| 2565 } |
| 2566 } |
| 2567 if (setter != null) { |
| 2568 element = setter; |
| 2569 } |
| 2570 } |
| 2571 } else if (element == null && |
| 2572 (identifier.inSetterContext() || |
| 2573 identifier.parent is CommentReference)) { |
| 2574 element = _resolver.nameScope.lookup( |
| 2575 new SyntheticIdentifier("${identifier.name}=", identifier), |
| 2576 _definingLibrary); |
| 2577 } |
| 2578 ClassElement enclosingClass = _resolver.enclosingClass; |
| 2579 if (element == null && enclosingClass != null) { |
| 2580 InterfaceType enclosingType = enclosingClass.type; |
| 2581 if (element == null && |
| 2582 (identifier.inSetterContext() || |
| 2583 identifier.parent is CommentReference)) { |
| 2584 element = _lookUpSetter(null, enclosingType, identifier.name); |
| 2585 } |
| 2586 if (element == null && identifier.inGetterContext()) { |
| 2587 element = _lookUpGetter(null, enclosingType, identifier.name); |
| 2588 } |
| 2589 if (element == null) { |
| 2590 element = _lookUpMethod(null, enclosingType, identifier.name); |
| 2591 } |
| 2592 } |
| 2593 return element; |
| 2594 } |
| 2595 |
| 2596 /** |
| 2597 * If the given [type] is a type parameter, resolve it to the type that should |
| 2598 * be used when looking up members. Otherwise, return the original type. |
| 2599 */ |
| 2600 DartType _resolveTypeParameter(DartType type) { |
| 2601 if (type is TypeParameterType) { |
| 2602 DartType bound = type.element.bound; |
| 2603 if (bound == null) { |
| 2604 return _resolver.typeProvider.objectType; |
| 2605 } |
| 2606 return bound; |
| 2607 } |
| 2608 return type; |
| 2609 } |
| 2610 |
| 2611 /** |
| 2612 * Given a [node] that can have annotations associated with it and the |
| 2613 * [element] to which that node has been resolved, create the annotations in |
| 2614 * the element model representing the annotations on the node. |
| 2615 */ |
| 2616 void _setMetadataForParameter(Element element, NormalFormalParameter node) { |
| 2617 if (element is! ElementImpl) { |
| 2618 return; |
| 2619 } |
| 2620 List<ElementAnnotationImpl> annotationList = |
| 2621 new List<ElementAnnotationImpl>(); |
| 2622 _addAnnotations(annotationList, node.metadata); |
| 2623 if (!annotationList.isEmpty) { |
| 2624 (element as ElementImpl).metadata = annotationList; |
| 2625 } |
| 2626 } |
| 2627 |
| 2628 /** |
| 2629 * Return `true` if we should report an error as a result of looking up a |
| 2630 * [member] in the given [type] and not finding any member. |
| 2631 */ |
| 2632 bool _shouldReportMissingMember(DartType type, Element member) { |
| 2633 if (member != null || type == null || type.isDynamic || type.isBottom) { |
| 2634 return false; |
| 2635 } |
| 2636 return true; |
| 2637 } |
| 2638 |
| 2639 /** |
| 2640 * Checks whether the given [expression] is a reference to a class. If it is |
| 2641 * then the element representing the class is returned, otherwise `null` is |
| 2642 * returned. |
| 2643 */ |
| 2644 static ClassElementImpl getTypeReference(Expression expression) { |
| 2645 if (expression is Identifier) { |
| 2646 Element staticElement = expression.staticElement; |
| 2647 if (staticElement is ClassElementImpl) { |
| 2648 return staticElement; |
| 2649 } |
| 2650 } |
| 2651 return null; |
| 2652 } |
| 2653 |
| 2654 /** |
| 2655 * Given a [node] that can have annotations associated with it and the |
| 2656 * [element] to which that node has been resolved, create the annotations in |
| 2657 * the element model representing the annotations on the node. |
| 2658 */ |
| 2659 static void setMetadata(Element element, AnnotatedNode node) { |
| 2660 if (element is! ElementImpl) { |
| 2661 return; |
| 2662 } |
| 2663 List<ElementAnnotationImpl> annotationList = <ElementAnnotationImpl>[]; |
| 2664 _addAnnotations(annotationList, node.metadata); |
| 2665 if (node is VariableDeclaration && node.parent is VariableDeclarationList) { |
| 2666 VariableDeclarationList list = node.parent as VariableDeclarationList; |
| 2667 _addAnnotations(annotationList, list.metadata); |
| 2668 if (list.parent is FieldDeclaration) { |
| 2669 FieldDeclaration fieldDeclaration = list.parent as FieldDeclaration; |
| 2670 _addAnnotations(annotationList, fieldDeclaration.metadata); |
| 2671 } else if (list.parent is TopLevelVariableDeclaration) { |
| 2672 TopLevelVariableDeclaration variableDeclaration = |
| 2673 list.parent as TopLevelVariableDeclaration; |
| 2674 _addAnnotations(annotationList, variableDeclaration.metadata); |
| 2675 } |
| 2676 } |
| 2677 if (!annotationList.isEmpty) { |
| 2678 (element as ElementImpl).metadata = annotationList; |
| 2679 } |
| 2680 } |
| 2681 |
| 2682 /** |
| 2683 * Generate annotation elements for each of the annotations in the |
| 2684 * [annotationList] and add them to the given list of [annotations]. |
| 2685 */ |
| 2686 static void _addAnnotations(List<ElementAnnotationImpl> annotationList, |
| 2687 NodeList<Annotation> annotations) { |
| 2688 int annotationCount = annotations.length; |
| 2689 for (int i = 0; i < annotationCount; i++) { |
| 2690 Annotation annotation = annotations[i]; |
| 2691 Element resolvedElement = annotation.element; |
| 2692 if (resolvedElement != null) { |
| 2693 ElementAnnotationImpl elementAnnotation = |
| 2694 new ElementAnnotationImpl(resolvedElement); |
| 2695 annotation.elementAnnotation = elementAnnotation; |
| 2696 annotationList.add(elementAnnotation); |
| 2697 } |
| 2698 } |
| 2699 } |
| 2700 |
| 2701 /** |
| 2702 * Return `true` if the given [identifier] is the return type of a constructor |
| 2703 * declaration. |
| 2704 */ |
| 2705 static bool _isConstructorReturnType(SimpleIdentifier identifier) { |
| 2706 AstNode parent = identifier.parent; |
| 2707 if (parent is ConstructorDeclaration) { |
| 2708 return identical(parent.returnType, identifier); |
| 2709 } |
| 2710 return false; |
| 2711 } |
| 2712 |
| 2713 /** |
| 2714 * Return `true` if the given [identifier] is the return type of a factory |
| 2715 * constructor. |
| 2716 */ |
| 2717 static bool _isFactoryConstructorReturnType(SimpleIdentifier identifier) { |
| 2718 AstNode parent = identifier.parent; |
| 2719 if (parent is ConstructorDeclaration) { |
| 2720 ConstructorDeclaration constructor = parent; |
| 2721 return identical(constructor.returnType, identifier) && |
| 2722 constructor.factoryKeyword != null; |
| 2723 } |
| 2724 return false; |
| 2725 } |
| 2726 |
| 2727 /** |
| 2728 * Return `true` if the given 'super' [expression] is used in a valid context. |
| 2729 */ |
| 2730 static bool _isSuperInValidContext(SuperExpression expression) { |
| 2731 for (AstNode node = expression; node != null; node = node.parent) { |
| 2732 if (node is CompilationUnit) { |
| 2733 return false; |
| 2734 } |
| 2735 if (node is ConstructorDeclaration) { |
| 2736 return node.factoryKeyword == null; |
| 2737 } |
| 2738 if (node is ConstructorFieldInitializer) { |
| 2739 return false; |
| 2740 } |
| 2741 if (node is MethodDeclaration) { |
| 2742 return !node.isStatic; |
| 2743 } |
| 2744 } |
| 2745 return false; |
| 2746 } |
| 2747 } |
| 2748 |
| 2749 /** |
| 2750 * An identifier that can be used to look up names in the lexical scope when |
| 2751 * there is no identifier in the AST structure. There is no identifier in the |
| 2752 * AST when the parser could not distinguish between a method invocation and an |
| 2753 * invocation of a top-level function imported with a prefix. |
| 2754 */ |
| 2755 class SyntheticIdentifier extends Identifier { |
| 2756 /** |
| 2757 * The name of the synthetic identifier. |
| 2758 */ |
| 2759 final String name; |
| 2760 |
| 2761 /** |
| 2762 * The identifier to be highlighted in case of an error |
| 2763 */ |
| 2764 final Identifier targetIdentifier; |
| 2765 |
| 2766 /** |
| 2767 * Initialize a newly created synthetic identifier to have the given [name] |
| 2768 * and [targetIdentifier]. |
| 2769 */ |
| 2770 SyntheticIdentifier(this.name, this.targetIdentifier); |
| 2771 |
| 2772 @override |
| 2773 sc.Token get beginToken => null; |
| 2774 |
| 2775 @override |
| 2776 Element get bestElement => null; |
| 2777 |
| 2778 @override |
| 2779 Iterable get childEntities { |
| 2780 // Should never be called, since a SyntheticIdentifier never appears in the |
| 2781 // AST--it is just used for lookup. |
| 2782 assert(false); |
| 2783 return new ChildEntities(); |
| 2784 } |
| 2785 |
| 2786 @override |
| 2787 sc.Token get endToken => null; |
| 2788 |
| 2789 @override |
| 2790 int get length => targetIdentifier.length; |
| 2791 |
| 2792 @override |
| 2793 int get offset => targetIdentifier.offset; |
| 2794 |
| 2795 @override |
| 2796 int get precedence => 16; |
| 2797 |
| 2798 @override |
| 2799 Element get propagatedElement => null; |
| 2800 |
| 2801 @override |
| 2802 Element get staticElement => null; |
| 2803 |
| 2804 @override |
| 2805 accept(AstVisitor visitor) => null; |
| 2806 |
| 2807 @override |
| 2808 void visitChildren(AstVisitor visitor) {} |
| 2809 } |
OLD | NEW |