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