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; |
| 6 |
| 7 import 'dart:collection'; |
| 8 |
| 9 import 'ast.dart'; |
| 10 import 'constant.dart'; |
| 11 import 'element.dart'; |
| 12 import 'element_resolver.dart'; |
| 13 import 'engine.dart'; |
| 14 import 'error.dart'; |
| 15 import 'error_verifier.dart'; |
| 16 import 'html.dart' as ht; |
| 17 import 'java_core.dart'; |
| 18 import 'java_engine.dart'; |
| 19 import 'scanner.dart' as sc; |
| 20 import 'sdk.dart' show DartSdk, SdkLibrary; |
| 21 import 'source.dart'; |
| 22 import 'static_type_analyzer.dart'; |
| 23 import 'utilities_dart.dart'; |
| 24 |
| 25 /** |
| 26 * Callback signature used by ImplicitConstructorBuilder to register |
| 27 * computations to be performed, and their dependencies. A call to this |
| 28 * callback indicates that [computation] may be used to compute implicit |
| 29 * constructors for [classElement], but that the computation may not be invoked |
| 30 * until after implicit constructors have been built for [superclassElement]. |
| 31 */ |
| 32 typedef void ImplicitConstructorBuilderCallback(ClassElement classElement, |
| 33 ClassElement superclassElement, void computation()); |
| 34 |
| 35 typedef LibraryResolver LibraryResolverFactory(AnalysisContext context); |
| 36 |
| 37 typedef ResolverVisitor ResolverVisitorFactory( |
| 38 Library library, Source source, TypeProvider typeProvider); |
| 39 |
| 40 typedef StaticTypeAnalyzer StaticTypeAnalyzerFactory(ResolverVisitor visitor); |
| 41 |
| 42 typedef TypeResolverVisitor TypeResolverVisitorFactory( |
| 43 Library library, Source source, TypeProvider typeProvider); |
| 44 |
| 45 typedef void VoidFunction(); |
| 46 |
| 47 /** |
| 48 * Instances of the class `BestPracticesVerifier` traverse an AST structure look
ing for |
| 49 * violations of Dart best practices. |
| 50 */ |
| 51 class BestPracticesVerifier extends RecursiveAstVisitor<Object> { |
| 52 // static String _HASHCODE_GETTER_NAME = "hashCode"; |
| 53 |
| 54 static String _NULL_TYPE_NAME = "Null"; |
| 55 |
| 56 static String _TO_INT_METHOD_NAME = "toInt"; |
| 57 |
| 58 /** |
| 59 * The class containing the AST nodes being visited, or `null` if we are not i
n the scope of |
| 60 * a class. |
| 61 */ |
| 62 ClassElement _enclosingClass; |
| 63 |
| 64 /** |
| 65 * The error reporter by which errors will be reported. |
| 66 */ |
| 67 final ErrorReporter _errorReporter; |
| 68 |
| 69 /** |
| 70 * The type Future<Null>, which is needed for determining whether it is safe |
| 71 * to have a bare "return;" in an async method. |
| 72 */ |
| 73 final InterfaceType _futureNullType; |
| 74 |
| 75 /** |
| 76 * Create a new instance of the [BestPracticesVerifier]. |
| 77 * |
| 78 * @param errorReporter the error reporter |
| 79 */ |
| 80 BestPracticesVerifier(this._errorReporter, TypeProvider typeProvider) |
| 81 : _futureNullType = typeProvider.futureNullType; |
| 82 |
| 83 @override |
| 84 Object visitArgumentList(ArgumentList node) { |
| 85 _checkForArgumentTypesNotAssignableInList(node); |
| 86 return super.visitArgumentList(node); |
| 87 } |
| 88 |
| 89 @override |
| 90 Object visitAsExpression(AsExpression node) { |
| 91 _checkForUnnecessaryCast(node); |
| 92 return super.visitAsExpression(node); |
| 93 } |
| 94 |
| 95 @override |
| 96 Object visitAssignmentExpression(AssignmentExpression node) { |
| 97 sc.TokenType operatorType = node.operator.type; |
| 98 if (operatorType == sc.TokenType.EQ) { |
| 99 _checkForUseOfVoidResult(node.rightHandSide); |
| 100 _checkForInvalidAssignment(node.leftHandSide, node.rightHandSide); |
| 101 } else { |
| 102 _checkForDeprecatedMemberUse(node.bestElement, node); |
| 103 } |
| 104 return super.visitAssignmentExpression(node); |
| 105 } |
| 106 |
| 107 @override |
| 108 Object visitBinaryExpression(BinaryExpression node) { |
| 109 _checkForDivisionOptimizationHint(node); |
| 110 _checkForDeprecatedMemberUse(node.bestElement, node); |
| 111 return super.visitBinaryExpression(node); |
| 112 } |
| 113 |
| 114 @override |
| 115 Object visitClassDeclaration(ClassDeclaration node) { |
| 116 ClassElement outerClass = _enclosingClass; |
| 117 try { |
| 118 _enclosingClass = node.element; |
| 119 // Commented out until we decide that we want this hint in the analyzer |
| 120 // checkForOverrideEqualsButNotHashCode(node); |
| 121 return super.visitClassDeclaration(node); |
| 122 } finally { |
| 123 _enclosingClass = outerClass; |
| 124 } |
| 125 } |
| 126 |
| 127 @override |
| 128 Object visitExportDirective(ExportDirective node) { |
| 129 _checkForDeprecatedMemberUse(node.uriElement, node); |
| 130 return super.visitExportDirective(node); |
| 131 } |
| 132 |
| 133 @override |
| 134 Object visitFunctionDeclaration(FunctionDeclaration node) { |
| 135 _checkForMissingReturn(node.returnType, node.functionExpression.body); |
| 136 return super.visitFunctionDeclaration(node); |
| 137 } |
| 138 |
| 139 @override |
| 140 Object visitImportDirective(ImportDirective node) { |
| 141 _checkForDeprecatedMemberUse(node.uriElement, node); |
| 142 ImportElement importElement = node.element; |
| 143 if (importElement != null) { |
| 144 if (importElement.isDeferred) { |
| 145 _checkForLoadLibraryFunction(node, importElement); |
| 146 } |
| 147 } |
| 148 return super.visitImportDirective(node); |
| 149 } |
| 150 |
| 151 @override |
| 152 Object visitIndexExpression(IndexExpression node) { |
| 153 _checkForDeprecatedMemberUse(node.bestElement, node); |
| 154 return super.visitIndexExpression(node); |
| 155 } |
| 156 |
| 157 @override |
| 158 Object visitInstanceCreationExpression(InstanceCreationExpression node) { |
| 159 _checkForDeprecatedMemberUse(node.staticElement, node); |
| 160 return super.visitInstanceCreationExpression(node); |
| 161 } |
| 162 |
| 163 @override |
| 164 Object visitIsExpression(IsExpression node) { |
| 165 _checkAllTypeChecks(node); |
| 166 return super.visitIsExpression(node); |
| 167 } |
| 168 |
| 169 @override |
| 170 Object visitMethodDeclaration(MethodDeclaration node) { |
| 171 // This was determined to not be a good hint, see: dartbug.com/16029 |
| 172 //checkForOverridingPrivateMember(node); |
| 173 _checkForMissingReturn(node.returnType, node.body); |
| 174 return super.visitMethodDeclaration(node); |
| 175 } |
| 176 |
| 177 @override |
| 178 Object visitPostfixExpression(PostfixExpression node) { |
| 179 _checkForDeprecatedMemberUse(node.bestElement, node); |
| 180 return super.visitPostfixExpression(node); |
| 181 } |
| 182 |
| 183 @override |
| 184 Object visitPrefixExpression(PrefixExpression node) { |
| 185 _checkForDeprecatedMemberUse(node.bestElement, node); |
| 186 return super.visitPrefixExpression(node); |
| 187 } |
| 188 |
| 189 @override |
| 190 Object visitRedirectingConstructorInvocation( |
| 191 RedirectingConstructorInvocation node) { |
| 192 _checkForDeprecatedMemberUse(node.staticElement, node); |
| 193 return super.visitRedirectingConstructorInvocation(node); |
| 194 } |
| 195 |
| 196 @override |
| 197 Object visitSimpleIdentifier(SimpleIdentifier node) { |
| 198 _checkForDeprecatedMemberUseAtIdentifier(node); |
| 199 return super.visitSimpleIdentifier(node); |
| 200 } |
| 201 |
| 202 @override |
| 203 Object visitSuperConstructorInvocation(SuperConstructorInvocation node) { |
| 204 _checkForDeprecatedMemberUse(node.staticElement, node); |
| 205 return super.visitSuperConstructorInvocation(node); |
| 206 } |
| 207 |
| 208 @override |
| 209 Object visitVariableDeclaration(VariableDeclaration node) { |
| 210 _checkForUseOfVoidResult(node.initializer); |
| 211 _checkForInvalidAssignment(node.name, node.initializer); |
| 212 return super.visitVariableDeclaration(node); |
| 213 } |
| 214 |
| 215 /** |
| 216 * Check for the passed is expression for the unnecessary type check hint code
s as well as null |
| 217 * checks expressed using an is expression. |
| 218 * |
| 219 * @param node the is expression to check |
| 220 * @return `true` if and only if a hint code is generated on the passed node |
| 221 * See [HintCode.TYPE_CHECK_IS_NOT_NULL], [HintCode.TYPE_CHECK_IS_NULL], |
| 222 * [HintCode.UNNECESSARY_TYPE_CHECK_TRUE], and |
| 223 * [HintCode.UNNECESSARY_TYPE_CHECK_FALSE]. |
| 224 */ |
| 225 bool _checkAllTypeChecks(IsExpression node) { |
| 226 Expression expression = node.expression; |
| 227 TypeName typeName = node.type; |
| 228 DartType lhsType = expression.staticType; |
| 229 DartType rhsType = typeName.type; |
| 230 if (lhsType == null || rhsType == null) { |
| 231 return false; |
| 232 } |
| 233 String rhsNameStr = typeName.name.name; |
| 234 // if x is dynamic |
| 235 if (rhsType.isDynamic && rhsNameStr == sc.Keyword.DYNAMIC.syntax) { |
| 236 if (node.notOperator == null) { |
| 237 // the is case |
| 238 _errorReporter.reportErrorForNode( |
| 239 HintCode.UNNECESSARY_TYPE_CHECK_TRUE, node); |
| 240 } else { |
| 241 // the is not case |
| 242 _errorReporter.reportErrorForNode( |
| 243 HintCode.UNNECESSARY_TYPE_CHECK_FALSE, node); |
| 244 } |
| 245 return true; |
| 246 } |
| 247 Element rhsElement = rhsType.element; |
| 248 LibraryElement libraryElement = |
| 249 rhsElement != null ? rhsElement.library : null; |
| 250 if (libraryElement != null && libraryElement.isDartCore) { |
| 251 // if x is Object or null is Null |
| 252 if (rhsType.isObject || |
| 253 (expression is NullLiteral && rhsNameStr == _NULL_TYPE_NAME)) { |
| 254 if (node.notOperator == null) { |
| 255 // the is case |
| 256 _errorReporter.reportErrorForNode( |
| 257 HintCode.UNNECESSARY_TYPE_CHECK_TRUE, node); |
| 258 } else { |
| 259 // the is not case |
| 260 _errorReporter.reportErrorForNode( |
| 261 HintCode.UNNECESSARY_TYPE_CHECK_FALSE, node); |
| 262 } |
| 263 return true; |
| 264 } else if (rhsNameStr == _NULL_TYPE_NAME) { |
| 265 if (node.notOperator == null) { |
| 266 // the is case |
| 267 _errorReporter.reportErrorForNode(HintCode.TYPE_CHECK_IS_NULL, node); |
| 268 } else { |
| 269 // the is not case |
| 270 _errorReporter.reportErrorForNode( |
| 271 HintCode.TYPE_CHECK_IS_NOT_NULL, node); |
| 272 } |
| 273 return true; |
| 274 } |
| 275 } |
| 276 return false; |
| 277 } |
| 278 |
| 279 /** |
| 280 * This verifies that the passed expression can be assigned to its correspondi
ng parameters. |
| 281 * |
| 282 * This method corresponds to ErrorVerifier.checkForArgumentTypeNotAssignable. |
| 283 * |
| 284 * TODO (jwren) In the ErrorVerifier there are other warnings that we could ha
ve a corresponding |
| 285 * hint for: see other callers of ErrorVerifier.checkForArgumentTypeNotAssigna
ble(..). |
| 286 * |
| 287 * @param expression the expression to evaluate |
| 288 * @param expectedStaticType the expected static type of the parameter |
| 289 * @param actualStaticType the actual static type of the argument |
| 290 * @param expectedPropagatedType the expected propagated type of the parameter
, may be |
| 291 * `null` |
| 292 * @param actualPropagatedType the expected propagated type of the parameter,
may be `null` |
| 293 * @return `true` if and only if an hint code is generated on the passed node |
| 294 * See [HintCode.ARGUMENT_TYPE_NOT_ASSIGNABLE]. |
| 295 */ |
| 296 bool _checkForArgumentTypeNotAssignable( |
| 297 Expression expression, |
| 298 DartType expectedStaticType, |
| 299 DartType actualStaticType, |
| 300 DartType expectedPropagatedType, |
| 301 DartType actualPropagatedType, |
| 302 ErrorCode hintCode) { |
| 303 // |
| 304 // Warning case: test static type information |
| 305 // |
| 306 if (actualStaticType != null && expectedStaticType != null) { |
| 307 if (!actualStaticType.isAssignableTo(expectedStaticType)) { |
| 308 // A warning was created in the ErrorVerifier, return false, don't |
| 309 // create a hint when a warning has already been created. |
| 310 return false; |
| 311 } |
| 312 } |
| 313 // |
| 314 // Hint case: test propagated type information |
| 315 // |
| 316 // Compute the best types to use. |
| 317 DartType expectedBestType = expectedPropagatedType != null |
| 318 ? expectedPropagatedType |
| 319 : expectedStaticType; |
| 320 DartType actualBestType = |
| 321 actualPropagatedType != null ? actualPropagatedType : actualStaticType; |
| 322 if (actualBestType != null && expectedBestType != null) { |
| 323 if (!actualBestType.isAssignableTo(expectedBestType)) { |
| 324 _errorReporter.reportTypeErrorForNode( |
| 325 hintCode, expression, [actualBestType, expectedBestType]); |
| 326 return true; |
| 327 } |
| 328 } |
| 329 return false; |
| 330 } |
| 331 |
| 332 /** |
| 333 * This verifies that the passed argument can be assigned to its corresponding
parameter. |
| 334 * |
| 335 * This method corresponds to ErrorCode.checkForArgumentTypeNotAssignableForAr
gument. |
| 336 * |
| 337 * @param argument the argument to evaluate |
| 338 * @return `true` if and only if an hint code is generated on the passed node |
| 339 * See [HintCode.ARGUMENT_TYPE_NOT_ASSIGNABLE]. |
| 340 */ |
| 341 bool _checkForArgumentTypeNotAssignableForArgument(Expression argument) { |
| 342 if (argument == null) { |
| 343 return false; |
| 344 } |
| 345 ParameterElement staticParameterElement = argument.staticParameterElement; |
| 346 DartType staticParameterType = |
| 347 staticParameterElement == null ? null : staticParameterElement.type; |
| 348 ParameterElement propagatedParameterElement = |
| 349 argument.propagatedParameterElement; |
| 350 DartType propagatedParameterType = propagatedParameterElement == null |
| 351 ? null |
| 352 : propagatedParameterElement.type; |
| 353 return _checkForArgumentTypeNotAssignableWithExpectedTypes( |
| 354 argument, |
| 355 staticParameterType, |
| 356 propagatedParameterType, |
| 357 HintCode.ARGUMENT_TYPE_NOT_ASSIGNABLE); |
| 358 } |
| 359 |
| 360 /** |
| 361 * This verifies that the passed expression can be assigned to its correspondi
ng parameters. |
| 362 * |
| 363 * This method corresponds to ErrorCode.checkForArgumentTypeNotAssignableWithE
xpectedTypes. |
| 364 * |
| 365 * @param expression the expression to evaluate |
| 366 * @param expectedStaticType the expected static type |
| 367 * @param expectedPropagatedType the expected propagated type, may be `null` |
| 368 * @return `true` if and only if an hint code is generated on the passed node |
| 369 * See [HintCode.ARGUMENT_TYPE_NOT_ASSIGNABLE]. |
| 370 */ |
| 371 bool _checkForArgumentTypeNotAssignableWithExpectedTypes( |
| 372 Expression expression, |
| 373 DartType expectedStaticType, |
| 374 DartType expectedPropagatedType, |
| 375 ErrorCode errorCode) => |
| 376 _checkForArgumentTypeNotAssignable( |
| 377 expression, |
| 378 expectedStaticType, |
| 379 expression.staticType, |
| 380 expectedPropagatedType, |
| 381 expression.propagatedType, |
| 382 errorCode); |
| 383 |
| 384 /** |
| 385 * This verifies that the passed arguments can be assigned to their correspond
ing parameters. |
| 386 * |
| 387 * This method corresponds to ErrorCode.checkForArgumentTypesNotAssignableInLi
st. |
| 388 * |
| 389 * @param node the arguments to evaluate |
| 390 * @return `true` if and only if an hint code is generated on the passed node |
| 391 * See [HintCode.ARGUMENT_TYPE_NOT_ASSIGNABLE]. |
| 392 */ |
| 393 bool _checkForArgumentTypesNotAssignableInList(ArgumentList argumentList) { |
| 394 if (argumentList == null) { |
| 395 return false; |
| 396 } |
| 397 bool problemReported = false; |
| 398 for (Expression argument in argumentList.arguments) { |
| 399 if (_checkForArgumentTypeNotAssignableForArgument(argument)) { |
| 400 problemReported = true; |
| 401 } |
| 402 } |
| 403 return problemReported; |
| 404 } |
| 405 |
| 406 /** |
| 407 * Given some [Element], look at the associated metadata and report the use of
the member if |
| 408 * it is declared as deprecated. |
| 409 * |
| 410 * @param element some element to check for deprecated use of |
| 411 * @param node the node use for the location of the error |
| 412 * @return `true` if and only if a hint code is generated on the passed node |
| 413 * See [HintCode.DEPRECATED_MEMBER_USE]. |
| 414 */ |
| 415 bool _checkForDeprecatedMemberUse(Element element, AstNode node) { |
| 416 if (element != null && element.isDeprecated) { |
| 417 String displayName = element.displayName; |
| 418 if (element is ConstructorElement) { |
| 419 // TODO(jwren) We should modify ConstructorElement.getDisplayName(), |
| 420 // or have the logic centralized elsewhere, instead of doing this logic |
| 421 // here. |
| 422 ConstructorElement constructorElement = element; |
| 423 displayName = constructorElement.enclosingElement.displayName; |
| 424 if (!constructorElement.displayName.isEmpty) { |
| 425 displayName = "$displayName.${constructorElement.displayName}"; |
| 426 } |
| 427 } |
| 428 _errorReporter.reportErrorForNode( |
| 429 HintCode.DEPRECATED_MEMBER_USE, node, [displayName]); |
| 430 return true; |
| 431 } |
| 432 return false; |
| 433 } |
| 434 |
| 435 /** |
| 436 * For [SimpleIdentifier]s, only call [checkForDeprecatedMemberUse] |
| 437 * if the node is not in a declaration context. |
| 438 * |
| 439 * Also, if the identifier is a constructor name in a constructor invocation,
then calls to the |
| 440 * deprecated constructor will be caught by |
| 441 * [visitInstanceCreationExpression] and |
| 442 * [visitSuperConstructorInvocation], and can be ignored by |
| 443 * this visit method. |
| 444 * |
| 445 * @param identifier some simple identifier to check for deprecated use of |
| 446 * @return `true` if and only if a hint code is generated on the passed node |
| 447 * See [HintCode.DEPRECATED_MEMBER_USE]. |
| 448 */ |
| 449 bool _checkForDeprecatedMemberUseAtIdentifier(SimpleIdentifier identifier) { |
| 450 if (identifier.inDeclarationContext()) { |
| 451 return false; |
| 452 } |
| 453 AstNode parent = identifier.parent; |
| 454 if ((parent is ConstructorName && identical(identifier, parent.name)) || |
| 455 (parent is SuperConstructorInvocation && |
| 456 identical(identifier, parent.constructorName)) || |
| 457 parent is HideCombinator) { |
| 458 return false; |
| 459 } |
| 460 return _checkForDeprecatedMemberUse(identifier.bestElement, identifier); |
| 461 } |
| 462 |
| 463 /** |
| 464 * Check for the passed binary expression for the [HintCode.DIVISION_OPTIMIZAT
ION]. |
| 465 * |
| 466 * @param node the binary expression to check |
| 467 * @return `true` if and only if a hint code is generated on the passed node |
| 468 * See [HintCode.DIVISION_OPTIMIZATION]. |
| 469 */ |
| 470 bool _checkForDivisionOptimizationHint(BinaryExpression node) { |
| 471 // Return if the operator is not '/' |
| 472 if (node.operator.type != sc.TokenType.SLASH) { |
| 473 return false; |
| 474 } |
| 475 // Return if the '/' operator is not defined in core, or if we don't know |
| 476 // its static or propagated type |
| 477 MethodElement methodElement = node.bestElement; |
| 478 if (methodElement == null) { |
| 479 return false; |
| 480 } |
| 481 LibraryElement libraryElement = methodElement.library; |
| 482 if (libraryElement != null && !libraryElement.isDartCore) { |
| 483 return false; |
| 484 } |
| 485 // Report error if the (x/y) has toInt() invoked on it |
| 486 if (node.parent is ParenthesizedExpression) { |
| 487 ParenthesizedExpression parenthesizedExpression = |
| 488 _wrapParenthesizedExpression(node.parent as ParenthesizedExpression); |
| 489 if (parenthesizedExpression.parent is MethodInvocation) { |
| 490 MethodInvocation methodInvocation = |
| 491 parenthesizedExpression.parent as MethodInvocation; |
| 492 if (_TO_INT_METHOD_NAME == methodInvocation.methodName.name && |
| 493 methodInvocation.argumentList.arguments.isEmpty) { |
| 494 _errorReporter.reportErrorForNode( |
| 495 HintCode.DIVISION_OPTIMIZATION, methodInvocation); |
| 496 return true; |
| 497 } |
| 498 } |
| 499 } |
| 500 return false; |
| 501 } |
| 502 |
| 503 /** |
| 504 * This verifies that the passed left hand side and right hand side represent
a valid assignment. |
| 505 * |
| 506 * This method corresponds to ErrorVerifier.checkForInvalidAssignment. |
| 507 * |
| 508 * @param lhs the left hand side expression |
| 509 * @param rhs the right hand side expression |
| 510 * @return `true` if and only if an error code is generated on the passed node |
| 511 * See [HintCode.INVALID_ASSIGNMENT]. |
| 512 */ |
| 513 bool _checkForInvalidAssignment(Expression lhs, Expression rhs) { |
| 514 if (lhs == null || rhs == null) { |
| 515 return false; |
| 516 } |
| 517 VariableElement leftVariableElement = ErrorVerifier.getVariableElement(lhs); |
| 518 DartType leftType = (leftVariableElement == null) |
| 519 ? ErrorVerifier.getStaticType(lhs) |
| 520 : leftVariableElement.type; |
| 521 DartType staticRightType = ErrorVerifier.getStaticType(rhs); |
| 522 if (!staticRightType.isAssignableTo(leftType)) { |
| 523 // The warning was generated on this rhs |
| 524 return false; |
| 525 } |
| 526 // Test for, and then generate the hint |
| 527 DartType bestRightType = rhs.bestType; |
| 528 if (leftType != null && bestRightType != null) { |
| 529 if (!bestRightType.isAssignableTo(leftType)) { |
| 530 _errorReporter.reportTypeErrorForNode( |
| 531 HintCode.INVALID_ASSIGNMENT, rhs, [bestRightType, leftType]); |
| 532 return true; |
| 533 } |
| 534 } |
| 535 return false; |
| 536 } |
| 537 |
| 538 /** |
| 539 * Check that the imported library does not define a loadLibrary function. The
import has already |
| 540 * been determined to be deferred when this is called. |
| 541 * |
| 542 * @param node the import directive to evaluate |
| 543 * @param importElement the [ImportElement] retrieved from the node |
| 544 * @return `true` if and only if an error code is generated on the passed node |
| 545 * See [CompileTimeErrorCode.IMPORT_DEFERRED_LIBRARY_WITH_LOAD_FUNCTION]. |
| 546 */ |
| 547 bool _checkForLoadLibraryFunction( |
| 548 ImportDirective node, ImportElement importElement) { |
| 549 LibraryElement importedLibrary = importElement.importedLibrary; |
| 550 if (importedLibrary == null) { |
| 551 return false; |
| 552 } |
| 553 if (importedLibrary.hasLoadLibraryFunction) { |
| 554 _errorReporter.reportErrorForNode( |
| 555 HintCode.IMPORT_DEFERRED_LIBRARY_WITH_LOAD_FUNCTION, |
| 556 node, |
| 557 [importedLibrary.name]); |
| 558 return true; |
| 559 } |
| 560 return false; |
| 561 } |
| 562 |
| 563 /** |
| 564 * Generate a hint for functions or methods that have a return type, but do no
t have a return |
| 565 * statement on all branches. At the end of blocks with no return, Dart implic
itly returns |
| 566 * `null`, avoiding these implicit returns is considered a best practice. |
| 567 * |
| 568 * Note: for async functions/methods, this hint only applies when the |
| 569 * function has a return type that Future<Null> is not assignable to. |
| 570 * |
| 571 * @param node the binary expression to check |
| 572 * @param body the function body |
| 573 * @return `true` if and only if a hint code is generated on the passed node |
| 574 * See [HintCode.MISSING_RETURN]. |
| 575 */ |
| 576 bool _checkForMissingReturn(TypeName returnType, FunctionBody body) { |
| 577 // Check that the method or function has a return type, and a function body |
| 578 if (returnType == null || body == null) { |
| 579 return false; |
| 580 } |
| 581 // Check that the body is a BlockFunctionBody |
| 582 if (body is! BlockFunctionBody) { |
| 583 return false; |
| 584 } |
| 585 // Generators are never required to have a return statement. |
| 586 if (body.isGenerator) { |
| 587 return false; |
| 588 } |
| 589 // Check that the type is resolvable, and is not "void" |
| 590 DartType returnTypeType = returnType.type; |
| 591 if (returnTypeType == null || returnTypeType.isVoid) { |
| 592 return false; |
| 593 } |
| 594 // For async, give no hint if Future<Null> is assignable to the return |
| 595 // type. |
| 596 if (body.isAsynchronous && _futureNullType.isAssignableTo(returnTypeType)) { |
| 597 return false; |
| 598 } |
| 599 // Check the block for a return statement, if not, create the hint |
| 600 BlockFunctionBody blockFunctionBody = body as BlockFunctionBody; |
| 601 if (!ExitDetector.exits(blockFunctionBody)) { |
| 602 _errorReporter.reportErrorForNode( |
| 603 HintCode.MISSING_RETURN, returnType, [returnTypeType.displayName]); |
| 604 return true; |
| 605 } |
| 606 return false; |
| 607 } |
| 608 |
| 609 /** |
| 610 * Check for the passed class declaration for the |
| 611 * [HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE] hint code. |
| 612 * |
| 613 * @param node the class declaration to check |
| 614 * @return `true` if and only if a hint code is generated on the passed node |
| 615 * See [HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE]. |
| 616 */ |
| 617 // bool _checkForOverrideEqualsButNotHashCode(ClassDeclaration node) { |
| 618 // ClassElement classElement = node.element; |
| 619 // if (classElement == null) { |
| 620 // return false; |
| 621 // } |
| 622 // MethodElement equalsOperatorMethodElement = |
| 623 // classElement.getMethod(sc.TokenType.EQ_EQ.lexeme); |
| 624 // if (equalsOperatorMethodElement != null) { |
| 625 // PropertyAccessorElement hashCodeElement = |
| 626 // classElement.getGetter(_HASHCODE_GETTER_NAME); |
| 627 // if (hashCodeElement == null) { |
| 628 // _errorReporter.reportErrorForNode( |
| 629 // HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE, |
| 630 // node.name, |
| 631 // [classElement.displayName]); |
| 632 // return true; |
| 633 // } |
| 634 // } |
| 635 // return false; |
| 636 // } |
| 637 |
| 638 /** |
| 639 * Check for the passed as expression for the [HintCode.UNNECESSARY_CAST] hint
code. |
| 640 * |
| 641 * @param node the as expression to check |
| 642 * @return `true` if and only if a hint code is generated on the passed node |
| 643 * See [HintCode.UNNECESSARY_CAST]. |
| 644 */ |
| 645 bool _checkForUnnecessaryCast(AsExpression node) { |
| 646 // TODO(jwren) After dartbug.com/13732, revisit this, we should be able to |
| 647 // remove the (x is! TypeParameterType) checks. |
| 648 AstNode parent = node.parent; |
| 649 if (parent is ConditionalExpression && |
| 650 (node == parent.thenExpression || node == parent.elseExpression)) { |
| 651 Expression thenExpression = parent.thenExpression; |
| 652 DartType thenType; |
| 653 if (thenExpression is AsExpression) { |
| 654 thenType = thenExpression.expression.staticType; |
| 655 } else { |
| 656 thenType = thenExpression.staticType; |
| 657 } |
| 658 Expression elseExpression = parent.elseExpression; |
| 659 DartType elseType; |
| 660 if (elseExpression is AsExpression) { |
| 661 elseType = elseExpression.expression.staticType; |
| 662 } else { |
| 663 elseType = elseExpression.staticType; |
| 664 } |
| 665 if (thenType != null && |
| 666 elseType != null && |
| 667 !thenType.isDynamic && |
| 668 !elseType.isDynamic && |
| 669 !thenType.isMoreSpecificThan(elseType) && |
| 670 !elseType.isMoreSpecificThan(thenType)) { |
| 671 return false; |
| 672 } |
| 673 } |
| 674 DartType lhsType = node.expression.staticType; |
| 675 DartType rhsType = node.type.type; |
| 676 if (lhsType != null && |
| 677 rhsType != null && |
| 678 !lhsType.isDynamic && |
| 679 !rhsType.isDynamic && |
| 680 lhsType.isMoreSpecificThan(rhsType)) { |
| 681 _errorReporter.reportErrorForNode(HintCode.UNNECESSARY_CAST, node); |
| 682 return true; |
| 683 } |
| 684 return false; |
| 685 } |
| 686 |
| 687 /** |
| 688 * Check for situations where the result of a method or function is used, when
it returns 'void'. |
| 689 * |
| 690 * TODO(jwren) Many other situations of use could be covered. We currently cov
er the cases var x = |
| 691 * m() and x = m(), but we could also cover cases such as m().x, m()[k], a + m
(), f(m()), return |
| 692 * m(). |
| 693 * |
| 694 * @param node expression on the RHS of some assignment |
| 695 * @return `true` if and only if a hint code is generated on the passed node |
| 696 * See [HintCode.USE_OF_VOID_RESULT]. |
| 697 */ |
| 698 bool _checkForUseOfVoidResult(Expression expression) { |
| 699 if (expression == null || expression is! MethodInvocation) { |
| 700 return false; |
| 701 } |
| 702 MethodInvocation methodInvocation = expression as MethodInvocation; |
| 703 if (identical(methodInvocation.staticType, VoidTypeImpl.instance)) { |
| 704 SimpleIdentifier methodName = methodInvocation.methodName; |
| 705 _errorReporter.reportErrorForNode( |
| 706 HintCode.USE_OF_VOID_RESULT, methodName, [methodName.name]); |
| 707 return true; |
| 708 } |
| 709 return false; |
| 710 } |
| 711 |
| 712 /** |
| 713 * Given a parenthesized expression, this returns the parent (or recursively g
rand-parent) of the |
| 714 * expression that is a parenthesized expression, but whose parent is not a pa
renthesized |
| 715 * expression. |
| 716 * |
| 717 * For example given the code `(((e)))`: `(e) -> (((e)))`. |
| 718 * |
| 719 * @param parenthesizedExpression some expression whose parent is a parenthesi
zed expression |
| 720 * @return the first parent or grand-parent that is a parenthesized expression
, that does not have |
| 721 * a parenthesized expression parent |
| 722 */ |
| 723 static ParenthesizedExpression _wrapParenthesizedExpression( |
| 724 ParenthesizedExpression parenthesizedExpression) { |
| 725 if (parenthesizedExpression.parent is ParenthesizedExpression) { |
| 726 return _wrapParenthesizedExpression( |
| 727 parenthesizedExpression.parent as ParenthesizedExpression); |
| 728 } |
| 729 return parenthesizedExpression; |
| 730 } |
| 731 } |
| 732 |
| 733 /** |
| 734 * Instances of the class `ClassScope` implement the scope defined by a class. |
| 735 */ |
| 736 class ClassScope extends EnclosedScope { |
| 737 /** |
| 738 * Initialize a newly created scope enclosed within another scope. |
| 739 * |
| 740 * @param enclosingScope the scope in which this scope is lexically enclosed |
| 741 * @param typeElement the element representing the type represented by this sc
ope |
| 742 */ |
| 743 ClassScope(Scope enclosingScope, ClassElement typeElement) |
| 744 : super(enclosingScope) { |
| 745 if (typeElement == null) { |
| 746 throw new IllegalArgumentException("class element cannot be null"); |
| 747 } |
| 748 _defineMembers(typeElement); |
| 749 } |
| 750 |
| 751 @override |
| 752 AnalysisError getErrorForDuplicate(Element existing, Element duplicate) { |
| 753 if (existing is PropertyAccessorElement && duplicate is MethodElement) { |
| 754 if (existing.nameOffset < duplicate.nameOffset) { |
| 755 return new AnalysisError( |
| 756 duplicate.source, |
| 757 duplicate.nameOffset, |
| 758 duplicate.displayName.length, |
| 759 CompileTimeErrorCode.METHOD_AND_GETTER_WITH_SAME_NAME, |
| 760 [existing.displayName]); |
| 761 } else { |
| 762 return new AnalysisError( |
| 763 existing.source, |
| 764 existing.nameOffset, |
| 765 existing.displayName.length, |
| 766 CompileTimeErrorCode.GETTER_AND_METHOD_WITH_SAME_NAME, |
| 767 [existing.displayName]); |
| 768 } |
| 769 } |
| 770 return super.getErrorForDuplicate(existing, duplicate); |
| 771 } |
| 772 |
| 773 /** |
| 774 * Define the instance members defined by the class. |
| 775 * |
| 776 * @param typeElement the element representing the type represented by this sc
ope |
| 777 */ |
| 778 void _defineMembers(ClassElement typeElement) { |
| 779 for (PropertyAccessorElement accessor in typeElement.accessors) { |
| 780 define(accessor); |
| 781 } |
| 782 for (MethodElement method in typeElement.methods) { |
| 783 define(method); |
| 784 } |
| 785 } |
| 786 } |
| 787 |
| 788 /** |
| 789 * A `CompilationUnitBuilder` builds an element model for a single compilation |
| 790 * unit. |
| 791 */ |
| 792 class CompilationUnitBuilder { |
| 793 /** |
| 794 * Build the compilation unit element for the given [source] based on the |
| 795 * compilation [unit] associated with the source. Throw an AnalysisException |
| 796 * if the element could not be built. [librarySource] is the source for the |
| 797 * containing library. |
| 798 */ |
| 799 CompilationUnitElementImpl buildCompilationUnit( |
| 800 Source source, CompilationUnit unit, Source librarySource) { |
| 801 return PerformanceStatistics.resolve.makeCurrentWhile(() { |
| 802 if (unit == null) { |
| 803 return null; |
| 804 } |
| 805 ElementHolder holder = new ElementHolder(); |
| 806 ElementBuilder builder = new ElementBuilder(holder); |
| 807 unit.accept(builder); |
| 808 CompilationUnitElementImpl element = |
| 809 new CompilationUnitElementImpl(source.shortName); |
| 810 element.accessors = holder.accessors; |
| 811 element.enums = holder.enums; |
| 812 element.functions = holder.functions; |
| 813 element.source = source; |
| 814 element.librarySource = librarySource; |
| 815 element.typeAliases = holder.typeAliases; |
| 816 element.types = holder.types; |
| 817 element.topLevelVariables = holder.topLevelVariables; |
| 818 unit.element = element; |
| 819 holder.validate(); |
| 820 return element; |
| 821 }); |
| 822 } |
| 823 } |
| 824 |
| 825 /** |
| 826 * Instances of the class `ConstantVerifier` traverse an AST structure looking f
or additional |
| 827 * errors and warnings not covered by the parser and resolver. In particular, it
looks for errors |
| 828 * and warnings related to constant expressions. |
| 829 */ |
| 830 class ConstantVerifier extends RecursiveAstVisitor<Object> { |
| 831 /** |
| 832 * The error reporter by which errors will be reported. |
| 833 */ |
| 834 final ErrorReporter _errorReporter; |
| 835 |
| 836 /** |
| 837 * The type provider used to access the known types. |
| 838 */ |
| 839 final TypeProvider _typeProvider; |
| 840 |
| 841 /** |
| 842 * The set of variables declared using '-D' on the command line. |
| 843 */ |
| 844 final DeclaredVariables declaredVariables; |
| 845 |
| 846 /** |
| 847 * The type representing the type 'bool'. |
| 848 */ |
| 849 InterfaceType _boolType; |
| 850 |
| 851 /** |
| 852 * The type representing the type 'int'. |
| 853 */ |
| 854 InterfaceType _intType; |
| 855 |
| 856 /** |
| 857 * The type representing the type 'num'. |
| 858 */ |
| 859 InterfaceType _numType; |
| 860 |
| 861 /** |
| 862 * The type representing the type 'string'. |
| 863 */ |
| 864 InterfaceType _stringType; |
| 865 |
| 866 /** |
| 867 * The current library that is being analyzed. |
| 868 */ |
| 869 final LibraryElement _currentLibrary; |
| 870 |
| 871 /** |
| 872 * Initialize a newly created constant verifier. |
| 873 * |
| 874 * @param errorReporter the error reporter by which errors will be reported |
| 875 */ |
| 876 ConstantVerifier(this._errorReporter, this._currentLibrary, |
| 877 this._typeProvider, this.declaredVariables) { |
| 878 this._boolType = _typeProvider.boolType; |
| 879 this._intType = _typeProvider.intType; |
| 880 this._numType = _typeProvider.numType; |
| 881 this._stringType = _typeProvider.stringType; |
| 882 } |
| 883 |
| 884 @override |
| 885 Object visitAnnotation(Annotation node) { |
| 886 super.visitAnnotation(node); |
| 887 // check annotation creation |
| 888 Element element = node.element; |
| 889 if (element is ConstructorElement) { |
| 890 ConstructorElement constructorElement = element; |
| 891 // should 'const' constructor |
| 892 if (!constructorElement.isConst) { |
| 893 _errorReporter.reportErrorForNode( |
| 894 CompileTimeErrorCode.NON_CONSTANT_ANNOTATION_CONSTRUCTOR, node); |
| 895 return null; |
| 896 } |
| 897 // should have arguments |
| 898 ArgumentList argumentList = node.arguments; |
| 899 if (argumentList == null) { |
| 900 _errorReporter.reportErrorForNode( |
| 901 CompileTimeErrorCode.NO_ANNOTATION_CONSTRUCTOR_ARGUMENTS, node); |
| 902 return null; |
| 903 } |
| 904 // arguments should be constants |
| 905 _validateConstantArguments(argumentList); |
| 906 } |
| 907 return null; |
| 908 } |
| 909 |
| 910 @override |
| 911 Object visitConstructorDeclaration(ConstructorDeclaration node) { |
| 912 if (node.constKeyword != null) { |
| 913 _validateConstructorInitializers(node); |
| 914 _validateFieldInitializers(node.parent as ClassDeclaration, node); |
| 915 } |
| 916 _validateDefaultValues(node.parameters); |
| 917 return super.visitConstructorDeclaration(node); |
| 918 } |
| 919 |
| 920 @override |
| 921 Object visitFunctionExpression(FunctionExpression node) { |
| 922 super.visitFunctionExpression(node); |
| 923 _validateDefaultValues(node.parameters); |
| 924 return null; |
| 925 } |
| 926 |
| 927 @override |
| 928 Object visitInstanceCreationExpression(InstanceCreationExpression node) { |
| 929 if (node.isConst) { |
| 930 // We need to evaluate the constant to see if any errors occur during its |
| 931 // evaluation. |
| 932 ConstructorElement constructor = node.staticElement; |
| 933 if (constructor != null) { |
| 934 ConstantEvaluationEngine evaluationEngine = |
| 935 new ConstantEvaluationEngine(_typeProvider, declaredVariables); |
| 936 ConstantVisitor constantVisitor = |
| 937 new ConstantVisitor(evaluationEngine, _errorReporter); |
| 938 evaluationEngine.evaluateConstructorCall( |
| 939 node, |
| 940 node.argumentList.arguments, |
| 941 constructor, |
| 942 constantVisitor, |
| 943 _errorReporter); |
| 944 } |
| 945 } |
| 946 _validateInstanceCreationArguments(node); |
| 947 return super.visitInstanceCreationExpression(node); |
| 948 } |
| 949 |
| 950 @override |
| 951 Object visitListLiteral(ListLiteral node) { |
| 952 super.visitListLiteral(node); |
| 953 if (node.constKeyword != null) { |
| 954 DartObjectImpl result; |
| 955 for (Expression element in node.elements) { |
| 956 result = |
| 957 _validate(element, CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT); |
| 958 if (result != null) { |
| 959 _reportErrorIfFromDeferredLibrary(element, |
| 960 CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT_FROM_DEFERRED_LIBRA
RY); |
| 961 } |
| 962 } |
| 963 } |
| 964 return null; |
| 965 } |
| 966 |
| 967 @override |
| 968 Object visitMapLiteral(MapLiteral node) { |
| 969 super.visitMapLiteral(node); |
| 970 bool isConst = node.constKeyword != null; |
| 971 bool reportEqualKeys = true; |
| 972 HashSet<DartObject> keys = new HashSet<DartObject>(); |
| 973 List<Expression> invalidKeys = new List<Expression>(); |
| 974 for (MapLiteralEntry entry in node.entries) { |
| 975 Expression key = entry.key; |
| 976 if (isConst) { |
| 977 DartObjectImpl keyResult = |
| 978 _validate(key, CompileTimeErrorCode.NON_CONSTANT_MAP_KEY); |
| 979 Expression valueExpression = entry.value; |
| 980 DartObjectImpl valueResult = _validate( |
| 981 valueExpression, CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE); |
| 982 if (valueResult != null) { |
| 983 _reportErrorIfFromDeferredLibrary(valueExpression, |
| 984 CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE_FROM_DEFERRED_LIBRARY)
; |
| 985 } |
| 986 if (keyResult != null) { |
| 987 _reportErrorIfFromDeferredLibrary(key, |
| 988 CompileTimeErrorCode.NON_CONSTANT_MAP_KEY_FROM_DEFERRED_LIBRARY); |
| 989 if (keys.contains(keyResult)) { |
| 990 invalidKeys.add(key); |
| 991 } else { |
| 992 keys.add(keyResult); |
| 993 } |
| 994 DartType type = keyResult.type; |
| 995 if (_implementsEqualsWhenNotAllowed(type)) { |
| 996 _errorReporter.reportErrorForNode( |
| 997 CompileTimeErrorCode.CONST_MAP_KEY_EXPRESSION_TYPE_IMPLEMENTS_EQ
UALS, |
| 998 key, |
| 999 [type.displayName]); |
| 1000 } |
| 1001 } |
| 1002 } else { |
| 1003 // Note: we throw the errors away because this isn't actually a const. |
| 1004 AnalysisErrorListener errorListener = |
| 1005 AnalysisErrorListener.NULL_LISTENER; |
| 1006 ErrorReporter subErrorReporter = |
| 1007 new ErrorReporter(errorListener, _errorReporter.source); |
| 1008 DartObjectImpl result = key.accept(new ConstantVisitor( |
| 1009 new ConstantEvaluationEngine(_typeProvider, declaredVariables), |
| 1010 subErrorReporter)); |
| 1011 if (result != null) { |
| 1012 if (keys.contains(result)) { |
| 1013 invalidKeys.add(key); |
| 1014 } else { |
| 1015 keys.add(result); |
| 1016 } |
| 1017 } else { |
| 1018 reportEqualKeys = false; |
| 1019 } |
| 1020 } |
| 1021 } |
| 1022 if (reportEqualKeys) { |
| 1023 for (Expression key in invalidKeys) { |
| 1024 _errorReporter.reportErrorForNode( |
| 1025 StaticWarningCode.EQUAL_KEYS_IN_MAP, key); |
| 1026 } |
| 1027 } |
| 1028 return null; |
| 1029 } |
| 1030 |
| 1031 @override |
| 1032 Object visitMethodDeclaration(MethodDeclaration node) { |
| 1033 super.visitMethodDeclaration(node); |
| 1034 _validateDefaultValues(node.parameters); |
| 1035 return null; |
| 1036 } |
| 1037 |
| 1038 @override |
| 1039 Object visitSwitchStatement(SwitchStatement node) { |
| 1040 // TODO(paulberry): to minimize error messages, it would be nice to |
| 1041 // compare all types with the most popular type rather than the first |
| 1042 // type. |
| 1043 NodeList<SwitchMember> switchMembers = node.members; |
| 1044 bool foundError = false; |
| 1045 DartType firstType = null; |
| 1046 for (SwitchMember switchMember in switchMembers) { |
| 1047 if (switchMember is SwitchCase) { |
| 1048 SwitchCase switchCase = switchMember; |
| 1049 Expression expression = switchCase.expression; |
| 1050 DartObjectImpl caseResult = _validate( |
| 1051 expression, CompileTimeErrorCode.NON_CONSTANT_CASE_EXPRESSION); |
| 1052 if (caseResult != null) { |
| 1053 _reportErrorIfFromDeferredLibrary(expression, |
| 1054 CompileTimeErrorCode.NON_CONSTANT_CASE_EXPRESSION_FROM_DEFERRED_LI
BRARY); |
| 1055 DartObject value = caseResult; |
| 1056 if (firstType == null) { |
| 1057 firstType = value.type; |
| 1058 } else { |
| 1059 DartType nType = value.type; |
| 1060 if (firstType != nType) { |
| 1061 _errorReporter.reportErrorForNode( |
| 1062 CompileTimeErrorCode.INCONSISTENT_CASE_EXPRESSION_TYPES, |
| 1063 expression, |
| 1064 [expression.toSource(), firstType.displayName]); |
| 1065 foundError = true; |
| 1066 } |
| 1067 } |
| 1068 } |
| 1069 } |
| 1070 } |
| 1071 if (!foundError) { |
| 1072 _checkForCaseExpressionTypeImplementsEquals(node, firstType); |
| 1073 } |
| 1074 return super.visitSwitchStatement(node); |
| 1075 } |
| 1076 |
| 1077 @override |
| 1078 Object visitVariableDeclaration(VariableDeclaration node) { |
| 1079 super.visitVariableDeclaration(node); |
| 1080 Expression initializer = node.initializer; |
| 1081 if (initializer != null && (node.isConst || node.isFinal)) { |
| 1082 VariableElementImpl element = node.element as VariableElementImpl; |
| 1083 EvaluationResultImpl result = element.evaluationResult; |
| 1084 if (result == null) { |
| 1085 // Variables marked "const" should have had their values computed by |
| 1086 // ConstantValueComputer. Other variables will only have had their |
| 1087 // values computed if the value was needed (e.g. final variables in a |
| 1088 // class containing const constructors). |
| 1089 assert(!node.isConst); |
| 1090 return null; |
| 1091 } |
| 1092 _reportErrors(result.errors, |
| 1093 CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE); |
| 1094 _reportErrorIfFromDeferredLibrary(initializer, |
| 1095 CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE_FROM_DE
FERRED_LIBRARY); |
| 1096 } |
| 1097 return null; |
| 1098 } |
| 1099 |
| 1100 /** |
| 1101 * This verifies that the passed switch statement does not have a case express
ion with the |
| 1102 * operator '==' overridden. |
| 1103 * |
| 1104 * @param node the switch statement to evaluate |
| 1105 * @param type the common type of all 'case' expressions |
| 1106 * @return `true` if and only if an error code is generated on the passed node |
| 1107 * See [CompileTimeErrorCode.CASE_EXPRESSION_TYPE_IMPLEMENTS_EQUALS]. |
| 1108 */ |
| 1109 bool _checkForCaseExpressionTypeImplementsEquals( |
| 1110 SwitchStatement node, DartType type) { |
| 1111 if (!_implementsEqualsWhenNotAllowed(type)) { |
| 1112 return false; |
| 1113 } |
| 1114 // report error |
| 1115 _errorReporter.reportErrorForToken( |
| 1116 CompileTimeErrorCode.CASE_EXPRESSION_TYPE_IMPLEMENTS_EQUALS, |
| 1117 node.switchKeyword, |
| 1118 [type.displayName]); |
| 1119 return true; |
| 1120 } |
| 1121 |
| 1122 /** |
| 1123 * @return `true` if given [Type] implements operator <i>==</i>, and it is not |
| 1124 * <i>int</i> or <i>String</i>. |
| 1125 */ |
| 1126 bool _implementsEqualsWhenNotAllowed(DartType type) { |
| 1127 // ignore int or String |
| 1128 if (type == null || type == _intType || type == _typeProvider.stringType) { |
| 1129 return false; |
| 1130 } else if (type == _typeProvider.doubleType) { |
| 1131 return true; |
| 1132 } |
| 1133 // prepare ClassElement |
| 1134 Element element = type.element; |
| 1135 if (element is! ClassElement) { |
| 1136 return false; |
| 1137 } |
| 1138 ClassElement classElement = element as ClassElement; |
| 1139 // lookup for == |
| 1140 MethodElement method = |
| 1141 classElement.lookUpConcreteMethod("==", _currentLibrary); |
| 1142 if (method == null || method.enclosingElement.type.isObject) { |
| 1143 return false; |
| 1144 } |
| 1145 // there is == that we don't like |
| 1146 return true; |
| 1147 } |
| 1148 |
| 1149 /** |
| 1150 * Given some computed [Expression], this method generates the passed [ErrorCo
de] on |
| 1151 * the node if its' value consists of information from a deferred library. |
| 1152 * |
| 1153 * @param expression the expression to be tested for a deferred library refere
nce |
| 1154 * @param errorCode the error code to be used if the expression is or consists
of a reference to a |
| 1155 * deferred library |
| 1156 */ |
| 1157 void _reportErrorIfFromDeferredLibrary( |
| 1158 Expression expression, ErrorCode errorCode) { |
| 1159 DeferredLibraryReferenceDetector referenceDetector = |
| 1160 new DeferredLibraryReferenceDetector(); |
| 1161 expression.accept(referenceDetector); |
| 1162 if (referenceDetector.result) { |
| 1163 _errorReporter.reportErrorForNode(errorCode, expression); |
| 1164 } |
| 1165 } |
| 1166 |
| 1167 /** |
| 1168 * Report any errors in the given list. Except for special cases, use the give
n error code rather |
| 1169 * than the one reported in the error. |
| 1170 * |
| 1171 * @param errors the errors that need to be reported |
| 1172 * @param errorCode the error code to be used |
| 1173 */ |
| 1174 void _reportErrors(List<AnalysisError> errors, ErrorCode errorCode) { |
| 1175 for (AnalysisError data in errors) { |
| 1176 ErrorCode dataErrorCode = data.errorCode; |
| 1177 if (identical(dataErrorCode, |
| 1178 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION) || |
| 1179 identical( |
| 1180 dataErrorCode, CompileTimeErrorCode.CONST_EVAL_THROWS_IDBZE) || |
| 1181 identical(dataErrorCode, |
| 1182 CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING) || |
| 1183 identical(dataErrorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL) || |
| 1184 identical(dataErrorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_INT) || |
| 1185 identical(dataErrorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_NUM) || |
| 1186 identical(dataErrorCode, |
| 1187 CompileTimeErrorCode.RECURSIVE_COMPILE_TIME_CONSTANT) || |
| 1188 identical(dataErrorCode, |
| 1189 CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_FIELD_TYPE_MISMA
TCH) || |
| 1190 identical(dataErrorCode, |
| 1191 CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMA
TCH) || |
| 1192 identical(dataErrorCode, |
| 1193 CheckedModeCompileTimeErrorCode.VARIABLE_TYPE_MISMATCH)) { |
| 1194 _errorReporter.reportError(data); |
| 1195 } else if (errorCode != null) { |
| 1196 _errorReporter.reportError(new AnalysisError( |
| 1197 data.source, data.offset, data.length, errorCode)); |
| 1198 } |
| 1199 } |
| 1200 } |
| 1201 |
| 1202 /** |
| 1203 * Validate that the given expression is a compile time constant. Return the v
alue of the compile |
| 1204 * time constant, or `null` if the expression is not a compile time constant. |
| 1205 * |
| 1206 * @param expression the expression to be validated |
| 1207 * @param errorCode the error code to be used if the expression is not a compi
le time constant |
| 1208 * @return the value of the compile time constant |
| 1209 */ |
| 1210 DartObjectImpl _validate(Expression expression, ErrorCode errorCode) { |
| 1211 RecordingErrorListener errorListener = new RecordingErrorListener(); |
| 1212 ErrorReporter subErrorReporter = |
| 1213 new ErrorReporter(errorListener, _errorReporter.source); |
| 1214 DartObjectImpl result = expression.accept(new ConstantVisitor( |
| 1215 new ConstantEvaluationEngine(_typeProvider, declaredVariables), |
| 1216 subErrorReporter)); |
| 1217 _reportErrors(errorListener.errors, errorCode); |
| 1218 return result; |
| 1219 } |
| 1220 |
| 1221 /** |
| 1222 * Validate that if the passed arguments are constant expressions. |
| 1223 * |
| 1224 * @param argumentList the argument list to evaluate |
| 1225 */ |
| 1226 void _validateConstantArguments(ArgumentList argumentList) { |
| 1227 for (Expression argument in argumentList.arguments) { |
| 1228 if (argument is NamedExpression) { |
| 1229 argument = (argument as NamedExpression).expression; |
| 1230 } |
| 1231 _validate( |
| 1232 argument, CompileTimeErrorCode.CONST_WITH_NON_CONSTANT_ARGUMENT); |
| 1233 } |
| 1234 } |
| 1235 |
| 1236 /** |
| 1237 * Validates that the expressions of the given initializers (of a constant con
structor) are all |
| 1238 * compile time constants. |
| 1239 * |
| 1240 * @param constructor the constant constructor declaration to validate |
| 1241 */ |
| 1242 void _validateConstructorInitializers(ConstructorDeclaration constructor) { |
| 1243 List<ParameterElement> parameterElements = |
| 1244 constructor.parameters.parameterElements; |
| 1245 NodeList<ConstructorInitializer> initializers = constructor.initializers; |
| 1246 for (ConstructorInitializer initializer in initializers) { |
| 1247 if (initializer is ConstructorFieldInitializer) { |
| 1248 ConstructorFieldInitializer fieldInitializer = initializer; |
| 1249 _validateInitializerExpression( |
| 1250 parameterElements, fieldInitializer.expression); |
| 1251 } |
| 1252 if (initializer is RedirectingConstructorInvocation) { |
| 1253 RedirectingConstructorInvocation invocation = initializer; |
| 1254 _validateInitializerInvocationArguments( |
| 1255 parameterElements, invocation.argumentList); |
| 1256 } |
| 1257 if (initializer is SuperConstructorInvocation) { |
| 1258 SuperConstructorInvocation invocation = initializer; |
| 1259 _validateInitializerInvocationArguments( |
| 1260 parameterElements, invocation.argumentList); |
| 1261 } |
| 1262 } |
| 1263 } |
| 1264 |
| 1265 /** |
| 1266 * Validate that the default value associated with each of the parameters in t
he given list is a |
| 1267 * compile time constant. |
| 1268 * |
| 1269 * @param parameters the list of parameters to be validated |
| 1270 */ |
| 1271 void _validateDefaultValues(FormalParameterList parameters) { |
| 1272 if (parameters == null) { |
| 1273 return; |
| 1274 } |
| 1275 for (FormalParameter parameter in parameters.parameters) { |
| 1276 if (parameter is DefaultFormalParameter) { |
| 1277 DefaultFormalParameter defaultParameter = parameter; |
| 1278 Expression defaultValue = defaultParameter.defaultValue; |
| 1279 DartObjectImpl result; |
| 1280 if (defaultValue == null) { |
| 1281 result = |
| 1282 new DartObjectImpl(_typeProvider.nullType, NullState.NULL_STATE); |
| 1283 } else { |
| 1284 result = _validate( |
| 1285 defaultValue, CompileTimeErrorCode.NON_CONSTANT_DEFAULT_VALUE); |
| 1286 if (result != null) { |
| 1287 _reportErrorIfFromDeferredLibrary(defaultValue, |
| 1288 CompileTimeErrorCode.NON_CONSTANT_DEFAULT_VALUE_FROM_DEFERRED_LI
BRARY); |
| 1289 } |
| 1290 } |
| 1291 VariableElementImpl element = parameter.element as VariableElementImpl; |
| 1292 element.evaluationResult = new EvaluationResultImpl(result); |
| 1293 } |
| 1294 } |
| 1295 } |
| 1296 |
| 1297 /** |
| 1298 * Validates that the expressions of any field initializers in the class decla
ration are all |
| 1299 * compile time constants. Since this is only required if the class has a cons
tant constructor, |
| 1300 * the error is reported at the constructor site. |
| 1301 * |
| 1302 * @param classDeclaration the class which should be validated |
| 1303 * @param errorSite the site at which errors should be reported. |
| 1304 */ |
| 1305 void _validateFieldInitializers( |
| 1306 ClassDeclaration classDeclaration, ConstructorDeclaration errorSite) { |
| 1307 NodeList<ClassMember> members = classDeclaration.members; |
| 1308 for (ClassMember member in members) { |
| 1309 if (member is FieldDeclaration) { |
| 1310 FieldDeclaration fieldDeclaration = member; |
| 1311 if (!fieldDeclaration.isStatic) { |
| 1312 for (VariableDeclaration variableDeclaration |
| 1313 in fieldDeclaration.fields.variables) { |
| 1314 Expression initializer = variableDeclaration.initializer; |
| 1315 if (initializer != null) { |
| 1316 // Ignore any errors produced during validation--if the constant |
| 1317 // can't be eavluated we'll just report a single error. |
| 1318 AnalysisErrorListener errorListener = |
| 1319 AnalysisErrorListener.NULL_LISTENER; |
| 1320 ErrorReporter subErrorReporter = |
| 1321 new ErrorReporter(errorListener, _errorReporter.source); |
| 1322 DartObjectImpl result = initializer.accept(new ConstantVisitor( |
| 1323 new ConstantEvaluationEngine( |
| 1324 _typeProvider, declaredVariables), |
| 1325 subErrorReporter)); |
| 1326 if (result == null) { |
| 1327 _errorReporter.reportErrorForNode( |
| 1328 CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_FIELD_INITIALIZE
D_BY_NON_CONST, |
| 1329 errorSite, |
| 1330 [variableDeclaration.name.name]); |
| 1331 } |
| 1332 } |
| 1333 } |
| 1334 } |
| 1335 } |
| 1336 } |
| 1337 } |
| 1338 |
| 1339 /** |
| 1340 * Validates that the given expression is a compile time constant. |
| 1341 * |
| 1342 * @param parameterElements the elements of parameters of constant constructor
, they are |
| 1343 * considered as a valid potentially constant expressions |
| 1344 * @param expression the expression to validate |
| 1345 */ |
| 1346 void _validateInitializerExpression( |
| 1347 List<ParameterElement> parameterElements, Expression expression) { |
| 1348 RecordingErrorListener errorListener = new RecordingErrorListener(); |
| 1349 ErrorReporter subErrorReporter = |
| 1350 new ErrorReporter(errorListener, _errorReporter.source); |
| 1351 DartObjectImpl result = expression.accept( |
| 1352 new _ConstantVerifier_validateInitializerExpression(_typeProvider, |
| 1353 subErrorReporter, this, parameterElements, declaredVariables)); |
| 1354 _reportErrors(errorListener.errors, |
| 1355 CompileTimeErrorCode.NON_CONSTANT_VALUE_IN_INITIALIZER); |
| 1356 if (result != null) { |
| 1357 _reportErrorIfFromDeferredLibrary(expression, |
| 1358 CompileTimeErrorCode.NON_CONSTANT_VALUE_IN_INITIALIZER_FROM_DEFERRED_L
IBRARY); |
| 1359 } |
| 1360 } |
| 1361 |
| 1362 /** |
| 1363 * Validates that all of the arguments of a constructor initializer are compil
e time constants. |
| 1364 * |
| 1365 * @param parameterElements the elements of parameters of constant constructor
, they are |
| 1366 * considered as a valid potentially constant expressions |
| 1367 * @param argumentList the argument list to validate |
| 1368 */ |
| 1369 void _validateInitializerInvocationArguments( |
| 1370 List<ParameterElement> parameterElements, ArgumentList argumentList) { |
| 1371 if (argumentList == null) { |
| 1372 return; |
| 1373 } |
| 1374 for (Expression argument in argumentList.arguments) { |
| 1375 _validateInitializerExpression(parameterElements, argument); |
| 1376 } |
| 1377 } |
| 1378 |
| 1379 /** |
| 1380 * Validate that if the passed instance creation is 'const' then all its argum
ents are constant |
| 1381 * expressions. |
| 1382 * |
| 1383 * @param node the instance creation evaluate |
| 1384 */ |
| 1385 void _validateInstanceCreationArguments(InstanceCreationExpression node) { |
| 1386 if (!node.isConst) { |
| 1387 return; |
| 1388 } |
| 1389 ArgumentList argumentList = node.argumentList; |
| 1390 if (argumentList == null) { |
| 1391 return; |
| 1392 } |
| 1393 _validateConstantArguments(argumentList); |
| 1394 } |
| 1395 } |
| 1396 |
| 1397 /** |
| 1398 * Instances of the class `Dart2JSVerifier` traverse an AST structure looking fo
r hints for |
| 1399 * code that will be compiled to JS, such as [HintCode.IS_DOUBLE]. |
| 1400 */ |
| 1401 class Dart2JSVerifier extends RecursiveAstVisitor<Object> { |
| 1402 /** |
| 1403 * The name of the `double` type. |
| 1404 */ |
| 1405 static String _DOUBLE_TYPE_NAME = "double"; |
| 1406 |
| 1407 /** |
| 1408 * The error reporter by which errors will be reported. |
| 1409 */ |
| 1410 final ErrorReporter _errorReporter; |
| 1411 |
| 1412 /** |
| 1413 * Create a new instance of the [Dart2JSVerifier]. |
| 1414 * |
| 1415 * @param errorReporter the error reporter |
| 1416 */ |
| 1417 Dart2JSVerifier(this._errorReporter); |
| 1418 |
| 1419 @override |
| 1420 Object visitIsExpression(IsExpression node) { |
| 1421 _checkForIsDoubleHints(node); |
| 1422 return super.visitIsExpression(node); |
| 1423 } |
| 1424 |
| 1425 /** |
| 1426 * Check for instances of `x is double`, `x is int`, `x is! double` and |
| 1427 * `x is! int`. |
| 1428 * |
| 1429 * @param node the is expression to check |
| 1430 * @return `true` if and only if a hint code is generated on the passed node |
| 1431 * See [HintCode.IS_DOUBLE], |
| 1432 * [HintCode.IS_INT], |
| 1433 * [HintCode.IS_NOT_DOUBLE], and |
| 1434 * [HintCode.IS_NOT_INT]. |
| 1435 */ |
| 1436 bool _checkForIsDoubleHints(IsExpression node) { |
| 1437 TypeName typeName = node.type; |
| 1438 DartType type = typeName.type; |
| 1439 if (type != null && type.element != null) { |
| 1440 Element element = type.element; |
| 1441 String typeNameStr = element.name; |
| 1442 LibraryElement libraryElement = element.library; |
| 1443 // if (typeNameStr.equals(INT_TYPE_NAME) && libraryElement != null |
| 1444 // && libraryElement.isDartCore()) { |
| 1445 // if (node.getNotOperator() == null) { |
| 1446 // errorReporter.reportError(HintCode.IS_INT, node); |
| 1447 // } else { |
| 1448 // errorReporter.reportError(HintCode.IS_NOT_INT, node); |
| 1449 // } |
| 1450 // return true; |
| 1451 // } else |
| 1452 if (typeNameStr == _DOUBLE_TYPE_NAME && |
| 1453 libraryElement != null && |
| 1454 libraryElement.isDartCore) { |
| 1455 if (node.notOperator == null) { |
| 1456 _errorReporter.reportErrorForNode(HintCode.IS_DOUBLE, node); |
| 1457 } else { |
| 1458 _errorReporter.reportErrorForNode(HintCode.IS_NOT_DOUBLE, node); |
| 1459 } |
| 1460 return true; |
| 1461 } |
| 1462 } |
| 1463 return false; |
| 1464 } |
| 1465 } |
| 1466 |
| 1467 /** |
| 1468 * Instances of the class `DeadCodeVerifier` traverse an AST structure looking f
or cases of |
| 1469 * [HintCode.DEAD_CODE]. |
| 1470 */ |
| 1471 class DeadCodeVerifier extends RecursiveAstVisitor<Object> { |
| 1472 /** |
| 1473 * The error reporter by which errors will be reported. |
| 1474 */ |
| 1475 final ErrorReporter _errorReporter; |
| 1476 |
| 1477 /** |
| 1478 * Create a new instance of the [DeadCodeVerifier]. |
| 1479 * |
| 1480 * @param errorReporter the error reporter |
| 1481 */ |
| 1482 DeadCodeVerifier(this._errorReporter); |
| 1483 |
| 1484 @override |
| 1485 Object visitBinaryExpression(BinaryExpression node) { |
| 1486 sc.Token operator = node.operator; |
| 1487 bool isAmpAmp = operator.type == sc.TokenType.AMPERSAND_AMPERSAND; |
| 1488 bool isBarBar = operator.type == sc.TokenType.BAR_BAR; |
| 1489 if (isAmpAmp || isBarBar) { |
| 1490 Expression lhsCondition = node.leftOperand; |
| 1491 if (!_isDebugConstant(lhsCondition)) { |
| 1492 EvaluationResultImpl lhsResult = _getConstantBooleanValue(lhsCondition); |
| 1493 if (lhsResult != null) { |
| 1494 if (lhsResult.value.isTrue && isBarBar) { |
| 1495 // report error on else block: true || !e! |
| 1496 _errorReporter.reportErrorForNode( |
| 1497 HintCode.DEAD_CODE, node.rightOperand); |
| 1498 // only visit the LHS: |
| 1499 _safelyVisit(lhsCondition); |
| 1500 return null; |
| 1501 } else if (lhsResult.value.isFalse && isAmpAmp) { |
| 1502 // report error on if block: false && !e! |
| 1503 _errorReporter.reportErrorForNode( |
| 1504 HintCode.DEAD_CODE, node.rightOperand); |
| 1505 // only visit the LHS: |
| 1506 _safelyVisit(lhsCondition); |
| 1507 return null; |
| 1508 } |
| 1509 } |
| 1510 } |
| 1511 // How do we want to handle the RHS? It isn't dead code, but "pointless" |
| 1512 // or "obscure"... |
| 1513 // Expression rhsCondition = node.getRightOperand(); |
| 1514 // ValidResult rhsResult = getConstantBooleanValue(rhsCondition); |
| 1515 // if (rhsResult != null) { |
| 1516 // if (rhsResult == ValidResult.RESULT_TRUE && isBarBar) { |
| 1517 // // report error on else block: !e! || true |
| 1518 // errorReporter.reportError(HintCode.DEAD_CODE, node.getRightOpe
rand()); |
| 1519 // // only visit the RHS: |
| 1520 // safelyVisit(rhsCondition); |
| 1521 // return null; |
| 1522 // } else if (rhsResult == ValidResult.RESULT_FALSE && isAmpAmp) { |
| 1523 // // report error on if block: !e! && false |
| 1524 // errorReporter.reportError(HintCode.DEAD_CODE, node.getRightOpe
rand()); |
| 1525 // // only visit the RHS: |
| 1526 // safelyVisit(rhsCondition); |
| 1527 // return null; |
| 1528 // } |
| 1529 // } |
| 1530 } |
| 1531 return super.visitBinaryExpression(node); |
| 1532 } |
| 1533 |
| 1534 /** |
| 1535 * For each [Block], this method reports and error on all statements between t
he end of the |
| 1536 * block and the first return statement (assuming there it is not at the end o
f the block.) |
| 1537 * |
| 1538 * @param node the block to evaluate |
| 1539 */ |
| 1540 @override |
| 1541 Object visitBlock(Block node) { |
| 1542 NodeList<Statement> statements = node.statements; |
| 1543 _checkForDeadStatementsInNodeList(statements); |
| 1544 return null; |
| 1545 } |
| 1546 |
| 1547 @override |
| 1548 Object visitConditionalExpression(ConditionalExpression node) { |
| 1549 Expression conditionExpression = node.condition; |
| 1550 _safelyVisit(conditionExpression); |
| 1551 if (!_isDebugConstant(conditionExpression)) { |
| 1552 EvaluationResultImpl result = |
| 1553 _getConstantBooleanValue(conditionExpression); |
| 1554 if (result != null) { |
| 1555 if (result.value.isTrue) { |
| 1556 // report error on else block: true ? 1 : !2! |
| 1557 _errorReporter.reportErrorForNode( |
| 1558 HintCode.DEAD_CODE, node.elseExpression); |
| 1559 _safelyVisit(node.thenExpression); |
| 1560 return null; |
| 1561 } else { |
| 1562 // report error on if block: false ? !1! : 2 |
| 1563 _errorReporter.reportErrorForNode( |
| 1564 HintCode.DEAD_CODE, node.thenExpression); |
| 1565 _safelyVisit(node.elseExpression); |
| 1566 return null; |
| 1567 } |
| 1568 } |
| 1569 } |
| 1570 return super.visitConditionalExpression(node); |
| 1571 } |
| 1572 |
| 1573 @override |
| 1574 Object visitIfStatement(IfStatement node) { |
| 1575 Expression conditionExpression = node.condition; |
| 1576 _safelyVisit(conditionExpression); |
| 1577 if (!_isDebugConstant(conditionExpression)) { |
| 1578 EvaluationResultImpl result = |
| 1579 _getConstantBooleanValue(conditionExpression); |
| 1580 if (result != null) { |
| 1581 if (result.value.isTrue) { |
| 1582 // report error on else block: if(true) {} else {!} |
| 1583 Statement elseStatement = node.elseStatement; |
| 1584 if (elseStatement != null) { |
| 1585 _errorReporter.reportErrorForNode( |
| 1586 HintCode.DEAD_CODE, elseStatement); |
| 1587 _safelyVisit(node.thenStatement); |
| 1588 return null; |
| 1589 } |
| 1590 } else { |
| 1591 // report error on if block: if (false) {!} else {} |
| 1592 _errorReporter.reportErrorForNode( |
| 1593 HintCode.DEAD_CODE, node.thenStatement); |
| 1594 _safelyVisit(node.elseStatement); |
| 1595 return null; |
| 1596 } |
| 1597 } |
| 1598 } |
| 1599 return super.visitIfStatement(node); |
| 1600 } |
| 1601 |
| 1602 @override |
| 1603 Object visitSwitchCase(SwitchCase node) { |
| 1604 _checkForDeadStatementsInNodeList(node.statements); |
| 1605 return super.visitSwitchCase(node); |
| 1606 } |
| 1607 |
| 1608 @override |
| 1609 Object visitSwitchDefault(SwitchDefault node) { |
| 1610 _checkForDeadStatementsInNodeList(node.statements); |
| 1611 return super.visitSwitchDefault(node); |
| 1612 } |
| 1613 |
| 1614 @override |
| 1615 Object visitTryStatement(TryStatement node) { |
| 1616 _safelyVisit(node.body); |
| 1617 _safelyVisit(node.finallyBlock); |
| 1618 NodeList<CatchClause> catchClauses = node.catchClauses; |
| 1619 int numOfCatchClauses = catchClauses.length; |
| 1620 List<DartType> visitedTypes = new List<DartType>(); |
| 1621 for (int i = 0; i < numOfCatchClauses; i++) { |
| 1622 CatchClause catchClause = catchClauses[i]; |
| 1623 if (catchClause.onKeyword != null) { |
| 1624 // on-catch clause found, verify that the exception type is not a |
| 1625 // subtype of a previous on-catch exception type |
| 1626 TypeName typeName = catchClause.exceptionType; |
| 1627 if (typeName != null && typeName.type != null) { |
| 1628 DartType currentType = typeName.type; |
| 1629 if (currentType.isObject) { |
| 1630 // Found catch clause clause that has Object as an exception type, |
| 1631 // this is equivalent to having a catch clause that doesn't have an |
| 1632 // exception type, visit the block, but generate an error on any |
| 1633 // following catch clauses (and don't visit them). |
| 1634 _safelyVisit(catchClause); |
| 1635 if (i + 1 != numOfCatchClauses) { |
| 1636 // this catch clause is not the last in the try statement |
| 1637 CatchClause nextCatchClause = catchClauses[i + 1]; |
| 1638 CatchClause lastCatchClause = catchClauses[numOfCatchClauses - 1]; |
| 1639 int offset = nextCatchClause.offset; |
| 1640 int length = lastCatchClause.end - offset; |
| 1641 _errorReporter.reportErrorForOffset( |
| 1642 HintCode.DEAD_CODE_CATCH_FOLLOWING_CATCH, offset, length); |
| 1643 return null; |
| 1644 } |
| 1645 } |
| 1646 for (DartType type in visitedTypes) { |
| 1647 if (currentType.isSubtypeOf(type)) { |
| 1648 CatchClause lastCatchClause = catchClauses[numOfCatchClauses - 1]; |
| 1649 int offset = catchClause.offset; |
| 1650 int length = lastCatchClause.end - offset; |
| 1651 _errorReporter.reportErrorForOffset( |
| 1652 HintCode.DEAD_CODE_ON_CATCH_SUBTYPE, |
| 1653 offset, |
| 1654 length, |
| 1655 [currentType.displayName, type.displayName]); |
| 1656 return null; |
| 1657 } |
| 1658 } |
| 1659 visitedTypes.add(currentType); |
| 1660 } |
| 1661 _safelyVisit(catchClause); |
| 1662 } else { |
| 1663 // Found catch clause clause that doesn't have an exception type, |
| 1664 // visit the block, but generate an error on any following catch clauses |
| 1665 // (and don't visit them). |
| 1666 _safelyVisit(catchClause); |
| 1667 if (i + 1 != numOfCatchClauses) { |
| 1668 // this catch clause is not the last in the try statement |
| 1669 CatchClause nextCatchClause = catchClauses[i + 1]; |
| 1670 CatchClause lastCatchClause = catchClauses[numOfCatchClauses - 1]; |
| 1671 int offset = nextCatchClause.offset; |
| 1672 int length = lastCatchClause.end - offset; |
| 1673 _errorReporter.reportErrorForOffset( |
| 1674 HintCode.DEAD_CODE_CATCH_FOLLOWING_CATCH, offset, length); |
| 1675 return null; |
| 1676 } |
| 1677 } |
| 1678 } |
| 1679 return null; |
| 1680 } |
| 1681 |
| 1682 @override |
| 1683 Object visitWhileStatement(WhileStatement node) { |
| 1684 Expression conditionExpression = node.condition; |
| 1685 _safelyVisit(conditionExpression); |
| 1686 if (!_isDebugConstant(conditionExpression)) { |
| 1687 EvaluationResultImpl result = |
| 1688 _getConstantBooleanValue(conditionExpression); |
| 1689 if (result != null) { |
| 1690 if (result.value.isFalse) { |
| 1691 // report error on if block: while (false) {!} |
| 1692 _errorReporter.reportErrorForNode(HintCode.DEAD_CODE, node.body); |
| 1693 return null; |
| 1694 } |
| 1695 } |
| 1696 } |
| 1697 _safelyVisit(node.body); |
| 1698 return null; |
| 1699 } |
| 1700 |
| 1701 /** |
| 1702 * Given some [NodeList] of [Statement]s, from either a [Block] or |
| 1703 * [SwitchMember], this loops through the list in reverse order searching for
statements |
| 1704 * after a return, unlabeled break or unlabeled continue statement to mark the
m as dead code. |
| 1705 * |
| 1706 * @param statements some ordered list of statements in a [Block] or [SwitchMe
mber] |
| 1707 */ |
| 1708 void _checkForDeadStatementsInNodeList(NodeList<Statement> statements) { |
| 1709 int size = statements.length; |
| 1710 for (int i = 0; i < size; i++) { |
| 1711 Statement currentStatement = statements[i]; |
| 1712 _safelyVisit(currentStatement); |
| 1713 bool returnOrBreakingStatement = currentStatement is ReturnStatement || |
| 1714 (currentStatement is BreakStatement && |
| 1715 currentStatement.label == null) || |
| 1716 (currentStatement is ContinueStatement && |
| 1717 currentStatement.label == null); |
| 1718 if (returnOrBreakingStatement && i != size - 1) { |
| 1719 Statement nextStatement = statements[i + 1]; |
| 1720 Statement lastStatement = statements[size - 1]; |
| 1721 int offset = nextStatement.offset; |
| 1722 int length = lastStatement.end - offset; |
| 1723 _errorReporter.reportErrorForOffset(HintCode.DEAD_CODE, offset, length); |
| 1724 return; |
| 1725 } |
| 1726 } |
| 1727 } |
| 1728 |
| 1729 /** |
| 1730 * Given some [Expression], this method returns [ValidResult.RESULT_TRUE] if i
t is |
| 1731 * `true`, [ValidResult.RESULT_FALSE] if it is `false`, or `null` if the |
| 1732 * expression is not a constant boolean value. |
| 1733 * |
| 1734 * @param expression the expression to evaluate |
| 1735 * @return [ValidResult.RESULT_TRUE] if it is `true`, [ValidResult.RESULT_FALS
E] |
| 1736 * if it is `false`, or `null` if the expression is not a constant boo
lean |
| 1737 * value |
| 1738 */ |
| 1739 EvaluationResultImpl _getConstantBooleanValue(Expression expression) { |
| 1740 if (expression is BooleanLiteral) { |
| 1741 if (expression.value) { |
| 1742 return new EvaluationResultImpl( |
| 1743 new DartObjectImpl(null, BoolState.from(true))); |
| 1744 } else { |
| 1745 return new EvaluationResultImpl( |
| 1746 new DartObjectImpl(null, BoolState.from(false))); |
| 1747 } |
| 1748 } |
| 1749 // Don't consider situations where we could evaluate to a constant boolean |
| 1750 // expression with the ConstantVisitor |
| 1751 // else { |
| 1752 // EvaluationResultImpl result = expression.accept(new ConstantVisitor()); |
| 1753 // if (result == ValidResult.RESULT_TRUE) { |
| 1754 // return ValidResult.RESULT_TRUE; |
| 1755 // } else if (result == ValidResult.RESULT_FALSE) { |
| 1756 // return ValidResult.RESULT_FALSE; |
| 1757 // } |
| 1758 // return null; |
| 1759 // } |
| 1760 return null; |
| 1761 } |
| 1762 |
| 1763 /** |
| 1764 * Return `true` if and only if the passed expression is resolved to a constan
t variable. |
| 1765 * |
| 1766 * @param expression some conditional expression |
| 1767 * @return `true` if and only if the passed expression is resolved to a consta
nt variable |
| 1768 */ |
| 1769 bool _isDebugConstant(Expression expression) { |
| 1770 Element element = null; |
| 1771 if (expression is Identifier) { |
| 1772 Identifier identifier = expression; |
| 1773 element = identifier.staticElement; |
| 1774 } else if (expression is PropertyAccess) { |
| 1775 PropertyAccess propertyAccess = expression; |
| 1776 element = propertyAccess.propertyName.staticElement; |
| 1777 } |
| 1778 if (element is PropertyAccessorElement) { |
| 1779 PropertyInducingElement variable = element.variable; |
| 1780 return variable != null && variable.isConst; |
| 1781 } |
| 1782 return false; |
| 1783 } |
| 1784 |
| 1785 /** |
| 1786 * If the given node is not `null`, visit this instance of the dead code verif
ier. |
| 1787 * |
| 1788 * @param node the node to be visited |
| 1789 */ |
| 1790 void _safelyVisit(AstNode node) { |
| 1791 if (node != null) { |
| 1792 node.accept(this); |
| 1793 } |
| 1794 } |
| 1795 } |
| 1796 |
| 1797 /** |
| 1798 * Instances of the class `DeclarationResolver` are used to resolve declarations
in an AST |
| 1799 * structure to already built elements. |
| 1800 */ |
| 1801 class DeclarationResolver extends RecursiveAstVisitor<Object> { |
| 1802 /** |
| 1803 * The compilation unit containing the AST nodes being visited. |
| 1804 */ |
| 1805 CompilationUnitElement _enclosingUnit; |
| 1806 |
| 1807 /** |
| 1808 * The function type alias containing the AST nodes being visited, or `null` i
f we are not |
| 1809 * in the scope of a function type alias. |
| 1810 */ |
| 1811 FunctionTypeAliasElement _enclosingAlias; |
| 1812 |
| 1813 /** |
| 1814 * The class containing the AST nodes being visited, or `null` if we are not i
n the scope of |
| 1815 * a class. |
| 1816 */ |
| 1817 ClassElement _enclosingClass; |
| 1818 |
| 1819 /** |
| 1820 * The method or function containing the AST nodes being visited, or `null` if
we are not in |
| 1821 * the scope of a method or function. |
| 1822 */ |
| 1823 ExecutableElement _enclosingExecutable; |
| 1824 |
| 1825 /** |
| 1826 * The parameter containing the AST nodes being visited, or `null` if we are n
ot in the |
| 1827 * scope of a parameter. |
| 1828 */ |
| 1829 ParameterElement _enclosingParameter; |
| 1830 |
| 1831 /** |
| 1832 * Resolve the declarations within the given compilation unit to the elements
rooted at the given |
| 1833 * element. |
| 1834 * |
| 1835 * @param unit the compilation unit to be resolved |
| 1836 * @param element the root of the element model used to resolve the AST nodes |
| 1837 */ |
| 1838 void resolve(CompilationUnit unit, CompilationUnitElement element) { |
| 1839 _enclosingUnit = element; |
| 1840 unit.element = element; |
| 1841 unit.accept(this); |
| 1842 } |
| 1843 |
| 1844 @override |
| 1845 Object visitCatchClause(CatchClause node) { |
| 1846 SimpleIdentifier exceptionParameter = node.exceptionParameter; |
| 1847 if (exceptionParameter != null) { |
| 1848 List<LocalVariableElement> localVariables = |
| 1849 _enclosingExecutable.localVariables; |
| 1850 _findIdentifier(localVariables, exceptionParameter); |
| 1851 SimpleIdentifier stackTraceParameter = node.stackTraceParameter; |
| 1852 if (stackTraceParameter != null) { |
| 1853 _findIdentifier(localVariables, stackTraceParameter); |
| 1854 } |
| 1855 } |
| 1856 return super.visitCatchClause(node); |
| 1857 } |
| 1858 |
| 1859 @override |
| 1860 Object visitClassDeclaration(ClassDeclaration node) { |
| 1861 ClassElement outerClass = _enclosingClass; |
| 1862 try { |
| 1863 SimpleIdentifier className = node.name; |
| 1864 _enclosingClass = _findIdentifier(_enclosingUnit.types, className); |
| 1865 return super.visitClassDeclaration(node); |
| 1866 } finally { |
| 1867 _enclosingClass = outerClass; |
| 1868 } |
| 1869 } |
| 1870 |
| 1871 @override |
| 1872 Object visitClassTypeAlias(ClassTypeAlias node) { |
| 1873 ClassElement outerClass = _enclosingClass; |
| 1874 try { |
| 1875 SimpleIdentifier className = node.name; |
| 1876 _enclosingClass = _findIdentifier(_enclosingUnit.types, className); |
| 1877 return super.visitClassTypeAlias(node); |
| 1878 } finally { |
| 1879 _enclosingClass = outerClass; |
| 1880 } |
| 1881 } |
| 1882 |
| 1883 @override |
| 1884 Object visitConstructorDeclaration(ConstructorDeclaration node) { |
| 1885 ExecutableElement outerExecutable = _enclosingExecutable; |
| 1886 try { |
| 1887 SimpleIdentifier constructorName = node.name; |
| 1888 if (constructorName == null) { |
| 1889 _enclosingExecutable = _enclosingClass.unnamedConstructor; |
| 1890 } else { |
| 1891 _enclosingExecutable = |
| 1892 _enclosingClass.getNamedConstructor(constructorName.name); |
| 1893 constructorName.staticElement = _enclosingExecutable; |
| 1894 } |
| 1895 node.element = _enclosingExecutable as ConstructorElement; |
| 1896 return super.visitConstructorDeclaration(node); |
| 1897 } finally { |
| 1898 _enclosingExecutable = outerExecutable; |
| 1899 } |
| 1900 } |
| 1901 |
| 1902 @override |
| 1903 Object visitDeclaredIdentifier(DeclaredIdentifier node) { |
| 1904 SimpleIdentifier variableName = node.identifier; |
| 1905 _findIdentifier(_enclosingExecutable.localVariables, variableName); |
| 1906 return super.visitDeclaredIdentifier(node); |
| 1907 } |
| 1908 |
| 1909 @override |
| 1910 Object visitDefaultFormalParameter(DefaultFormalParameter node) { |
| 1911 SimpleIdentifier parameterName = node.parameter.identifier; |
| 1912 ParameterElement element = _getElementForParameter(node, parameterName); |
| 1913 Expression defaultValue = node.defaultValue; |
| 1914 if (defaultValue != null) { |
| 1915 ExecutableElement outerExecutable = _enclosingExecutable; |
| 1916 try { |
| 1917 if (element == null) { |
| 1918 // TODO(brianwilkerson) Report this internal error. |
| 1919 } else { |
| 1920 _enclosingExecutable = element.initializer; |
| 1921 } |
| 1922 defaultValue.accept(this); |
| 1923 } finally { |
| 1924 _enclosingExecutable = outerExecutable; |
| 1925 } |
| 1926 } |
| 1927 ParameterElement outerParameter = _enclosingParameter; |
| 1928 try { |
| 1929 _enclosingParameter = element; |
| 1930 return super.visitDefaultFormalParameter(node); |
| 1931 } finally { |
| 1932 _enclosingParameter = outerParameter; |
| 1933 } |
| 1934 } |
| 1935 |
| 1936 @override |
| 1937 Object visitEnumDeclaration(EnumDeclaration node) { |
| 1938 ClassElement enclosingEnum = |
| 1939 _findIdentifier(_enclosingUnit.enums, node.name); |
| 1940 List<FieldElement> constants = enclosingEnum.fields; |
| 1941 for (EnumConstantDeclaration constant in node.constants) { |
| 1942 _findIdentifier(constants, constant.name); |
| 1943 } |
| 1944 return super.visitEnumDeclaration(node); |
| 1945 } |
| 1946 |
| 1947 @override |
| 1948 Object visitExportDirective(ExportDirective node) { |
| 1949 String uri = _getStringValue(node.uri); |
| 1950 if (uri != null) { |
| 1951 LibraryElement library = _enclosingUnit.library; |
| 1952 ExportElement exportElement = _findExport( |
| 1953 library.exports, |
| 1954 _enclosingUnit.context.sourceFactory |
| 1955 .resolveUri(_enclosingUnit.source, uri)); |
| 1956 node.element = exportElement; |
| 1957 } |
| 1958 return super.visitExportDirective(node); |
| 1959 } |
| 1960 |
| 1961 @override |
| 1962 Object visitFieldFormalParameter(FieldFormalParameter node) { |
| 1963 if (node.parent is! DefaultFormalParameter) { |
| 1964 SimpleIdentifier parameterName = node.identifier; |
| 1965 ParameterElement element = _getElementForParameter(node, parameterName); |
| 1966 ParameterElement outerParameter = _enclosingParameter; |
| 1967 try { |
| 1968 _enclosingParameter = element; |
| 1969 return super.visitFieldFormalParameter(node); |
| 1970 } finally { |
| 1971 _enclosingParameter = outerParameter; |
| 1972 } |
| 1973 } else { |
| 1974 return super.visitFieldFormalParameter(node); |
| 1975 } |
| 1976 } |
| 1977 |
| 1978 @override |
| 1979 Object visitFunctionDeclaration(FunctionDeclaration node) { |
| 1980 ExecutableElement outerExecutable = _enclosingExecutable; |
| 1981 try { |
| 1982 SimpleIdentifier functionName = node.name; |
| 1983 sc.Token property = node.propertyKeyword; |
| 1984 if (property == null) { |
| 1985 if (_enclosingExecutable != null) { |
| 1986 _enclosingExecutable = |
| 1987 _findIdentifier(_enclosingExecutable.functions, functionName); |
| 1988 } else { |
| 1989 _enclosingExecutable = |
| 1990 _findIdentifier(_enclosingUnit.functions, functionName); |
| 1991 } |
| 1992 } else { |
| 1993 PropertyAccessorElement accessor = |
| 1994 _findIdentifier(_enclosingUnit.accessors, functionName); |
| 1995 if ((property as sc.KeywordToken).keyword == sc.Keyword.SET) { |
| 1996 accessor = accessor.variable.setter; |
| 1997 functionName.staticElement = accessor; |
| 1998 } |
| 1999 _enclosingExecutable = accessor; |
| 2000 } |
| 2001 node.functionExpression.element = _enclosingExecutable; |
| 2002 return super.visitFunctionDeclaration(node); |
| 2003 } finally { |
| 2004 _enclosingExecutable = outerExecutable; |
| 2005 } |
| 2006 } |
| 2007 |
| 2008 @override |
| 2009 Object visitFunctionExpression(FunctionExpression node) { |
| 2010 if (node.parent is! FunctionDeclaration) { |
| 2011 FunctionElement element = |
| 2012 _findAtOffset(_enclosingExecutable.functions, node.beginToken.offset); |
| 2013 node.element = element; |
| 2014 } |
| 2015 ExecutableElement outerExecutable = _enclosingExecutable; |
| 2016 try { |
| 2017 _enclosingExecutable = node.element; |
| 2018 return super.visitFunctionExpression(node); |
| 2019 } finally { |
| 2020 _enclosingExecutable = outerExecutable; |
| 2021 } |
| 2022 } |
| 2023 |
| 2024 @override |
| 2025 Object visitFunctionTypeAlias(FunctionTypeAlias node) { |
| 2026 FunctionTypeAliasElement outerAlias = _enclosingAlias; |
| 2027 try { |
| 2028 SimpleIdentifier aliasName = node.name; |
| 2029 _enclosingAlias = |
| 2030 _findIdentifier(_enclosingUnit.functionTypeAliases, aliasName); |
| 2031 return super.visitFunctionTypeAlias(node); |
| 2032 } finally { |
| 2033 _enclosingAlias = outerAlias; |
| 2034 } |
| 2035 } |
| 2036 |
| 2037 @override |
| 2038 Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { |
| 2039 if (node.parent is! DefaultFormalParameter) { |
| 2040 SimpleIdentifier parameterName = node.identifier; |
| 2041 ParameterElement element = _getElementForParameter(node, parameterName); |
| 2042 ParameterElement outerParameter = _enclosingParameter; |
| 2043 try { |
| 2044 _enclosingParameter = element; |
| 2045 return super.visitFunctionTypedFormalParameter(node); |
| 2046 } finally { |
| 2047 _enclosingParameter = outerParameter; |
| 2048 } |
| 2049 } else { |
| 2050 return super.visitFunctionTypedFormalParameter(node); |
| 2051 } |
| 2052 } |
| 2053 |
| 2054 @override |
| 2055 Object visitImportDirective(ImportDirective node) { |
| 2056 String uri = _getStringValue(node.uri); |
| 2057 if (uri != null) { |
| 2058 LibraryElement library = _enclosingUnit.library; |
| 2059 ImportElement importElement = _findImport( |
| 2060 library.imports, |
| 2061 _enclosingUnit.context.sourceFactory |
| 2062 .resolveUri(_enclosingUnit.source, uri), |
| 2063 node.prefix); |
| 2064 node.element = importElement; |
| 2065 } |
| 2066 return super.visitImportDirective(node); |
| 2067 } |
| 2068 |
| 2069 @override |
| 2070 Object visitLabeledStatement(LabeledStatement node) { |
| 2071 for (Label label in node.labels) { |
| 2072 SimpleIdentifier labelName = label.label; |
| 2073 _findIdentifier(_enclosingExecutable.labels, labelName); |
| 2074 } |
| 2075 return super.visitLabeledStatement(node); |
| 2076 } |
| 2077 |
| 2078 @override |
| 2079 Object visitLibraryDirective(LibraryDirective node) { |
| 2080 node.element = _enclosingUnit.library; |
| 2081 return super.visitLibraryDirective(node); |
| 2082 } |
| 2083 |
| 2084 @override |
| 2085 Object visitMethodDeclaration(MethodDeclaration node) { |
| 2086 ExecutableElement outerExecutable = _enclosingExecutable; |
| 2087 try { |
| 2088 sc.Token property = node.propertyKeyword; |
| 2089 SimpleIdentifier methodName = node.name; |
| 2090 String nameOfMethod = methodName.name; |
| 2091 if (property == null) { |
| 2092 _enclosingExecutable = _findWithNameAndOffset( |
| 2093 _enclosingClass.methods, nameOfMethod, methodName.offset); |
| 2094 methodName.staticElement = _enclosingExecutable; |
| 2095 } else { |
| 2096 PropertyAccessorElement accessor = |
| 2097 _findIdentifier(_enclosingClass.accessors, methodName); |
| 2098 if ((property as sc.KeywordToken).keyword == sc.Keyword.SET) { |
| 2099 accessor = accessor.variable.setter; |
| 2100 methodName.staticElement = accessor; |
| 2101 } |
| 2102 _enclosingExecutable = accessor; |
| 2103 } |
| 2104 return super.visitMethodDeclaration(node); |
| 2105 } finally { |
| 2106 _enclosingExecutable = outerExecutable; |
| 2107 } |
| 2108 } |
| 2109 |
| 2110 @override |
| 2111 Object visitPartDirective(PartDirective node) { |
| 2112 String uri = _getStringValue(node.uri); |
| 2113 if (uri != null) { |
| 2114 Source partSource = _enclosingUnit.context.sourceFactory |
| 2115 .resolveUri(_enclosingUnit.source, uri); |
| 2116 node.element = _findPart(_enclosingUnit.library.parts, partSource); |
| 2117 } |
| 2118 return super.visitPartDirective(node); |
| 2119 } |
| 2120 |
| 2121 @override |
| 2122 Object visitPartOfDirective(PartOfDirective node) { |
| 2123 node.element = _enclosingUnit.library; |
| 2124 return super.visitPartOfDirective(node); |
| 2125 } |
| 2126 |
| 2127 @override |
| 2128 Object visitSimpleFormalParameter(SimpleFormalParameter node) { |
| 2129 if (node.parent is! DefaultFormalParameter) { |
| 2130 SimpleIdentifier parameterName = node.identifier; |
| 2131 ParameterElement element = _getElementForParameter(node, parameterName); |
| 2132 ParameterElement outerParameter = _enclosingParameter; |
| 2133 try { |
| 2134 _enclosingParameter = element; |
| 2135 return super.visitSimpleFormalParameter(node); |
| 2136 } finally { |
| 2137 _enclosingParameter = outerParameter; |
| 2138 } |
| 2139 } else {} |
| 2140 return super.visitSimpleFormalParameter(node); |
| 2141 } |
| 2142 |
| 2143 @override |
| 2144 Object visitSwitchCase(SwitchCase node) { |
| 2145 for (Label label in node.labels) { |
| 2146 SimpleIdentifier labelName = label.label; |
| 2147 _findIdentifier(_enclosingExecutable.labels, labelName); |
| 2148 } |
| 2149 return super.visitSwitchCase(node); |
| 2150 } |
| 2151 |
| 2152 @override |
| 2153 Object visitSwitchDefault(SwitchDefault node) { |
| 2154 for (Label label in node.labels) { |
| 2155 SimpleIdentifier labelName = label.label; |
| 2156 _findIdentifier(_enclosingExecutable.labels, labelName); |
| 2157 } |
| 2158 return super.visitSwitchDefault(node); |
| 2159 } |
| 2160 |
| 2161 @override |
| 2162 Object visitTypeParameter(TypeParameter node) { |
| 2163 SimpleIdentifier parameterName = node.name; |
| 2164 if (_enclosingClass != null) { |
| 2165 _findIdentifier(_enclosingClass.typeParameters, parameterName); |
| 2166 } else if (_enclosingAlias != null) { |
| 2167 _findIdentifier(_enclosingAlias.typeParameters, parameterName); |
| 2168 } |
| 2169 return super.visitTypeParameter(node); |
| 2170 } |
| 2171 |
| 2172 @override |
| 2173 Object visitVariableDeclaration(VariableDeclaration node) { |
| 2174 VariableElement element = null; |
| 2175 SimpleIdentifier variableName = node.name; |
| 2176 if (_enclosingExecutable != null) { |
| 2177 element = |
| 2178 _findIdentifier(_enclosingExecutable.localVariables, variableName); |
| 2179 } |
| 2180 if (element == null && _enclosingClass != null) { |
| 2181 element = _findIdentifier(_enclosingClass.fields, variableName); |
| 2182 } |
| 2183 if (element == null && _enclosingUnit != null) { |
| 2184 element = _findIdentifier(_enclosingUnit.topLevelVariables, variableName); |
| 2185 } |
| 2186 Expression initializer = node.initializer; |
| 2187 if (initializer != null) { |
| 2188 ExecutableElement outerExecutable = _enclosingExecutable; |
| 2189 try { |
| 2190 if (element == null) { |
| 2191 // TODO(brianwilkerson) Report this internal error. |
| 2192 } else { |
| 2193 _enclosingExecutable = element.initializer; |
| 2194 } |
| 2195 return super.visitVariableDeclaration(node); |
| 2196 } finally { |
| 2197 _enclosingExecutable = outerExecutable; |
| 2198 } |
| 2199 } |
| 2200 return super.visitVariableDeclaration(node); |
| 2201 } |
| 2202 |
| 2203 /** |
| 2204 * Return the element in the given array of elements that was created for the
declaration at the |
| 2205 * given offset. This method should only be used when there is no name |
| 2206 * |
| 2207 * @param elements the elements of the appropriate kind that exist in the curr
ent context |
| 2208 * @param offset the offset of the name of the element to be returned |
| 2209 * @return the element at the given offset |
| 2210 */ |
| 2211 Element _findAtOffset(List<Element> elements, int offset) => |
| 2212 _findWithNameAndOffset(elements, "", offset); |
| 2213 |
| 2214 /** |
| 2215 * Return the export element from the given array whose library has the given
source, or |
| 2216 * `null` if there is no such export. |
| 2217 * |
| 2218 * @param exports the export elements being searched |
| 2219 * @param source the source of the library associated with the export element
to being searched |
| 2220 * for |
| 2221 * @return the export element whose library has the given source |
| 2222 */ |
| 2223 ExportElement _findExport(List<ExportElement> exports, Source source) { |
| 2224 for (ExportElement export in exports) { |
| 2225 if (export.exportedLibrary.source == source) { |
| 2226 return export; |
| 2227 } |
| 2228 } |
| 2229 return null; |
| 2230 } |
| 2231 |
| 2232 /** |
| 2233 * Return the element in the given array of elements that was created for the
declaration with the |
| 2234 * given name. |
| 2235 * |
| 2236 * @param elements the elements of the appropriate kind that exist in the curr
ent context |
| 2237 * @param identifier the name node in the declaration of the element to be ret
urned |
| 2238 * @return the element created for the declaration with the given name |
| 2239 */ |
| 2240 Element _findIdentifier(List<Element> elements, SimpleIdentifier identifier) { |
| 2241 Element element = |
| 2242 _findWithNameAndOffset(elements, identifier.name, identifier.offset); |
| 2243 identifier.staticElement = element; |
| 2244 return element; |
| 2245 } |
| 2246 |
| 2247 /** |
| 2248 * Return the import element from the given array whose library has the given
source and that has |
| 2249 * the given prefix, or `null` if there is no such import. |
| 2250 * |
| 2251 * @param imports the import elements being searched |
| 2252 * @param source the source of the library associated with the import element
to being searched |
| 2253 * for |
| 2254 * @param prefix the prefix with which the library was imported |
| 2255 * @return the import element whose library has the given source and prefix |
| 2256 */ |
| 2257 ImportElement _findImport( |
| 2258 List<ImportElement> imports, Source source, SimpleIdentifier prefix) { |
| 2259 for (ImportElement element in imports) { |
| 2260 if (element.importedLibrary.source == source) { |
| 2261 PrefixElement prefixElement = element.prefix; |
| 2262 if (prefix == null) { |
| 2263 if (prefixElement == null) { |
| 2264 return element; |
| 2265 } |
| 2266 } else { |
| 2267 if (prefixElement != null && |
| 2268 prefix.name == prefixElement.displayName) { |
| 2269 return element; |
| 2270 } |
| 2271 } |
| 2272 } |
| 2273 } |
| 2274 return null; |
| 2275 } |
| 2276 |
| 2277 /** |
| 2278 * Return the element for the part with the given source, or `null` if there i
s no element |
| 2279 * for the given source. |
| 2280 * |
| 2281 * @param parts the elements for the parts |
| 2282 * @param partSource the source for the part whose element is to be returned |
| 2283 * @return the element for the part with the given source |
| 2284 */ |
| 2285 CompilationUnitElement _findPart( |
| 2286 List<CompilationUnitElement> parts, Source partSource) { |
| 2287 for (CompilationUnitElement part in parts) { |
| 2288 if (part.source == partSource) { |
| 2289 return part; |
| 2290 } |
| 2291 } |
| 2292 return null; |
| 2293 } |
| 2294 |
| 2295 /** |
| 2296 * Return the element in the given array of elements that was created for the
declaration with the |
| 2297 * given name at the given offset. |
| 2298 * |
| 2299 * @param elements the elements of the appropriate kind that exist in the curr
ent context |
| 2300 * @param name the name of the element to be returned |
| 2301 * @param offset the offset of the name of the element to be returned |
| 2302 * @return the element with the given name and offset |
| 2303 */ |
| 2304 Element _findWithNameAndOffset( |
| 2305 List<Element> elements, String name, int offset) { |
| 2306 for (Element element in elements) { |
| 2307 if (element.nameOffset == offset && element.displayName == name) { |
| 2308 return element; |
| 2309 } |
| 2310 } |
| 2311 return null; |
| 2312 } |
| 2313 |
| 2314 /** |
| 2315 * Search the most closely enclosing list of parameters for a parameter with t
he given name. |
| 2316 * |
| 2317 * @param node the node defining the parameter with the given name |
| 2318 * @param parameterName the name of the parameter being searched for |
| 2319 * @return the element representing the parameter with that name |
| 2320 */ |
| 2321 ParameterElement _getElementForParameter( |
| 2322 FormalParameter node, SimpleIdentifier parameterName) { |
| 2323 List<ParameterElement> parameters = null; |
| 2324 if (_enclosingParameter != null) { |
| 2325 parameters = _enclosingParameter.parameters; |
| 2326 } |
| 2327 if (parameters == null && _enclosingExecutable != null) { |
| 2328 parameters = _enclosingExecutable.parameters; |
| 2329 } |
| 2330 if (parameters == null && _enclosingAlias != null) { |
| 2331 parameters = _enclosingAlias.parameters; |
| 2332 } |
| 2333 ParameterElement element = |
| 2334 parameters == null ? null : _findIdentifier(parameters, parameterName); |
| 2335 if (element == null) { |
| 2336 StringBuffer buffer = new StringBuffer(); |
| 2337 buffer.writeln("Invalid state found in the Analysis Engine:"); |
| 2338 buffer.writeln( |
| 2339 "DeclarationResolver.getElementForParameter() is visiting a parameter
that does not appear to be in a method or function."); |
| 2340 buffer.writeln("Ancestors:"); |
| 2341 AstNode parent = node.parent; |
| 2342 while (parent != null) { |
| 2343 buffer.writeln(parent.runtimeType.toString()); |
| 2344 buffer.writeln("---------"); |
| 2345 parent = parent.parent; |
| 2346 } |
| 2347 AnalysisEngine.instance.logger.logError(buffer.toString(), |
| 2348 new CaughtException(new AnalysisException(), null)); |
| 2349 } |
| 2350 return element; |
| 2351 } |
| 2352 |
| 2353 /** |
| 2354 * Return the value of the given string literal, or `null` if the string is no
t a constant |
| 2355 * string without any string interpolation. |
| 2356 * |
| 2357 * @param literal the string literal whose value is to be returned |
| 2358 * @return the value of the given string literal |
| 2359 */ |
| 2360 String _getStringValue(StringLiteral literal) { |
| 2361 if (literal is StringInterpolation) { |
| 2362 return null; |
| 2363 } |
| 2364 return literal.stringValue; |
| 2365 } |
| 2366 } |
| 2367 |
| 2368 /** |
| 2369 * Instances of the class `ElementBuilder` traverse an AST structure and build t
he element |
| 2370 * model representing the AST structure. |
| 2371 */ |
| 2372 class ElementBuilder extends RecursiveAstVisitor<Object> { |
| 2373 /** |
| 2374 * The element holder associated with the element that is currently being buil
t. |
| 2375 */ |
| 2376 ElementHolder _currentHolder; |
| 2377 |
| 2378 /** |
| 2379 * A flag indicating whether a variable declaration is in the context of a fie
ld declaration. |
| 2380 */ |
| 2381 bool _inFieldContext = false; |
| 2382 |
| 2383 /** |
| 2384 * A flag indicating whether a variable declaration is within the body of a me
thod or function. |
| 2385 */ |
| 2386 bool _inFunction = false; |
| 2387 |
| 2388 /** |
| 2389 * A flag indicating whether the class currently being visited can be used as
a mixin. |
| 2390 */ |
| 2391 bool _isValidMixin = false; |
| 2392 |
| 2393 /** |
| 2394 * A collection holding the function types defined in a class that need to hav
e their type |
| 2395 * arguments set to the types of the type parameters for the class, or `null`
if we are not |
| 2396 * currently processing nodes within a class. |
| 2397 */ |
| 2398 List<FunctionTypeImpl> _functionTypesToFix = null; |
| 2399 |
| 2400 /** |
| 2401 * A table mapping field names to field elements for the fields defined in the
current class, or |
| 2402 * `null` if we are not in the scope of a class. |
| 2403 */ |
| 2404 HashMap<String, FieldElement> _fieldMap; |
| 2405 |
| 2406 /** |
| 2407 * Initialize a newly created element builder to build the elements for a comp
ilation unit. |
| 2408 * |
| 2409 * @param initialHolder the element holder associated with the compilation uni
t being built |
| 2410 */ |
| 2411 ElementBuilder(ElementHolder initialHolder) { |
| 2412 _currentHolder = initialHolder; |
| 2413 } |
| 2414 |
| 2415 @override |
| 2416 Object visitBlock(Block node) { |
| 2417 bool wasInField = _inFieldContext; |
| 2418 _inFieldContext = false; |
| 2419 try { |
| 2420 node.visitChildren(this); |
| 2421 } finally { |
| 2422 _inFieldContext = wasInField; |
| 2423 } |
| 2424 return null; |
| 2425 } |
| 2426 |
| 2427 @override |
| 2428 Object visitCatchClause(CatchClause node) { |
| 2429 SimpleIdentifier exceptionParameter = node.exceptionParameter; |
| 2430 if (exceptionParameter != null) { |
| 2431 // exception |
| 2432 LocalVariableElementImpl exception = |
| 2433 new LocalVariableElementImpl.forNode(exceptionParameter); |
| 2434 if (node.exceptionType == null) { |
| 2435 exception.hasImplicitType = true; |
| 2436 } |
| 2437 _currentHolder.addLocalVariable(exception); |
| 2438 exceptionParameter.staticElement = exception; |
| 2439 // stack trace |
| 2440 SimpleIdentifier stackTraceParameter = node.stackTraceParameter; |
| 2441 if (stackTraceParameter != null) { |
| 2442 LocalVariableElementImpl stackTrace = |
| 2443 new LocalVariableElementImpl.forNode(stackTraceParameter); |
| 2444 _currentHolder.addLocalVariable(stackTrace); |
| 2445 stackTraceParameter.staticElement = stackTrace; |
| 2446 } |
| 2447 } |
| 2448 return super.visitCatchClause(node); |
| 2449 } |
| 2450 |
| 2451 @override |
| 2452 Object visitClassDeclaration(ClassDeclaration node) { |
| 2453 ElementHolder holder = new ElementHolder(); |
| 2454 _isValidMixin = true; |
| 2455 _functionTypesToFix = new List<FunctionTypeImpl>(); |
| 2456 // |
| 2457 // Process field declarations before constructors and methods so that field |
| 2458 // formal parameters can be correctly resolved to their fields. |
| 2459 // |
| 2460 ElementHolder previousHolder = _currentHolder; |
| 2461 _currentHolder = holder; |
| 2462 try { |
| 2463 List<ClassMember> nonFields = new List<ClassMember>(); |
| 2464 node.visitChildren( |
| 2465 new _ElementBuilder_visitClassDeclaration(this, nonFields)); |
| 2466 _buildFieldMap(holder.fieldsWithoutFlushing); |
| 2467 int count = nonFields.length; |
| 2468 for (int i = 0; i < count; i++) { |
| 2469 nonFields[i].accept(this); |
| 2470 } |
| 2471 } finally { |
| 2472 _currentHolder = previousHolder; |
| 2473 } |
| 2474 SimpleIdentifier className = node.name; |
| 2475 ClassElementImpl element = new ClassElementImpl.forNode(className); |
| 2476 List<TypeParameterElement> typeParameters = holder.typeParameters; |
| 2477 List<DartType> typeArguments = _createTypeParameterTypes(typeParameters); |
| 2478 InterfaceTypeImpl interfaceType = new InterfaceTypeImpl(element); |
| 2479 interfaceType.typeArguments = typeArguments; |
| 2480 element.type = interfaceType; |
| 2481 List<ConstructorElement> constructors = holder.constructors; |
| 2482 if (constructors.length == 0) { |
| 2483 // |
| 2484 // Create the default constructor. |
| 2485 // |
| 2486 constructors = _createDefaultConstructors(interfaceType); |
| 2487 } |
| 2488 element.abstract = node.isAbstract; |
| 2489 element.accessors = holder.accessors; |
| 2490 element.constructors = constructors; |
| 2491 element.fields = holder.fields; |
| 2492 element.methods = holder.methods; |
| 2493 element.typeParameters = typeParameters; |
| 2494 element.validMixin = _isValidMixin; |
| 2495 int functionTypeCount = _functionTypesToFix.length; |
| 2496 for (int i = 0; i < functionTypeCount; i++) { |
| 2497 _functionTypesToFix[i].typeArguments = typeArguments; |
| 2498 } |
| 2499 _functionTypesToFix = null; |
| 2500 _currentHolder.addType(element); |
| 2501 className.staticElement = element; |
| 2502 _fieldMap = null; |
| 2503 holder.validate(); |
| 2504 return null; |
| 2505 } |
| 2506 |
| 2507 /** |
| 2508 * Implementation of this method should be synchronized with |
| 2509 * [visitClassDeclaration]. |
| 2510 */ |
| 2511 void visitClassDeclarationIncrementally(ClassDeclaration node) { |
| 2512 // |
| 2513 // Process field declarations before constructors and methods so that field |
| 2514 // formal parameters can be correctly resolved to their fields. |
| 2515 // |
| 2516 ClassElement classElement = node.element; |
| 2517 _buildFieldMap(classElement.fields); |
| 2518 } |
| 2519 |
| 2520 @override |
| 2521 Object visitClassTypeAlias(ClassTypeAlias node) { |
| 2522 ElementHolder holder = new ElementHolder(); |
| 2523 _functionTypesToFix = new List<FunctionTypeImpl>(); |
| 2524 _visitChildren(holder, node); |
| 2525 SimpleIdentifier className = node.name; |
| 2526 ClassElementImpl element = new ClassElementImpl.forNode(className); |
| 2527 element.abstract = node.abstractKeyword != null; |
| 2528 element.mixinApplication = true; |
| 2529 List<TypeParameterElement> typeParameters = holder.typeParameters; |
| 2530 element.typeParameters = typeParameters; |
| 2531 List<DartType> typeArguments = _createTypeParameterTypes(typeParameters); |
| 2532 InterfaceTypeImpl interfaceType = new InterfaceTypeImpl(element); |
| 2533 interfaceType.typeArguments = typeArguments; |
| 2534 element.type = interfaceType; |
| 2535 // set default constructor |
| 2536 for (FunctionTypeImpl functionType in _functionTypesToFix) { |
| 2537 functionType.typeArguments = typeArguments; |
| 2538 } |
| 2539 _functionTypesToFix = null; |
| 2540 _currentHolder.addType(element); |
| 2541 className.staticElement = element; |
| 2542 holder.validate(); |
| 2543 return null; |
| 2544 } |
| 2545 |
| 2546 @override |
| 2547 Object visitConstructorDeclaration(ConstructorDeclaration node) { |
| 2548 _isValidMixin = false; |
| 2549 ElementHolder holder = new ElementHolder(); |
| 2550 bool wasInFunction = _inFunction; |
| 2551 _inFunction = true; |
| 2552 try { |
| 2553 _visitChildren(holder, node); |
| 2554 } finally { |
| 2555 _inFunction = wasInFunction; |
| 2556 } |
| 2557 FunctionBody body = node.body; |
| 2558 SimpleIdentifier constructorName = node.name; |
| 2559 ConstructorElementImpl element = |
| 2560 new ConstructorElementImpl.forNode(constructorName); |
| 2561 if (node.externalKeyword != null) { |
| 2562 element.external = true; |
| 2563 } |
| 2564 if (node.factoryKeyword != null) { |
| 2565 element.factory = true; |
| 2566 } |
| 2567 element.functions = holder.functions; |
| 2568 element.labels = holder.labels; |
| 2569 element.localVariables = holder.localVariables; |
| 2570 element.parameters = holder.parameters; |
| 2571 element.const2 = node.constKeyword != null; |
| 2572 if (body.isAsynchronous) { |
| 2573 element.asynchronous = true; |
| 2574 } |
| 2575 if (body.isGenerator) { |
| 2576 element.generator = true; |
| 2577 } |
| 2578 _currentHolder.addConstructor(element); |
| 2579 node.element = element; |
| 2580 if (constructorName == null) { |
| 2581 Identifier returnType = node.returnType; |
| 2582 if (returnType != null) { |
| 2583 element.nameOffset = returnType.offset; |
| 2584 element.nameEnd = returnType.end; |
| 2585 } |
| 2586 } else { |
| 2587 constructorName.staticElement = element; |
| 2588 element.periodOffset = node.period.offset; |
| 2589 element.nameEnd = constructorName.end; |
| 2590 } |
| 2591 holder.validate(); |
| 2592 return null; |
| 2593 } |
| 2594 |
| 2595 @override |
| 2596 Object visitDeclaredIdentifier(DeclaredIdentifier node) { |
| 2597 SimpleIdentifier variableName = node.identifier; |
| 2598 LocalVariableElementImpl element = |
| 2599 new LocalVariableElementImpl.forNode(variableName); |
| 2600 ForEachStatement statement = node.parent as ForEachStatement; |
| 2601 int declarationEnd = node.offset + node.length; |
| 2602 int statementEnd = statement.offset + statement.length; |
| 2603 element.setVisibleRange(declarationEnd, statementEnd - declarationEnd - 1); |
| 2604 element.const3 = node.isConst; |
| 2605 element.final2 = node.isFinal; |
| 2606 if (node.type == null) { |
| 2607 element.hasImplicitType = true; |
| 2608 } |
| 2609 _currentHolder.addLocalVariable(element); |
| 2610 variableName.staticElement = element; |
| 2611 return super.visitDeclaredIdentifier(node); |
| 2612 } |
| 2613 |
| 2614 @override |
| 2615 Object visitDefaultFormalParameter(DefaultFormalParameter node) { |
| 2616 ElementHolder holder = new ElementHolder(); |
| 2617 NormalFormalParameter normalParameter = node.parameter; |
| 2618 SimpleIdentifier parameterName = normalParameter.identifier; |
| 2619 ParameterElementImpl parameter; |
| 2620 if (normalParameter is FieldFormalParameter) { |
| 2621 parameter = new DefaultFieldFormalParameterElementImpl(parameterName); |
| 2622 FieldElement field = |
| 2623 _fieldMap == null ? null : _fieldMap[parameterName.name]; |
| 2624 if (field != null) { |
| 2625 (parameter as DefaultFieldFormalParameterElementImpl).field = field; |
| 2626 } |
| 2627 } else { |
| 2628 parameter = new DefaultParameterElementImpl(parameterName); |
| 2629 } |
| 2630 parameter.const3 = node.isConst; |
| 2631 parameter.final2 = node.isFinal; |
| 2632 parameter.parameterKind = node.kind; |
| 2633 // set initializer, default value range |
| 2634 Expression defaultValue = node.defaultValue; |
| 2635 if (defaultValue != null) { |
| 2636 _visit(holder, defaultValue); |
| 2637 FunctionElementImpl initializer = |
| 2638 new FunctionElementImpl.forOffset(defaultValue.beginToken.offset); |
| 2639 initializer.functions = holder.functions; |
| 2640 initializer.labels = holder.labels; |
| 2641 initializer.localVariables = holder.localVariables; |
| 2642 initializer.parameters = holder.parameters; |
| 2643 initializer.synthetic = true; |
| 2644 parameter.initializer = initializer; |
| 2645 parameter.defaultValueCode = defaultValue.toSource(); |
| 2646 } |
| 2647 // visible range |
| 2648 _setParameterVisibleRange(node, parameter); |
| 2649 if (normalParameter is SimpleFormalParameter && |
| 2650 normalParameter.type == null) { |
| 2651 parameter.hasImplicitType = true; |
| 2652 } |
| 2653 _currentHolder.addParameter(parameter); |
| 2654 parameterName.staticElement = parameter; |
| 2655 normalParameter.accept(this); |
| 2656 holder.validate(); |
| 2657 return null; |
| 2658 } |
| 2659 |
| 2660 @override |
| 2661 Object visitEnumDeclaration(EnumDeclaration node) { |
| 2662 SimpleIdentifier enumName = node.name; |
| 2663 ClassElementImpl enumElement = new ClassElementImpl.forNode(enumName); |
| 2664 enumElement.enum2 = true; |
| 2665 InterfaceTypeImpl enumType = new InterfaceTypeImpl(enumElement); |
| 2666 enumElement.type = enumType; |
| 2667 // The equivalent code for enums in the spec shows a single constructor, |
| 2668 // but that constructor is not callable (since it is a compile-time error |
| 2669 // to subclass, mix-in, implement, or explicitly instantiate an enum). So |
| 2670 // we represent this as having no constructors. |
| 2671 enumElement.constructors = ConstructorElement.EMPTY_LIST; |
| 2672 _currentHolder.addEnum(enumElement); |
| 2673 enumName.staticElement = enumElement; |
| 2674 return super.visitEnumDeclaration(node); |
| 2675 } |
| 2676 |
| 2677 @override |
| 2678 Object visitFieldDeclaration(FieldDeclaration node) { |
| 2679 bool wasInField = _inFieldContext; |
| 2680 _inFieldContext = true; |
| 2681 try { |
| 2682 node.visitChildren(this); |
| 2683 } finally { |
| 2684 _inFieldContext = wasInField; |
| 2685 } |
| 2686 return null; |
| 2687 } |
| 2688 |
| 2689 @override |
| 2690 Object visitFieldFormalParameter(FieldFormalParameter node) { |
| 2691 if (node.parent is! DefaultFormalParameter) { |
| 2692 SimpleIdentifier parameterName = node.identifier; |
| 2693 FieldElement field = |
| 2694 _fieldMap == null ? null : _fieldMap[parameterName.name]; |
| 2695 FieldFormalParameterElementImpl parameter = |
| 2696 new FieldFormalParameterElementImpl(parameterName); |
| 2697 parameter.const3 = node.isConst; |
| 2698 parameter.final2 = node.isFinal; |
| 2699 parameter.parameterKind = node.kind; |
| 2700 if (field != null) { |
| 2701 parameter.field = field; |
| 2702 } |
| 2703 _currentHolder.addParameter(parameter); |
| 2704 parameterName.staticElement = parameter; |
| 2705 } |
| 2706 // |
| 2707 // The children of this parameter include any parameters defined on the type |
| 2708 // of this parameter. |
| 2709 // |
| 2710 ElementHolder holder = new ElementHolder(); |
| 2711 _visitChildren(holder, node); |
| 2712 ParameterElementImpl element = node.element; |
| 2713 element.parameters = holder.parameters; |
| 2714 element.typeParameters = holder.typeParameters; |
| 2715 holder.validate(); |
| 2716 return null; |
| 2717 } |
| 2718 |
| 2719 @override |
| 2720 Object visitFunctionDeclaration(FunctionDeclaration node) { |
| 2721 FunctionExpression expression = node.functionExpression; |
| 2722 if (expression != null) { |
| 2723 ElementHolder holder = new ElementHolder(); |
| 2724 bool wasInFunction = _inFunction; |
| 2725 _inFunction = true; |
| 2726 try { |
| 2727 _visitChildren(holder, node); |
| 2728 } finally { |
| 2729 _inFunction = wasInFunction; |
| 2730 } |
| 2731 FunctionBody body = expression.body; |
| 2732 sc.Token property = node.propertyKeyword; |
| 2733 if (property == null || _inFunction) { |
| 2734 SimpleIdentifier functionName = node.name; |
| 2735 FunctionElementImpl element = |
| 2736 new FunctionElementImpl.forNode(functionName); |
| 2737 if (node.externalKeyword != null) { |
| 2738 element.external = true; |
| 2739 } |
| 2740 element.functions = holder.functions; |
| 2741 element.labels = holder.labels; |
| 2742 element.localVariables = holder.localVariables; |
| 2743 element.parameters = holder.parameters; |
| 2744 element.typeParameters = holder.typeParameters; |
| 2745 if (body.isAsynchronous) { |
| 2746 element.asynchronous = true; |
| 2747 } |
| 2748 if (body.isGenerator) { |
| 2749 element.generator = true; |
| 2750 } |
| 2751 if (_inFunction) { |
| 2752 Block enclosingBlock = node.getAncestor((node) => node is Block); |
| 2753 if (enclosingBlock != null) { |
| 2754 int functionEnd = node.offset + node.length; |
| 2755 int blockEnd = enclosingBlock.offset + enclosingBlock.length; |
| 2756 element.setVisibleRange(functionEnd, blockEnd - functionEnd - 1); |
| 2757 } |
| 2758 } |
| 2759 if (node.returnType == null) { |
| 2760 element.hasImplicitReturnType = true; |
| 2761 } |
| 2762 _currentHolder.addFunction(element); |
| 2763 expression.element = element; |
| 2764 functionName.staticElement = element; |
| 2765 } else { |
| 2766 SimpleIdentifier propertyNameNode = node.name; |
| 2767 if (propertyNameNode == null) { |
| 2768 // TODO(brianwilkerson) Report this internal error. |
| 2769 return null; |
| 2770 } |
| 2771 String propertyName = propertyNameNode.name; |
| 2772 TopLevelVariableElementImpl variable = _currentHolder |
| 2773 .getTopLevelVariable(propertyName) as TopLevelVariableElementImpl; |
| 2774 if (variable == null) { |
| 2775 variable = new TopLevelVariableElementImpl(node.name.name, -1); |
| 2776 variable.final2 = true; |
| 2777 variable.synthetic = true; |
| 2778 _currentHolder.addTopLevelVariable(variable); |
| 2779 } |
| 2780 if (node.isGetter) { |
| 2781 PropertyAccessorElementImpl getter = |
| 2782 new PropertyAccessorElementImpl.forNode(propertyNameNode); |
| 2783 if (node.externalKeyword != null) { |
| 2784 getter.external = true; |
| 2785 } |
| 2786 getter.functions = holder.functions; |
| 2787 getter.labels = holder.labels; |
| 2788 getter.localVariables = holder.localVariables; |
| 2789 if (body.isAsynchronous) { |
| 2790 getter.asynchronous = true; |
| 2791 } |
| 2792 if (body.isGenerator) { |
| 2793 getter.generator = true; |
| 2794 } |
| 2795 getter.variable = variable; |
| 2796 getter.getter = true; |
| 2797 getter.static = true; |
| 2798 variable.getter = getter; |
| 2799 if (node.returnType == null) { |
| 2800 getter.hasImplicitReturnType = true; |
| 2801 } |
| 2802 _currentHolder.addAccessor(getter); |
| 2803 expression.element = getter; |
| 2804 propertyNameNode.staticElement = getter; |
| 2805 } else { |
| 2806 PropertyAccessorElementImpl setter = |
| 2807 new PropertyAccessorElementImpl.forNode(propertyNameNode); |
| 2808 if (node.externalKeyword != null) { |
| 2809 setter.external = true; |
| 2810 } |
| 2811 setter.functions = holder.functions; |
| 2812 setter.labels = holder.labels; |
| 2813 setter.localVariables = holder.localVariables; |
| 2814 setter.parameters = holder.parameters; |
| 2815 if (body.isAsynchronous) { |
| 2816 setter.asynchronous = true; |
| 2817 } |
| 2818 if (body.isGenerator) { |
| 2819 setter.generator = true; |
| 2820 } |
| 2821 setter.variable = variable; |
| 2822 setter.setter = true; |
| 2823 setter.static = true; |
| 2824 variable.setter = setter; |
| 2825 variable.final2 = false; |
| 2826 _currentHolder.addAccessor(setter); |
| 2827 expression.element = setter; |
| 2828 propertyNameNode.staticElement = setter; |
| 2829 } |
| 2830 } |
| 2831 holder.validate(); |
| 2832 } |
| 2833 return null; |
| 2834 } |
| 2835 |
| 2836 @override |
| 2837 Object visitFunctionExpression(FunctionExpression node) { |
| 2838 if (node.parent is FunctionDeclaration) { |
| 2839 // visitFunctionDeclaration has already created the element for the |
| 2840 // declaration. We just need to visit children. |
| 2841 return super.visitFunctionExpression(node); |
| 2842 } |
| 2843 ElementHolder holder = new ElementHolder(); |
| 2844 bool wasInFunction = _inFunction; |
| 2845 _inFunction = true; |
| 2846 try { |
| 2847 _visitChildren(holder, node); |
| 2848 } finally { |
| 2849 _inFunction = wasInFunction; |
| 2850 } |
| 2851 FunctionBody body = node.body; |
| 2852 FunctionElementImpl element = |
| 2853 new FunctionElementImpl.forOffset(node.beginToken.offset); |
| 2854 element.functions = holder.functions; |
| 2855 element.labels = holder.labels; |
| 2856 element.localVariables = holder.localVariables; |
| 2857 element.parameters = holder.parameters; |
| 2858 element.typeParameters = holder.typeParameters; |
| 2859 if (body.isAsynchronous) { |
| 2860 element.asynchronous = true; |
| 2861 } |
| 2862 if (body.isGenerator) { |
| 2863 element.generator = true; |
| 2864 } |
| 2865 if (_inFunction) { |
| 2866 Block enclosingBlock = node.getAncestor((node) => node is Block); |
| 2867 if (enclosingBlock != null) { |
| 2868 int functionEnd = node.offset + node.length; |
| 2869 int blockEnd = enclosingBlock.offset + enclosingBlock.length; |
| 2870 element.setVisibleRange(functionEnd, blockEnd - functionEnd - 1); |
| 2871 } |
| 2872 } |
| 2873 FunctionTypeImpl type = new FunctionTypeImpl(element); |
| 2874 if (_functionTypesToFix != null) { |
| 2875 _functionTypesToFix.add(type); |
| 2876 } |
| 2877 element.type = type; |
| 2878 element.hasImplicitReturnType = true; |
| 2879 _currentHolder.addFunction(element); |
| 2880 node.element = element; |
| 2881 holder.validate(); |
| 2882 return null; |
| 2883 } |
| 2884 |
| 2885 @override |
| 2886 Object visitFunctionTypeAlias(FunctionTypeAlias node) { |
| 2887 ElementHolder holder = new ElementHolder(); |
| 2888 _visitChildren(holder, node); |
| 2889 SimpleIdentifier aliasName = node.name; |
| 2890 List<ParameterElement> parameters = holder.parameters; |
| 2891 List<TypeParameterElement> typeParameters = holder.typeParameters; |
| 2892 FunctionTypeAliasElementImpl element = |
| 2893 new FunctionTypeAliasElementImpl.forNode(aliasName); |
| 2894 element.parameters = parameters; |
| 2895 element.typeParameters = typeParameters; |
| 2896 FunctionTypeImpl type = new FunctionTypeImpl.forTypedef(element); |
| 2897 type.typeArguments = _createTypeParameterTypes(typeParameters); |
| 2898 element.type = type; |
| 2899 _currentHolder.addTypeAlias(element); |
| 2900 aliasName.staticElement = element; |
| 2901 holder.validate(); |
| 2902 return null; |
| 2903 } |
| 2904 |
| 2905 @override |
| 2906 Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { |
| 2907 if (node.parent is! DefaultFormalParameter) { |
| 2908 SimpleIdentifier parameterName = node.identifier; |
| 2909 ParameterElementImpl parameter = |
| 2910 new ParameterElementImpl.forNode(parameterName); |
| 2911 parameter.parameterKind = node.kind; |
| 2912 _setParameterVisibleRange(node, parameter); |
| 2913 _currentHolder.addParameter(parameter); |
| 2914 parameterName.staticElement = parameter; |
| 2915 } |
| 2916 // |
| 2917 // The children of this parameter include any parameters defined on the type |
| 2918 //of this parameter. |
| 2919 // |
| 2920 ElementHolder holder = new ElementHolder(); |
| 2921 _visitChildren(holder, node); |
| 2922 ParameterElementImpl element = node.element; |
| 2923 element.parameters = holder.parameters; |
| 2924 element.typeParameters = holder.typeParameters; |
| 2925 holder.validate(); |
| 2926 return null; |
| 2927 } |
| 2928 |
| 2929 @override |
| 2930 Object visitLabeledStatement(LabeledStatement node) { |
| 2931 bool onSwitchStatement = node.statement is SwitchStatement; |
| 2932 for (Label label in node.labels) { |
| 2933 SimpleIdentifier labelName = label.label; |
| 2934 LabelElementImpl element = |
| 2935 new LabelElementImpl(labelName, onSwitchStatement, false); |
| 2936 _currentHolder.addLabel(element); |
| 2937 labelName.staticElement = element; |
| 2938 } |
| 2939 return super.visitLabeledStatement(node); |
| 2940 } |
| 2941 |
| 2942 @override |
| 2943 Object visitMethodDeclaration(MethodDeclaration node) { |
| 2944 try { |
| 2945 ElementHolder holder = new ElementHolder(); |
| 2946 bool wasInFunction = _inFunction; |
| 2947 _inFunction = true; |
| 2948 try { |
| 2949 _visitChildren(holder, node); |
| 2950 } finally { |
| 2951 _inFunction = wasInFunction; |
| 2952 } |
| 2953 bool isStatic = node.isStatic; |
| 2954 sc.Token property = node.propertyKeyword; |
| 2955 FunctionBody body = node.body; |
| 2956 if (property == null) { |
| 2957 SimpleIdentifier methodName = node.name; |
| 2958 String nameOfMethod = methodName.name; |
| 2959 if (nameOfMethod == sc.TokenType.MINUS.lexeme && |
| 2960 node.parameters.parameters.length == 0) { |
| 2961 nameOfMethod = "unary-"; |
| 2962 } |
| 2963 MethodElementImpl element = |
| 2964 new MethodElementImpl(nameOfMethod, methodName.offset); |
| 2965 element.abstract = node.isAbstract; |
| 2966 if (node.externalKeyword != null) { |
| 2967 element.external = true; |
| 2968 } |
| 2969 element.functions = holder.functions; |
| 2970 element.labels = holder.labels; |
| 2971 element.localVariables = holder.localVariables; |
| 2972 element.parameters = holder.parameters; |
| 2973 element.static = isStatic; |
| 2974 element.typeParameters = holder.typeParameters; |
| 2975 if (body.isAsynchronous) { |
| 2976 element.asynchronous = true; |
| 2977 } |
| 2978 if (body.isGenerator) { |
| 2979 element.generator = true; |
| 2980 } |
| 2981 if (node.returnType == null) { |
| 2982 element.hasImplicitReturnType = true; |
| 2983 } |
| 2984 _currentHolder.addMethod(element); |
| 2985 methodName.staticElement = element; |
| 2986 } else { |
| 2987 SimpleIdentifier propertyNameNode = node.name; |
| 2988 String propertyName = propertyNameNode.name; |
| 2989 FieldElementImpl field = |
| 2990 _currentHolder.getField(propertyName) as FieldElementImpl; |
| 2991 if (field == null) { |
| 2992 field = new FieldElementImpl(node.name.name, -1); |
| 2993 field.final2 = true; |
| 2994 field.static = isStatic; |
| 2995 field.synthetic = true; |
| 2996 _currentHolder.addField(field); |
| 2997 } |
| 2998 if (node.isGetter) { |
| 2999 PropertyAccessorElementImpl getter = |
| 3000 new PropertyAccessorElementImpl.forNode(propertyNameNode); |
| 3001 if (node.externalKeyword != null) { |
| 3002 getter.external = true; |
| 3003 } |
| 3004 getter.functions = holder.functions; |
| 3005 getter.labels = holder.labels; |
| 3006 getter.localVariables = holder.localVariables; |
| 3007 if (body.isAsynchronous) { |
| 3008 getter.asynchronous = true; |
| 3009 } |
| 3010 if (body.isGenerator) { |
| 3011 getter.generator = true; |
| 3012 } |
| 3013 getter.variable = field; |
| 3014 getter.abstract = node.isAbstract; |
| 3015 getter.getter = true; |
| 3016 getter.static = isStatic; |
| 3017 field.getter = getter; |
| 3018 if (node.returnType == null) { |
| 3019 getter.hasImplicitReturnType = true; |
| 3020 } |
| 3021 _currentHolder.addAccessor(getter); |
| 3022 propertyNameNode.staticElement = getter; |
| 3023 } else { |
| 3024 PropertyAccessorElementImpl setter = |
| 3025 new PropertyAccessorElementImpl.forNode(propertyNameNode); |
| 3026 if (node.externalKeyword != null) { |
| 3027 setter.external = true; |
| 3028 } |
| 3029 setter.functions = holder.functions; |
| 3030 setter.labels = holder.labels; |
| 3031 setter.localVariables = holder.localVariables; |
| 3032 setter.parameters = holder.parameters; |
| 3033 if (body.isAsynchronous) { |
| 3034 setter.asynchronous = true; |
| 3035 } |
| 3036 if (body.isGenerator) { |
| 3037 setter.generator = true; |
| 3038 } |
| 3039 setter.variable = field; |
| 3040 setter.abstract = node.isAbstract; |
| 3041 setter.setter = true; |
| 3042 setter.static = isStatic; |
| 3043 field.setter = setter; |
| 3044 field.final2 = false; |
| 3045 _currentHolder.addAccessor(setter); |
| 3046 propertyNameNode.staticElement = setter; |
| 3047 } |
| 3048 } |
| 3049 holder.validate(); |
| 3050 } catch (exception, stackTrace) { |
| 3051 if (node.name.staticElement == null) { |
| 3052 ClassDeclaration classNode = |
| 3053 node.getAncestor((node) => node is ClassDeclaration); |
| 3054 StringBuffer buffer = new StringBuffer(); |
| 3055 buffer.write("The element for the method "); |
| 3056 buffer.write(node.name); |
| 3057 buffer.write(" in "); |
| 3058 buffer.write(classNode.name); |
| 3059 buffer.write(" was not set while trying to build the element model."); |
| 3060 AnalysisEngine.instance.logger.logError( |
| 3061 buffer.toString(), new CaughtException(exception, stackTrace)); |
| 3062 } else { |
| 3063 String message = |
| 3064 "Exception caught in ElementBuilder.visitMethodDeclaration()"; |
| 3065 AnalysisEngine.instance.logger |
| 3066 .logError(message, new CaughtException(exception, stackTrace)); |
| 3067 } |
| 3068 } finally { |
| 3069 if (node.name.staticElement == null) { |
| 3070 ClassDeclaration classNode = |
| 3071 node.getAncestor((node) => node is ClassDeclaration); |
| 3072 StringBuffer buffer = new StringBuffer(); |
| 3073 buffer.write("The element for the method "); |
| 3074 buffer.write(node.name); |
| 3075 buffer.write(" in "); |
| 3076 buffer.write(classNode.name); |
| 3077 buffer.write(" was not set while trying to resolve types."); |
| 3078 AnalysisEngine.instance.logger.logError( |
| 3079 buffer.toString(), |
| 3080 new CaughtException( |
| 3081 new AnalysisException(buffer.toString()), null)); |
| 3082 } |
| 3083 } |
| 3084 return null; |
| 3085 } |
| 3086 |
| 3087 @override |
| 3088 Object visitSimpleFormalParameter(SimpleFormalParameter node) { |
| 3089 if (node.parent is! DefaultFormalParameter) { |
| 3090 SimpleIdentifier parameterName = node.identifier; |
| 3091 ParameterElementImpl parameter = |
| 3092 new ParameterElementImpl.forNode(parameterName); |
| 3093 parameter.const3 = node.isConst; |
| 3094 parameter.final2 = node.isFinal; |
| 3095 parameter.parameterKind = node.kind; |
| 3096 _setParameterVisibleRange(node, parameter); |
| 3097 if (node.type == null) { |
| 3098 parameter.hasImplicitType = true; |
| 3099 } |
| 3100 _currentHolder.addParameter(parameter); |
| 3101 parameterName.staticElement = parameter; |
| 3102 } |
| 3103 return super.visitSimpleFormalParameter(node); |
| 3104 } |
| 3105 |
| 3106 @override |
| 3107 Object visitSuperExpression(SuperExpression node) { |
| 3108 _isValidMixin = false; |
| 3109 return super.visitSuperExpression(node); |
| 3110 } |
| 3111 |
| 3112 @override |
| 3113 Object visitSwitchCase(SwitchCase node) { |
| 3114 for (Label label in node.labels) { |
| 3115 SimpleIdentifier labelName = label.label; |
| 3116 LabelElementImpl element = new LabelElementImpl(labelName, false, true); |
| 3117 _currentHolder.addLabel(element); |
| 3118 labelName.staticElement = element; |
| 3119 } |
| 3120 return super.visitSwitchCase(node); |
| 3121 } |
| 3122 |
| 3123 @override |
| 3124 Object visitSwitchDefault(SwitchDefault node) { |
| 3125 for (Label label in node.labels) { |
| 3126 SimpleIdentifier labelName = label.label; |
| 3127 LabelElementImpl element = new LabelElementImpl(labelName, false, true); |
| 3128 _currentHolder.addLabel(element); |
| 3129 labelName.staticElement = element; |
| 3130 } |
| 3131 return super.visitSwitchDefault(node); |
| 3132 } |
| 3133 |
| 3134 @override |
| 3135 Object visitTypeParameter(TypeParameter node) { |
| 3136 SimpleIdentifier parameterName = node.name; |
| 3137 TypeParameterElementImpl typeParameter = |
| 3138 new TypeParameterElementImpl.forNode(parameterName); |
| 3139 TypeParameterTypeImpl typeParameterType = |
| 3140 new TypeParameterTypeImpl(typeParameter); |
| 3141 typeParameter.type = typeParameterType; |
| 3142 _currentHolder.addTypeParameter(typeParameter); |
| 3143 parameterName.staticElement = typeParameter; |
| 3144 return super.visitTypeParameter(node); |
| 3145 } |
| 3146 |
| 3147 @override |
| 3148 Object visitVariableDeclaration(VariableDeclaration node) { |
| 3149 bool isConst = node.isConst; |
| 3150 bool isFinal = node.isFinal; |
| 3151 bool hasInitializer = node.initializer != null; |
| 3152 VariableElementImpl element; |
| 3153 if (_inFieldContext) { |
| 3154 SimpleIdentifier fieldName = node.name; |
| 3155 FieldElementImpl field; |
| 3156 if ((isConst || isFinal) && hasInitializer) { |
| 3157 field = new ConstFieldElementImpl.forNode(fieldName); |
| 3158 } else { |
| 3159 field = new FieldElementImpl.forNode(fieldName); |
| 3160 } |
| 3161 element = field; |
| 3162 if ((node.parent as VariableDeclarationList).type == null) { |
| 3163 field.hasImplicitType = true; |
| 3164 } |
| 3165 _currentHolder.addField(field); |
| 3166 fieldName.staticElement = field; |
| 3167 } else if (_inFunction) { |
| 3168 SimpleIdentifier variableName = node.name; |
| 3169 LocalVariableElementImpl variable; |
| 3170 if (isConst && hasInitializer) { |
| 3171 variable = new ConstLocalVariableElementImpl.forNode(variableName); |
| 3172 } else { |
| 3173 variable = new LocalVariableElementImpl.forNode(variableName); |
| 3174 } |
| 3175 element = variable; |
| 3176 Block enclosingBlock = node.getAncestor((node) => node is Block); |
| 3177 // TODO(brianwilkerson) This isn't right for variables declared in a for |
| 3178 // loop. |
| 3179 variable.setVisibleRange(enclosingBlock.offset, enclosingBlock.length); |
| 3180 if ((node.parent as VariableDeclarationList).type == null) { |
| 3181 variable.hasImplicitType = true; |
| 3182 } |
| 3183 _currentHolder.addLocalVariable(variable); |
| 3184 variableName.staticElement = element; |
| 3185 } else { |
| 3186 SimpleIdentifier variableName = node.name; |
| 3187 TopLevelVariableElementImpl variable; |
| 3188 if (isConst && hasInitializer) { |
| 3189 variable = new ConstTopLevelVariableElementImpl(variableName); |
| 3190 } else { |
| 3191 variable = new TopLevelVariableElementImpl.forNode(variableName); |
| 3192 } |
| 3193 element = variable; |
| 3194 if ((node.parent as VariableDeclarationList).type == null) { |
| 3195 variable.hasImplicitType = true; |
| 3196 } |
| 3197 _currentHolder.addTopLevelVariable(variable); |
| 3198 variableName.staticElement = element; |
| 3199 } |
| 3200 element.const3 = isConst; |
| 3201 element.final2 = isFinal; |
| 3202 if (hasInitializer) { |
| 3203 ElementHolder holder = new ElementHolder(); |
| 3204 bool wasInFieldContext = _inFieldContext; |
| 3205 _inFieldContext = false; |
| 3206 try { |
| 3207 _visit(holder, node.initializer); |
| 3208 } finally { |
| 3209 _inFieldContext = wasInFieldContext; |
| 3210 } |
| 3211 FunctionElementImpl initializer = |
| 3212 new FunctionElementImpl.forOffset(node.initializer.beginToken.offset); |
| 3213 initializer.functions = holder.functions; |
| 3214 initializer.labels = holder.labels; |
| 3215 initializer.localVariables = holder.localVariables; |
| 3216 initializer.synthetic = true; |
| 3217 element.initializer = initializer; |
| 3218 holder.validate(); |
| 3219 } |
| 3220 if (element is PropertyInducingElementImpl) { |
| 3221 if (_inFieldContext) { |
| 3222 (element as FieldElementImpl).static = |
| 3223 (node.parent.parent as FieldDeclaration).isStatic; |
| 3224 } |
| 3225 PropertyAccessorElementImpl getter = |
| 3226 new PropertyAccessorElementImpl.forVariable(element); |
| 3227 getter.getter = true; |
| 3228 if (element.hasImplicitType) { |
| 3229 getter.hasImplicitReturnType = true; |
| 3230 } |
| 3231 _currentHolder.addAccessor(getter); |
| 3232 element.getter = getter; |
| 3233 if (!isConst && !isFinal) { |
| 3234 PropertyAccessorElementImpl setter = |
| 3235 new PropertyAccessorElementImpl.forVariable(element); |
| 3236 setter.setter = true; |
| 3237 ParameterElementImpl parameter = |
| 3238 new ParameterElementImpl("_${element.name}", element.nameOffset); |
| 3239 parameter.synthetic = true; |
| 3240 parameter.parameterKind = ParameterKind.REQUIRED; |
| 3241 setter.parameters = <ParameterElement>[parameter]; |
| 3242 _currentHolder.addAccessor(setter); |
| 3243 element.setter = setter; |
| 3244 } |
| 3245 } |
| 3246 return null; |
| 3247 } |
| 3248 |
| 3249 /** |
| 3250 * Build the table mapping field names to field elements for the fields define
d in the current |
| 3251 * class. |
| 3252 * |
| 3253 * @param fields the field elements defined in the current class |
| 3254 */ |
| 3255 void _buildFieldMap(List<FieldElement> fields) { |
| 3256 _fieldMap = new HashMap<String, FieldElement>(); |
| 3257 int count = fields.length; |
| 3258 for (int i = 0; i < count; i++) { |
| 3259 FieldElement field = fields[i]; |
| 3260 _fieldMap[field.name] = field; |
| 3261 } |
| 3262 } |
| 3263 |
| 3264 /** |
| 3265 * Creates the [ConstructorElement]s array with the single default constructor
element. |
| 3266 * |
| 3267 * @param interfaceType the interface type for which to create a default const
ructor |
| 3268 * @return the [ConstructorElement]s array with the single default constructor
element |
| 3269 */ |
| 3270 List<ConstructorElement> _createDefaultConstructors( |
| 3271 InterfaceTypeImpl interfaceType) { |
| 3272 ConstructorElementImpl constructor = |
| 3273 new ConstructorElementImpl.forNode(null); |
| 3274 constructor.synthetic = true; |
| 3275 constructor.returnType = interfaceType; |
| 3276 FunctionTypeImpl type = new FunctionTypeImpl(constructor); |
| 3277 _functionTypesToFix.add(type); |
| 3278 constructor.type = type; |
| 3279 return <ConstructorElement>[constructor]; |
| 3280 } |
| 3281 |
| 3282 /** |
| 3283 * Create the types associated with the given type parameters, setting the typ
e of each type |
| 3284 * parameter, and return an array of types corresponding to the given paramete
rs. |
| 3285 * |
| 3286 * @param typeParameters the type parameters for which types are to be created |
| 3287 * @return an array of types corresponding to the given parameters |
| 3288 */ |
| 3289 List<DartType> _createTypeParameterTypes( |
| 3290 List<TypeParameterElement> typeParameters) { |
| 3291 int typeParameterCount = typeParameters.length; |
| 3292 List<DartType> typeArguments = new List<DartType>(typeParameterCount); |
| 3293 for (int i = 0; i < typeParameterCount; i++) { |
| 3294 TypeParameterElementImpl typeParameter = |
| 3295 typeParameters[i] as TypeParameterElementImpl; |
| 3296 TypeParameterTypeImpl typeParameterType = |
| 3297 new TypeParameterTypeImpl(typeParameter); |
| 3298 typeParameter.type = typeParameterType; |
| 3299 typeArguments[i] = typeParameterType; |
| 3300 } |
| 3301 return typeArguments; |
| 3302 } |
| 3303 |
| 3304 /** |
| 3305 * Return the body of the function that contains the given parameter, or `null
` if no |
| 3306 * function body could be found. |
| 3307 * |
| 3308 * @param node the parameter contained in the function whose body is to be ret
urned |
| 3309 * @return the body of the function that contains the given parameter |
| 3310 */ |
| 3311 FunctionBody _getFunctionBody(FormalParameter node) { |
| 3312 AstNode parent = node.parent; |
| 3313 while (parent != null) { |
| 3314 if (parent is ConstructorDeclaration) { |
| 3315 return parent.body; |
| 3316 } else if (parent is FunctionExpression) { |
| 3317 return parent.body; |
| 3318 } else if (parent is MethodDeclaration) { |
| 3319 return parent.body; |
| 3320 } |
| 3321 parent = parent.parent; |
| 3322 } |
| 3323 return null; |
| 3324 } |
| 3325 |
| 3326 /** |
| 3327 * Sets the visible source range for formal parameter. |
| 3328 */ |
| 3329 void _setParameterVisibleRange( |
| 3330 FormalParameter node, ParameterElementImpl element) { |
| 3331 FunctionBody body = _getFunctionBody(node); |
| 3332 if (body != null) { |
| 3333 element.setVisibleRange(body.offset, body.length); |
| 3334 } |
| 3335 } |
| 3336 |
| 3337 /** |
| 3338 * Make the given holder be the current holder while visiting the given node. |
| 3339 * |
| 3340 * @param holder the holder that will gather elements that are built while vis
iting the children |
| 3341 * @param node the node to be visited |
| 3342 */ |
| 3343 void _visit(ElementHolder holder, AstNode node) { |
| 3344 if (node != null) { |
| 3345 ElementHolder previousHolder = _currentHolder; |
| 3346 _currentHolder = holder; |
| 3347 try { |
| 3348 node.accept(this); |
| 3349 } finally { |
| 3350 _currentHolder = previousHolder; |
| 3351 } |
| 3352 } |
| 3353 } |
| 3354 |
| 3355 /** |
| 3356 * Make the given holder be the current holder while visiting the children of
the given node. |
| 3357 * |
| 3358 * @param holder the holder that will gather elements that are built while vis
iting the children |
| 3359 * @param node the node whose children are to be visited |
| 3360 */ |
| 3361 void _visitChildren(ElementHolder holder, AstNode node) { |
| 3362 if (node != null) { |
| 3363 ElementHolder previousHolder = _currentHolder; |
| 3364 _currentHolder = holder; |
| 3365 try { |
| 3366 node.visitChildren(this); |
| 3367 } finally { |
| 3368 _currentHolder = previousHolder; |
| 3369 } |
| 3370 } |
| 3371 } |
| 3372 } |
| 3373 |
| 3374 /** |
| 3375 * Instances of the class `ElementHolder` hold on to elements created while trav
ersing an AST |
| 3376 * structure so that they can be accessed when creating their enclosing element. |
| 3377 */ |
| 3378 class ElementHolder { |
| 3379 List<PropertyAccessorElement> _accessors; |
| 3380 |
| 3381 List<ConstructorElement> _constructors; |
| 3382 |
| 3383 List<ClassElement> _enums; |
| 3384 |
| 3385 List<FieldElement> _fields; |
| 3386 |
| 3387 List<FunctionElement> _functions; |
| 3388 |
| 3389 List<LabelElement> _labels; |
| 3390 |
| 3391 List<LocalVariableElement> _localVariables; |
| 3392 |
| 3393 List<MethodElement> _methods; |
| 3394 |
| 3395 List<ParameterElement> _parameters; |
| 3396 |
| 3397 List<TopLevelVariableElement> _topLevelVariables; |
| 3398 |
| 3399 List<ClassElement> _types; |
| 3400 |
| 3401 List<FunctionTypeAliasElement> _typeAliases; |
| 3402 |
| 3403 List<TypeParameterElement> _typeParameters; |
| 3404 |
| 3405 List<PropertyAccessorElement> get accessors { |
| 3406 if (_accessors == null) { |
| 3407 return PropertyAccessorElement.EMPTY_LIST; |
| 3408 } |
| 3409 List<PropertyAccessorElement> result = _accessors; |
| 3410 _accessors = null; |
| 3411 return result; |
| 3412 } |
| 3413 |
| 3414 List<ConstructorElement> get constructors { |
| 3415 if (_constructors == null) { |
| 3416 return ConstructorElement.EMPTY_LIST; |
| 3417 } |
| 3418 List<ConstructorElement> result = _constructors; |
| 3419 _constructors = null; |
| 3420 return result; |
| 3421 } |
| 3422 |
| 3423 List<ClassElement> get enums { |
| 3424 if (_enums == null) { |
| 3425 return ClassElement.EMPTY_LIST; |
| 3426 } |
| 3427 List<ClassElement> result = _enums; |
| 3428 _enums = null; |
| 3429 return result; |
| 3430 } |
| 3431 |
| 3432 List<FieldElement> get fields { |
| 3433 if (_fields == null) { |
| 3434 return FieldElement.EMPTY_LIST; |
| 3435 } |
| 3436 List<FieldElement> result = _fields; |
| 3437 _fields = null; |
| 3438 return result; |
| 3439 } |
| 3440 |
| 3441 List<FieldElement> get fieldsWithoutFlushing { |
| 3442 if (_fields == null) { |
| 3443 return FieldElement.EMPTY_LIST; |
| 3444 } |
| 3445 List<FieldElement> result = _fields; |
| 3446 return result; |
| 3447 } |
| 3448 |
| 3449 List<FunctionElement> get functions { |
| 3450 if (_functions == null) { |
| 3451 return FunctionElement.EMPTY_LIST; |
| 3452 } |
| 3453 List<FunctionElement> result = _functions; |
| 3454 _functions = null; |
| 3455 return result; |
| 3456 } |
| 3457 |
| 3458 List<LabelElement> get labels { |
| 3459 if (_labels == null) { |
| 3460 return LabelElement.EMPTY_LIST; |
| 3461 } |
| 3462 List<LabelElement> result = _labels; |
| 3463 _labels = null; |
| 3464 return result; |
| 3465 } |
| 3466 |
| 3467 List<LocalVariableElement> get localVariables { |
| 3468 if (_localVariables == null) { |
| 3469 return LocalVariableElement.EMPTY_LIST; |
| 3470 } |
| 3471 List<LocalVariableElement> result = _localVariables; |
| 3472 _localVariables = null; |
| 3473 return result; |
| 3474 } |
| 3475 |
| 3476 List<MethodElement> get methods { |
| 3477 if (_methods == null) { |
| 3478 return MethodElement.EMPTY_LIST; |
| 3479 } |
| 3480 List<MethodElement> result = _methods; |
| 3481 _methods = null; |
| 3482 return result; |
| 3483 } |
| 3484 |
| 3485 List<ParameterElement> get parameters { |
| 3486 if (_parameters == null) { |
| 3487 return ParameterElement.EMPTY_LIST; |
| 3488 } |
| 3489 List<ParameterElement> result = _parameters; |
| 3490 _parameters = null; |
| 3491 return result; |
| 3492 } |
| 3493 |
| 3494 List<TopLevelVariableElement> get topLevelVariables { |
| 3495 if (_topLevelVariables == null) { |
| 3496 return TopLevelVariableElement.EMPTY_LIST; |
| 3497 } |
| 3498 List<TopLevelVariableElement> result = _topLevelVariables; |
| 3499 _topLevelVariables = null; |
| 3500 return result; |
| 3501 } |
| 3502 |
| 3503 List<FunctionTypeAliasElement> get typeAliases { |
| 3504 if (_typeAliases == null) { |
| 3505 return FunctionTypeAliasElement.EMPTY_LIST; |
| 3506 } |
| 3507 List<FunctionTypeAliasElement> result = _typeAliases; |
| 3508 _typeAliases = null; |
| 3509 return result; |
| 3510 } |
| 3511 |
| 3512 List<TypeParameterElement> get typeParameters { |
| 3513 if (_typeParameters == null) { |
| 3514 return TypeParameterElement.EMPTY_LIST; |
| 3515 } |
| 3516 List<TypeParameterElement> result = _typeParameters; |
| 3517 _typeParameters = null; |
| 3518 return result; |
| 3519 } |
| 3520 |
| 3521 List<ClassElement> get types { |
| 3522 if (_types == null) { |
| 3523 return ClassElement.EMPTY_LIST; |
| 3524 } |
| 3525 List<ClassElement> result = _types; |
| 3526 _types = null; |
| 3527 return result; |
| 3528 } |
| 3529 |
| 3530 void addAccessor(PropertyAccessorElement element) { |
| 3531 if (_accessors == null) { |
| 3532 _accessors = new List<PropertyAccessorElement>(); |
| 3533 } |
| 3534 _accessors.add(element); |
| 3535 } |
| 3536 |
| 3537 void addConstructor(ConstructorElement element) { |
| 3538 if (_constructors == null) { |
| 3539 _constructors = new List<ConstructorElement>(); |
| 3540 } |
| 3541 _constructors.add(element); |
| 3542 } |
| 3543 |
| 3544 void addEnum(ClassElement element) { |
| 3545 if (_enums == null) { |
| 3546 _enums = new List<ClassElement>(); |
| 3547 } |
| 3548 _enums.add(element); |
| 3549 } |
| 3550 |
| 3551 void addField(FieldElement element) { |
| 3552 if (_fields == null) { |
| 3553 _fields = new List<FieldElement>(); |
| 3554 } |
| 3555 _fields.add(element); |
| 3556 } |
| 3557 |
| 3558 void addFunction(FunctionElement element) { |
| 3559 if (_functions == null) { |
| 3560 _functions = new List<FunctionElement>(); |
| 3561 } |
| 3562 _functions.add(element); |
| 3563 } |
| 3564 |
| 3565 void addLabel(LabelElement element) { |
| 3566 if (_labels == null) { |
| 3567 _labels = new List<LabelElement>(); |
| 3568 } |
| 3569 _labels.add(element); |
| 3570 } |
| 3571 |
| 3572 void addLocalVariable(LocalVariableElement element) { |
| 3573 if (_localVariables == null) { |
| 3574 _localVariables = new List<LocalVariableElement>(); |
| 3575 } |
| 3576 _localVariables.add(element); |
| 3577 } |
| 3578 |
| 3579 void addMethod(MethodElement element) { |
| 3580 if (_methods == null) { |
| 3581 _methods = new List<MethodElement>(); |
| 3582 } |
| 3583 _methods.add(element); |
| 3584 } |
| 3585 |
| 3586 void addParameter(ParameterElement element) { |
| 3587 if (_parameters == null) { |
| 3588 _parameters = new List<ParameterElement>(); |
| 3589 } |
| 3590 _parameters.add(element); |
| 3591 } |
| 3592 |
| 3593 void addTopLevelVariable(TopLevelVariableElement element) { |
| 3594 if (_topLevelVariables == null) { |
| 3595 _topLevelVariables = new List<TopLevelVariableElement>(); |
| 3596 } |
| 3597 _topLevelVariables.add(element); |
| 3598 } |
| 3599 |
| 3600 void addType(ClassElement element) { |
| 3601 if (_types == null) { |
| 3602 _types = new List<ClassElement>(); |
| 3603 } |
| 3604 _types.add(element); |
| 3605 } |
| 3606 |
| 3607 void addTypeAlias(FunctionTypeAliasElement element) { |
| 3608 if (_typeAliases == null) { |
| 3609 _typeAliases = new List<FunctionTypeAliasElement>(); |
| 3610 } |
| 3611 _typeAliases.add(element); |
| 3612 } |
| 3613 |
| 3614 void addTypeParameter(TypeParameterElement element) { |
| 3615 if (_typeParameters == null) { |
| 3616 _typeParameters = new List<TypeParameterElement>(); |
| 3617 } |
| 3618 _typeParameters.add(element); |
| 3619 } |
| 3620 |
| 3621 FieldElement getField(String fieldName) { |
| 3622 if (_fields == null) { |
| 3623 return null; |
| 3624 } |
| 3625 for (FieldElement field in _fields) { |
| 3626 if (field.name == fieldName) { |
| 3627 return field; |
| 3628 } |
| 3629 } |
| 3630 return null; |
| 3631 } |
| 3632 |
| 3633 TopLevelVariableElement getTopLevelVariable(String variableName) { |
| 3634 if (_topLevelVariables == null) { |
| 3635 return null; |
| 3636 } |
| 3637 for (TopLevelVariableElement variable in _topLevelVariables) { |
| 3638 if (variable.name == variableName) { |
| 3639 return variable; |
| 3640 } |
| 3641 } |
| 3642 return null; |
| 3643 } |
| 3644 |
| 3645 void validate() { |
| 3646 StringBuffer buffer = new StringBuffer(); |
| 3647 if (_accessors != null) { |
| 3648 buffer.write(_accessors.length); |
| 3649 buffer.write(" accessors"); |
| 3650 } |
| 3651 if (_constructors != null) { |
| 3652 if (buffer.length > 0) { |
| 3653 buffer.write("; "); |
| 3654 } |
| 3655 buffer.write(_constructors.length); |
| 3656 buffer.write(" constructors"); |
| 3657 } |
| 3658 if (_fields != null) { |
| 3659 if (buffer.length > 0) { |
| 3660 buffer.write("; "); |
| 3661 } |
| 3662 buffer.write(_fields.length); |
| 3663 buffer.write(" fields"); |
| 3664 } |
| 3665 if (_functions != null) { |
| 3666 if (buffer.length > 0) { |
| 3667 buffer.write("; "); |
| 3668 } |
| 3669 buffer.write(_functions.length); |
| 3670 buffer.write(" functions"); |
| 3671 } |
| 3672 if (_labels != null) { |
| 3673 if (buffer.length > 0) { |
| 3674 buffer.write("; "); |
| 3675 } |
| 3676 buffer.write(_labels.length); |
| 3677 buffer.write(" labels"); |
| 3678 } |
| 3679 if (_localVariables != null) { |
| 3680 if (buffer.length > 0) { |
| 3681 buffer.write("; "); |
| 3682 } |
| 3683 buffer.write(_localVariables.length); |
| 3684 buffer.write(" local variables"); |
| 3685 } |
| 3686 if (_methods != null) { |
| 3687 if (buffer.length > 0) { |
| 3688 buffer.write("; "); |
| 3689 } |
| 3690 buffer.write(_methods.length); |
| 3691 buffer.write(" methods"); |
| 3692 } |
| 3693 if (_parameters != null) { |
| 3694 if (buffer.length > 0) { |
| 3695 buffer.write("; "); |
| 3696 } |
| 3697 buffer.write(_parameters.length); |
| 3698 buffer.write(" parameters"); |
| 3699 } |
| 3700 if (_topLevelVariables != null) { |
| 3701 if (buffer.length > 0) { |
| 3702 buffer.write("; "); |
| 3703 } |
| 3704 buffer.write(_topLevelVariables.length); |
| 3705 buffer.write(" top-level variables"); |
| 3706 } |
| 3707 if (_types != null) { |
| 3708 if (buffer.length > 0) { |
| 3709 buffer.write("; "); |
| 3710 } |
| 3711 buffer.write(_types.length); |
| 3712 buffer.write(" types"); |
| 3713 } |
| 3714 if (_typeAliases != null) { |
| 3715 if (buffer.length > 0) { |
| 3716 buffer.write("; "); |
| 3717 } |
| 3718 buffer.write(_typeAliases.length); |
| 3719 buffer.write(" type aliases"); |
| 3720 } |
| 3721 if (_typeParameters != null) { |
| 3722 if (buffer.length > 0) { |
| 3723 buffer.write("; "); |
| 3724 } |
| 3725 buffer.write(_typeParameters.length); |
| 3726 buffer.write(" type parameters"); |
| 3727 } |
| 3728 if (buffer.length > 0) { |
| 3729 AnalysisEngine.instance.logger |
| 3730 .logError("Failed to capture elements: $buffer"); |
| 3731 } |
| 3732 } |
| 3733 } |
| 3734 |
| 3735 /** |
| 3736 * Instances of the class `EnclosedScope` implement a scope that is lexically en
closed in |
| 3737 * another scope. |
| 3738 */ |
| 3739 class EnclosedScope extends Scope { |
| 3740 /** |
| 3741 * The scope in which this scope is lexically enclosed. |
| 3742 */ |
| 3743 final Scope enclosingScope; |
| 3744 |
| 3745 /** |
| 3746 * A table mapping names that will be defined in this scope, but right now are
not initialized. |
| 3747 * According to the scoping rules these names are hidden, even if they were de
fined in an outer |
| 3748 * scope. |
| 3749 */ |
| 3750 HashMap<String, Element> _hiddenElements = new HashMap<String, Element>(); |
| 3751 |
| 3752 /** |
| 3753 * A flag indicating whether there are any names defined in this scope. |
| 3754 */ |
| 3755 bool _hasHiddenName = false; |
| 3756 |
| 3757 /** |
| 3758 * Initialize a newly created scope enclosed within another scope. |
| 3759 * |
| 3760 * @param enclosingScope the scope in which this scope is lexically enclosed |
| 3761 */ |
| 3762 EnclosedScope(this.enclosingScope); |
| 3763 |
| 3764 @override |
| 3765 AnalysisErrorListener get errorListener => enclosingScope.errorListener; |
| 3766 |
| 3767 /** |
| 3768 * Record that given element is declared in this scope, but hasn't been initia
lized yet, so it is |
| 3769 * error to use. If there is already an element with the given name defined in
an outer scope, |
| 3770 * then it will become unavailable. |
| 3771 * |
| 3772 * @param element the element declared, but not initialized in this scope |
| 3773 */ |
| 3774 void hide(Element element) { |
| 3775 if (element != null) { |
| 3776 String name = element.name; |
| 3777 if (name != null && !name.isEmpty) { |
| 3778 _hiddenElements[name] = element; |
| 3779 _hasHiddenName = true; |
| 3780 } |
| 3781 } |
| 3782 } |
| 3783 |
| 3784 @override |
| 3785 Element internalLookup( |
| 3786 Identifier identifier, String name, LibraryElement referencingLibrary) { |
| 3787 Element element = localLookup(name, referencingLibrary); |
| 3788 if (element != null) { |
| 3789 return element; |
| 3790 } |
| 3791 // May be there is a hidden Element. |
| 3792 if (_hasHiddenName) { |
| 3793 Element hiddenElement = _hiddenElements[name]; |
| 3794 if (hiddenElement != null) { |
| 3795 errorListener.onError(new AnalysisError( |
| 3796 getSource(identifier), |
| 3797 identifier.offset, |
| 3798 identifier.length, |
| 3799 CompileTimeErrorCode.REFERENCED_BEFORE_DECLARATION, [])); |
| 3800 return hiddenElement; |
| 3801 } |
| 3802 } |
| 3803 // Check enclosing scope. |
| 3804 return enclosingScope.internalLookup(identifier, name, referencingLibrary); |
| 3805 } |
| 3806 } |
| 3807 |
| 3808 /** |
| 3809 * Instances of the class `EnumMemberBuilder` build the members in enum declarat
ions. |
| 3810 */ |
| 3811 class EnumMemberBuilder extends RecursiveAstVisitor<Object> { |
| 3812 /** |
| 3813 * The type provider used to access the types needed to build an element model
for enum |
| 3814 * declarations. |
| 3815 */ |
| 3816 final TypeProvider _typeProvider; |
| 3817 |
| 3818 /** |
| 3819 * Initialize a newly created enum member builder. |
| 3820 * |
| 3821 * @param typeProvider the type provider used to access the types needed to bu
ild an element model |
| 3822 * for enum declarations |
| 3823 */ |
| 3824 EnumMemberBuilder(this._typeProvider); |
| 3825 |
| 3826 @override |
| 3827 Object visitEnumDeclaration(EnumDeclaration node) { |
| 3828 // |
| 3829 // Finish building the enum. |
| 3830 // |
| 3831 ClassElementImpl enumElement = node.name.staticElement as ClassElementImpl; |
| 3832 InterfaceType enumType = enumElement.type; |
| 3833 enumElement.supertype = _typeProvider.objectType; |
| 3834 // |
| 3835 // Populate the fields. |
| 3836 // |
| 3837 List<FieldElement> fields = new List<FieldElement>(); |
| 3838 List<PropertyAccessorElement> getters = new List<PropertyAccessorElement>(); |
| 3839 InterfaceType intType = _typeProvider.intType; |
| 3840 String indexFieldName = "index"; |
| 3841 FieldElementImpl indexField = new FieldElementImpl(indexFieldName, -1); |
| 3842 indexField.final2 = true; |
| 3843 indexField.synthetic = true; |
| 3844 indexField.type = intType; |
| 3845 fields.add(indexField); |
| 3846 getters.add(_createGetter(indexField)); |
| 3847 ConstFieldElementImpl valuesField = new ConstFieldElementImpl("values", -1); |
| 3848 valuesField.static = true; |
| 3849 valuesField.const3 = true; |
| 3850 valuesField.synthetic = true; |
| 3851 valuesField.type = _typeProvider.listType.substitute4(<DartType>[enumType]); |
| 3852 fields.add(valuesField); |
| 3853 getters.add(_createGetter(valuesField)); |
| 3854 // |
| 3855 // Build the enum constants. |
| 3856 // |
| 3857 NodeList<EnumConstantDeclaration> constants = node.constants; |
| 3858 List<DartObjectImpl> constantValues = new List<DartObjectImpl>(); |
| 3859 int constantCount = constants.length; |
| 3860 for (int i = 0; i < constantCount; i++) { |
| 3861 SimpleIdentifier constantName = constants[i].name; |
| 3862 FieldElementImpl constantField = |
| 3863 new ConstFieldElementImpl.forNode(constantName); |
| 3864 constantField.static = true; |
| 3865 constantField.const3 = true; |
| 3866 constantField.type = enumType; |
| 3867 // |
| 3868 // Create a value for the constant. |
| 3869 // |
| 3870 HashMap<String, DartObjectImpl> fieldMap = |
| 3871 new HashMap<String, DartObjectImpl>(); |
| 3872 fieldMap[indexFieldName] = new DartObjectImpl(intType, new IntState(i)); |
| 3873 DartObjectImpl value = |
| 3874 new DartObjectImpl(enumType, new GenericState(fieldMap)); |
| 3875 constantValues.add(value); |
| 3876 constantField.evaluationResult = new EvaluationResultImpl(value); |
| 3877 fields.add(constantField); |
| 3878 getters.add(_createGetter(constantField)); |
| 3879 constantName.staticElement = constantField; |
| 3880 } |
| 3881 // |
| 3882 // Build the value of the 'values' field. |
| 3883 // |
| 3884 valuesField.evaluationResult = new EvaluationResultImpl( |
| 3885 new DartObjectImpl(valuesField.type, new ListState(constantValues))); |
| 3886 // |
| 3887 // Finish building the enum. |
| 3888 // |
| 3889 enumElement.fields = fields; |
| 3890 enumElement.accessors = getters; |
| 3891 // Client code isn't allowed to invoke the constructor, so we do not model |
| 3892 // it. |
| 3893 return super.visitEnumDeclaration(node); |
| 3894 } |
| 3895 |
| 3896 /** |
| 3897 * Create a getter that corresponds to the given field. |
| 3898 * |
| 3899 * @param field the field for which a getter is to be created |
| 3900 * @return the getter that was created |
| 3901 */ |
| 3902 PropertyAccessorElement _createGetter(FieldElementImpl field) { |
| 3903 PropertyAccessorElementImpl getter = |
| 3904 new PropertyAccessorElementImpl.forVariable(field); |
| 3905 getter.getter = true; |
| 3906 getter.returnType = field.type; |
| 3907 getter.type = new FunctionTypeImpl(getter); |
| 3908 field.getter = getter; |
| 3909 return getter; |
| 3910 } |
| 3911 } |
| 3912 |
| 3913 /** |
| 3914 * Instances of the class `ExitDetector` determine whether the visited AST node
is guaranteed |
| 3915 * to terminate by executing a `return` statement, `throw` expression, `rethrow` |
| 3916 * expression, or simple infinite loop such as `while(true)`. |
| 3917 */ |
| 3918 class ExitDetector extends GeneralizingAstVisitor<bool> { |
| 3919 /** |
| 3920 * Set to `true` when a `break` is encountered, and reset to `false` when a |
| 3921 * `do`, `while`, `for` or `switch` block is entered. |
| 3922 */ |
| 3923 bool _enclosingBlockContainsBreak = false; |
| 3924 |
| 3925 @override |
| 3926 bool visitArgumentList(ArgumentList node) => |
| 3927 _visitExpressions(node.arguments); |
| 3928 |
| 3929 @override |
| 3930 bool visitAsExpression(AsExpression node) => _nodeExits(node.expression); |
| 3931 |
| 3932 @override |
| 3933 bool visitAssertStatement(AssertStatement node) => _nodeExits(node.condition); |
| 3934 |
| 3935 @override |
| 3936 bool visitAssignmentExpression(AssignmentExpression node) => |
| 3937 _nodeExits(node.leftHandSide) || _nodeExits(node.rightHandSide); |
| 3938 |
| 3939 @override |
| 3940 bool visitAwaitExpression(AwaitExpression node) => |
| 3941 _nodeExits(node.expression); |
| 3942 |
| 3943 @override |
| 3944 bool visitBinaryExpression(BinaryExpression node) { |
| 3945 Expression lhsExpression = node.leftOperand; |
| 3946 sc.TokenType operatorType = node.operator.type; |
| 3947 // If the operator is || and the left hand side is false literal, don't |
| 3948 // consider the RHS of the binary expression. |
| 3949 // TODO(jwren) Do we want to take constant expressions into account, |
| 3950 // evaluate if(false) {} differently than if(<condition>), when <condition> |
| 3951 // evaluates to a constant false value? |
| 3952 if (operatorType == sc.TokenType.BAR_BAR) { |
| 3953 if (lhsExpression is BooleanLiteral) { |
| 3954 BooleanLiteral booleanLiteral = lhsExpression; |
| 3955 if (!booleanLiteral.value) { |
| 3956 return false; |
| 3957 } |
| 3958 } |
| 3959 } |
| 3960 // If the operator is && and the left hand side is true literal, don't |
| 3961 // consider the RHS of the binary expression. |
| 3962 if (operatorType == sc.TokenType.AMPERSAND_AMPERSAND) { |
| 3963 if (lhsExpression is BooleanLiteral) { |
| 3964 BooleanLiteral booleanLiteral = lhsExpression; |
| 3965 if (booleanLiteral.value) { |
| 3966 return false; |
| 3967 } |
| 3968 } |
| 3969 } |
| 3970 Expression rhsExpression = node.rightOperand; |
| 3971 return _nodeExits(lhsExpression) || _nodeExits(rhsExpression); |
| 3972 } |
| 3973 |
| 3974 @override |
| 3975 bool visitBlock(Block node) => _visitStatements(node.statements); |
| 3976 |
| 3977 @override |
| 3978 bool visitBlockFunctionBody(BlockFunctionBody node) => _nodeExits(node.block); |
| 3979 |
| 3980 @override |
| 3981 bool visitBreakStatement(BreakStatement node) { |
| 3982 _enclosingBlockContainsBreak = true; |
| 3983 return false; |
| 3984 } |
| 3985 |
| 3986 @override |
| 3987 bool visitCascadeExpression(CascadeExpression node) => |
| 3988 _nodeExits(node.target) || _visitExpressions(node.cascadeSections); |
| 3989 |
| 3990 @override |
| 3991 bool visitConditionalExpression(ConditionalExpression node) { |
| 3992 Expression conditionExpression = node.condition; |
| 3993 Expression thenStatement = node.thenExpression; |
| 3994 Expression elseStatement = node.elseExpression; |
| 3995 // TODO(jwren) Do we want to take constant expressions into account, |
| 3996 // evaluate if(false) {} differently than if(<condition>), when <condition> |
| 3997 // evaluates to a constant false value? |
| 3998 if (_nodeExits(conditionExpression)) { |
| 3999 return true; |
| 4000 } |
| 4001 if (thenStatement == null || elseStatement == null) { |
| 4002 return false; |
| 4003 } |
| 4004 return thenStatement.accept(this) && elseStatement.accept(this); |
| 4005 } |
| 4006 |
| 4007 @override |
| 4008 bool visitContinueStatement(ContinueStatement node) => false; |
| 4009 |
| 4010 @override |
| 4011 bool visitDoStatement(DoStatement node) { |
| 4012 bool outerBreakValue = _enclosingBlockContainsBreak; |
| 4013 _enclosingBlockContainsBreak = false; |
| 4014 try { |
| 4015 Expression conditionExpression = node.condition; |
| 4016 if (_nodeExits(conditionExpression)) { |
| 4017 return true; |
| 4018 } |
| 4019 // TODO(jwren) Do we want to take all constant expressions into account? |
| 4020 if (conditionExpression is BooleanLiteral) { |
| 4021 BooleanLiteral booleanLiteral = conditionExpression; |
| 4022 // If do {} while (true), and the body doesn't return or the body |
| 4023 // doesn't have a break, then return true. |
| 4024 bool blockReturns = _nodeExits(node.body); |
| 4025 if (booleanLiteral.value && |
| 4026 (blockReturns || !_enclosingBlockContainsBreak)) { |
| 4027 return true; |
| 4028 } |
| 4029 } |
| 4030 return false; |
| 4031 } finally { |
| 4032 _enclosingBlockContainsBreak = outerBreakValue; |
| 4033 } |
| 4034 } |
| 4035 |
| 4036 @override |
| 4037 bool visitEmptyStatement(EmptyStatement node) => false; |
| 4038 |
| 4039 @override |
| 4040 bool visitExpressionStatement(ExpressionStatement node) => |
| 4041 _nodeExits(node.expression); |
| 4042 |
| 4043 @override |
| 4044 bool visitForEachStatement(ForEachStatement node) { |
| 4045 bool outerBreakValue = _enclosingBlockContainsBreak; |
| 4046 _enclosingBlockContainsBreak = false; |
| 4047 try { |
| 4048 return _nodeExits(node.iterable); |
| 4049 } finally { |
| 4050 _enclosingBlockContainsBreak = outerBreakValue; |
| 4051 } |
| 4052 } |
| 4053 |
| 4054 @override |
| 4055 bool visitForStatement(ForStatement node) { |
| 4056 bool outerBreakValue = _enclosingBlockContainsBreak; |
| 4057 _enclosingBlockContainsBreak = false; |
| 4058 try { |
| 4059 if (node.variables != null && |
| 4060 _visitVariableDeclarations(node.variables.variables)) { |
| 4061 return true; |
| 4062 } |
| 4063 if (node.initialization != null && _nodeExits(node.initialization)) { |
| 4064 return true; |
| 4065 } |
| 4066 Expression conditionExpression = node.condition; |
| 4067 if (conditionExpression != null && _nodeExits(conditionExpression)) { |
| 4068 return true; |
| 4069 } |
| 4070 if (_visitExpressions(node.updaters)) { |
| 4071 return true; |
| 4072 } |
| 4073 // TODO(jwren) Do we want to take all constant expressions into account? |
| 4074 // If for(; true; ) (or for(;;)), and the body doesn't return or the body |
| 4075 // doesn't have a break, then return true. |
| 4076 bool implicitOrExplictTrue = conditionExpression == null || |
| 4077 (conditionExpression is BooleanLiteral && conditionExpression.value); |
| 4078 if (implicitOrExplictTrue) { |
| 4079 bool blockReturns = _nodeExits(node.body); |
| 4080 if (blockReturns || !_enclosingBlockContainsBreak) { |
| 4081 return true; |
| 4082 } |
| 4083 } |
| 4084 return false; |
| 4085 } finally { |
| 4086 _enclosingBlockContainsBreak = outerBreakValue; |
| 4087 } |
| 4088 } |
| 4089 |
| 4090 @override |
| 4091 bool visitFunctionDeclarationStatement(FunctionDeclarationStatement node) => |
| 4092 false; |
| 4093 |
| 4094 @override |
| 4095 bool visitFunctionExpression(FunctionExpression node) => false; |
| 4096 |
| 4097 @override |
| 4098 bool visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { |
| 4099 if (_nodeExits(node.function)) { |
| 4100 return true; |
| 4101 } |
| 4102 return node.argumentList.accept(this); |
| 4103 } |
| 4104 |
| 4105 @override |
| 4106 bool visitIdentifier(Identifier node) => false; |
| 4107 |
| 4108 @override |
| 4109 bool visitIfStatement(IfStatement node) { |
| 4110 Expression conditionExpression = node.condition; |
| 4111 Statement thenStatement = node.thenStatement; |
| 4112 Statement elseStatement = node.elseStatement; |
| 4113 if (_nodeExits(conditionExpression)) { |
| 4114 return true; |
| 4115 } |
| 4116 // TODO(jwren) Do we want to take all constant expressions into account? |
| 4117 if (conditionExpression is BooleanLiteral) { |
| 4118 BooleanLiteral booleanLiteral = conditionExpression; |
| 4119 if (booleanLiteral.value) { |
| 4120 // if(true) ... |
| 4121 return _nodeExits(thenStatement); |
| 4122 } else if (elseStatement != null) { |
| 4123 // if (false) ... |
| 4124 return _nodeExits(elseStatement); |
| 4125 } |
| 4126 } |
| 4127 if (thenStatement == null || elseStatement == null) { |
| 4128 return false; |
| 4129 } |
| 4130 return _nodeExits(thenStatement) && _nodeExits(elseStatement); |
| 4131 } |
| 4132 |
| 4133 @override |
| 4134 bool visitIndexExpression(IndexExpression node) { |
| 4135 Expression target = node.realTarget; |
| 4136 if (_nodeExits(target)) { |
| 4137 return true; |
| 4138 } |
| 4139 if (_nodeExits(node.index)) { |
| 4140 return true; |
| 4141 } |
| 4142 return false; |
| 4143 } |
| 4144 |
| 4145 @override |
| 4146 bool visitInstanceCreationExpression(InstanceCreationExpression node) => |
| 4147 _nodeExits(node.argumentList); |
| 4148 |
| 4149 @override |
| 4150 bool visitIsExpression(IsExpression node) => node.expression.accept(this); |
| 4151 |
| 4152 @override |
| 4153 bool visitLabel(Label node) => false; |
| 4154 |
| 4155 @override |
| 4156 bool visitLabeledStatement(LabeledStatement node) => |
| 4157 node.statement.accept(this); |
| 4158 |
| 4159 @override |
| 4160 bool visitLiteral(Literal node) => false; |
| 4161 |
| 4162 @override |
| 4163 bool visitMethodInvocation(MethodInvocation node) { |
| 4164 Expression target = node.realTarget; |
| 4165 if (target != null && target.accept(this)) { |
| 4166 return true; |
| 4167 } |
| 4168 return _nodeExits(node.argumentList); |
| 4169 } |
| 4170 |
| 4171 @override |
| 4172 bool visitNamedExpression(NamedExpression node) => |
| 4173 node.expression.accept(this); |
| 4174 |
| 4175 @override |
| 4176 bool visitParenthesizedExpression(ParenthesizedExpression node) => |
| 4177 node.expression.accept(this); |
| 4178 |
| 4179 @override |
| 4180 bool visitPostfixExpression(PostfixExpression node) => false; |
| 4181 |
| 4182 @override |
| 4183 bool visitPrefixExpression(PrefixExpression node) => false; |
| 4184 |
| 4185 @override |
| 4186 bool visitPropertyAccess(PropertyAccess node) { |
| 4187 Expression target = node.realTarget; |
| 4188 if (target != null && target.accept(this)) { |
| 4189 return true; |
| 4190 } |
| 4191 return false; |
| 4192 } |
| 4193 |
| 4194 @override |
| 4195 bool visitRethrowExpression(RethrowExpression node) => true; |
| 4196 |
| 4197 @override |
| 4198 bool visitReturnStatement(ReturnStatement node) => true; |
| 4199 |
| 4200 @override |
| 4201 bool visitSuperExpression(SuperExpression node) => false; |
| 4202 |
| 4203 @override |
| 4204 bool visitSwitchCase(SwitchCase node) => _visitStatements(node.statements); |
| 4205 |
| 4206 @override |
| 4207 bool visitSwitchDefault(SwitchDefault node) => |
| 4208 _visitStatements(node.statements); |
| 4209 |
| 4210 @override |
| 4211 bool visitSwitchStatement(SwitchStatement node) { |
| 4212 bool outerBreakValue = _enclosingBlockContainsBreak; |
| 4213 _enclosingBlockContainsBreak = false; |
| 4214 try { |
| 4215 bool hasDefault = false; |
| 4216 List<SwitchMember> members = node.members; |
| 4217 for (int i = 0; i < members.length; i++) { |
| 4218 SwitchMember switchMember = members[i]; |
| 4219 if (switchMember is SwitchDefault) { |
| 4220 hasDefault = true; |
| 4221 // If this is the last member and there are no statements, return |
| 4222 // false |
| 4223 if (switchMember.statements.isEmpty && i + 1 == members.length) { |
| 4224 return false; |
| 4225 } |
| 4226 } |
| 4227 // For switch members with no statements, don't visit the children, |
| 4228 // otherwise, return false if no return is found in the children |
| 4229 // statements. |
| 4230 if (!switchMember.statements.isEmpty && !switchMember.accept(this)) { |
| 4231 return false; |
| 4232 } |
| 4233 } |
| 4234 // All of the members exit, determine whether there are possible cases |
| 4235 // that are not caught by the members. |
| 4236 DartType type = node.expression == null ? null : node.expression.bestType; |
| 4237 if (type is InterfaceType) { |
| 4238 ClassElement element = type.element; |
| 4239 if (element != null && element.isEnum) { |
| 4240 // If some of the enum values are not covered, then a warning will |
| 4241 // have already been generated, so there's no point in generating a |
| 4242 // hint. |
| 4243 return true; |
| 4244 } |
| 4245 } |
| 4246 return hasDefault; |
| 4247 } finally { |
| 4248 _enclosingBlockContainsBreak = outerBreakValue; |
| 4249 } |
| 4250 } |
| 4251 |
| 4252 @override |
| 4253 bool visitThisExpression(ThisExpression node) => false; |
| 4254 |
| 4255 @override |
| 4256 bool visitThrowExpression(ThrowExpression node) => true; |
| 4257 |
| 4258 @override |
| 4259 bool visitTryStatement(TryStatement node) { |
| 4260 if (_nodeExits(node.body)) { |
| 4261 return true; |
| 4262 } |
| 4263 Block finallyBlock = node.finallyBlock; |
| 4264 if (_nodeExits(finallyBlock)) { |
| 4265 return true; |
| 4266 } |
| 4267 return false; |
| 4268 } |
| 4269 |
| 4270 @override |
| 4271 bool visitTypeName(TypeName node) => false; |
| 4272 |
| 4273 @override |
| 4274 bool visitVariableDeclaration(VariableDeclaration node) { |
| 4275 Expression initializer = node.initializer; |
| 4276 if (initializer != null) { |
| 4277 return initializer.accept(this); |
| 4278 } |
| 4279 return false; |
| 4280 } |
| 4281 |
| 4282 @override |
| 4283 bool visitVariableDeclarationList(VariableDeclarationList node) => |
| 4284 _visitVariableDeclarations(node.variables); |
| 4285 |
| 4286 @override |
| 4287 bool visitVariableDeclarationStatement(VariableDeclarationStatement node) { |
| 4288 NodeList<VariableDeclaration> variables = node.variables.variables; |
| 4289 for (int i = 0; i < variables.length; i++) { |
| 4290 if (variables[i].accept(this)) { |
| 4291 return true; |
| 4292 } |
| 4293 } |
| 4294 return false; |
| 4295 } |
| 4296 |
| 4297 @override |
| 4298 bool visitWhileStatement(WhileStatement node) { |
| 4299 bool outerBreakValue = _enclosingBlockContainsBreak; |
| 4300 _enclosingBlockContainsBreak = false; |
| 4301 try { |
| 4302 Expression conditionExpression = node.condition; |
| 4303 if (conditionExpression.accept(this)) { |
| 4304 return true; |
| 4305 } |
| 4306 // TODO(jwren) Do we want to take all constant expressions into account? |
| 4307 if (conditionExpression is BooleanLiteral) { |
| 4308 BooleanLiteral booleanLiteral = conditionExpression; |
| 4309 // If while(true), and the body doesn't return or the body doesn't have |
| 4310 // a break, then return true. |
| 4311 bool blockReturns = node.body.accept(this); |
| 4312 if (booleanLiteral.value && |
| 4313 (blockReturns || !_enclosingBlockContainsBreak)) { |
| 4314 return true; |
| 4315 } |
| 4316 } |
| 4317 return false; |
| 4318 } finally { |
| 4319 _enclosingBlockContainsBreak = outerBreakValue; |
| 4320 } |
| 4321 } |
| 4322 |
| 4323 /** |
| 4324 * Return `true` if the given node exits. |
| 4325 * |
| 4326 * @param node the node being tested |
| 4327 * @return `true` if the given node exits |
| 4328 */ |
| 4329 bool _nodeExits(AstNode node) { |
| 4330 if (node == null) { |
| 4331 return false; |
| 4332 } |
| 4333 return node.accept(this); |
| 4334 } |
| 4335 |
| 4336 bool _visitExpressions(NodeList<Expression> expressions) { |
| 4337 for (int i = expressions.length - 1; i >= 0; i--) { |
| 4338 if (expressions[i].accept(this)) { |
| 4339 return true; |
| 4340 } |
| 4341 } |
| 4342 return false; |
| 4343 } |
| 4344 |
| 4345 bool _visitStatements(NodeList<Statement> statements) { |
| 4346 for (int i = statements.length - 1; i >= 0; i--) { |
| 4347 if (statements[i].accept(this)) { |
| 4348 return true; |
| 4349 } |
| 4350 } |
| 4351 return false; |
| 4352 } |
| 4353 |
| 4354 bool _visitVariableDeclarations( |
| 4355 NodeList<VariableDeclaration> variableDeclarations) { |
| 4356 for (int i = variableDeclarations.length - 1; i >= 0; i--) { |
| 4357 if (variableDeclarations[i].accept(this)) { |
| 4358 return true; |
| 4359 } |
| 4360 } |
| 4361 return false; |
| 4362 } |
| 4363 |
| 4364 /** |
| 4365 * Return `true` if the given [node] exits. |
| 4366 */ |
| 4367 static bool exits(AstNode node) { |
| 4368 return new ExitDetector()._nodeExits(node); |
| 4369 } |
| 4370 } |
| 4371 |
| 4372 /** |
| 4373 * The scope defined by a function. |
| 4374 */ |
| 4375 class FunctionScope extends EnclosedScope { |
| 4376 /** |
| 4377 * The element representing the function that defines this scope. |
| 4378 */ |
| 4379 final ExecutableElement _functionElement; |
| 4380 |
| 4381 /** |
| 4382 * A flag indicating whether the parameters have already been defined, used to |
| 4383 * prevent the parameters from being defined multiple times. |
| 4384 */ |
| 4385 bool _parametersDefined = false; |
| 4386 |
| 4387 /** |
| 4388 * Initialize a newly created scope enclosed within the [enclosingScope] that |
| 4389 * represents the given [_functionElement]. |
| 4390 */ |
| 4391 FunctionScope(Scope enclosingScope, this._functionElement) |
| 4392 : super(new EnclosedScope(new EnclosedScope(enclosingScope))) { |
| 4393 if (_functionElement == null) { |
| 4394 throw new IllegalArgumentException("function element cannot be null"); |
| 4395 } |
| 4396 _defineTypeParameters(); |
| 4397 } |
| 4398 |
| 4399 /** |
| 4400 * Define the parameters for the given function in the scope that encloses |
| 4401 * this function. |
| 4402 */ |
| 4403 void defineParameters() { |
| 4404 if (_parametersDefined) { |
| 4405 return; |
| 4406 } |
| 4407 _parametersDefined = true; |
| 4408 Scope parameterScope = enclosingScope; |
| 4409 for (ParameterElement parameter in _functionElement.parameters) { |
| 4410 if (!parameter.isInitializingFormal) { |
| 4411 parameterScope.define(parameter); |
| 4412 } |
| 4413 } |
| 4414 } |
| 4415 |
| 4416 /** |
| 4417 * Define the type parameters for the function. |
| 4418 */ |
| 4419 void _defineTypeParameters() { |
| 4420 Scope typeParameterScope = enclosingScope.enclosingScope; |
| 4421 for (TypeParameterElement typeParameter |
| 4422 in _functionElement.typeParameters) { |
| 4423 typeParameterScope.define(typeParameter); |
| 4424 } |
| 4425 } |
| 4426 } |
| 4427 |
| 4428 /** |
| 4429 * The scope defined by a function type alias. |
| 4430 */ |
| 4431 class FunctionTypeScope extends EnclosedScope { |
| 4432 final FunctionTypeAliasElement _typeElement; |
| 4433 |
| 4434 bool _parametersDefined = false; |
| 4435 |
| 4436 /** |
| 4437 * Initialize a newly created scope enclosed within the [enclosingScope] that |
| 4438 * represents the given [_typeElement]. |
| 4439 */ |
| 4440 FunctionTypeScope(Scope enclosingScope, this._typeElement) |
| 4441 : super(new EnclosedScope(enclosingScope)) { |
| 4442 _defineTypeParameters(); |
| 4443 } |
| 4444 |
| 4445 /** |
| 4446 * Define the parameters for the function type alias. |
| 4447 */ |
| 4448 void defineParameters() { |
| 4449 if (_parametersDefined) { |
| 4450 return; |
| 4451 } |
| 4452 _parametersDefined = true; |
| 4453 for (ParameterElement parameter in _typeElement.parameters) { |
| 4454 define(parameter); |
| 4455 } |
| 4456 } |
| 4457 |
| 4458 /** |
| 4459 * Define the type parameters for the function type alias. |
| 4460 */ |
| 4461 void _defineTypeParameters() { |
| 4462 Scope typeParameterScope = enclosingScope; |
| 4463 for (TypeParameterElement typeParameter in _typeElement.typeParameters) { |
| 4464 typeParameterScope.define(typeParameter); |
| 4465 } |
| 4466 } |
| 4467 } |
| 4468 |
| 4469 /** |
| 4470 * A visitor that visits ASTs and fills [UsedImportedElements]. |
| 4471 */ |
| 4472 class GatherUsedImportedElementsVisitor extends RecursiveAstVisitor { |
| 4473 final LibraryElement library; |
| 4474 final UsedImportedElements usedElements = new UsedImportedElements(); |
| 4475 |
| 4476 GatherUsedImportedElementsVisitor(this.library); |
| 4477 |
| 4478 @override |
| 4479 void visitExportDirective(ExportDirective node) { |
| 4480 _visitMetadata(node.metadata); |
| 4481 } |
| 4482 |
| 4483 @override |
| 4484 void visitImportDirective(ImportDirective node) { |
| 4485 _visitMetadata(node.metadata); |
| 4486 } |
| 4487 |
| 4488 @override |
| 4489 void visitLibraryDirective(LibraryDirective node) { |
| 4490 _visitMetadata(node.metadata); |
| 4491 } |
| 4492 |
| 4493 @override |
| 4494 void visitPrefixedIdentifier(PrefixedIdentifier node) { |
| 4495 // If the prefixed identifier references some A.B, where A is a library |
| 4496 // prefix, then we can lookup the associated ImportDirective in |
| 4497 // prefixElementMap and remove it from the unusedImports list. |
| 4498 SimpleIdentifier prefixIdentifier = node.prefix; |
| 4499 Element element = prefixIdentifier.staticElement; |
| 4500 if (element is PrefixElement) { |
| 4501 usedElements.prefixes.add(element); |
| 4502 return; |
| 4503 } |
| 4504 // Otherwise, pass the prefixed identifier element and name onto |
| 4505 // visitIdentifier. |
| 4506 _visitIdentifier(element, prefixIdentifier.name); |
| 4507 } |
| 4508 |
| 4509 @override |
| 4510 void visitSimpleIdentifier(SimpleIdentifier node) { |
| 4511 _visitIdentifier(node.staticElement, node.name); |
| 4512 } |
| 4513 |
| 4514 void _visitIdentifier(Element element, String name) { |
| 4515 if (element == null) { |
| 4516 return; |
| 4517 } |
| 4518 // If the element is multiply defined then call this method recursively for |
| 4519 // each of the conflicting elements. |
| 4520 if (element is MultiplyDefinedElement) { |
| 4521 MultiplyDefinedElement multiplyDefinedElement = element; |
| 4522 for (Element elt in multiplyDefinedElement.conflictingElements) { |
| 4523 _visitIdentifier(elt, name); |
| 4524 } |
| 4525 return; |
| 4526 } else if (element is PrefixElement) { |
| 4527 usedElements.prefixes.add(element); |
| 4528 return; |
| 4529 } else if (element.enclosingElement is! CompilationUnitElement) { |
| 4530 // Identifiers that aren't a prefix element and whose enclosing element |
| 4531 // isn't a CompilationUnit are ignored- this covers the case the |
| 4532 // identifier is a relative-reference, a reference to an identifier not |
| 4533 // imported by this library. |
| 4534 return; |
| 4535 } |
| 4536 // Ignore if an unknown library. |
| 4537 LibraryElement containingLibrary = element.library; |
| 4538 if (containingLibrary == null) { |
| 4539 return; |
| 4540 } |
| 4541 // Ignore if a local element. |
| 4542 if (library == containingLibrary) { |
| 4543 return; |
| 4544 } |
| 4545 // Remember the element. |
| 4546 usedElements.elements.add(element); |
| 4547 } |
| 4548 |
| 4549 /** |
| 4550 * Given some [NodeList] of [Annotation]s, ensure that the identifiers are vis
ited by |
| 4551 * this visitor. Specifically, this covers the cases where AST nodes don't hav
e their identifiers |
| 4552 * visited by this visitor, but still need their annotations visited. |
| 4553 * |
| 4554 * @param annotations the list of annotations to visit |
| 4555 */ |
| 4556 void _visitMetadata(NodeList<Annotation> annotations) { |
| 4557 int count = annotations.length; |
| 4558 for (int i = 0; i < count; i++) { |
| 4559 annotations[i].accept(this); |
| 4560 } |
| 4561 } |
| 4562 } |
| 4563 |
| 4564 /** |
| 4565 * An [AstVisitor] that fills [UsedLocalElements]. |
| 4566 */ |
| 4567 class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { |
| 4568 final UsedLocalElements usedElements = new UsedLocalElements(); |
| 4569 |
| 4570 final LibraryElement _enclosingLibrary; |
| 4571 ClassElement _enclosingClass; |
| 4572 ExecutableElement _enclosingExec; |
| 4573 |
| 4574 GatherUsedLocalElementsVisitor(this._enclosingLibrary); |
| 4575 |
| 4576 @override |
| 4577 visitCatchClause(CatchClause node) { |
| 4578 SimpleIdentifier exceptionParameter = node.exceptionParameter; |
| 4579 SimpleIdentifier stackTraceParameter = node.stackTraceParameter; |
| 4580 if (exceptionParameter != null) { |
| 4581 Element element = exceptionParameter.staticElement; |
| 4582 usedElements.addCatchException(element); |
| 4583 if (stackTraceParameter != null || node.onKeyword == null) { |
| 4584 usedElements.addElement(element); |
| 4585 } |
| 4586 } |
| 4587 if (stackTraceParameter != null) { |
| 4588 Element element = stackTraceParameter.staticElement; |
| 4589 usedElements.addCatchStackTrace(element); |
| 4590 } |
| 4591 super.visitCatchClause(node); |
| 4592 } |
| 4593 |
| 4594 @override |
| 4595 visitClassDeclaration(ClassDeclaration node) { |
| 4596 ClassElement enclosingClassOld = _enclosingClass; |
| 4597 try { |
| 4598 _enclosingClass = node.element; |
| 4599 super.visitClassDeclaration(node); |
| 4600 } finally { |
| 4601 _enclosingClass = enclosingClassOld; |
| 4602 } |
| 4603 } |
| 4604 |
| 4605 @override |
| 4606 visitFunctionDeclaration(FunctionDeclaration node) { |
| 4607 ExecutableElement enclosingExecOld = _enclosingExec; |
| 4608 try { |
| 4609 _enclosingExec = node.element; |
| 4610 super.visitFunctionDeclaration(node); |
| 4611 } finally { |
| 4612 _enclosingExec = enclosingExecOld; |
| 4613 } |
| 4614 } |
| 4615 |
| 4616 @override |
| 4617 visitFunctionExpression(FunctionExpression node) { |
| 4618 if (node.parent is! FunctionDeclaration) { |
| 4619 usedElements.addElement(node.element); |
| 4620 } |
| 4621 super.visitFunctionExpression(node); |
| 4622 } |
| 4623 |
| 4624 @override |
| 4625 visitMethodDeclaration(MethodDeclaration node) { |
| 4626 ExecutableElement enclosingExecOld = _enclosingExec; |
| 4627 try { |
| 4628 _enclosingExec = node.element; |
| 4629 super.visitMethodDeclaration(node); |
| 4630 } finally { |
| 4631 _enclosingExec = enclosingExecOld; |
| 4632 } |
| 4633 } |
| 4634 |
| 4635 @override |
| 4636 visitSimpleIdentifier(SimpleIdentifier node) { |
| 4637 if (node.inDeclarationContext()) { |
| 4638 return; |
| 4639 } |
| 4640 Element element = node.staticElement; |
| 4641 bool isIdentifierRead = _isReadIdentifier(node); |
| 4642 if (element is LocalVariableElement) { |
| 4643 if (isIdentifierRead) { |
| 4644 usedElements.addElement(element); |
| 4645 } |
| 4646 } else { |
| 4647 _useIdentifierElement(node); |
| 4648 if (element == null || |
| 4649 element.enclosingElement is ClassElement && |
| 4650 !identical(element, _enclosingExec)) { |
| 4651 usedElements.members.add(node.name); |
| 4652 if (isIdentifierRead) { |
| 4653 usedElements.readMembers.add(node.name); |
| 4654 } |
| 4655 } |
| 4656 } |
| 4657 } |
| 4658 |
| 4659 /** |
| 4660 * Marks an [Element] of [node] as used in the library. |
| 4661 */ |
| 4662 void _useIdentifierElement(Identifier node) { |
| 4663 Element element = node.staticElement; |
| 4664 if (element == null) { |
| 4665 return; |
| 4666 } |
| 4667 // check if a local element |
| 4668 if (!identical(element.library, _enclosingLibrary)) { |
| 4669 return; |
| 4670 } |
| 4671 // ignore references to an element from itself |
| 4672 if (identical(element, _enclosingClass)) { |
| 4673 return; |
| 4674 } |
| 4675 if (identical(element, _enclosingExec)) { |
| 4676 return; |
| 4677 } |
| 4678 // ignore places where the element is not actually used |
| 4679 if (node.parent is TypeName) { |
| 4680 if (element is ClassElement) { |
| 4681 AstNode parent2 = node.parent.parent; |
| 4682 if (parent2 is IsExpression) { |
| 4683 return; |
| 4684 } |
| 4685 if (parent2 is VariableDeclarationList) { |
| 4686 return; |
| 4687 } |
| 4688 } |
| 4689 } |
| 4690 // OK |
| 4691 usedElements.addElement(element); |
| 4692 } |
| 4693 |
| 4694 static bool _isReadIdentifier(SimpleIdentifier node) { |
| 4695 // not reading at all |
| 4696 if (!node.inGetterContext()) { |
| 4697 return false; |
| 4698 } |
| 4699 // check if useless reading |
| 4700 AstNode parent = node.parent; |
| 4701 if (parent.parent is ExpressionStatement && |
| 4702 (parent is PrefixExpression || |
| 4703 parent is PostfixExpression || |
| 4704 parent is AssignmentExpression && parent.leftHandSide == node)) { |
| 4705 // v++; |
| 4706 // ++v; |
| 4707 // v += 2; |
| 4708 return false; |
| 4709 } |
| 4710 // OK |
| 4711 return true; |
| 4712 } |
| 4713 } |
| 4714 |
| 4715 /** |
| 4716 * Instances of the class `HintGenerator` traverse a library's worth of dart cod
e at a time to |
| 4717 * generate hints over the set of sources. |
| 4718 * |
| 4719 * See [HintCode]. |
| 4720 */ |
| 4721 class HintGenerator { |
| 4722 final List<CompilationUnit> _compilationUnits; |
| 4723 |
| 4724 final InternalAnalysisContext _context; |
| 4725 |
| 4726 final AnalysisErrorListener _errorListener; |
| 4727 |
| 4728 LibraryElement _library; |
| 4729 |
| 4730 GatherUsedImportedElementsVisitor _usedImportedElementsVisitor; |
| 4731 |
| 4732 bool _enableDart2JSHints = false; |
| 4733 |
| 4734 /** |
| 4735 * The inheritance manager used to find overridden methods. |
| 4736 */ |
| 4737 InheritanceManager _manager; |
| 4738 |
| 4739 GatherUsedLocalElementsVisitor _usedLocalElementsVisitor; |
| 4740 |
| 4741 HintGenerator(this._compilationUnits, this._context, this._errorListener) { |
| 4742 _library = _compilationUnits[0].element.library; |
| 4743 _usedImportedElementsVisitor = |
| 4744 new GatherUsedImportedElementsVisitor(_library); |
| 4745 _enableDart2JSHints = _context.analysisOptions.dart2jsHint; |
| 4746 _manager = new InheritanceManager(_compilationUnits[0].element.library); |
| 4747 _usedLocalElementsVisitor = new GatherUsedLocalElementsVisitor(_library); |
| 4748 } |
| 4749 |
| 4750 void generateForLibrary() { |
| 4751 PerformanceStatistics.hints.makeCurrentWhile(() { |
| 4752 for (CompilationUnit unit in _compilationUnits) { |
| 4753 CompilationUnitElement element = unit.element; |
| 4754 if (element != null) { |
| 4755 _generateForCompilationUnit(unit, element.source); |
| 4756 } |
| 4757 } |
| 4758 CompilationUnit definingUnit = _compilationUnits[0]; |
| 4759 ErrorReporter definingUnitErrorReporter = |
| 4760 new ErrorReporter(_errorListener, definingUnit.element.source); |
| 4761 { |
| 4762 ImportsVerifier importsVerifier = new ImportsVerifier(); |
| 4763 importsVerifier.addImports(definingUnit); |
| 4764 importsVerifier |
| 4765 .removeUsedElements(_usedImportedElementsVisitor.usedElements); |
| 4766 importsVerifier.generateDuplicateImportHints(definingUnitErrorReporter); |
| 4767 importsVerifier.generateUnusedImportHints(definingUnitErrorReporter); |
| 4768 } |
| 4769 _library.accept(new UnusedLocalElementsVerifier( |
| 4770 _errorListener, _usedLocalElementsVisitor.usedElements)); |
| 4771 }); |
| 4772 } |
| 4773 |
| 4774 void _generateForCompilationUnit(CompilationUnit unit, Source source) { |
| 4775 ErrorReporter errorReporter = new ErrorReporter(_errorListener, source); |
| 4776 unit.accept(_usedImportedElementsVisitor); |
| 4777 // dead code analysis |
| 4778 unit.accept(new DeadCodeVerifier(errorReporter)); |
| 4779 unit.accept(_usedLocalElementsVisitor); |
| 4780 // dart2js analysis |
| 4781 if (_enableDart2JSHints) { |
| 4782 unit.accept(new Dart2JSVerifier(errorReporter)); |
| 4783 } |
| 4784 // Dart best practices |
| 4785 unit.accept( |
| 4786 new BestPracticesVerifier(errorReporter, _context.typeProvider)); |
| 4787 unit.accept(new OverrideVerifier(errorReporter, _manager)); |
| 4788 // Find to-do comments |
| 4789 new ToDoFinder(errorReporter).findIn(unit); |
| 4790 // pub analysis |
| 4791 // TODO(danrubel/jwren) Commented out until bugs in the pub verifier are |
| 4792 // fixed |
| 4793 // unit.accept(new PubVerifier(context, errorReporter)); |
| 4794 } |
| 4795 } |
| 4796 |
| 4797 /** |
| 4798 * Instances of the class {@code HtmlTagInfo} record information about the tags
used in an HTML |
| 4799 * file. |
| 4800 */ |
| 4801 class HtmlTagInfo { |
| 4802 /** |
| 4803 * An array containing all of the tags used in the HTML file. |
| 4804 */ |
| 4805 List<String> allTags; |
| 4806 |
| 4807 /** |
| 4808 * A table mapping the id's defined in the HTML file to an array containing th
e names of tags with |
| 4809 * that identifier. |
| 4810 */ |
| 4811 HashMap<String, String> idToTagMap; |
| 4812 |
| 4813 /** |
| 4814 * A table mapping the classes defined in the HTML file to an array containing
the names of tags |
| 4815 * with that class. |
| 4816 */ |
| 4817 HashMap<String, List<String>> classToTagsMap; |
| 4818 |
| 4819 /** |
| 4820 * Initialize a newly created information holder to hold the given information
about the tags in |
| 4821 * an HTML file. |
| 4822 * |
| 4823 * @param allTags an array containing all of the tags used in the HTML file |
| 4824 * @param idToTagMap a table mapping the id's defined in the HTML file to an a
rray containing the |
| 4825 * names of tags with that identifier |
| 4826 * @param classToTagsMap a table mapping the classes defined in the HTML file
to an array |
| 4827 * containing the names of tags with that class |
| 4828 */ |
| 4829 HtmlTagInfo(this.allTags, this.idToTagMap, this.classToTagsMap); |
| 4830 |
| 4831 /** |
| 4832 * Return an array containing the tags that have the given class, or {@code nu
ll} if there are no |
| 4833 * such tags. |
| 4834 * |
| 4835 * @return an array containing the tags that have the given class |
| 4836 */ |
| 4837 List<String> getTagsWithClass(String identifier) { |
| 4838 return classToTagsMap[identifier]; |
| 4839 } |
| 4840 |
| 4841 /** |
| 4842 * Return the tag that has the given identifier, or {@code null} if there is n
o such tag (the |
| 4843 * identifier is not defined). |
| 4844 * |
| 4845 * @return the tag that has the given identifier |
| 4846 */ |
| 4847 String getTagWithId(String identifier) { |
| 4848 return idToTagMap[identifier]; |
| 4849 } |
| 4850 } |
| 4851 |
| 4852 /** |
| 4853 * Instances of the class {@code HtmlTagInfoBuilder} gather information about th
e tags used in one |
| 4854 * or more HTML structures. |
| 4855 */ |
| 4856 class HtmlTagInfoBuilder implements ht.XmlVisitor { |
| 4857 /** |
| 4858 * The name of the 'id' attribute. |
| 4859 */ |
| 4860 static final String ID_ATTRIBUTE = "id"; |
| 4861 |
| 4862 /** |
| 4863 * The name of the 'class' attribute. |
| 4864 */ |
| 4865 static final String ID_CLASS = "class"; |
| 4866 |
| 4867 /** |
| 4868 * A set containing all of the tag names used in the HTML. |
| 4869 */ |
| 4870 HashSet<String> tagSet = new HashSet<String>(); |
| 4871 |
| 4872 /** |
| 4873 * A table mapping the id's that are defined to the tag name with that id. |
| 4874 */ |
| 4875 HashMap<String, String> idMap = new HashMap<String, String>(); |
| 4876 |
| 4877 /** |
| 4878 * A table mapping the classes that are defined to a set of the tag names with
that class. |
| 4879 */ |
| 4880 HashMap<String, HashSet<String>> classMap = |
| 4881 new HashMap<String, HashSet<String>>(); |
| 4882 |
| 4883 /** |
| 4884 * Initialize a newly created HTML tag info builder. |
| 4885 */ |
| 4886 HtmlTagInfoBuilder(); |
| 4887 |
| 4888 /** |
| 4889 * Create a tag information holder holding all of the information gathered abo
ut the tags in the |
| 4890 * HTML structures that were visited. |
| 4891 * |
| 4892 * @return the information gathered about the tags in the visited HTML structu
res |
| 4893 */ |
| 4894 HtmlTagInfo getTagInfo() { |
| 4895 List<String> allTags = tagSet.toList(); |
| 4896 HashMap<String, List<String>> classToTagsMap = |
| 4897 new HashMap<String, List<String>>(); |
| 4898 classMap.forEach((String key, Set<String> tags) { |
| 4899 classToTagsMap[key] = tags.toList(); |
| 4900 }); |
| 4901 return new HtmlTagInfo(allTags, idMap, classToTagsMap); |
| 4902 } |
| 4903 |
| 4904 @override |
| 4905 visitHtmlScriptTagNode(ht.HtmlScriptTagNode node) { |
| 4906 visitXmlTagNode(node); |
| 4907 } |
| 4908 |
| 4909 @override |
| 4910 visitHtmlUnit(ht.HtmlUnit node) { |
| 4911 node.visitChildren(this); |
| 4912 } |
| 4913 |
| 4914 @override |
| 4915 visitXmlAttributeNode(ht.XmlAttributeNode node) {} |
| 4916 |
| 4917 @override |
| 4918 visitXmlTagNode(ht.XmlTagNode node) { |
| 4919 node.visitChildren(this); |
| 4920 String tagName = node.tag; |
| 4921 tagSet.add(tagName); |
| 4922 for (ht.XmlAttributeNode attribute in node.attributes) { |
| 4923 String attributeName = attribute.name; |
| 4924 if (attributeName == ID_ATTRIBUTE) { |
| 4925 String attributeValue = attribute.text; |
| 4926 if (attributeValue != null) { |
| 4927 String tag = idMap[attributeValue]; |
| 4928 if (tag == null) { |
| 4929 idMap[attributeValue] = tagName; |
| 4930 } else { |
| 4931 // reportError(HtmlWarningCode.MULTIPLY_DEFINED_ID, valueToken); |
| 4932 } |
| 4933 } |
| 4934 } else if (attributeName == ID_CLASS) { |
| 4935 String attributeValue = attribute.text; |
| 4936 if (attributeValue != null) { |
| 4937 HashSet<String> tagList = classMap[attributeValue]; |
| 4938 if (tagList == null) { |
| 4939 tagList = new HashSet<String>(); |
| 4940 classMap[attributeValue] = tagList; |
| 4941 } else { |
| 4942 // reportError(HtmlWarningCode.MULTIPLY_DEFINED_ID, valueToken); |
| 4943 } |
| 4944 tagList.add(tagName); |
| 4945 } |
| 4946 } |
| 4947 } |
| 4948 } |
| 4949 |
| 4950 // /** |
| 4951 // * Report an error with the given error code at the given location. Use the
given arguments to |
| 4952 // * compose the error message. |
| 4953 // * |
| 4954 // * @param errorCode the error code of the error to be reported |
| 4955 // * @param offset the offset of the first character to be highlighted |
| 4956 // * @param length the number of characters to be highlighted |
| 4957 // * @param arguments the arguments used to compose the error message |
| 4958 // */ |
| 4959 // private void reportError(ErrorCode errorCode, Token token, Object... argumen
ts) { |
| 4960 // errorListener.onError(new AnalysisError( |
| 4961 // htmlElement.getSource(), |
| 4962 // token.getOffset(), |
| 4963 // token.getLength(), |
| 4964 // errorCode, |
| 4965 // arguments)); |
| 4966 // } |
| 4967 // |
| 4968 // /** |
| 4969 // * Report an error with the given error code at the given location. Use the
given arguments to |
| 4970 // * compose the error message. |
| 4971 // * |
| 4972 // * @param errorCode the error code of the error to be reported |
| 4973 // * @param offset the offset of the first character to be highlighted |
| 4974 // * @param length the number of characters to be highlighted |
| 4975 // * @param arguments the arguments used to compose the error message |
| 4976 // */ |
| 4977 // private void reportError(ErrorCode errorCode, int offset, int length, Object
... arguments) { |
| 4978 // errorListener.onError(new AnalysisError( |
| 4979 // htmlElement.getSource(), |
| 4980 // offset, |
| 4981 // length, |
| 4982 // errorCode, |
| 4983 // arguments)); |
| 4984 // } |
| 4985 } |
| 4986 |
| 4987 /** |
| 4988 * Instances of the class `HtmlUnitBuilder` build an element model for a single
HTML unit. |
| 4989 */ |
| 4990 class HtmlUnitBuilder implements ht.XmlVisitor<Object> { |
| 4991 static String _SRC = "src"; |
| 4992 |
| 4993 /** |
| 4994 * The analysis context in which the element model will be built. |
| 4995 */ |
| 4996 final InternalAnalysisContext _context; |
| 4997 |
| 4998 /** |
| 4999 * The error listener to which errors will be reported. |
| 5000 */ |
| 5001 RecordingErrorListener _errorListener; |
| 5002 |
| 5003 /** |
| 5004 * The HTML element being built. |
| 5005 */ |
| 5006 HtmlElementImpl _htmlElement; |
| 5007 |
| 5008 /** |
| 5009 * The elements in the path from the HTML unit to the current tag node. |
| 5010 */ |
| 5011 List<ht.XmlTagNode> _parentNodes; |
| 5012 |
| 5013 /** |
| 5014 * The script elements being built. |
| 5015 */ |
| 5016 List<HtmlScriptElement> _scripts; |
| 5017 |
| 5018 /** |
| 5019 * A set of the libraries that were resolved while resolving the HTML unit. |
| 5020 */ |
| 5021 Set<Library> _resolvedLibraries = new HashSet<Library>(); |
| 5022 |
| 5023 /** |
| 5024 * Initialize a newly created HTML unit builder. |
| 5025 * |
| 5026 * @param context the analysis context in which the element model will be buil
t |
| 5027 */ |
| 5028 HtmlUnitBuilder(this._context) { |
| 5029 this._errorListener = new RecordingErrorListener(); |
| 5030 } |
| 5031 |
| 5032 /** |
| 5033 * Return the listener to which analysis errors will be reported. |
| 5034 * |
| 5035 * @return the listener to which analysis errors will be reported |
| 5036 */ |
| 5037 RecordingErrorListener get errorListener => _errorListener; |
| 5038 |
| 5039 /** |
| 5040 * Return an array containing information about all of the libraries that were
resolved. |
| 5041 * |
| 5042 * @return an array containing the libraries that were resolved |
| 5043 */ |
| 5044 Set<Library> get resolvedLibraries => _resolvedLibraries; |
| 5045 |
| 5046 /** |
| 5047 * Build the HTML element for the given source. |
| 5048 * |
| 5049 * @param source the source describing the compilation unit |
| 5050 * @param unit the AST structure representing the HTML |
| 5051 * @throws AnalysisException if the analysis could not be performed |
| 5052 */ |
| 5053 HtmlElementImpl buildHtmlElement(Source source, ht.HtmlUnit unit) { |
| 5054 HtmlElementImpl result = new HtmlElementImpl(_context, source.shortName); |
| 5055 result.source = source; |
| 5056 _htmlElement = result; |
| 5057 unit.accept(this); |
| 5058 _htmlElement = null; |
| 5059 unit.element = result; |
| 5060 return result; |
| 5061 } |
| 5062 |
| 5063 @override |
| 5064 Object visitHtmlScriptTagNode(ht.HtmlScriptTagNode node) { |
| 5065 if (_parentNodes.contains(node)) { |
| 5066 return _reportCircularity(node); |
| 5067 } |
| 5068 _parentNodes.add(node); |
| 5069 try { |
| 5070 Source htmlSource = _htmlElement.source; |
| 5071 ht.XmlAttributeNode scriptAttribute = _getScriptSourcePath(node); |
| 5072 String scriptSourcePath = |
| 5073 scriptAttribute == null ? null : scriptAttribute.text; |
| 5074 if (node.attributeEnd.type == ht.TokenType.GT && |
| 5075 scriptSourcePath == null) { |
| 5076 EmbeddedHtmlScriptElementImpl script = |
| 5077 new EmbeddedHtmlScriptElementImpl(node); |
| 5078 try { |
| 5079 LibraryResolver resolver = new LibraryResolver(_context); |
| 5080 LibraryElementImpl library = |
| 5081 resolver.resolveEmbeddedLibrary(htmlSource, node.script, true); |
| 5082 script.scriptLibrary = library; |
| 5083 _resolvedLibraries.addAll(resolver.resolvedLibraries); |
| 5084 _errorListener.addAll(resolver.errorListener); |
| 5085 } on AnalysisException catch (exception, stackTrace) { |
| 5086 //TODO (danrubel): Handle or forward the exception |
| 5087 AnalysisEngine.instance.logger.logError( |
| 5088 "Could not resolve script tag", |
| 5089 new CaughtException(exception, stackTrace)); |
| 5090 } |
| 5091 node.scriptElement = script; |
| 5092 _scripts.add(script); |
| 5093 } else { |
| 5094 ExternalHtmlScriptElementImpl script = |
| 5095 new ExternalHtmlScriptElementImpl(node); |
| 5096 if (scriptSourcePath != null) { |
| 5097 try { |
| 5098 scriptSourcePath = Uri.encodeFull(scriptSourcePath); |
| 5099 // Force an exception to be thrown if the URI is invalid so that we |
| 5100 // can report the problem. |
| 5101 parseUriWithException(scriptSourcePath); |
| 5102 Source scriptSource = |
| 5103 _context.sourceFactory.resolveUri(htmlSource, scriptSourcePath); |
| 5104 script.scriptSource = scriptSource; |
| 5105 if (!_context.exists(scriptSource)) { |
| 5106 _reportValueError(HtmlWarningCode.URI_DOES_NOT_EXIST, |
| 5107 scriptAttribute, [scriptSourcePath]); |
| 5108 } |
| 5109 } on URISyntaxException { |
| 5110 _reportValueError(HtmlWarningCode.INVALID_URI, scriptAttribute, |
| 5111 [scriptSourcePath]); |
| 5112 } |
| 5113 } |
| 5114 node.scriptElement = script; |
| 5115 _scripts.add(script); |
| 5116 } |
| 5117 } finally { |
| 5118 _parentNodes.remove(node); |
| 5119 } |
| 5120 return null; |
| 5121 } |
| 5122 |
| 5123 @override |
| 5124 Object visitHtmlUnit(ht.HtmlUnit node) { |
| 5125 _parentNodes = new List<ht.XmlTagNode>(); |
| 5126 _scripts = new List<HtmlScriptElement>(); |
| 5127 try { |
| 5128 node.visitChildren(this); |
| 5129 _htmlElement.scripts = new List.from(_scripts); |
| 5130 } finally { |
| 5131 _scripts = null; |
| 5132 _parentNodes = null; |
| 5133 } |
| 5134 return null; |
| 5135 } |
| 5136 |
| 5137 @override |
| 5138 Object visitXmlAttributeNode(ht.XmlAttributeNode node) => null; |
| 5139 |
| 5140 @override |
| 5141 Object visitXmlTagNode(ht.XmlTagNode node) { |
| 5142 if (_parentNodes.contains(node)) { |
| 5143 return _reportCircularity(node); |
| 5144 } |
| 5145 _parentNodes.add(node); |
| 5146 try { |
| 5147 node.visitChildren(this); |
| 5148 } finally { |
| 5149 _parentNodes.remove(node); |
| 5150 } |
| 5151 return null; |
| 5152 } |
| 5153 |
| 5154 /** |
| 5155 * Return the first source attribute for the given tag node, or `null` if it d
oes not exist. |
| 5156 * |
| 5157 * @param node the node containing attributes |
| 5158 * @return the source attribute contained in the given tag |
| 5159 */ |
| 5160 ht.XmlAttributeNode _getScriptSourcePath(ht.XmlTagNode node) { |
| 5161 for (ht.XmlAttributeNode attribute in node.attributes) { |
| 5162 if (attribute.name == _SRC) { |
| 5163 return attribute; |
| 5164 } |
| 5165 } |
| 5166 return null; |
| 5167 } |
| 5168 |
| 5169 Object _reportCircularity(ht.XmlTagNode node) { |
| 5170 // |
| 5171 // This should not be possible, but we have an error report that suggests |
| 5172 // that it happened at least once. This code will guard against infinite |
| 5173 // recursion and might help us identify the cause of the issue. |
| 5174 // |
| 5175 StringBuffer buffer = new StringBuffer(); |
| 5176 buffer.write("Found circularity in XML nodes: "); |
| 5177 bool first = true; |
| 5178 for (ht.XmlTagNode pathNode in _parentNodes) { |
| 5179 if (first) { |
| 5180 first = false; |
| 5181 } else { |
| 5182 buffer.write(", "); |
| 5183 } |
| 5184 String tagName = pathNode.tag; |
| 5185 if (identical(pathNode, node)) { |
| 5186 buffer.write("*"); |
| 5187 buffer.write(tagName); |
| 5188 buffer.write("*"); |
| 5189 } else { |
| 5190 buffer.write(tagName); |
| 5191 } |
| 5192 } |
| 5193 AnalysisEngine.instance.logger.logError(buffer.toString()); |
| 5194 return null; |
| 5195 } |
| 5196 |
| 5197 /** |
| 5198 * Report an error with the given error code at the given location. Use the gi
ven arguments to |
| 5199 * compose the error message. |
| 5200 * |
| 5201 * @param errorCode the error code of the error to be reported |
| 5202 * @param offset the offset of the first character to be highlighted |
| 5203 * @param length the number of characters to be highlighted |
| 5204 * @param arguments the arguments used to compose the error message |
| 5205 */ |
| 5206 void _reportErrorForOffset( |
| 5207 ErrorCode errorCode, int offset, int length, List<Object> arguments) { |
| 5208 _errorListener.onError(new AnalysisError( |
| 5209 _htmlElement.source, offset, length, errorCode, arguments)); |
| 5210 } |
| 5211 |
| 5212 /** |
| 5213 * Report an error with the given error code at the location of the value of t
he given attribute. |
| 5214 * Use the given arguments to compose the error message. |
| 5215 * |
| 5216 * @param errorCode the error code of the error to be reported |
| 5217 * @param offset the offset of the first character to be highlighted |
| 5218 * @param length the number of characters to be highlighted |
| 5219 * @param arguments the arguments used to compose the error message |
| 5220 */ |
| 5221 void _reportValueError(ErrorCode errorCode, ht.XmlAttributeNode attribute, |
| 5222 List<Object> arguments) { |
| 5223 int offset = attribute.valueToken.offset + 1; |
| 5224 int length = attribute.valueToken.length - 2; |
| 5225 _reportErrorForOffset(errorCode, offset, length, arguments); |
| 5226 } |
| 5227 } |
| 5228 |
| 5229 /** |
| 5230 * Instances of the class `ImplicitLabelScope` represent the scope statements |
| 5231 * that can be the target of unlabeled break and continue statements. |
| 5232 */ |
| 5233 class ImplicitLabelScope { |
| 5234 /** |
| 5235 * The implicit label scope associated with the top level of a function. |
| 5236 */ |
| 5237 static const ImplicitLabelScope ROOT = const ImplicitLabelScope._(null, null); |
| 5238 |
| 5239 /** |
| 5240 * The implicit label scope enclosing this implicit label scope. |
| 5241 */ |
| 5242 final ImplicitLabelScope outerScope; |
| 5243 |
| 5244 /** |
| 5245 * The statement that acts as a target for break and/or continue statements |
| 5246 * at this scoping level. |
| 5247 */ |
| 5248 final Statement statement; |
| 5249 |
| 5250 /** |
| 5251 * Private constructor. |
| 5252 */ |
| 5253 const ImplicitLabelScope._(this.outerScope, this.statement); |
| 5254 |
| 5255 /** |
| 5256 * Get the statement which should be the target of an unlabeled `break` or |
| 5257 * `continue` statement, or `null` if there is no appropriate target. |
| 5258 */ |
| 5259 Statement getTarget(bool isContinue) { |
| 5260 if (outerScope == null) { |
| 5261 // This scope represents the toplevel of a function body, so it doesn't |
| 5262 // match either break or continue. |
| 5263 return null; |
| 5264 } |
| 5265 if (isContinue && statement is SwitchStatement) { |
| 5266 return outerScope.getTarget(isContinue); |
| 5267 } |
| 5268 return statement; |
| 5269 } |
| 5270 |
| 5271 /** |
| 5272 * Initialize a newly created scope to represent a switch statement or loop |
| 5273 * nested within the current scope. [statement] is the statement associated |
| 5274 * with the newly created scope. |
| 5275 */ |
| 5276 ImplicitLabelScope nest(Statement statement) => |
| 5277 new ImplicitLabelScope._(this, statement); |
| 5278 } |
| 5279 |
| 5280 /** |
| 5281 * Instances of the class `ImportsVerifier` visit all of the referenced librarie
s in the |
| 5282 * source code verifying that all of the imports are used, otherwise a |
| 5283 * [HintCode.UNUSED_IMPORT] is generated with |
| 5284 * [generateUnusedImportHints]. |
| 5285 * |
| 5286 * While this class does not yet have support for an "Organize Imports" action,
this logic built up |
| 5287 * in this class could be used for such an action in the future. |
| 5288 */ |
| 5289 class ImportsVerifier /*extends RecursiveAstVisitor<Object>*/ { |
| 5290 /** |
| 5291 * A list of [ImportDirective]s that the current library imports, as identifie
rs are visited |
| 5292 * by this visitor and an import has been identified as being used by the libr
ary, the |
| 5293 * [ImportDirective] is removed from this list. After all the sources in the l
ibrary have |
| 5294 * been evaluated, this list represents the set of unused imports. |
| 5295 * |
| 5296 * See [ImportsVerifier.generateUnusedImportErrors]. |
| 5297 */ |
| 5298 final List<ImportDirective> _unusedImports = <ImportDirective>[]; |
| 5299 |
| 5300 /** |
| 5301 * After the list of [unusedImports] has been computed, this list is a proper
subset of the |
| 5302 * unused imports that are listed more than once. |
| 5303 */ |
| 5304 final List<ImportDirective> _duplicateImports = <ImportDirective>[]; |
| 5305 |
| 5306 /** |
| 5307 * This is a map between the set of [LibraryElement]s that the current library
imports, and |
| 5308 * a list of [ImportDirective]s that imports the library. In cases where the c
urrent library |
| 5309 * imports a library with a single directive (such as `import lib1.dart;`), th
e library |
| 5310 * element will map to a list of one [ImportDirective], which will then be rem
oved from the |
| 5311 * [unusedImports] list. In cases where the current library imports a library
with multiple |
| 5312 * directives (such as `import lib1.dart; import lib1.dart show C;`), the |
| 5313 * [LibraryElement] will be mapped to a list of the import directives, and the
namespace |
| 5314 * will need to be used to compute the correct [ImportDirective] being used, s
ee |
| 5315 * [namespaceMap]. |
| 5316 */ |
| 5317 final HashMap<LibraryElement, List<ImportDirective>> _libraryMap = |
| 5318 new HashMap<LibraryElement, List<ImportDirective>>(); |
| 5319 |
| 5320 /** |
| 5321 * In cases where there is more than one import directive per library element,
this mapping is |
| 5322 * used to determine which of the multiple import directives are used by gener
ating a |
| 5323 * [Namespace] for each of the imports to do lookups in the same way that they
are done from |
| 5324 * the [ElementResolver]. |
| 5325 */ |
| 5326 final HashMap<ImportDirective, Namespace> _namespaceMap = |
| 5327 new HashMap<ImportDirective, Namespace>(); |
| 5328 |
| 5329 /** |
| 5330 * This is a map between prefix elements and the import directives from which
they are derived. In |
| 5331 * cases where a type is referenced via a prefix element, the import directive
can be marked as |
| 5332 * used (removed from the unusedImports) by looking at the resolved `lib` in `
lib.X`, |
| 5333 * instead of looking at which library the `lib.X` resolves. |
| 5334 * |
| 5335 * TODO (jwren) Since multiple [ImportDirective]s can share the same [PrefixEl
ement], |
| 5336 * it is possible to have an unreported unused import in situations where two
imports use the same |
| 5337 * prefix and at least one import directive is used. |
| 5338 */ |
| 5339 final HashMap<PrefixElement, List<ImportDirective>> _prefixElementMap = |
| 5340 new HashMap<PrefixElement, List<ImportDirective>>(); |
| 5341 |
| 5342 void addImports(CompilationUnit node) { |
| 5343 for (Directive directive in node.directives) { |
| 5344 if (directive is ImportDirective) { |
| 5345 ImportDirective importDirective = directive; |
| 5346 LibraryElement libraryElement = importDirective.uriElement; |
| 5347 if (libraryElement != null) { |
| 5348 _unusedImports.add(importDirective); |
| 5349 // |
| 5350 // Initialize prefixElementMap |
| 5351 // |
| 5352 if (importDirective.asKeyword != null) { |
| 5353 SimpleIdentifier prefixIdentifier = importDirective.prefix; |
| 5354 if (prefixIdentifier != null) { |
| 5355 Element element = prefixIdentifier.staticElement; |
| 5356 if (element is PrefixElement) { |
| 5357 PrefixElement prefixElementKey = element; |
| 5358 List<ImportDirective> list = |
| 5359 _prefixElementMap[prefixElementKey]; |
| 5360 if (list == null) { |
| 5361 list = new List<ImportDirective>(); |
| 5362 _prefixElementMap[prefixElementKey] = list; |
| 5363 } |
| 5364 list.add(importDirective); |
| 5365 } |
| 5366 // TODO (jwren) Can the element ever not be a PrefixElement? |
| 5367 } |
| 5368 } |
| 5369 // |
| 5370 // Initialize libraryMap: libraryElement -> importDirective |
| 5371 // |
| 5372 _putIntoLibraryMap(libraryElement, importDirective); |
| 5373 // |
| 5374 // For this new addition to the libraryMap, also recursively add any |
| 5375 // exports from the libraryElement. |
| 5376 // |
| 5377 _addAdditionalLibrariesForExports( |
| 5378 libraryElement, importDirective, new List<LibraryElement>()); |
| 5379 } |
| 5380 } |
| 5381 } |
| 5382 if (_unusedImports.length > 1) { |
| 5383 // order the list of unusedImports to find duplicates in faster than |
| 5384 // O(n^2) time |
| 5385 List<ImportDirective> importDirectiveArray = |
| 5386 new List<ImportDirective>.from(_unusedImports); |
| 5387 importDirectiveArray.sort(ImportDirective.COMPARATOR); |
| 5388 ImportDirective currentDirective = importDirectiveArray[0]; |
| 5389 for (int i = 1; i < importDirectiveArray.length; i++) { |
| 5390 ImportDirective nextDirective = importDirectiveArray[i]; |
| 5391 if (ImportDirective.COMPARATOR(currentDirective, nextDirective) == 0) { |
| 5392 // Add either the currentDirective or nextDirective depending on which |
| 5393 // comes second, this guarantees that the first of the duplicates |
| 5394 // won't be highlighted. |
| 5395 if (currentDirective.offset < nextDirective.offset) { |
| 5396 _duplicateImports.add(nextDirective); |
| 5397 } else { |
| 5398 _duplicateImports.add(currentDirective); |
| 5399 } |
| 5400 } |
| 5401 currentDirective = nextDirective; |
| 5402 } |
| 5403 } |
| 5404 } |
| 5405 |
| 5406 /** |
| 5407 * Any time after the defining compilation unit has been visited by this visit
or, this method can |
| 5408 * be called to report an [HintCode.DUPLICATE_IMPORT] hint for each of the imp
ort directives |
| 5409 * in the [duplicateImports] list. |
| 5410 * |
| 5411 * @param errorReporter the error reporter to report the set of [HintCode.DUPL
ICATE_IMPORT] |
| 5412 * hints to |
| 5413 */ |
| 5414 void generateDuplicateImportHints(ErrorReporter errorReporter) { |
| 5415 for (ImportDirective duplicateImport in _duplicateImports) { |
| 5416 errorReporter.reportErrorForNode( |
| 5417 HintCode.DUPLICATE_IMPORT, duplicateImport.uri); |
| 5418 } |
| 5419 } |
| 5420 |
| 5421 /** |
| 5422 * After all of the compilation units have been visited by this visitor, this
method can be called |
| 5423 * to report an [HintCode.UNUSED_IMPORT] hint for each of the import directive
s in the |
| 5424 * [unusedImports] list. |
| 5425 * |
| 5426 * @param errorReporter the error reporter to report the set of [HintCode.UNUS
ED_IMPORT] |
| 5427 * hints to |
| 5428 */ |
| 5429 void generateUnusedImportHints(ErrorReporter errorReporter) { |
| 5430 for (ImportDirective unusedImport in _unusedImports) { |
| 5431 // Check that the import isn't dart:core |
| 5432 ImportElement importElement = unusedImport.element; |
| 5433 if (importElement != null) { |
| 5434 LibraryElement libraryElement = importElement.importedLibrary; |
| 5435 if (libraryElement != null && libraryElement.isDartCore) { |
| 5436 continue; |
| 5437 } |
| 5438 } |
| 5439 errorReporter.reportErrorForNode( |
| 5440 HintCode.UNUSED_IMPORT, unusedImport.uri); |
| 5441 } |
| 5442 } |
| 5443 |
| 5444 /** |
| 5445 * Remove elements from [_unusedImports] using the given [usedElements]. |
| 5446 */ |
| 5447 void removeUsedElements(UsedImportedElements usedElements) { |
| 5448 // Stop if all the imports are known to be used. |
| 5449 if (_unusedImports.isEmpty) { |
| 5450 return; |
| 5451 } |
| 5452 // Process import prefixes. |
| 5453 for (PrefixElement prefix in usedElements.prefixes) { |
| 5454 List<ImportDirective> importDirectives = _prefixElementMap[prefix]; |
| 5455 if (importDirectives != null) { |
| 5456 for (ImportDirective importDirective in importDirectives) { |
| 5457 _unusedImports.remove(importDirective); |
| 5458 } |
| 5459 } |
| 5460 } |
| 5461 // Process top-level elements. |
| 5462 for (Element element in usedElements.elements) { |
| 5463 // Stop if all the imports are known to be used. |
| 5464 if (_unusedImports.isEmpty) { |
| 5465 return; |
| 5466 } |
| 5467 // Prepare import directives for this library. |
| 5468 LibraryElement library = element.library; |
| 5469 List<ImportDirective> importsLibrary = _libraryMap[library]; |
| 5470 if (importsLibrary == null) { |
| 5471 continue; |
| 5472 } |
| 5473 // If there is only one import directive for this library, then it must be |
| 5474 // the directive that this element is imported with, remove it from the |
| 5475 // unusedImports list. |
| 5476 if (importsLibrary.length == 1) { |
| 5477 ImportDirective usedImportDirective = importsLibrary[0]; |
| 5478 _unusedImports.remove(usedImportDirective); |
| 5479 continue; |
| 5480 } |
| 5481 // Otherwise, find import directives using namespaces. |
| 5482 String name = element.displayName; |
| 5483 for (ImportDirective importDirective in importsLibrary) { |
| 5484 Namespace namespace = _computeNamespace(importDirective); |
| 5485 if (namespace != null && namespace.get(name) != null) { |
| 5486 _unusedImports.remove(importDirective); |
| 5487 } |
| 5488 } |
| 5489 } |
| 5490 } |
| 5491 |
| 5492 /** |
| 5493 * Recursively add any exported library elements into the [libraryMap]. |
| 5494 */ |
| 5495 void _addAdditionalLibrariesForExports(LibraryElement library, |
| 5496 ImportDirective importDirective, List<LibraryElement> exportPath) { |
| 5497 if (exportPath.contains(library)) { |
| 5498 return; |
| 5499 } |
| 5500 exportPath.add(library); |
| 5501 for (LibraryElement exportedLibraryElt in library.exportedLibraries) { |
| 5502 _putIntoLibraryMap(exportedLibraryElt, importDirective); |
| 5503 _addAdditionalLibrariesForExports( |
| 5504 exportedLibraryElt, importDirective, exportPath); |
| 5505 } |
| 5506 } |
| 5507 |
| 5508 /** |
| 5509 * Lookup and return the [Namespace] from the [namespaceMap], if the map does
not |
| 5510 * have the computed namespace, compute it and cache it in the map. If the imp
ort directive is not |
| 5511 * resolved or is not resolvable, `null` is returned. |
| 5512 * |
| 5513 * @param importDirective the import directive used to compute the returned na
mespace |
| 5514 * @return the computed or looked up [Namespace] |
| 5515 */ |
| 5516 Namespace _computeNamespace(ImportDirective importDirective) { |
| 5517 Namespace namespace = _namespaceMap[importDirective]; |
| 5518 if (namespace == null) { |
| 5519 // If the namespace isn't in the namespaceMap, then compute and put it in |
| 5520 // the map. |
| 5521 ImportElement importElement = importDirective.element; |
| 5522 if (importElement != null) { |
| 5523 NamespaceBuilder builder = new NamespaceBuilder(); |
| 5524 namespace = builder.createImportNamespaceForDirective(importElement); |
| 5525 _namespaceMap[importDirective] = namespace; |
| 5526 } |
| 5527 } |
| 5528 return namespace; |
| 5529 } |
| 5530 |
| 5531 /** |
| 5532 * The [libraryMap] is a mapping between a library elements and a list of impo
rt |
| 5533 * directives, but when adding these mappings into the [libraryMap], this meth
od can be |
| 5534 * used to simply add the mapping between the library element an an import dir
ective without |
| 5535 * needing to check to see if a list needs to be created. |
| 5536 */ |
| 5537 void _putIntoLibraryMap( |
| 5538 LibraryElement libraryElement, ImportDirective importDirective) { |
| 5539 List<ImportDirective> importList = _libraryMap[libraryElement]; |
| 5540 if (importList == null) { |
| 5541 importList = new List<ImportDirective>(); |
| 5542 _libraryMap[libraryElement] = importList; |
| 5543 } |
| 5544 importList.add(importDirective); |
| 5545 } |
| 5546 } |
| 5547 |
| 5548 /** |
| 5549 * Instances of the class `InheritanceManager` manage the knowledge of where cla
ss members |
| 5550 * (methods, getters & setters) are inherited from. |
| 5551 */ |
| 5552 class InheritanceManager { |
| 5553 /** |
| 5554 * The [LibraryElement] that is managed by this manager. |
| 5555 */ |
| 5556 LibraryElement _library; |
| 5557 |
| 5558 /** |
| 5559 * This is a mapping between each [ClassElement] and a map between the [String
] member |
| 5560 * names and the associated [ExecutableElement] in the mixin and superclass ch
ain. |
| 5561 */ |
| 5562 HashMap<ClassElement, MemberMap> _classLookup; |
| 5563 |
| 5564 /** |
| 5565 * This is a mapping between each [ClassElement] and a map between the [String
] member |
| 5566 * names and the associated [ExecutableElement] in the interface set. |
| 5567 */ |
| 5568 HashMap<ClassElement, MemberMap> _interfaceLookup; |
| 5569 |
| 5570 /** |
| 5571 * A map between each visited [ClassElement] and the set of [AnalysisError]s f
ound on |
| 5572 * the class element. |
| 5573 */ |
| 5574 HashMap<ClassElement, HashSet<AnalysisError>> _errorsInClassElement = |
| 5575 new HashMap<ClassElement, HashSet<AnalysisError>>(); |
| 5576 |
| 5577 /** |
| 5578 * Initialize a newly created inheritance manager. |
| 5579 * |
| 5580 * @param library the library element context that the inheritance mappings ar
e being generated |
| 5581 */ |
| 5582 InheritanceManager(LibraryElement library) { |
| 5583 this._library = library; |
| 5584 _classLookup = new HashMap<ClassElement, MemberMap>(); |
| 5585 _interfaceLookup = new HashMap<ClassElement, MemberMap>(); |
| 5586 } |
| 5587 |
| 5588 /** |
| 5589 * Set the new library element context. |
| 5590 * |
| 5591 * @param library the new library element |
| 5592 */ |
| 5593 void set libraryElement(LibraryElement library) { |
| 5594 this._library = library; |
| 5595 } |
| 5596 |
| 5597 /** |
| 5598 * Return the set of [AnalysisError]s found on the passed [ClassElement], or |
| 5599 * `null` if there are none. |
| 5600 * |
| 5601 * @param classElt the class element to query |
| 5602 * @return the set of [AnalysisError]s found on the passed [ClassElement], or |
| 5603 * `null` if there are none |
| 5604 */ |
| 5605 HashSet<AnalysisError> getErrors(ClassElement classElt) => |
| 5606 _errorsInClassElement[classElt]; |
| 5607 |
| 5608 /** |
| 5609 * Get and return a mapping between the set of all string names of the members
inherited from the |
| 5610 * passed [ClassElement] superclass hierarchy, and the associated [ExecutableE
lement]. |
| 5611 * |
| 5612 * @param classElt the class element to query |
| 5613 * @return a mapping between the set of all members inherited from the passed
[ClassElement] |
| 5614 * superclass hierarchy, and the associated [ExecutableElement] |
| 5615 */ |
| 5616 MemberMap getMapOfMembersInheritedFromClasses(ClassElement classElt) => |
| 5617 _computeClassChainLookupMap(classElt, new HashSet<ClassElement>()); |
| 5618 |
| 5619 /** |
| 5620 * Get and return a mapping between the set of all string names of the members
inherited from the |
| 5621 * passed [ClassElement] interface hierarchy, and the associated [ExecutableEl
ement]. |
| 5622 * |
| 5623 * @param classElt the class element to query |
| 5624 * @return a mapping between the set of all string names of the members inheri
ted from the passed |
| 5625 * [ClassElement] interface hierarchy, and the associated [ExecutableE
lement]. |
| 5626 */ |
| 5627 MemberMap getMapOfMembersInheritedFromInterfaces(ClassElement classElt) => |
| 5628 _computeInterfaceLookupMap(classElt, new HashSet<ClassElement>()); |
| 5629 |
| 5630 /** |
| 5631 * Given some [ClassElement] and some member name, this returns the |
| 5632 * [ExecutableElement] that the class inherits from the mixins, |
| 5633 * superclasses or interfaces, that has the member name, if no member is inher
ited `null` is |
| 5634 * returned. |
| 5635 * |
| 5636 * @param classElt the class element to query |
| 5637 * @param memberName the name of the executable element to find and return |
| 5638 * @return the inherited executable element with the member name, or `null` if
no such |
| 5639 * member exists |
| 5640 */ |
| 5641 ExecutableElement lookupInheritance( |
| 5642 ClassElement classElt, String memberName) { |
| 5643 if (memberName == null || memberName.isEmpty) { |
| 5644 return null; |
| 5645 } |
| 5646 ExecutableElement executable = _computeClassChainLookupMap( |
| 5647 classElt, new HashSet<ClassElement>()).get(memberName); |
| 5648 if (executable == null) { |
| 5649 return _computeInterfaceLookupMap(classElt, new HashSet<ClassElement>()) |
| 5650 .get(memberName); |
| 5651 } |
| 5652 return executable; |
| 5653 } |
| 5654 |
| 5655 /** |
| 5656 * Given some [ClassElement] and some member name, this returns the |
| 5657 * [ExecutableElement] that the class either declares itself, or |
| 5658 * inherits, that has the member name, if no member is inherited `null` is ret
urned. |
| 5659 * |
| 5660 * @param classElt the class element to query |
| 5661 * @param memberName the name of the executable element to find and return |
| 5662 * @return the inherited executable element with the member name, or `null` if
no such |
| 5663 * member exists |
| 5664 */ |
| 5665 ExecutableElement lookupMember(ClassElement classElt, String memberName) { |
| 5666 ExecutableElement element = _lookupMemberInClass(classElt, memberName); |
| 5667 if (element != null) { |
| 5668 return element; |
| 5669 } |
| 5670 return lookupInheritance(classElt, memberName); |
| 5671 } |
| 5672 |
| 5673 /** |
| 5674 * Given some [InterfaceType] and some member name, this returns the |
| 5675 * [FunctionType] of the [ExecutableElement] that the |
| 5676 * class either declares itself, or inherits, that has the member name, if no
member is inherited |
| 5677 * `null` is returned. The returned [FunctionType] has all type |
| 5678 * parameters substituted with corresponding type arguments from the given [In
terfaceType]. |
| 5679 * |
| 5680 * @param interfaceType the interface type to query |
| 5681 * @param memberName the name of the executable element to find and return |
| 5682 * @return the member's function type, or `null` if no such member exists |
| 5683 */ |
| 5684 FunctionType lookupMemberType( |
| 5685 InterfaceType interfaceType, String memberName) { |
| 5686 ExecutableElement iteratorMember = |
| 5687 lookupMember(interfaceType.element, memberName); |
| 5688 if (iteratorMember == null) { |
| 5689 return null; |
| 5690 } |
| 5691 return substituteTypeArgumentsInMemberFromInheritance( |
| 5692 iteratorMember.type, memberName, interfaceType); |
| 5693 } |
| 5694 |
| 5695 /** |
| 5696 * Determine the set of methods which is overridden by the given class member.
If no member is |
| 5697 * inherited, an empty list is returned. If one of the inherited members is a |
| 5698 * [MultiplyInheritedExecutableElement], then it is expanded into its constitu
ent inherited |
| 5699 * elements. |
| 5700 * |
| 5701 * @param classElt the class to query |
| 5702 * @param memberName the name of the class member to query |
| 5703 * @return a list of overridden methods |
| 5704 */ |
| 5705 List<ExecutableElement> lookupOverrides( |
| 5706 ClassElement classElt, String memberName) { |
| 5707 List<ExecutableElement> result = new List<ExecutableElement>(); |
| 5708 if (memberName == null || memberName.isEmpty) { |
| 5709 return result; |
| 5710 } |
| 5711 List<MemberMap> interfaceMaps = |
| 5712 _gatherInterfaceLookupMaps(classElt, new HashSet<ClassElement>()); |
| 5713 if (interfaceMaps != null) { |
| 5714 for (MemberMap interfaceMap in interfaceMaps) { |
| 5715 ExecutableElement overriddenElement = interfaceMap.get(memberName); |
| 5716 if (overriddenElement != null) { |
| 5717 if (overriddenElement is MultiplyInheritedExecutableElement) { |
| 5718 MultiplyInheritedExecutableElement multiplyInheritedElement = |
| 5719 overriddenElement; |
| 5720 for (ExecutableElement element |
| 5721 in multiplyInheritedElement.inheritedElements) { |
| 5722 result.add(element); |
| 5723 } |
| 5724 } else { |
| 5725 result.add(overriddenElement); |
| 5726 } |
| 5727 } |
| 5728 } |
| 5729 } |
| 5730 return result; |
| 5731 } |
| 5732 |
| 5733 /** |
| 5734 * This method takes some inherited [FunctionType], and resolves all the param
eterized types |
| 5735 * in the function type, dependent on the class in which it is being overridde
n. |
| 5736 * |
| 5737 * @param baseFunctionType the function type that is being overridden |
| 5738 * @param memberName the name of the member, this is used to lookup the inheri
tance path of the |
| 5739 * override |
| 5740 * @param definingType the type that is overriding the member |
| 5741 * @return the passed function type with any parameterized types substituted |
| 5742 */ |
| 5743 FunctionType substituteTypeArgumentsInMemberFromInheritance( |
| 5744 FunctionType baseFunctionType, |
| 5745 String memberName, |
| 5746 InterfaceType definingType) { |
| 5747 // if the baseFunctionType is null, or does not have any parameters, |
| 5748 // return it. |
| 5749 if (baseFunctionType == null || |
| 5750 baseFunctionType.typeArguments.length == 0) { |
| 5751 return baseFunctionType; |
| 5752 } |
| 5753 // First, generate the path from the defining type to the overridden member |
| 5754 Queue<InterfaceType> inheritancePath = new Queue<InterfaceType>(); |
| 5755 _computeInheritancePath(inheritancePath, definingType, memberName); |
| 5756 if (inheritancePath == null || inheritancePath.isEmpty) { |
| 5757 // TODO(jwren) log analysis engine error |
| 5758 return baseFunctionType; |
| 5759 } |
| 5760 FunctionType functionTypeToReturn = baseFunctionType; |
| 5761 // loop backward through the list substituting as we go: |
| 5762 while (!inheritancePath.isEmpty) { |
| 5763 InterfaceType lastType = inheritancePath.removeLast(); |
| 5764 List<DartType> parameterTypes = lastType.element.type.typeArguments; |
| 5765 List<DartType> argumentTypes = lastType.typeArguments; |
| 5766 functionTypeToReturn = |
| 5767 functionTypeToReturn.substitute2(argumentTypes, parameterTypes); |
| 5768 } |
| 5769 return functionTypeToReturn; |
| 5770 } |
| 5771 |
| 5772 /** |
| 5773 * Compute and return a mapping between the set of all string names of the mem
bers inherited from |
| 5774 * the passed [ClassElement] superclass hierarchy, and the associated |
| 5775 * [ExecutableElement]. |
| 5776 * |
| 5777 * @param classElt the class element to query |
| 5778 * @param visitedClasses a set of visited classes passed back into this method
when it calls |
| 5779 * itself recursively |
| 5780 * @return a mapping between the set of all string names of the members inheri
ted from the passed |
| 5781 * [ClassElement] superclass hierarchy, and the associated [Executable
Element] |
| 5782 */ |
| 5783 MemberMap _computeClassChainLookupMap( |
| 5784 ClassElement classElt, HashSet<ClassElement> visitedClasses) { |
| 5785 MemberMap resultMap = _classLookup[classElt]; |
| 5786 if (resultMap != null) { |
| 5787 return resultMap; |
| 5788 } else { |
| 5789 resultMap = new MemberMap(); |
| 5790 } |
| 5791 ClassElement superclassElt = null; |
| 5792 InterfaceType supertype = classElt.supertype; |
| 5793 if (supertype != null) { |
| 5794 superclassElt = supertype.element; |
| 5795 } else { |
| 5796 // classElt is Object |
| 5797 _classLookup[classElt] = resultMap; |
| 5798 return resultMap; |
| 5799 } |
| 5800 if (superclassElt != null) { |
| 5801 if (!visitedClasses.contains(superclassElt)) { |
| 5802 visitedClasses.add(superclassElt); |
| 5803 try { |
| 5804 resultMap = new MemberMap.from( |
| 5805 _computeClassChainLookupMap(superclassElt, visitedClasses)); |
| 5806 // |
| 5807 // Substitute the super types down the hierarchy. |
| 5808 // |
| 5809 _substituteTypeParametersDownHierarchy(supertype, resultMap); |
| 5810 // |
| 5811 // Include the members from the superclass in the resultMap. |
| 5812 // |
| 5813 _recordMapWithClassMembers(resultMap, supertype, false); |
| 5814 } finally { |
| 5815 visitedClasses.remove(superclassElt); |
| 5816 } |
| 5817 } else { |
| 5818 // This case happens only when the superclass was previously visited and |
| 5819 // not in the lookup, meaning this is meant to shorten the compute for |
| 5820 // recursive cases. |
| 5821 _classLookup[superclassElt] = resultMap; |
| 5822 return resultMap; |
| 5823 } |
| 5824 } |
| 5825 // |
| 5826 // Include the members from the mixins in the resultMap. If there are |
| 5827 // multiple mixins, visit them in the order listed so that methods in later |
| 5828 // mixins will overwrite identically-named methods in earlier mixins. |
| 5829 // |
| 5830 List<InterfaceType> mixins = classElt.mixins; |
| 5831 for (InterfaceType mixin in mixins) { |
| 5832 ClassElement mixinElement = mixin.element; |
| 5833 if (mixinElement != null) { |
| 5834 if (!visitedClasses.contains(mixinElement)) { |
| 5835 visitedClasses.add(mixinElement); |
| 5836 try { |
| 5837 MemberMap map = new MemberMap.from( |
| 5838 _computeClassChainLookupMap(mixinElement, visitedClasses)); |
| 5839 // |
| 5840 // Substitute the super types down the hierarchy. |
| 5841 // |
| 5842 _substituteTypeParametersDownHierarchy(mixin, map); |
| 5843 // |
| 5844 // Include the members from the superclass in the resultMap. |
| 5845 // |
| 5846 _recordMapWithClassMembers(map, mixin, false); |
| 5847 // |
| 5848 // Add the members from map into result map. |
| 5849 // |
| 5850 for (int j = 0; j < map.size; j++) { |
| 5851 String key = map.getKey(j); |
| 5852 ExecutableElement value = map.getValue(j); |
| 5853 if (key != null) { |
| 5854 ClassElement definingClass = value |
| 5855 .getAncestor((Element element) => element is ClassElement); |
| 5856 if (!definingClass.type.isObject) { |
| 5857 ExecutableElement existingValue = resultMap.get(key); |
| 5858 if (existingValue == null || |
| 5859 (existingValue != null && !_isAbstract(value))) { |
| 5860 resultMap.put(key, value); |
| 5861 } |
| 5862 } |
| 5863 } |
| 5864 } |
| 5865 } finally { |
| 5866 visitedClasses.remove(mixinElement); |
| 5867 } |
| 5868 } else { |
| 5869 // This case happens only when the superclass was previously visited |
| 5870 // and not in the lookup, meaning this is meant to shorten the compute |
| 5871 // for recursive cases. |
| 5872 _classLookup[mixinElement] = resultMap; |
| 5873 return resultMap; |
| 5874 } |
| 5875 } |
| 5876 } |
| 5877 _classLookup[classElt] = resultMap; |
| 5878 return resultMap; |
| 5879 } |
| 5880 |
| 5881 /** |
| 5882 * Compute and return the inheritance path given the context of a type and a m
ember that is |
| 5883 * overridden in the inheritance path (for which the type is in the path). |
| 5884 * |
| 5885 * @param chain the inheritance path that is built up as this method calls its
elf recursively, |
| 5886 * when this method is called an empty [LinkedList] should be provide
d |
| 5887 * @param currentType the current type in the inheritance path |
| 5888 * @param memberName the name of the member that is being looked up the inheri
tance path |
| 5889 */ |
| 5890 void _computeInheritancePath(Queue<InterfaceType> chain, |
| 5891 InterfaceType currentType, String memberName) { |
| 5892 // TODO (jwren) create a public version of this method which doesn't require |
| 5893 // the initial chain to be provided, then provided tests for this |
| 5894 // functionality in InheritanceManagerTest |
| 5895 chain.add(currentType); |
| 5896 ClassElement classElt = currentType.element; |
| 5897 InterfaceType supertype = classElt.supertype; |
| 5898 // Base case- reached Object |
| 5899 if (supertype == null) { |
| 5900 // Looked up the chain all the way to Object, return null. |
| 5901 // This should never happen. |
| 5902 return; |
| 5903 } |
| 5904 // If we are done, return the chain |
| 5905 // We are not done if this is the first recursive call on this method. |
| 5906 if (chain.length != 1) { |
| 5907 // We are done however if the member is in this classElt |
| 5908 if (_lookupMemberInClass(classElt, memberName) != null) { |
| 5909 return; |
| 5910 } |
| 5911 } |
| 5912 // Mixins- note that mixins call lookupMemberInClass, not lookupMember |
| 5913 List<InterfaceType> mixins = classElt.mixins; |
| 5914 for (int i = mixins.length - 1; i >= 0; i--) { |
| 5915 ClassElement mixinElement = mixins[i].element; |
| 5916 if (mixinElement != null) { |
| 5917 ExecutableElement elt = _lookupMemberInClass(mixinElement, memberName); |
| 5918 if (elt != null) { |
| 5919 // this is equivalent (but faster than) calling this method |
| 5920 // recursively |
| 5921 // (return computeInheritancePath(chain, mixins[i], memberName);) |
| 5922 chain.add(mixins[i]); |
| 5923 return; |
| 5924 } |
| 5925 } |
| 5926 } |
| 5927 // Superclass |
| 5928 ClassElement superclassElt = supertype.element; |
| 5929 if (lookupMember(superclassElt, memberName) != null) { |
| 5930 _computeInheritancePath(chain, supertype, memberName); |
| 5931 return; |
| 5932 } |
| 5933 // Interfaces |
| 5934 List<InterfaceType> interfaces = classElt.interfaces; |
| 5935 for (InterfaceType interfaceType in interfaces) { |
| 5936 ClassElement interfaceElement = interfaceType.element; |
| 5937 if (interfaceElement != null && |
| 5938 lookupMember(interfaceElement, memberName) != null) { |
| 5939 _computeInheritancePath(chain, interfaceType, memberName); |
| 5940 return; |
| 5941 } |
| 5942 } |
| 5943 } |
| 5944 |
| 5945 /** |
| 5946 * Compute and return a mapping between the set of all string names of the mem
bers inherited from |
| 5947 * the passed [ClassElement] interface hierarchy, and the associated |
| 5948 * [ExecutableElement]. |
| 5949 * |
| 5950 * @param classElt the class element to query |
| 5951 * @param visitedInterfaces a set of visited classes passed back into this met
hod when it calls |
| 5952 * itself recursively |
| 5953 * @return a mapping between the set of all string names of the members inheri
ted from the passed |
| 5954 * [ClassElement] interface hierarchy, and the associated [ExecutableE
lement] |
| 5955 */ |
| 5956 MemberMap _computeInterfaceLookupMap( |
| 5957 ClassElement classElt, HashSet<ClassElement> visitedInterfaces) { |
| 5958 MemberMap resultMap = _interfaceLookup[classElt]; |
| 5959 if (resultMap != null) { |
| 5960 return resultMap; |
| 5961 } |
| 5962 List<MemberMap> lookupMaps = |
| 5963 _gatherInterfaceLookupMaps(classElt, visitedInterfaces); |
| 5964 if (lookupMaps == null) { |
| 5965 resultMap = new MemberMap(); |
| 5966 } else { |
| 5967 HashMap<String, List<ExecutableElement>> unionMap = |
| 5968 _unionInterfaceLookupMaps(lookupMaps); |
| 5969 resultMap = _resolveInheritanceLookup(classElt, unionMap); |
| 5970 } |
| 5971 _interfaceLookup[classElt] = resultMap; |
| 5972 return resultMap; |
| 5973 } |
| 5974 |
| 5975 /** |
| 5976 * Collect a list of interface lookup maps whose elements correspond to all of
the classes |
| 5977 * directly above [classElt] in the class hierarchy (the direct superclass if
any, all |
| 5978 * mixins, and all direct superinterfaces). Each item in the list is the inter
face lookup map |
| 5979 * returned by [computeInterfaceLookupMap] for the corresponding super, except
with type |
| 5980 * parameters appropriately substituted. |
| 5981 * |
| 5982 * @param classElt the class element to query |
| 5983 * @param visitedInterfaces a set of visited classes passed back into this met
hod when it calls |
| 5984 * itself recursively |
| 5985 * @return `null` if there was a problem (such as a loop in the class hierarch
y) or if there |
| 5986 * are no classes above this one in the class hierarchy. Otherwise, a
list of interface |
| 5987 * lookup maps. |
| 5988 */ |
| 5989 List<MemberMap> _gatherInterfaceLookupMaps( |
| 5990 ClassElement classElt, HashSet<ClassElement> visitedInterfaces) { |
| 5991 InterfaceType supertype = classElt.supertype; |
| 5992 ClassElement superclassElement = |
| 5993 supertype != null ? supertype.element : null; |
| 5994 List<InterfaceType> mixins = classElt.mixins; |
| 5995 List<InterfaceType> interfaces = classElt.interfaces; |
| 5996 // Recursively collect the list of mappings from all of the interface types |
| 5997 List<MemberMap> lookupMaps = new List<MemberMap>(); |
| 5998 // |
| 5999 // Superclass element |
| 6000 // |
| 6001 if (superclassElement != null) { |
| 6002 if (!visitedInterfaces.contains(superclassElement)) { |
| 6003 try { |
| 6004 visitedInterfaces.add(superclassElement); |
| 6005 // |
| 6006 // Recursively compute the map for the super type. |
| 6007 // |
| 6008 MemberMap map = |
| 6009 _computeInterfaceLookupMap(superclassElement, visitedInterfaces); |
| 6010 map = new MemberMap.from(map); |
| 6011 // |
| 6012 // Substitute the super type down the hierarchy. |
| 6013 // |
| 6014 _substituteTypeParametersDownHierarchy(supertype, map); |
| 6015 // |
| 6016 // Add any members from the super type into the map as well. |
| 6017 // |
| 6018 _recordMapWithClassMembers(map, supertype, true); |
| 6019 lookupMaps.add(map); |
| 6020 } finally { |
| 6021 visitedInterfaces.remove(superclassElement); |
| 6022 } |
| 6023 } else { |
| 6024 return null; |
| 6025 } |
| 6026 } |
| 6027 // |
| 6028 // Mixin elements |
| 6029 // |
| 6030 for (int i = mixins.length - 1; i >= 0; i--) { |
| 6031 InterfaceType mixinType = mixins[i]; |
| 6032 ClassElement mixinElement = mixinType.element; |
| 6033 if (mixinElement != null) { |
| 6034 if (!visitedInterfaces.contains(mixinElement)) { |
| 6035 try { |
| 6036 visitedInterfaces.add(mixinElement); |
| 6037 // |
| 6038 // Recursively compute the map for the mixin. |
| 6039 // |
| 6040 MemberMap map = |
| 6041 _computeInterfaceLookupMap(mixinElement, visitedInterfaces); |
| 6042 map = new MemberMap.from(map); |
| 6043 // |
| 6044 // Substitute the mixin type down the hierarchy. |
| 6045 // |
| 6046 _substituteTypeParametersDownHierarchy(mixinType, map); |
| 6047 // |
| 6048 // Add any members from the mixin type into the map as well. |
| 6049 // |
| 6050 _recordMapWithClassMembers(map, mixinType, true); |
| 6051 lookupMaps.add(map); |
| 6052 } finally { |
| 6053 visitedInterfaces.remove(mixinElement); |
| 6054 } |
| 6055 } else { |
| 6056 return null; |
| 6057 } |
| 6058 } |
| 6059 } |
| 6060 // |
| 6061 // Interface elements |
| 6062 // |
| 6063 for (InterfaceType interfaceType in interfaces) { |
| 6064 ClassElement interfaceElement = interfaceType.element; |
| 6065 if (interfaceElement != null) { |
| 6066 if (!visitedInterfaces.contains(interfaceElement)) { |
| 6067 try { |
| 6068 visitedInterfaces.add(interfaceElement); |
| 6069 // |
| 6070 // Recursively compute the map for the interfaces. |
| 6071 // |
| 6072 MemberMap map = |
| 6073 _computeInterfaceLookupMap(interfaceElement, visitedInterfaces); |
| 6074 map = new MemberMap.from(map); |
| 6075 // |
| 6076 // Substitute the supertypes down the hierarchy |
| 6077 // |
| 6078 _substituteTypeParametersDownHierarchy(interfaceType, map); |
| 6079 // |
| 6080 // And add any members from the interface into the map as well. |
| 6081 // |
| 6082 _recordMapWithClassMembers(map, interfaceType, true); |
| 6083 lookupMaps.add(map); |
| 6084 } finally { |
| 6085 visitedInterfaces.remove(interfaceElement); |
| 6086 } |
| 6087 } else { |
| 6088 return null; |
| 6089 } |
| 6090 } |
| 6091 } |
| 6092 if (lookupMaps.length == 0) { |
| 6093 return null; |
| 6094 } |
| 6095 return lookupMaps; |
| 6096 } |
| 6097 |
| 6098 /** |
| 6099 * Given some [ClassElement], this method finds and returns the [ExecutableEle
ment] of |
| 6100 * the passed name in the class element. Static members, members in super type
s and members not |
| 6101 * accessible from the current library are not considered. |
| 6102 * |
| 6103 * @param classElt the class element to query |
| 6104 * @param memberName the name of the member to lookup in the class |
| 6105 * @return the found [ExecutableElement], or `null` if no such member was foun
d |
| 6106 */ |
| 6107 ExecutableElement _lookupMemberInClass( |
| 6108 ClassElement classElt, String memberName) { |
| 6109 List<MethodElement> methods = classElt.methods; |
| 6110 for (MethodElement method in methods) { |
| 6111 if (memberName == method.name && |
| 6112 method.isAccessibleIn(_library) && |
| 6113 !method.isStatic) { |
| 6114 return method; |
| 6115 } |
| 6116 } |
| 6117 List<PropertyAccessorElement> accessors = classElt.accessors; |
| 6118 for (PropertyAccessorElement accessor in accessors) { |
| 6119 if (memberName == accessor.name && |
| 6120 accessor.isAccessibleIn(_library) && |
| 6121 !accessor.isStatic) { |
| 6122 return accessor; |
| 6123 } |
| 6124 } |
| 6125 return null; |
| 6126 } |
| 6127 |
| 6128 /** |
| 6129 * Record the passed map with the set of all members (methods, getters and set
ters) in the type |
| 6130 * into the passed map. |
| 6131 * |
| 6132 * @param map some non-`null` map to put the methods and accessors from the pa
ssed |
| 6133 * [ClassElement] into |
| 6134 * @param type the type that will be recorded into the passed map |
| 6135 * @param doIncludeAbstract `true` if abstract members will be put into the ma
p |
| 6136 */ |
| 6137 void _recordMapWithClassMembers( |
| 6138 MemberMap map, InterfaceType type, bool doIncludeAbstract) { |
| 6139 List<MethodElement> methods = type.methods; |
| 6140 for (MethodElement method in methods) { |
| 6141 if (method.isAccessibleIn(_library) && |
| 6142 !method.isStatic && |
| 6143 (doIncludeAbstract || !method.isAbstract)) { |
| 6144 map.put(method.name, method); |
| 6145 } |
| 6146 } |
| 6147 List<PropertyAccessorElement> accessors = type.accessors; |
| 6148 for (PropertyAccessorElement accessor in accessors) { |
| 6149 if (accessor.isAccessibleIn(_library) && |
| 6150 !accessor.isStatic && |
| 6151 (doIncludeAbstract || !accessor.isAbstract)) { |
| 6152 map.put(accessor.name, accessor); |
| 6153 } |
| 6154 } |
| 6155 } |
| 6156 |
| 6157 /** |
| 6158 * This method is used to report errors on when they are found computing inher
itance information. |
| 6159 * See [ErrorVerifier.checkForInconsistentMethodInheritance] to see where thes
e generated |
| 6160 * error codes are reported back into the analysis engine. |
| 6161 * |
| 6162 * @param classElt the location of the source for which the exception occurred |
| 6163 * @param offset the offset of the location of the error |
| 6164 * @param length the length of the location of the error |
| 6165 * @param errorCode the error code to be associated with this error |
| 6166 * @param arguments the arguments used to build the error message |
| 6167 */ |
| 6168 void _reportError(ClassElement classElt, int offset, int length, |
| 6169 ErrorCode errorCode, List<Object> arguments) { |
| 6170 HashSet<AnalysisError> errorSet = _errorsInClassElement[classElt]; |
| 6171 if (errorSet == null) { |
| 6172 errorSet = new HashSet<AnalysisError>(); |
| 6173 _errorsInClassElement[classElt] = errorSet; |
| 6174 } |
| 6175 errorSet.add(new AnalysisError( |
| 6176 classElt.source, offset, length, errorCode, arguments)); |
| 6177 } |
| 6178 |
| 6179 /** |
| 6180 * Given the set of methods defined by classes above [classElt] in the class h
ierarchy, |
| 6181 * apply the appropriate inheritance rules to determine those methods inherite
d by or overridden |
| 6182 * by [classElt]. Also report static warnings |
| 6183 * [StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE] and |
| 6184 * [StaticWarningCode.INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD] if ap
propriate. |
| 6185 * |
| 6186 * @param classElt the class element to query. |
| 6187 * @param unionMap a mapping from method name to the set of unique (in terms o
f signature) methods |
| 6188 * defined in superclasses of [classElt]. |
| 6189 * @return the inheritance lookup map for [classElt]. |
| 6190 */ |
| 6191 MemberMap _resolveInheritanceLookup(ClassElement classElt, |
| 6192 HashMap<String, List<ExecutableElement>> unionMap) { |
| 6193 MemberMap resultMap = new MemberMap(); |
| 6194 unionMap.forEach((String key, List<ExecutableElement> list) { |
| 6195 int numOfEltsWithMatchingNames = list.length; |
| 6196 if (numOfEltsWithMatchingNames == 1) { |
| 6197 // |
| 6198 // Example: class A inherits only 1 method named 'm'. |
| 6199 // Since it is the only such method, it is inherited. |
| 6200 // Another example: class A inherits 2 methods named 'm' from 2 |
| 6201 // different interfaces, but they both have the same signature, so it is |
| 6202 // the method inherited. |
| 6203 // |
| 6204 resultMap.put(key, list[0]); |
| 6205 } else { |
| 6206 // |
| 6207 // Then numOfEltsWithMatchingNames > 1, check for the warning cases. |
| 6208 // |
| 6209 bool allMethods = true; |
| 6210 bool allSetters = true; |
| 6211 bool allGetters = true; |
| 6212 for (ExecutableElement executableElement in list) { |
| 6213 if (executableElement is PropertyAccessorElement) { |
| 6214 allMethods = false; |
| 6215 if (executableElement.isSetter) { |
| 6216 allGetters = false; |
| 6217 } else { |
| 6218 allSetters = false; |
| 6219 } |
| 6220 } else { |
| 6221 allGetters = false; |
| 6222 allSetters = false; |
| 6223 } |
| 6224 } |
| 6225 // |
| 6226 // If there isn't a mixture of methods with getters, then continue, |
| 6227 // otherwise create a warning. |
| 6228 // |
| 6229 if (allMethods || allGetters || allSetters) { |
| 6230 // |
| 6231 // Compute the element whose type is the subtype of all of the other |
| 6232 // types. |
| 6233 // |
| 6234 List<ExecutableElement> elements = new List.from(list); |
| 6235 List<FunctionType> executableElementTypes = |
| 6236 new List<FunctionType>(numOfEltsWithMatchingNames); |
| 6237 for (int i = 0; i < numOfEltsWithMatchingNames; i++) { |
| 6238 executableElementTypes[i] = elements[i].type; |
| 6239 } |
| 6240 List<int> subtypesOfAllOtherTypesIndexes = new List<int>(); |
| 6241 for (int i = 0; i < numOfEltsWithMatchingNames; i++) { |
| 6242 FunctionType subtype = executableElementTypes[i]; |
| 6243 if (subtype == null) { |
| 6244 continue; |
| 6245 } |
| 6246 bool subtypeOfAllTypes = true; |
| 6247 for (int j = 0; |
| 6248 j < numOfEltsWithMatchingNames && subtypeOfAllTypes; |
| 6249 j++) { |
| 6250 if (i != j) { |
| 6251 if (!subtype.isSubtypeOf(executableElementTypes[j])) { |
| 6252 subtypeOfAllTypes = false; |
| 6253 break; |
| 6254 } |
| 6255 } |
| 6256 } |
| 6257 if (subtypeOfAllTypes) { |
| 6258 subtypesOfAllOtherTypesIndexes.add(i); |
| 6259 } |
| 6260 } |
| 6261 // |
| 6262 // The following is split into three cases determined by the number of |
| 6263 // elements in subtypesOfAllOtherTypes |
| 6264 // |
| 6265 if (subtypesOfAllOtherTypesIndexes.length == 1) { |
| 6266 // |
| 6267 // Example: class A inherited only 2 method named 'm'. |
| 6268 // One has the function type '() -> dynamic' and one has the |
| 6269 // function type '([int]) -> dynamic'. Since the second method is a |
| 6270 // subtype of all the others, it is the inherited method. |
| 6271 // Tests: InheritanceManagerTest. |
| 6272 // test_getMapOfMembersInheritedFromInterfaces_union_oneSubtype_* |
| 6273 // |
| 6274 resultMap.put(key, elements[subtypesOfAllOtherTypesIndexes[0]]); |
| 6275 } else { |
| 6276 if (subtypesOfAllOtherTypesIndexes.isEmpty) { |
| 6277 // |
| 6278 // Determine if the current class has a method or accessor with |
| 6279 // the member name, if it does then then this class does not |
| 6280 // "inherit" from any of the supertypes. See issue 16134. |
| 6281 // |
| 6282 bool classHasMember = false; |
| 6283 if (allMethods) { |
| 6284 classHasMember = classElt.getMethod(key) != null; |
| 6285 } else { |
| 6286 List<PropertyAccessorElement> accessors = classElt.accessors; |
| 6287 for (int i = 0; i < accessors.length; i++) { |
| 6288 if (accessors[i].name == key) { |
| 6289 classHasMember = true; |
| 6290 } |
| 6291 } |
| 6292 } |
| 6293 // |
| 6294 // Example: class A inherited only 2 method named 'm'. |
| 6295 // One has the function type '() -> int' and one has the function |
| 6296 // type '() -> String'. Since neither is a subtype of the other, |
| 6297 // we create a warning, and have this class inherit nothing. |
| 6298 // |
| 6299 if (!classHasMember) { |
| 6300 String firstTwoFuntionTypesStr = |
| 6301 "${executableElementTypes[0]}, ${executableElementTypes[1]}"
; |
| 6302 _reportError( |
| 6303 classElt, |
| 6304 classElt.nameOffset, |
| 6305 classElt.displayName.length, |
| 6306 StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE, |
| 6307 [key, firstTwoFuntionTypesStr]); |
| 6308 } |
| 6309 } else { |
| 6310 // |
| 6311 // Example: class A inherits 2 methods named 'm'. |
| 6312 // One has the function type '(int) -> dynamic' and one has the |
| 6313 // function type '(num) -> dynamic'. Since they are both a subtype |
| 6314 // of the other, a synthetic function '(dynamic) -> dynamic' is |
| 6315 // inherited. |
| 6316 // Tests: test_getMapOfMembersInheritedFromInterfaces_ |
| 6317 // union_multipleSubtypes_* |
| 6318 // |
| 6319 List<ExecutableElement> elementArrayToMerge = new List< |
| 6320 ExecutableElement>(subtypesOfAllOtherTypesIndexes.length); |
| 6321 for (int i = 0; i < elementArrayToMerge.length; i++) { |
| 6322 elementArrayToMerge[i] = |
| 6323 elements[subtypesOfAllOtherTypesIndexes[i]]; |
| 6324 } |
| 6325 ExecutableElement mergedExecutableElement = |
| 6326 _computeMergedExecutableElement(elementArrayToMerge); |
| 6327 resultMap.put(key, mergedExecutableElement); |
| 6328 } |
| 6329 } |
| 6330 } else { |
| 6331 _reportError( |
| 6332 classElt, |
| 6333 classElt.nameOffset, |
| 6334 classElt.displayName.length, |
| 6335 StaticWarningCode.INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHO
D, |
| 6336 [key]); |
| 6337 } |
| 6338 } |
| 6339 }); |
| 6340 return resultMap; |
| 6341 } |
| 6342 |
| 6343 /** |
| 6344 * Loop through all of the members in some [MemberMap], performing type parame
ter |
| 6345 * substitutions using a passed supertype. |
| 6346 * |
| 6347 * @param superType the supertype to substitute into the members of the [Membe
rMap] |
| 6348 * @param map the MemberMap to perform the substitutions on |
| 6349 */ |
| 6350 void _substituteTypeParametersDownHierarchy( |
| 6351 InterfaceType superType, MemberMap map) { |
| 6352 for (int i = 0; i < map.size; i++) { |
| 6353 ExecutableElement executableElement = map.getValue(i); |
| 6354 if (executableElement is MethodMember) { |
| 6355 executableElement = |
| 6356 MethodMember.from(executableElement as MethodMember, superType); |
| 6357 map.setValue(i, executableElement); |
| 6358 } else if (executableElement is PropertyAccessorMember) { |
| 6359 executableElement = PropertyAccessorMember.from( |
| 6360 executableElement as PropertyAccessorMember, superType); |
| 6361 map.setValue(i, executableElement); |
| 6362 } |
| 6363 } |
| 6364 } |
| 6365 |
| 6366 /** |
| 6367 * Union all of the [lookupMaps] together into a single map, grouping the Exec
utableElements |
| 6368 * into a list where none of the elements are equal where equality is determin
ed by having equal |
| 6369 * function types. (We also take note too of the kind of the element: ()->int
and () -> int may |
| 6370 * not be equal if one is a getter and the other is a method.) |
| 6371 * |
| 6372 * @param lookupMaps the maps to be unioned together. |
| 6373 * @return the resulting union map. |
| 6374 */ |
| 6375 HashMap<String, List<ExecutableElement>> _unionInterfaceLookupMaps( |
| 6376 List<MemberMap> lookupMaps) { |
| 6377 HashMap<String, List<ExecutableElement>> unionMap = |
| 6378 new HashMap<String, List<ExecutableElement>>(); |
| 6379 for (MemberMap lookupMap in lookupMaps) { |
| 6380 int lookupMapSize = lookupMap.size; |
| 6381 for (int i = 0; i < lookupMapSize; i++) { |
| 6382 // Get the string key, if null, break. |
| 6383 String key = lookupMap.getKey(i); |
| 6384 if (key == null) { |
| 6385 break; |
| 6386 } |
| 6387 // Get the list value out of the unionMap |
| 6388 List<ExecutableElement> list = unionMap[key]; |
| 6389 // If we haven't created such a map for this key yet, do create it and |
| 6390 // put the list entry into the unionMap. |
| 6391 if (list == null) { |
| 6392 list = new List<ExecutableElement>(); |
| 6393 unionMap[key] = list; |
| 6394 } |
| 6395 // Fetch the entry out of this lookupMap |
| 6396 ExecutableElement newExecutableElementEntry = lookupMap.getValue(i); |
| 6397 if (list.isEmpty) { |
| 6398 // If the list is empty, just the new value |
| 6399 list.add(newExecutableElementEntry); |
| 6400 } else { |
| 6401 // Otherwise, only add the newExecutableElementEntry if it isn't |
| 6402 // already in the list, this covers situation where a class inherits |
| 6403 // two methods (or two getters) that are identical. |
| 6404 bool alreadyInList = false; |
| 6405 bool isMethod1 = newExecutableElementEntry is MethodElement; |
| 6406 for (ExecutableElement executableElementInList in list) { |
| 6407 bool isMethod2 = executableElementInList is MethodElement; |
| 6408 if (isMethod1 == isMethod2 && |
| 6409 executableElementInList.type == |
| 6410 newExecutableElementEntry.type) { |
| 6411 alreadyInList = true; |
| 6412 break; |
| 6413 } |
| 6414 } |
| 6415 if (!alreadyInList) { |
| 6416 list.add(newExecutableElementEntry); |
| 6417 } |
| 6418 } |
| 6419 } |
| 6420 } |
| 6421 return unionMap; |
| 6422 } |
| 6423 |
| 6424 /** |
| 6425 * Given some array of [ExecutableElement]s, this method creates a synthetic e
lement as |
| 6426 * described in 8.1.1: |
| 6427 * |
| 6428 * Let <i>numberOfPositionals</i>(<i>f</i>) denote the number of positional pa
rameters of a |
| 6429 * function <i>f</i>, and let <i>numberOfRequiredParams</i>(<i>f</i>) denote t
he number of |
| 6430 * required parameters of a function <i>f</i>. Furthermore, let <i>s</i> denot
e the set of all |
| 6431 * named parameters of the <i>m<sub>1</sub>, …, m<sub>k</sub></i>. Then
let |
| 6432 * * <i>h = max(numberOfPositionals(m<sub>i</sub>)),</i> |
| 6433 * * <i>r = min(numberOfRequiredParams(m<sub>i</sub>)), for all <i>i</i>, 1 <=
i <= k.</i> |
| 6434 * Then <i>I</i> has a method named <i>n</i>, with <i>r</i> required parameter
s of type |
| 6435 * <b>dynamic</b>, <i>h</i> positional parameters of type <b>dynamic</b>, name
d parameters |
| 6436 * <i>s</i> of type <b>dynamic</b> and return type <b>dynamic</b>. |
| 6437 * |
| 6438 */ |
| 6439 static ExecutableElement _computeMergedExecutableElement( |
| 6440 List<ExecutableElement> elementArrayToMerge) { |
| 6441 int h = _getNumOfPositionalParameters(elementArrayToMerge[0]); |
| 6442 int r = _getNumOfRequiredParameters(elementArrayToMerge[0]); |
| 6443 Set<String> namedParametersList = new HashSet<String>(); |
| 6444 for (int i = 1; i < elementArrayToMerge.length; i++) { |
| 6445 ExecutableElement element = elementArrayToMerge[i]; |
| 6446 int numOfPositionalParams = _getNumOfPositionalParameters(element); |
| 6447 if (h < numOfPositionalParams) { |
| 6448 h = numOfPositionalParams; |
| 6449 } |
| 6450 int numOfRequiredParams = _getNumOfRequiredParameters(element); |
| 6451 if (r > numOfRequiredParams) { |
| 6452 r = numOfRequiredParams; |
| 6453 } |
| 6454 namedParametersList.addAll(_getNamedParameterNames(element)); |
| 6455 } |
| 6456 return _createSyntheticExecutableElement( |
| 6457 elementArrayToMerge, |
| 6458 elementArrayToMerge[0].displayName, |
| 6459 r, |
| 6460 h - r, |
| 6461 new List.from(namedParametersList)); |
| 6462 } |
| 6463 |
| 6464 /** |
| 6465 * Used by [computeMergedExecutableElement] to actually create the |
| 6466 * synthetic element. |
| 6467 * |
| 6468 * @param elementArrayToMerge the array used to create the synthetic element |
| 6469 * @param name the name of the method, getter or setter |
| 6470 * @param numOfRequiredParameters the number of required parameters |
| 6471 * @param numOfPositionalParameters the number of positional parameters |
| 6472 * @param namedParameters the list of [String]s that are the named parameters |
| 6473 * @return the created synthetic element |
| 6474 */ |
| 6475 static ExecutableElement _createSyntheticExecutableElement( |
| 6476 List<ExecutableElement> elementArrayToMerge, |
| 6477 String name, |
| 6478 int numOfRequiredParameters, |
| 6479 int numOfPositionalParameters, |
| 6480 List<String> namedParameters) { |
| 6481 DynamicTypeImpl dynamicType = DynamicTypeImpl.instance; |
| 6482 SimpleIdentifier nameIdentifier = new SimpleIdentifier( |
| 6483 new sc.StringToken(sc.TokenType.IDENTIFIER, name, 0)); |
| 6484 ExecutableElementImpl executable; |
| 6485 if (elementArrayToMerge[0] is MethodElement) { |
| 6486 MultiplyInheritedMethodElementImpl unionedMethod = |
| 6487 new MultiplyInheritedMethodElementImpl(nameIdentifier); |
| 6488 unionedMethod.inheritedElements = elementArrayToMerge; |
| 6489 executable = unionedMethod; |
| 6490 } else { |
| 6491 MultiplyInheritedPropertyAccessorElementImpl unionedPropertyAccessor = |
| 6492 new MultiplyInheritedPropertyAccessorElementImpl(nameIdentifier); |
| 6493 unionedPropertyAccessor.getter = |
| 6494 (elementArrayToMerge[0] as PropertyAccessorElement).isGetter; |
| 6495 unionedPropertyAccessor.setter = |
| 6496 (elementArrayToMerge[0] as PropertyAccessorElement).isSetter; |
| 6497 unionedPropertyAccessor.inheritedElements = elementArrayToMerge; |
| 6498 executable = unionedPropertyAccessor; |
| 6499 } |
| 6500 int numOfParameters = numOfRequiredParameters + |
| 6501 numOfPositionalParameters + |
| 6502 namedParameters.length; |
| 6503 List<ParameterElement> parameters = |
| 6504 new List<ParameterElement>(numOfParameters); |
| 6505 int i = 0; |
| 6506 for (int j = 0; j < numOfRequiredParameters; j++, i++) { |
| 6507 ParameterElementImpl parameter = new ParameterElementImpl("", 0); |
| 6508 parameter.type = dynamicType; |
| 6509 parameter.parameterKind = ParameterKind.REQUIRED; |
| 6510 parameters[i] = parameter; |
| 6511 } |
| 6512 for (int k = 0; k < numOfPositionalParameters; k++, i++) { |
| 6513 ParameterElementImpl parameter = new ParameterElementImpl("", 0); |
| 6514 parameter.type = dynamicType; |
| 6515 parameter.parameterKind = ParameterKind.POSITIONAL; |
| 6516 parameters[i] = parameter; |
| 6517 } |
| 6518 for (int m = 0; m < namedParameters.length; m++, i++) { |
| 6519 ParameterElementImpl parameter = |
| 6520 new ParameterElementImpl(namedParameters[m], 0); |
| 6521 parameter.type = dynamicType; |
| 6522 parameter.parameterKind = ParameterKind.NAMED; |
| 6523 parameters[i] = parameter; |
| 6524 } |
| 6525 executable.returnType = dynamicType; |
| 6526 executable.parameters = parameters; |
| 6527 FunctionTypeImpl methodType = new FunctionTypeImpl(executable); |
| 6528 executable.type = methodType; |
| 6529 return executable; |
| 6530 } |
| 6531 |
| 6532 /** |
| 6533 * Given some [ExecutableElement], return the list of named parameters. |
| 6534 */ |
| 6535 static List<String> _getNamedParameterNames( |
| 6536 ExecutableElement executableElement) { |
| 6537 List<String> namedParameterNames = new List<String>(); |
| 6538 List<ParameterElement> parameters = executableElement.parameters; |
| 6539 for (int i = 0; i < parameters.length; i++) { |
| 6540 ParameterElement parameterElement = parameters[i]; |
| 6541 if (parameterElement.parameterKind == ParameterKind.NAMED) { |
| 6542 namedParameterNames.add(parameterElement.name); |
| 6543 } |
| 6544 } |
| 6545 return namedParameterNames; |
| 6546 } |
| 6547 |
| 6548 /** |
| 6549 * Given some [ExecutableElement] return the number of parameters of the speci
fied kind. |
| 6550 */ |
| 6551 static int _getNumOfParameters( |
| 6552 ExecutableElement executableElement, ParameterKind parameterKind) { |
| 6553 int parameterCount = 0; |
| 6554 List<ParameterElement> parameters = executableElement.parameters; |
| 6555 for (int i = 0; i < parameters.length; i++) { |
| 6556 ParameterElement parameterElement = parameters[i]; |
| 6557 if (parameterElement.parameterKind == parameterKind) { |
| 6558 parameterCount++; |
| 6559 } |
| 6560 } |
| 6561 return parameterCount; |
| 6562 } |
| 6563 |
| 6564 /** |
| 6565 * Given some [ExecutableElement] return the number of positional parameters. |
| 6566 * |
| 6567 * Note: by positional we mean [ParameterKind.REQUIRED] or [ParameterKind.POSI
TIONAL]. |
| 6568 */ |
| 6569 static int _getNumOfPositionalParameters( |
| 6570 ExecutableElement executableElement) => |
| 6571 _getNumOfParameters(executableElement, ParameterKind.REQUIRED) + |
| 6572 _getNumOfParameters(executableElement, ParameterKind.POSITIONAL); |
| 6573 |
| 6574 /** |
| 6575 * Given some [ExecutableElement] return the number of required parameters. |
| 6576 */ |
| 6577 static int _getNumOfRequiredParameters(ExecutableElement executableElement) => |
| 6578 _getNumOfParameters(executableElement, ParameterKind.REQUIRED); |
| 6579 |
| 6580 /** |
| 6581 * Given some [ExecutableElement] returns `true` if it is an abstract member o
f a |
| 6582 * class. |
| 6583 * |
| 6584 * @param executableElement some [ExecutableElement] to evaluate |
| 6585 * @return `true` if the given element is an abstract member of a class |
| 6586 */ |
| 6587 static bool _isAbstract(ExecutableElement executableElement) { |
| 6588 if (executableElement is MethodElement) { |
| 6589 return executableElement.isAbstract; |
| 6590 } else if (executableElement is PropertyAccessorElement) { |
| 6591 return executableElement.isAbstract; |
| 6592 } |
| 6593 return false; |
| 6594 } |
| 6595 } |
| 6596 |
| 6597 /** |
| 6598 * This enum holds one of four states of a field initialization state through a
constructor |
| 6599 * signature, not initialized, initialized in the field declaration, initialized
in the field |
| 6600 * formal, and finally, initialized in the initializers list. |
| 6601 */ |
| 6602 class INIT_STATE extends Enum<INIT_STATE> { |
| 6603 static const INIT_STATE NOT_INIT = const INIT_STATE('NOT_INIT', 0); |
| 6604 |
| 6605 static const INIT_STATE INIT_IN_DECLARATION = |
| 6606 const INIT_STATE('INIT_IN_DECLARATION', 1); |
| 6607 |
| 6608 static const INIT_STATE INIT_IN_FIELD_FORMAL = |
| 6609 const INIT_STATE('INIT_IN_FIELD_FORMAL', 2); |
| 6610 |
| 6611 static const INIT_STATE INIT_IN_INITIALIZERS = |
| 6612 const INIT_STATE('INIT_IN_INITIALIZERS', 3); |
| 6613 |
| 6614 static const List<INIT_STATE> values = const [ |
| 6615 NOT_INIT, |
| 6616 INIT_IN_DECLARATION, |
| 6617 INIT_IN_FIELD_FORMAL, |
| 6618 INIT_IN_INITIALIZERS |
| 6619 ]; |
| 6620 |
| 6621 const INIT_STATE(String name, int ordinal) : super(name, ordinal); |
| 6622 } |
| 6623 |
| 6624 /** |
| 6625 * Instances of the class `LabelScope` represent a scope in which a single label
is defined. |
| 6626 */ |
| 6627 class LabelScope { |
| 6628 /** |
| 6629 * The label scope enclosing this label scope. |
| 6630 */ |
| 6631 final LabelScope _outerScope; |
| 6632 |
| 6633 /** |
| 6634 * The label defined in this scope. |
| 6635 */ |
| 6636 final String _label; |
| 6637 |
| 6638 /** |
| 6639 * The element to which the label resolves. |
| 6640 */ |
| 6641 final LabelElement element; |
| 6642 |
| 6643 /** |
| 6644 * The AST node to which the label resolves. |
| 6645 */ |
| 6646 final AstNode node; |
| 6647 |
| 6648 /** |
| 6649 * Initialize a newly created scope to represent the label [_label]. |
| 6650 * [_outerScope] is the scope enclosing the new label scope. [node] is the |
| 6651 * AST node the label resolves to. [element] is the element the label |
| 6652 * resolves to. |
| 6653 */ |
| 6654 LabelScope(this._outerScope, this._label, this.node, this.element); |
| 6655 |
| 6656 /** |
| 6657 * Return the LabelScope which defines [targetLabel], or `null` if it is not |
| 6658 * defined in this scope. |
| 6659 */ |
| 6660 LabelScope lookup(String targetLabel) { |
| 6661 if (_label == targetLabel) { |
| 6662 return this; |
| 6663 } else if (_outerScope != null) { |
| 6664 return _outerScope.lookup(targetLabel); |
| 6665 } else { |
| 6666 return null; |
| 6667 } |
| 6668 } |
| 6669 } |
| 6670 |
| 6671 /** |
| 6672 * Instances of the class `Library` represent the data about a single library du
ring the |
| 6673 * resolution of some (possibly different) library. They are not intended to be
used except during |
| 6674 * the resolution process. |
| 6675 */ |
| 6676 class Library { |
| 6677 /** |
| 6678 * An empty list that can be used to initialize lists of libraries. |
| 6679 */ |
| 6680 static const List<Library> _EMPTY_ARRAY = const <Library>[]; |
| 6681 |
| 6682 /** |
| 6683 * The prefix of a URI using the dart-ext scheme to reference a native code li
brary. |
| 6684 */ |
| 6685 static String _DART_EXT_SCHEME = "dart-ext:"; |
| 6686 |
| 6687 /** |
| 6688 * The analysis context in which this library is being analyzed. |
| 6689 */ |
| 6690 final InternalAnalysisContext _analysisContext; |
| 6691 |
| 6692 /** |
| 6693 * The inheritance manager which is used for this member lookups in this libra
ry. |
| 6694 */ |
| 6695 InheritanceManager _inheritanceManager; |
| 6696 |
| 6697 /** |
| 6698 * The listener to which analysis errors will be reported. |
| 6699 */ |
| 6700 final AnalysisErrorListener errorListener; |
| 6701 |
| 6702 /** |
| 6703 * The source specifying the defining compilation unit of this library. |
| 6704 */ |
| 6705 final Source librarySource; |
| 6706 |
| 6707 /** |
| 6708 * The library element representing this library. |
| 6709 */ |
| 6710 LibraryElementImpl _libraryElement; |
| 6711 |
| 6712 /** |
| 6713 * A list containing all of the libraries that are imported into this library. |
| 6714 */ |
| 6715 List<Library> _importedLibraries = _EMPTY_ARRAY; |
| 6716 |
| 6717 /** |
| 6718 * A table mapping URI-based directive to the actual URI value. |
| 6719 */ |
| 6720 HashMap<UriBasedDirective, String> _directiveUris = |
| 6721 new HashMap<UriBasedDirective, String>(); |
| 6722 |
| 6723 /** |
| 6724 * A flag indicating whether this library explicitly imports core. |
| 6725 */ |
| 6726 bool explicitlyImportsCore = false; |
| 6727 |
| 6728 /** |
| 6729 * A list containing all of the libraries that are exported from this library. |
| 6730 */ |
| 6731 List<Library> _exportedLibraries = _EMPTY_ARRAY; |
| 6732 |
| 6733 /** |
| 6734 * A table mapping the sources for the compilation units in this library to th
eir corresponding |
| 6735 * AST structures. |
| 6736 */ |
| 6737 HashMap<Source, CompilationUnit> _astMap = |
| 6738 new HashMap<Source, CompilationUnit>(); |
| 6739 |
| 6740 /** |
| 6741 * The library scope used when resolving elements within this library's compil
ation units. |
| 6742 */ |
| 6743 LibraryScope _libraryScope; |
| 6744 |
| 6745 /** |
| 6746 * Initialize a newly created data holder that can maintain the data associate
d with a library. |
| 6747 * |
| 6748 * @param analysisContext the analysis context in which this library is being
analyzed |
| 6749 * @param errorListener the listener to which analysis errors will be reported |
| 6750 * @param librarySource the source specifying the defining compilation unit of
this library |
| 6751 */ |
| 6752 Library(this._analysisContext, this.errorListener, this.librarySource) { |
| 6753 this._libraryElement = |
| 6754 _analysisContext.getLibraryElement(librarySource) as LibraryElementImpl; |
| 6755 } |
| 6756 |
| 6757 /** |
| 6758 * Return an array of the [CompilationUnit]s that make up the library. The fir
st unit is |
| 6759 * always the defining unit. |
| 6760 * |
| 6761 * @return an array of the [CompilationUnit]s that make up the library. The fi
rst unit is |
| 6762 * always the defining unit |
| 6763 */ |
| 6764 List<CompilationUnit> get compilationUnits { |
| 6765 List<CompilationUnit> unitArrayList = new List<CompilationUnit>(); |
| 6766 unitArrayList.add(definingCompilationUnit); |
| 6767 for (Source source in _astMap.keys.toSet()) { |
| 6768 if (librarySource != source) { |
| 6769 unitArrayList.add(getAST(source)); |
| 6770 } |
| 6771 } |
| 6772 return unitArrayList; |
| 6773 } |
| 6774 |
| 6775 /** |
| 6776 * Return a collection containing the sources for the compilation units in thi
s library, including |
| 6777 * the defining compilation unit. |
| 6778 * |
| 6779 * @return the sources for the compilation units in this library |
| 6780 */ |
| 6781 Set<Source> get compilationUnitSources => _astMap.keys.toSet(); |
| 6782 |
| 6783 /** |
| 6784 * Return the AST structure associated with the defining compilation unit for
this library. |
| 6785 * |
| 6786 * @return the AST structure associated with the defining compilation unit for
this library |
| 6787 * @throws AnalysisException if an AST structure could not be created for the
defining compilation |
| 6788 * unit |
| 6789 */ |
| 6790 CompilationUnit get definingCompilationUnit => getAST(librarySource); |
| 6791 |
| 6792 /** |
| 6793 * Set the libraries that are exported by this library to be those in the give
n array. |
| 6794 * |
| 6795 * @param exportedLibraries the libraries that are exported by this library |
| 6796 */ |
| 6797 void set exportedLibraries(List<Library> exportedLibraries) { |
| 6798 this._exportedLibraries = exportedLibraries; |
| 6799 } |
| 6800 |
| 6801 /** |
| 6802 * Return an array containing the libraries that are exported from this librar
y. |
| 6803 * |
| 6804 * @return an array containing the libraries that are exported from this libra
ry |
| 6805 */ |
| 6806 List<Library> get exports => _exportedLibraries; |
| 6807 |
| 6808 /** |
| 6809 * Set the libraries that are imported into this library to be those in the gi
ven array. |
| 6810 * |
| 6811 * @param importedLibraries the libraries that are imported into this library |
| 6812 */ |
| 6813 void set importedLibraries(List<Library> importedLibraries) { |
| 6814 this._importedLibraries = importedLibraries; |
| 6815 } |
| 6816 |
| 6817 /** |
| 6818 * Return an array containing the libraries that are imported into this librar
y. |
| 6819 * |
| 6820 * @return an array containing the libraries that are imported into this libra
ry |
| 6821 */ |
| 6822 List<Library> get imports => _importedLibraries; |
| 6823 |
| 6824 /** |
| 6825 * Return an array containing the libraries that are either imported or export
ed from this |
| 6826 * library. |
| 6827 * |
| 6828 * @return the libraries that are either imported or exported from this librar
y |
| 6829 */ |
| 6830 List<Library> get importsAndExports { |
| 6831 HashSet<Library> libraries = new HashSet<Library>(); |
| 6832 for (Library library in _importedLibraries) { |
| 6833 libraries.add(library); |
| 6834 } |
| 6835 for (Library library in _exportedLibraries) { |
| 6836 libraries.add(library); |
| 6837 } |
| 6838 return new List.from(libraries); |
| 6839 } |
| 6840 |
| 6841 /** |
| 6842 * Return the inheritance manager for this library. |
| 6843 * |
| 6844 * @return the inheritance manager for this library |
| 6845 */ |
| 6846 InheritanceManager get inheritanceManager { |
| 6847 if (_inheritanceManager == null) { |
| 6848 return _inheritanceManager = new InheritanceManager(_libraryElement); |
| 6849 } |
| 6850 return _inheritanceManager; |
| 6851 } |
| 6852 |
| 6853 /** |
| 6854 * Return the library element representing this library, creating it if necess
ary. |
| 6855 * |
| 6856 * @return the library element representing this library |
| 6857 */ |
| 6858 LibraryElementImpl get libraryElement { |
| 6859 if (_libraryElement == null) { |
| 6860 try { |
| 6861 _libraryElement = _analysisContext.computeLibraryElement(librarySource) |
| 6862 as LibraryElementImpl; |
| 6863 } on AnalysisException catch (exception, stackTrace) { |
| 6864 AnalysisEngine.instance.logger.logError( |
| 6865 "Could not compute library element for ${librarySource.fullName}", |
| 6866 new CaughtException(exception, stackTrace)); |
| 6867 } |
| 6868 } |
| 6869 return _libraryElement; |
| 6870 } |
| 6871 |
| 6872 /** |
| 6873 * Set the library element representing this library to the given library elem
ent. |
| 6874 * |
| 6875 * @param libraryElement the library element representing this library |
| 6876 */ |
| 6877 void set libraryElement(LibraryElementImpl libraryElement) { |
| 6878 this._libraryElement = libraryElement; |
| 6879 if (_inheritanceManager != null) { |
| 6880 _inheritanceManager.libraryElement = libraryElement; |
| 6881 } |
| 6882 } |
| 6883 |
| 6884 /** |
| 6885 * Return the library scope used when resolving elements within this library's
compilation units. |
| 6886 * |
| 6887 * @return the library scope used when resolving elements within this library'
s compilation units |
| 6888 */ |
| 6889 LibraryScope get libraryScope { |
| 6890 if (_libraryScope == null) { |
| 6891 _libraryScope = new LibraryScope(_libraryElement, errorListener); |
| 6892 } |
| 6893 return _libraryScope; |
| 6894 } |
| 6895 |
| 6896 /** |
| 6897 * Return the AST structure associated with the given source. |
| 6898 * |
| 6899 * @param source the source representing the compilation unit whose AST is to
be returned |
| 6900 * @return the AST structure associated with the given source |
| 6901 * @throws AnalysisException if an AST structure could not be created for the
compilation unit |
| 6902 */ |
| 6903 CompilationUnit getAST(Source source) { |
| 6904 CompilationUnit unit = _astMap[source]; |
| 6905 if (unit == null) { |
| 6906 unit = _analysisContext.computeResolvableCompilationUnit(source); |
| 6907 _astMap[source] = unit; |
| 6908 } |
| 6909 return unit; |
| 6910 } |
| 6911 |
| 6912 /** |
| 6913 * Return the result of resolving the URI of the given URI-based directive aga
inst the URI of the |
| 6914 * library, or `null` if the URI is not valid. If the URI is not valid, report
the error. |
| 6915 * |
| 6916 * @param directive the directive which URI should be resolved |
| 6917 * @return the result of resolving the URI against the URI of the library |
| 6918 */ |
| 6919 Source getSource(UriBasedDirective directive) { |
| 6920 StringLiteral uriLiteral = directive.uri; |
| 6921 if (uriLiteral is StringInterpolation) { |
| 6922 errorListener.onError(new AnalysisError(librarySource, uriLiteral.offset, |
| 6923 uriLiteral.length, CompileTimeErrorCode.URI_WITH_INTERPOLATION)); |
| 6924 return null; |
| 6925 } |
| 6926 String uriContent = uriLiteral.stringValue.trim(); |
| 6927 _directiveUris[directive] = uriContent; |
| 6928 uriContent = Uri.encodeFull(uriContent); |
| 6929 if (directive is ImportDirective && |
| 6930 uriContent.startsWith(_DART_EXT_SCHEME)) { |
| 6931 _libraryElement.hasExtUri = true; |
| 6932 return null; |
| 6933 } |
| 6934 try { |
| 6935 parseUriWithException(uriContent); |
| 6936 Source source = |
| 6937 _analysisContext.sourceFactory.resolveUri(librarySource, uriContent); |
| 6938 if (!_analysisContext.exists(source)) { |
| 6939 errorListener.onError(new AnalysisError( |
| 6940 librarySource, |
| 6941 uriLiteral.offset, |
| 6942 uriLiteral.length, |
| 6943 CompileTimeErrorCode.URI_DOES_NOT_EXIST, |
| 6944 [uriContent])); |
| 6945 } |
| 6946 return source; |
| 6947 } on URISyntaxException { |
| 6948 errorListener.onError(new AnalysisError(librarySource, uriLiteral.offset, |
| 6949 uriLiteral.length, CompileTimeErrorCode.INVALID_URI, [uriContent])); |
| 6950 } |
| 6951 return null; |
| 6952 } |
| 6953 |
| 6954 /** |
| 6955 * Returns the URI value of the given directive. |
| 6956 */ |
| 6957 String getUri(UriBasedDirective directive) => _directiveUris[directive]; |
| 6958 |
| 6959 /** |
| 6960 * Set the AST structure associated with the defining compilation unit for thi
s library to the |
| 6961 * given AST structure. |
| 6962 * |
| 6963 * @param unit the AST structure associated with the defining compilation unit
for this library |
| 6964 */ |
| 6965 void setDefiningCompilationUnit(CompilationUnit unit) { |
| 6966 _astMap[librarySource] = unit; |
| 6967 } |
| 6968 |
| 6969 @override |
| 6970 String toString() => librarySource.shortName; |
| 6971 } |
| 6972 |
| 6973 /** |
| 6974 * Instances of the class `LibraryElementBuilder` build an element model for a s
ingle library. |
| 6975 */ |
| 6976 class LibraryElementBuilder { |
| 6977 /** |
| 6978 * The analysis context in which the element model will be built. |
| 6979 */ |
| 6980 final InternalAnalysisContext _analysisContext; |
| 6981 |
| 6982 /** |
| 6983 * The listener to which errors will be reported. |
| 6984 */ |
| 6985 final AnalysisErrorListener _errorListener; |
| 6986 |
| 6987 /** |
| 6988 * Initialize a newly created library element builder. |
| 6989 * |
| 6990 * @param analysisContext the analysis context in which the element model will
be built |
| 6991 * @param errorListener the listener to which errors will be reported |
| 6992 */ |
| 6993 LibraryElementBuilder(this._analysisContext, this._errorListener); |
| 6994 |
| 6995 /** |
| 6996 * Build the library element for the given library. |
| 6997 * |
| 6998 * @param library the library for which an element model is to be built |
| 6999 * @return the library element that was built |
| 7000 * @throws AnalysisException if the analysis could not be performed |
| 7001 */ |
| 7002 LibraryElementImpl buildLibrary(Library library) { |
| 7003 CompilationUnitBuilder builder = new CompilationUnitBuilder(); |
| 7004 Source librarySource = library.librarySource; |
| 7005 CompilationUnit definingCompilationUnit = library.definingCompilationUnit; |
| 7006 CompilationUnitElementImpl definingCompilationUnitElement = builder |
| 7007 .buildCompilationUnit( |
| 7008 librarySource, definingCompilationUnit, librarySource); |
| 7009 NodeList<Directive> directives = definingCompilationUnit.directives; |
| 7010 LibraryIdentifier libraryNameNode = null; |
| 7011 bool hasPartDirective = false; |
| 7012 FunctionElement entryPoint = |
| 7013 _findEntryPoint(definingCompilationUnitElement); |
| 7014 List<Directive> directivesToResolve = new List<Directive>(); |
| 7015 List<CompilationUnitElementImpl> sourcedCompilationUnits = |
| 7016 new List<CompilationUnitElementImpl>(); |
| 7017 for (Directive directive in directives) { |
| 7018 // |
| 7019 // We do not build the elements representing the import and export |
| 7020 // directives at this point. That is not done until we get to |
| 7021 // LibraryResolver.buildDirectiveModels() because we need the |
| 7022 // LibraryElements for the referenced libraries, which might not exist at |
| 7023 // this point (due to the possibility of circular references). |
| 7024 // |
| 7025 if (directive is LibraryDirective) { |
| 7026 if (libraryNameNode == null) { |
| 7027 libraryNameNode = directive.name; |
| 7028 directivesToResolve.add(directive); |
| 7029 } |
| 7030 } else if (directive is PartDirective) { |
| 7031 PartDirective partDirective = directive; |
| 7032 StringLiteral partUri = partDirective.uri; |
| 7033 Source partSource = partDirective.source; |
| 7034 if (_analysisContext.exists(partSource)) { |
| 7035 hasPartDirective = true; |
| 7036 CompilationUnit partUnit = library.getAST(partSource); |
| 7037 CompilationUnitElementImpl part = |
| 7038 builder.buildCompilationUnit(partSource, partUnit, librarySource); |
| 7039 part.uriOffset = partUri.offset; |
| 7040 part.uriEnd = partUri.end; |
| 7041 part.uri = partDirective.uriContent; |
| 7042 // |
| 7043 // Validate that the part contains a part-of directive with the same |
| 7044 // name as the library. |
| 7045 // |
| 7046 String partLibraryName = |
| 7047 _getPartLibraryName(partSource, partUnit, directivesToResolve); |
| 7048 if (partLibraryName == null) { |
| 7049 _errorListener.onError(new AnalysisError( |
| 7050 librarySource, |
| 7051 partUri.offset, |
| 7052 partUri.length, |
| 7053 CompileTimeErrorCode.PART_OF_NON_PART, |
| 7054 [partUri.toSource()])); |
| 7055 } else if (libraryNameNode == null) { |
| 7056 // TODO(brianwilkerson) Collect the names declared by the part. |
| 7057 // If they are all the same then we can use that name as the |
| 7058 // inferred name of the library and present it in a quick-fix. |
| 7059 // partLibraryNames.add(partLibraryName); |
| 7060 } else if (libraryNameNode.name != partLibraryName) { |
| 7061 _errorListener.onError(new AnalysisError( |
| 7062 librarySource, |
| 7063 partUri.offset, |
| 7064 partUri.length, |
| 7065 StaticWarningCode.PART_OF_DIFFERENT_LIBRARY, |
| 7066 [libraryNameNode.name, partLibraryName])); |
| 7067 } |
| 7068 if (entryPoint == null) { |
| 7069 entryPoint = _findEntryPoint(part); |
| 7070 } |
| 7071 directive.element = part; |
| 7072 sourcedCompilationUnits.add(part); |
| 7073 } |
| 7074 } |
| 7075 } |
| 7076 if (hasPartDirective && libraryNameNode == null) { |
| 7077 _errorListener.onError(new AnalysisError(librarySource, 0, 0, |
| 7078 ResolverErrorCode.MISSING_LIBRARY_DIRECTIVE_WITH_PART)); |
| 7079 } |
| 7080 // |
| 7081 // Create and populate the library element. |
| 7082 // |
| 7083 LibraryElementImpl libraryElement = new LibraryElementImpl.forNode( |
| 7084 _analysisContext.getContextFor(librarySource), libraryNameNode); |
| 7085 libraryElement.definingCompilationUnit = definingCompilationUnitElement; |
| 7086 if (entryPoint != null) { |
| 7087 libraryElement.entryPoint = entryPoint; |
| 7088 } |
| 7089 int sourcedUnitCount = sourcedCompilationUnits.length; |
| 7090 libraryElement.parts = sourcedCompilationUnits; |
| 7091 for (Directive directive in directivesToResolve) { |
| 7092 directive.element = libraryElement; |
| 7093 } |
| 7094 library.libraryElement = libraryElement; |
| 7095 if (sourcedUnitCount > 0) { |
| 7096 _patchTopLevelAccessors(libraryElement); |
| 7097 } |
| 7098 return libraryElement; |
| 7099 } |
| 7100 |
| 7101 /** |
| 7102 * Build the library element for the given library. The resulting element is |
| 7103 * stored in the [ResolvableLibrary] structure. |
| 7104 * |
| 7105 * @param library the library for which an element model is to be built |
| 7106 * @throws AnalysisException if the analysis could not be performed |
| 7107 */ |
| 7108 void buildLibrary2(ResolvableLibrary library) { |
| 7109 CompilationUnitBuilder builder = new CompilationUnitBuilder(); |
| 7110 Source librarySource = library.librarySource; |
| 7111 CompilationUnit definingCompilationUnit = library.definingCompilationUnit; |
| 7112 CompilationUnitElementImpl definingCompilationUnitElement = builder |
| 7113 .buildCompilationUnit( |
| 7114 librarySource, definingCompilationUnit, librarySource); |
| 7115 NodeList<Directive> directives = definingCompilationUnit.directives; |
| 7116 LibraryIdentifier libraryNameNode = null; |
| 7117 bool hasPartDirective = false; |
| 7118 FunctionElement entryPoint = |
| 7119 _findEntryPoint(definingCompilationUnitElement); |
| 7120 List<Directive> directivesToResolve = new List<Directive>(); |
| 7121 List<CompilationUnitElementImpl> sourcedCompilationUnits = |
| 7122 new List<CompilationUnitElementImpl>(); |
| 7123 for (Directive directive in directives) { |
| 7124 // |
| 7125 // We do not build the elements representing the import and export |
| 7126 // directives at this point. That is not done until we get to |
| 7127 // LibraryResolver.buildDirectiveModels() because we need the |
| 7128 // LibraryElements for the referenced libraries, which might not exist at |
| 7129 // this point (due to the possibility of circular references). |
| 7130 // |
| 7131 if (directive is LibraryDirective) { |
| 7132 if (libraryNameNode == null) { |
| 7133 libraryNameNode = directive.name; |
| 7134 directivesToResolve.add(directive); |
| 7135 } |
| 7136 } else if (directive is PartDirective) { |
| 7137 PartDirective partDirective = directive; |
| 7138 StringLiteral partUri = partDirective.uri; |
| 7139 Source partSource = partDirective.source; |
| 7140 if (_analysisContext.exists(partSource)) { |
| 7141 hasPartDirective = true; |
| 7142 CompilationUnit partUnit = library.getAST(partSource); |
| 7143 if (partUnit != null) { |
| 7144 CompilationUnitElementImpl part = builder.buildCompilationUnit( |
| 7145 partSource, partUnit, librarySource); |
| 7146 part.uriOffset = partUri.offset; |
| 7147 part.uriEnd = partUri.end; |
| 7148 part.uri = partDirective.uriContent; |
| 7149 // |
| 7150 // Validate that the part contains a part-of directive with the same |
| 7151 // name as the library. |
| 7152 // |
| 7153 String partLibraryName = |
| 7154 _getPartLibraryName(partSource, partUnit, directivesToResolve); |
| 7155 if (partLibraryName == null) { |
| 7156 _errorListener.onError(new AnalysisError( |
| 7157 librarySource, |
| 7158 partUri.offset, |
| 7159 partUri.length, |
| 7160 CompileTimeErrorCode.PART_OF_NON_PART, |
| 7161 [partUri.toSource()])); |
| 7162 } else if (libraryNameNode == null) { |
| 7163 // TODO(brianwilkerson) Collect the names declared by the part. |
| 7164 // If they are all the same then we can use that name as the |
| 7165 // inferred name of the library and present it in a quick-fix. |
| 7166 // partLibraryNames.add(partLibraryName); |
| 7167 } else if (libraryNameNode.name != partLibraryName) { |
| 7168 _errorListener.onError(new AnalysisError( |
| 7169 librarySource, |
| 7170 partUri.offset, |
| 7171 partUri.length, |
| 7172 StaticWarningCode.PART_OF_DIFFERENT_LIBRARY, |
| 7173 [libraryNameNode.name, partLibraryName])); |
| 7174 } |
| 7175 if (entryPoint == null) { |
| 7176 entryPoint = _findEntryPoint(part); |
| 7177 } |
| 7178 directive.element = part; |
| 7179 sourcedCompilationUnits.add(part); |
| 7180 } |
| 7181 } |
| 7182 } |
| 7183 } |
| 7184 if (hasPartDirective && libraryNameNode == null) { |
| 7185 _errorListener.onError(new AnalysisError(librarySource, 0, 0, |
| 7186 ResolverErrorCode.MISSING_LIBRARY_DIRECTIVE_WITH_PART)); |
| 7187 } |
| 7188 // |
| 7189 // Create and populate the library element. |
| 7190 // |
| 7191 LibraryElementImpl libraryElement = new LibraryElementImpl.forNode( |
| 7192 _analysisContext.getContextFor(librarySource), libraryNameNode); |
| 7193 libraryElement.definingCompilationUnit = definingCompilationUnitElement; |
| 7194 if (entryPoint != null) { |
| 7195 libraryElement.entryPoint = entryPoint; |
| 7196 } |
| 7197 int sourcedUnitCount = sourcedCompilationUnits.length; |
| 7198 libraryElement.parts = sourcedCompilationUnits; |
| 7199 for (Directive directive in directivesToResolve) { |
| 7200 directive.element = libraryElement; |
| 7201 } |
| 7202 library.libraryElement = libraryElement; |
| 7203 if (sourcedUnitCount > 0) { |
| 7204 _patchTopLevelAccessors(libraryElement); |
| 7205 } |
| 7206 } |
| 7207 |
| 7208 /** |
| 7209 * Add all of the non-synthetic getters and setters defined in the given compi
lation unit that |
| 7210 * have no corresponding accessor to one of the given collections. |
| 7211 * |
| 7212 * @param getters the map to which getters are to be added |
| 7213 * @param setters the list to which setters are to be added |
| 7214 * @param unit the compilation unit defining the accessors that are potentiall
y being added |
| 7215 */ |
| 7216 void _collectAccessors(HashMap<String, PropertyAccessorElement> getters, |
| 7217 List<PropertyAccessorElement> setters, CompilationUnitElement unit) { |
| 7218 for (PropertyAccessorElement accessor in unit.accessors) { |
| 7219 if (accessor.isGetter) { |
| 7220 if (!accessor.isSynthetic && accessor.correspondingSetter == null) { |
| 7221 getters[accessor.displayName] = accessor; |
| 7222 } |
| 7223 } else { |
| 7224 if (!accessor.isSynthetic && accessor.correspondingGetter == null) { |
| 7225 setters.add(accessor); |
| 7226 } |
| 7227 } |
| 7228 } |
| 7229 } |
| 7230 |
| 7231 /** |
| 7232 * Search the top-level functions defined in the given compilation unit for th
e entry point. |
| 7233 * |
| 7234 * @param element the compilation unit to be searched |
| 7235 * @return the entry point that was found, or `null` if the compilation unit d
oes not define |
| 7236 * an entry point |
| 7237 */ |
| 7238 FunctionElement _findEntryPoint(CompilationUnitElementImpl element) { |
| 7239 for (FunctionElement function in element.functions) { |
| 7240 if (function.isEntryPoint) { |
| 7241 return function; |
| 7242 } |
| 7243 } |
| 7244 return null; |
| 7245 } |
| 7246 |
| 7247 /** |
| 7248 * Return the name of the library that the given part is declared to be a part
of, or `null` |
| 7249 * if the part does not contain a part-of directive. |
| 7250 * |
| 7251 * @param partSource the source representing the part |
| 7252 * @param partUnit the AST structure of the part |
| 7253 * @param directivesToResolve a list of directives that should be resolved to
the library being |
| 7254 * built |
| 7255 * @return the name of the library that the given part is declared to be a par
t of |
| 7256 */ |
| 7257 String _getPartLibraryName(Source partSource, CompilationUnit partUnit, |
| 7258 List<Directive> directivesToResolve) { |
| 7259 for (Directive directive in partUnit.directives) { |
| 7260 if (directive is PartOfDirective) { |
| 7261 directivesToResolve.add(directive); |
| 7262 LibraryIdentifier libraryName = directive.libraryName; |
| 7263 if (libraryName != null) { |
| 7264 return libraryName.name; |
| 7265 } |
| 7266 } |
| 7267 } |
| 7268 return null; |
| 7269 } |
| 7270 |
| 7271 /** |
| 7272 * Look through all of the compilation units defined for the given library, lo
oking for getters |
| 7273 * and setters that are defined in different compilation units but that have t
he same names. If |
| 7274 * any are found, make sure that they have the same variable element. |
| 7275 * |
| 7276 * @param libraryElement the library defining the compilation units to be proc
essed |
| 7277 */ |
| 7278 void _patchTopLevelAccessors(LibraryElementImpl libraryElement) { |
| 7279 HashMap<String, PropertyAccessorElement> getters = |
| 7280 new HashMap<String, PropertyAccessorElement>(); |
| 7281 List<PropertyAccessorElement> setters = new List<PropertyAccessorElement>(); |
| 7282 _collectAccessors(getters, setters, libraryElement.definingCompilationUnit); |
| 7283 for (CompilationUnitElement unit in libraryElement.parts) { |
| 7284 _collectAccessors(getters, setters, unit); |
| 7285 } |
| 7286 for (PropertyAccessorElement setter in setters) { |
| 7287 PropertyAccessorElement getter = getters[setter.displayName]; |
| 7288 if (getter != null) { |
| 7289 PropertyInducingElementImpl variable = |
| 7290 getter.variable as PropertyInducingElementImpl; |
| 7291 variable.setter = setter; |
| 7292 (setter as PropertyAccessorElementImpl).variable = variable; |
| 7293 } |
| 7294 } |
| 7295 } |
| 7296 } |
| 7297 |
| 7298 /** |
| 7299 * Instances of the class `LibraryImportScope` represent the scope containing al
l of the names |
| 7300 * available from imported libraries. |
| 7301 */ |
| 7302 class LibraryImportScope extends Scope { |
| 7303 /** |
| 7304 * The element representing the library in which this scope is enclosed. |
| 7305 */ |
| 7306 final LibraryElement _definingLibrary; |
| 7307 |
| 7308 /** |
| 7309 * The listener that is to be informed when an error is encountered. |
| 7310 */ |
| 7311 final AnalysisErrorListener errorListener; |
| 7312 |
| 7313 /** |
| 7314 * A list of the namespaces representing the names that are available in this
scope from imported |
| 7315 * libraries. |
| 7316 */ |
| 7317 List<Namespace> _importedNamespaces; |
| 7318 |
| 7319 /** |
| 7320 * Initialize a newly created scope representing the names imported into the g
iven library. |
| 7321 * |
| 7322 * @param definingLibrary the element representing the library that imports th
e names defined in |
| 7323 * this scope |
| 7324 * @param errorListener the listener that is to be informed when an error is e
ncountered |
| 7325 */ |
| 7326 LibraryImportScope(this._definingLibrary, this.errorListener) { |
| 7327 _createImportedNamespaces(); |
| 7328 } |
| 7329 |
| 7330 @override |
| 7331 void define(Element element) { |
| 7332 if (!Scope.isPrivateName(element.displayName)) { |
| 7333 super.define(element); |
| 7334 } |
| 7335 } |
| 7336 |
| 7337 @override |
| 7338 Source getSource(AstNode node) { |
| 7339 Source source = super.getSource(node); |
| 7340 if (source == null) { |
| 7341 source = _definingLibrary.definingCompilationUnit.source; |
| 7342 } |
| 7343 return source; |
| 7344 } |
| 7345 |
| 7346 @override |
| 7347 Element internalLookup( |
| 7348 Identifier identifier, String name, LibraryElement referencingLibrary) { |
| 7349 Element foundElement = localLookup(name, referencingLibrary); |
| 7350 if (foundElement != null) { |
| 7351 return foundElement; |
| 7352 } |
| 7353 for (int i = 0; i < _importedNamespaces.length; i++) { |
| 7354 Namespace nameSpace = _importedNamespaces[i]; |
| 7355 Element element = nameSpace.get(name); |
| 7356 if (element != null) { |
| 7357 if (foundElement == null) { |
| 7358 foundElement = element; |
| 7359 } else if (!identical(foundElement, element)) { |
| 7360 foundElement = MultiplyDefinedElementImpl.fromElements( |
| 7361 _definingLibrary.context, foundElement, element); |
| 7362 } |
| 7363 } |
| 7364 } |
| 7365 if (foundElement is MultiplyDefinedElementImpl) { |
| 7366 foundElement = _removeSdkElements( |
| 7367 identifier, name, foundElement as MultiplyDefinedElementImpl); |
| 7368 } |
| 7369 if (foundElement is MultiplyDefinedElementImpl) { |
| 7370 String foundEltName = foundElement.displayName; |
| 7371 List<Element> conflictingMembers = foundElement.conflictingElements; |
| 7372 int count = conflictingMembers.length; |
| 7373 List<String> libraryNames = new List<String>(count); |
| 7374 for (int i = 0; i < count; i++) { |
| 7375 libraryNames[i] = _getLibraryName(conflictingMembers[i]); |
| 7376 } |
| 7377 libraryNames.sort(); |
| 7378 errorListener.onError(new AnalysisError( |
| 7379 getSource(identifier), |
| 7380 identifier.offset, |
| 7381 identifier.length, |
| 7382 StaticWarningCode.AMBIGUOUS_IMPORT, [ |
| 7383 foundEltName, |
| 7384 StringUtilities.printListOfQuotedNames(libraryNames) |
| 7385 ])); |
| 7386 return foundElement; |
| 7387 } |
| 7388 if (foundElement != null) { |
| 7389 defineNameWithoutChecking(name, foundElement); |
| 7390 } |
| 7391 return foundElement; |
| 7392 } |
| 7393 |
| 7394 /** |
| 7395 * Create all of the namespaces associated with the libraries imported into th
is library. The |
| 7396 * names are not added to this scope, but are stored for later reference. |
| 7397 * |
| 7398 * @param definingLibrary the element representing the library that imports th
e libraries for |
| 7399 * which namespaces will be created |
| 7400 */ |
| 7401 void _createImportedNamespaces() { |
| 7402 NamespaceBuilder builder = new NamespaceBuilder(); |
| 7403 List<ImportElement> imports = _definingLibrary.imports; |
| 7404 int count = imports.length; |
| 7405 _importedNamespaces = new List<Namespace>(count); |
| 7406 for (int i = 0; i < count; i++) { |
| 7407 _importedNamespaces[i] = |
| 7408 builder.createImportNamespaceForDirective(imports[i]); |
| 7409 } |
| 7410 } |
| 7411 |
| 7412 /** |
| 7413 * Returns the name of the library that defines given element. |
| 7414 * |
| 7415 * @param element the element to get library name |
| 7416 * @return the name of the library that defines given element |
| 7417 */ |
| 7418 String _getLibraryName(Element element) { |
| 7419 if (element == null) { |
| 7420 return StringUtilities.EMPTY; |
| 7421 } |
| 7422 LibraryElement library = element.library; |
| 7423 if (library == null) { |
| 7424 return StringUtilities.EMPTY; |
| 7425 } |
| 7426 List<ImportElement> imports = _definingLibrary.imports; |
| 7427 int count = imports.length; |
| 7428 for (int i = 0; i < count; i++) { |
| 7429 if (identical(imports[i].importedLibrary, library)) { |
| 7430 return library.definingCompilationUnit.displayName; |
| 7431 } |
| 7432 } |
| 7433 List<String> indirectSources = new List<String>(); |
| 7434 for (int i = 0; i < count; i++) { |
| 7435 LibraryElement importedLibrary = imports[i].importedLibrary; |
| 7436 if (importedLibrary != null) { |
| 7437 for (LibraryElement exportedLibrary |
| 7438 in importedLibrary.exportedLibraries) { |
| 7439 if (identical(exportedLibrary, library)) { |
| 7440 indirectSources |
| 7441 .add(importedLibrary.definingCompilationUnit.displayName); |
| 7442 } |
| 7443 } |
| 7444 } |
| 7445 } |
| 7446 int indirectCount = indirectSources.length; |
| 7447 StringBuffer buffer = new StringBuffer(); |
| 7448 buffer.write(library.definingCompilationUnit.displayName); |
| 7449 if (indirectCount > 0) { |
| 7450 buffer.write(" (via "); |
| 7451 if (indirectCount > 1) { |
| 7452 indirectSources.sort(); |
| 7453 buffer.write(StringUtilities.printListOfQuotedNames(indirectSources)); |
| 7454 } else { |
| 7455 buffer.write(indirectSources[0]); |
| 7456 } |
| 7457 buffer.write(")"); |
| 7458 } |
| 7459 return buffer.toString(); |
| 7460 } |
| 7461 |
| 7462 /** |
| 7463 * Given a collection of elements (captured by the [foundElement]) that the |
| 7464 * [identifier] (with the given [name]) resolved to, remove from the list all |
| 7465 * of the names defined in the SDK and return the element(s) that remain. |
| 7466 */ |
| 7467 Element _removeSdkElements(Identifier identifier, String name, |
| 7468 MultiplyDefinedElementImpl foundElement) { |
| 7469 List<Element> conflictingElements = foundElement.conflictingElements; |
| 7470 List<Element> nonSdkElements = new List<Element>(); |
| 7471 Element sdkElement = null; |
| 7472 for (Element member in conflictingElements) { |
| 7473 if (member.library.isInSdk) { |
| 7474 sdkElement = member; |
| 7475 } else { |
| 7476 nonSdkElements.add(member); |
| 7477 } |
| 7478 } |
| 7479 if (sdkElement != null && nonSdkElements.length > 0) { |
| 7480 String sdkLibName = _getLibraryName(sdkElement); |
| 7481 String otherLibName = _getLibraryName(nonSdkElements[0]); |
| 7482 errorListener.onError(new AnalysisError( |
| 7483 getSource(identifier), |
| 7484 identifier.offset, |
| 7485 identifier.length, |
| 7486 StaticWarningCode.CONFLICTING_DART_IMPORT, |
| 7487 [name, sdkLibName, otherLibName])); |
| 7488 } |
| 7489 if (nonSdkElements.length == conflictingElements.length) { |
| 7490 // None of the members were removed |
| 7491 return foundElement; |
| 7492 } else if (nonSdkElements.length == 1) { |
| 7493 // All but one member was removed |
| 7494 return nonSdkElements[0]; |
| 7495 } else if (nonSdkElements.length == 0) { |
| 7496 // All members were removed |
| 7497 AnalysisEngine.instance.logger |
| 7498 .logInformation("Multiply defined SDK element: $foundElement"); |
| 7499 return foundElement; |
| 7500 } |
| 7501 return new MultiplyDefinedElementImpl( |
| 7502 _definingLibrary.context, nonSdkElements); |
| 7503 } |
| 7504 } |
| 7505 |
| 7506 /** |
| 7507 * Instances of the class `LibraryResolver` are used to resolve one or more mutu
ally dependent |
| 7508 * libraries within a single context. |
| 7509 */ |
| 7510 class LibraryResolver { |
| 7511 /** |
| 7512 * The analysis context in which the libraries are being analyzed. |
| 7513 */ |
| 7514 final InternalAnalysisContext analysisContext; |
| 7515 |
| 7516 /** |
| 7517 * The listener to which analysis errors will be reported, this error listener
is either |
| 7518 * references [recordingErrorListener], or it unions the passed |
| 7519 * [AnalysisErrorListener] with the [recordingErrorListener]. |
| 7520 */ |
| 7521 RecordingErrorListener _errorListener; |
| 7522 |
| 7523 /** |
| 7524 * A source object representing the core library (dart:core). |
| 7525 */ |
| 7526 Source _coreLibrarySource; |
| 7527 |
| 7528 /** |
| 7529 * A Source object representing the async library (dart:async). |
| 7530 */ |
| 7531 Source _asyncLibrarySource; |
| 7532 |
| 7533 /** |
| 7534 * The object representing the core library. |
| 7535 */ |
| 7536 Library _coreLibrary; |
| 7537 |
| 7538 /** |
| 7539 * The object representing the async library. |
| 7540 */ |
| 7541 Library _asyncLibrary; |
| 7542 |
| 7543 /** |
| 7544 * The object used to access the types from the core library. |
| 7545 */ |
| 7546 TypeProvider _typeProvider; |
| 7547 |
| 7548 /** |
| 7549 * A table mapping library sources to the information being maintained for tho
se libraries. |
| 7550 */ |
| 7551 HashMap<Source, Library> _libraryMap = new HashMap<Source, Library>(); |
| 7552 |
| 7553 /** |
| 7554 * A collection containing the libraries that are being resolved together. |
| 7555 */ |
| 7556 Set<Library> _librariesInCycles; |
| 7557 |
| 7558 /** |
| 7559 * Initialize a newly created library resolver to resolve libraries within the
given context. |
| 7560 * |
| 7561 * @param analysisContext the analysis context in which the library is being a
nalyzed |
| 7562 */ |
| 7563 LibraryResolver(this.analysisContext) { |
| 7564 this._errorListener = new RecordingErrorListener(); |
| 7565 _coreLibrarySource = |
| 7566 analysisContext.sourceFactory.forUri(DartSdk.DART_CORE); |
| 7567 _asyncLibrarySource = |
| 7568 analysisContext.sourceFactory.forUri(DartSdk.DART_ASYNC); |
| 7569 } |
| 7570 |
| 7571 /** |
| 7572 * Return the listener to which analysis errors will be reported. |
| 7573 * |
| 7574 * @return the listener to which analysis errors will be reported |
| 7575 */ |
| 7576 RecordingErrorListener get errorListener => _errorListener; |
| 7577 |
| 7578 /** |
| 7579 * Return an array containing information about all of the libraries that were
resolved. |
| 7580 * |
| 7581 * @return an array containing the libraries that were resolved |
| 7582 */ |
| 7583 Set<Library> get resolvedLibraries => _librariesInCycles; |
| 7584 |
| 7585 /** |
| 7586 * The object used to access the types from the core library. |
| 7587 */ |
| 7588 TypeProvider get typeProvider => _typeProvider; |
| 7589 |
| 7590 /** |
| 7591 * Create an object to represent the information about the library defined by
the compilation unit |
| 7592 * with the given source. |
| 7593 * |
| 7594 * @param librarySource the source of the library's defining compilation unit |
| 7595 * @return the library object that was created |
| 7596 * @throws AnalysisException if the library source is not valid |
| 7597 */ |
| 7598 Library createLibrary(Source librarySource) { |
| 7599 Library library = |
| 7600 new Library(analysisContext, _errorListener, librarySource); |
| 7601 _libraryMap[librarySource] = library; |
| 7602 return library; |
| 7603 } |
| 7604 |
| 7605 /** |
| 7606 * Resolve the library specified by the given source in the given context. The
library is assumed |
| 7607 * to be embedded in the given source. |
| 7608 * |
| 7609 * @param librarySource the source specifying the defining compilation unit of
the library to be |
| 7610 * resolved |
| 7611 * @param unit the compilation unit representing the embedded library |
| 7612 * @param fullAnalysis `true` if a full analysis should be performed |
| 7613 * @return the element representing the resolved library |
| 7614 * @throws AnalysisException if the library could not be resolved for some rea
son |
| 7615 */ |
| 7616 LibraryElement resolveEmbeddedLibrary( |
| 7617 Source librarySource, CompilationUnit unit, bool fullAnalysis) { |
| 7618 // |
| 7619 // Create the objects representing the library being resolved and the core |
| 7620 // library. |
| 7621 // |
| 7622 Library targetLibrary = _createLibraryWithUnit(librarySource, unit); |
| 7623 _coreLibrary = _libraryMap[_coreLibrarySource]; |
| 7624 if (_coreLibrary == null) { |
| 7625 // This will only happen if the library being analyzed is the core |
| 7626 // library. |
| 7627 _coreLibrary = createLibrary(_coreLibrarySource); |
| 7628 if (_coreLibrary == null) { |
| 7629 LibraryResolver2.missingCoreLibrary( |
| 7630 analysisContext, _coreLibrarySource); |
| 7631 } |
| 7632 } |
| 7633 _asyncLibrary = _libraryMap[_asyncLibrarySource]; |
| 7634 if (_asyncLibrary == null) { |
| 7635 // This will only happen if the library being analyzed is the async |
| 7636 // library. |
| 7637 _asyncLibrary = createLibrary(_asyncLibrarySource); |
| 7638 if (_asyncLibrary == null) { |
| 7639 LibraryResolver2.missingAsyncLibrary( |
| 7640 analysisContext, _asyncLibrarySource); |
| 7641 } |
| 7642 } |
| 7643 // |
| 7644 // Compute the set of libraries that need to be resolved together. |
| 7645 // |
| 7646 _computeEmbeddedLibraryDependencies(targetLibrary, unit); |
| 7647 _librariesInCycles = _computeLibrariesInCycles(targetLibrary); |
| 7648 // |
| 7649 // Build the element models representing the libraries being resolved. |
| 7650 // This is done in three steps: |
| 7651 // |
| 7652 // 1. Build the basic element models without making any connections |
| 7653 // between elements other than the basic parent/child relationships. |
| 7654 // This includes building the elements representing the libraries. |
| 7655 // 2. Build the elements for the import and export directives. This |
| 7656 // requires that we have the elements built for the referenced |
| 7657 // libraries, but because of the possibility of circular references |
| 7658 // needs to happen after all of the library elements have been created. |
| 7659 // 3. Build the rest of the type model by connecting superclasses, mixins, |
| 7660 // and interfaces. This requires that we be able to compute the names |
| 7661 // visible in the libraries being resolved, which in turn requires that |
| 7662 // we have resolved the import directives. |
| 7663 // |
| 7664 _buildElementModels(); |
| 7665 LibraryElement coreElement = _coreLibrary.libraryElement; |
| 7666 if (coreElement == null) { |
| 7667 throw new AnalysisException("Could not resolve dart:core"); |
| 7668 } |
| 7669 LibraryElement asyncElement = _asyncLibrary.libraryElement; |
| 7670 if (asyncElement == null) { |
| 7671 throw new AnalysisException("Could not resolve dart:async"); |
| 7672 } |
| 7673 _buildDirectiveModels(); |
| 7674 _typeProvider = new TypeProviderImpl(coreElement, asyncElement); |
| 7675 _buildTypeHierarchies(); |
| 7676 // |
| 7677 // Perform resolution and type analysis. |
| 7678 // |
| 7679 // TODO(brianwilkerson) Decide whether we want to resolve all of the |
| 7680 // libraries or whether we want to only resolve the target library. |
| 7681 // The advantage to resolving everything is that we have already done part |
| 7682 // of the work so we'll avoid duplicated effort. The disadvantage of |
| 7683 // resolving everything is that we might do extra work that we don't |
| 7684 // really care about. Another possibility is to add a parameter to this |
| 7685 // method and punt the decision to the clients. |
| 7686 // |
| 7687 //if (analyzeAll) { |
| 7688 resolveReferencesAndTypes(); |
| 7689 //} else { |
| 7690 // resolveReferencesAndTypes(targetLibrary); |
| 7691 //} |
| 7692 _performConstantEvaluation(); |
| 7693 return targetLibrary.libraryElement; |
| 7694 } |
| 7695 |
| 7696 /** |
| 7697 * Resolve the library specified by the given source in the given context. |
| 7698 * |
| 7699 * Note that because Dart allows circular imports between libraries, it is pos
sible that more than |
| 7700 * one library will need to be resolved. In such cases the error listener can
receive errors from |
| 7701 * multiple libraries. |
| 7702 * |
| 7703 * @param librarySource the source specifying the defining compilation unit of
the library to be |
| 7704 * resolved |
| 7705 * @param fullAnalysis `true` if a full analysis should be performed |
| 7706 * @return the element representing the resolved library |
| 7707 * @throws AnalysisException if the library could not be resolved for some rea
son |
| 7708 */ |
| 7709 LibraryElement resolveLibrary(Source librarySource, bool fullAnalysis) { |
| 7710 // |
| 7711 // Create the object representing the library being resolved and compute |
| 7712 // the dependency relationship. Note that all libraries depend implicitly |
| 7713 // on core, and we inject an ersatz dependency on async, so once this is |
| 7714 // done the core and async library elements will have been created. |
| 7715 // |
| 7716 Library targetLibrary = createLibrary(librarySource); |
| 7717 _computeLibraryDependencies(targetLibrary); |
| 7718 _coreLibrary = _libraryMap[_coreLibrarySource]; |
| 7719 _asyncLibrary = _libraryMap[_asyncLibrarySource]; |
| 7720 // |
| 7721 // Compute the set of libraries that need to be resolved together. |
| 7722 // |
| 7723 _librariesInCycles = _computeLibrariesInCycles(targetLibrary); |
| 7724 // |
| 7725 // Build the element models representing the libraries being resolved. |
| 7726 // This is done in three steps: |
| 7727 // |
| 7728 // 1. Build the basic element models without making any connections |
| 7729 // between elements other than the basic parent/child relationships. |
| 7730 // This includes building the elements representing the libraries, but |
| 7731 // excludes members defined in enums. |
| 7732 // 2. Build the elements for the import and export directives. This |
| 7733 // requires that we have the elements built for the referenced |
| 7734 // libraries, but because of the possibility of circular references |
| 7735 // needs to happen after all of the library elements have been created. |
| 7736 // 3. Build the members in enum declarations. |
| 7737 // 4. Build the rest of the type model by connecting superclasses, mixins, |
| 7738 // and interfaces. This requires that we be able to compute the names |
| 7739 // visible in the libraries being resolved, which in turn requires that |
| 7740 // we have resolved the import directives. |
| 7741 // |
| 7742 _buildElementModels(); |
| 7743 LibraryElement coreElement = _coreLibrary.libraryElement; |
| 7744 if (coreElement == null) { |
| 7745 throw new AnalysisException("Could not resolve dart:core"); |
| 7746 } |
| 7747 LibraryElement asyncElement = _asyncLibrary.libraryElement; |
| 7748 if (asyncElement == null) { |
| 7749 throw new AnalysisException("Could not resolve dart:async"); |
| 7750 } |
| 7751 _buildDirectiveModels(); |
| 7752 _typeProvider = new TypeProviderImpl(coreElement, asyncElement); |
| 7753 _buildEnumMembers(); |
| 7754 _buildTypeHierarchies(); |
| 7755 // |
| 7756 // Perform resolution and type analysis. |
| 7757 // |
| 7758 // TODO(brianwilkerson) Decide whether we want to resolve all of the |
| 7759 // libraries or whether we want to only resolve the target library. The |
| 7760 // advantage to resolving everything is that we have already done part of |
| 7761 // the work so we'll avoid duplicated effort. The disadvantage of |
| 7762 // resolving everything is that we might do extra work that we don't |
| 7763 // really care about. Another possibility is to add a parameter to this |
| 7764 // method and punt the decision to the clients. |
| 7765 // |
| 7766 //if (analyzeAll) { |
| 7767 resolveReferencesAndTypes(); |
| 7768 //} else { |
| 7769 // resolveReferencesAndTypes(targetLibrary); |
| 7770 //} |
| 7771 _performConstantEvaluation(); |
| 7772 return targetLibrary.libraryElement; |
| 7773 } |
| 7774 |
| 7775 /** |
| 7776 * Resolve the identifiers and perform type analysis in the libraries in the c
urrent cycle. |
| 7777 * |
| 7778 * @throws AnalysisException if any of the identifiers could not be resolved o
r if any of the |
| 7779 * libraries could not have their types analyzed |
| 7780 */ |
| 7781 void resolveReferencesAndTypes() { |
| 7782 for (Library library in _librariesInCycles) { |
| 7783 _resolveReferencesAndTypesInLibrary(library); |
| 7784 } |
| 7785 } |
| 7786 |
| 7787 /** |
| 7788 * Add a dependency to the given map from the referencing library to the refer
enced library. |
| 7789 * |
| 7790 * @param dependencyMap the map to which the dependency is to be added |
| 7791 * @param referencingLibrary the library that references the referenced librar
y |
| 7792 * @param referencedLibrary the library referenced by the referencing library |
| 7793 */ |
| 7794 void _addDependencyToMap(HashMap<Library, List<Library>> dependencyMap, |
| 7795 Library referencingLibrary, Library referencedLibrary) { |
| 7796 List<Library> dependentLibraries = dependencyMap[referencedLibrary]; |
| 7797 if (dependentLibraries == null) { |
| 7798 dependentLibraries = new List<Library>(); |
| 7799 dependencyMap[referencedLibrary] = dependentLibraries; |
| 7800 } |
| 7801 dependentLibraries.add(referencingLibrary); |
| 7802 } |
| 7803 |
| 7804 /** |
| 7805 * Given a library that is part of a cycle that includes the root library, add
to the given set of |
| 7806 * libraries all of the libraries reachable from the root library that are als
o included in the |
| 7807 * cycle. |
| 7808 * |
| 7809 * @param library the library to be added to the collection of libraries in cy
cles |
| 7810 * @param librariesInCycle a collection of the libraries that are in the cycle |
| 7811 * @param dependencyMap a table mapping libraries to the collection of librari
es from which those |
| 7812 * libraries are referenced |
| 7813 */ |
| 7814 void _addLibrariesInCycle(Library library, Set<Library> librariesInCycle, |
| 7815 HashMap<Library, List<Library>> dependencyMap) { |
| 7816 if (librariesInCycle.add(library)) { |
| 7817 List<Library> dependentLibraries = dependencyMap[library]; |
| 7818 if (dependentLibraries != null) { |
| 7819 for (Library dependentLibrary in dependentLibraries) { |
| 7820 _addLibrariesInCycle( |
| 7821 dependentLibrary, librariesInCycle, dependencyMap); |
| 7822 } |
| 7823 } |
| 7824 } |
| 7825 } |
| 7826 |
| 7827 /** |
| 7828 * Add the given library, and all libraries reachable from it that have not al
ready been visited, |
| 7829 * to the given dependency map. |
| 7830 * |
| 7831 * @param library the library currently being added to the dependency map |
| 7832 * @param dependencyMap the dependency map being computed |
| 7833 * @param visitedLibraries the libraries that have already been visited, used
to prevent infinite |
| 7834 * recursion |
| 7835 */ |
| 7836 void _addToDependencyMap( |
| 7837 Library library, |
| 7838 HashMap<Library, List<Library>> dependencyMap, |
| 7839 Set<Library> visitedLibraries) { |
| 7840 if (visitedLibraries.add(library)) { |
| 7841 bool asyncFound = false; |
| 7842 for (Library referencedLibrary in library.importsAndExports) { |
| 7843 _addDependencyToMap(dependencyMap, library, referencedLibrary); |
| 7844 _addToDependencyMap(referencedLibrary, dependencyMap, visitedLibraries); |
| 7845 if (identical(referencedLibrary, _asyncLibrary)) { |
| 7846 asyncFound = true; |
| 7847 } |
| 7848 } |
| 7849 if (!library.explicitlyImportsCore && !identical(library, _coreLibrary)) { |
| 7850 _addDependencyToMap(dependencyMap, library, _coreLibrary); |
| 7851 } |
| 7852 if (!asyncFound && !identical(library, _asyncLibrary)) { |
| 7853 _addDependencyToMap(dependencyMap, library, _asyncLibrary); |
| 7854 _addToDependencyMap(_asyncLibrary, dependencyMap, visitedLibraries); |
| 7855 } |
| 7856 } |
| 7857 } |
| 7858 |
| 7859 /** |
| 7860 * Build the element model representing the combinators declared by the given
directive. |
| 7861 * |
| 7862 * @param directive the directive that declares the combinators |
| 7863 * @return an array containing the import combinators that were built |
| 7864 */ |
| 7865 List<NamespaceCombinator> _buildCombinators(NamespaceDirective directive) { |
| 7866 List<NamespaceCombinator> combinators = new List<NamespaceCombinator>(); |
| 7867 for (Combinator combinator in directive.combinators) { |
| 7868 if (combinator is HideCombinator) { |
| 7869 HideElementCombinatorImpl hide = new HideElementCombinatorImpl(); |
| 7870 hide.hiddenNames = _getIdentifiers(combinator.hiddenNames); |
| 7871 combinators.add(hide); |
| 7872 } else { |
| 7873 ShowElementCombinatorImpl show = new ShowElementCombinatorImpl(); |
| 7874 show.offset = combinator.offset; |
| 7875 show.end = combinator.end; |
| 7876 show.shownNames = |
| 7877 _getIdentifiers((combinator as ShowCombinator).shownNames); |
| 7878 combinators.add(show); |
| 7879 } |
| 7880 } |
| 7881 return combinators; |
| 7882 } |
| 7883 |
| 7884 /** |
| 7885 * Every library now has a corresponding [LibraryElement], so it is now possib
le to resolve |
| 7886 * the import and export directives. |
| 7887 * |
| 7888 * @throws AnalysisException if the defining compilation unit for any of the l
ibraries could not |
| 7889 * be accessed |
| 7890 */ |
| 7891 void _buildDirectiveModels() { |
| 7892 for (Library library in _librariesInCycles) { |
| 7893 HashMap<String, PrefixElementImpl> nameToPrefixMap = |
| 7894 new HashMap<String, PrefixElementImpl>(); |
| 7895 List<ImportElement> imports = new List<ImportElement>(); |
| 7896 List<ExportElement> exports = new List<ExportElement>(); |
| 7897 for (Directive directive in library.definingCompilationUnit.directives) { |
| 7898 if (directive is ImportDirective) { |
| 7899 ImportDirective importDirective = directive; |
| 7900 String uriContent = importDirective.uriContent; |
| 7901 if (DartUriResolver.isDartExtUri(uriContent)) { |
| 7902 library.libraryElement.hasExtUri = true; |
| 7903 } |
| 7904 Source importedSource = importDirective.source; |
| 7905 if (importedSource != null) { |
| 7906 // The imported source will be null if the URI in the import |
| 7907 // directive was invalid. |
| 7908 Library importedLibrary = _libraryMap[importedSource]; |
| 7909 if (importedLibrary != null) { |
| 7910 ImportElementImpl importElement = |
| 7911 new ImportElementImpl(directive.offset); |
| 7912 StringLiteral uriLiteral = importDirective.uri; |
| 7913 importElement.uriOffset = uriLiteral.offset; |
| 7914 importElement.uriEnd = uriLiteral.end; |
| 7915 importElement.uri = uriContent; |
| 7916 importElement.deferred = importDirective.deferredKeyword != null; |
| 7917 importElement.combinators = _buildCombinators(importDirective); |
| 7918 LibraryElement importedLibraryElement = |
| 7919 importedLibrary.libraryElement; |
| 7920 if (importedLibraryElement != null) { |
| 7921 importElement.importedLibrary = importedLibraryElement; |
| 7922 } |
| 7923 SimpleIdentifier prefixNode = directive.prefix; |
| 7924 if (prefixNode != null) { |
| 7925 importElement.prefixOffset = prefixNode.offset; |
| 7926 String prefixName = prefixNode.name; |
| 7927 PrefixElementImpl prefix = nameToPrefixMap[prefixName]; |
| 7928 if (prefix == null) { |
| 7929 prefix = new PrefixElementImpl.forNode(prefixNode); |
| 7930 nameToPrefixMap[prefixName] = prefix; |
| 7931 } |
| 7932 importElement.prefix = prefix; |
| 7933 prefixNode.staticElement = prefix; |
| 7934 } |
| 7935 directive.element = importElement; |
| 7936 imports.add(importElement); |
| 7937 if (analysisContext.computeKindOf(importedSource) != |
| 7938 SourceKind.LIBRARY) { |
| 7939 ErrorCode errorCode = (importElement.isDeferred |
| 7940 ? StaticWarningCode.IMPORT_OF_NON_LIBRARY |
| 7941 : CompileTimeErrorCode.IMPORT_OF_NON_LIBRARY); |
| 7942 _errorListener.onError(new AnalysisError( |
| 7943 library.librarySource, |
| 7944 uriLiteral.offset, |
| 7945 uriLiteral.length, |
| 7946 errorCode, |
| 7947 [uriLiteral.toSource()])); |
| 7948 } |
| 7949 } |
| 7950 } |
| 7951 } else if (directive is ExportDirective) { |
| 7952 ExportDirective exportDirective = directive; |
| 7953 Source exportedSource = exportDirective.source; |
| 7954 if (exportedSource != null) { |
| 7955 // The exported source will be null if the URI in the export |
| 7956 // directive was invalid. |
| 7957 Library exportedLibrary = _libraryMap[exportedSource]; |
| 7958 if (exportedLibrary != null) { |
| 7959 ExportElementImpl exportElement = |
| 7960 new ExportElementImpl(directive.offset); |
| 7961 StringLiteral uriLiteral = exportDirective.uri; |
| 7962 exportElement.uriOffset = uriLiteral.offset; |
| 7963 exportElement.uriEnd = uriLiteral.end; |
| 7964 exportElement.uri = exportDirective.uriContent; |
| 7965 exportElement.combinators = _buildCombinators(exportDirective); |
| 7966 LibraryElement exportedLibraryElement = |
| 7967 exportedLibrary.libraryElement; |
| 7968 if (exportedLibraryElement != null) { |
| 7969 exportElement.exportedLibrary = exportedLibraryElement; |
| 7970 } |
| 7971 directive.element = exportElement; |
| 7972 exports.add(exportElement); |
| 7973 if (analysisContext.computeKindOf(exportedSource) != |
| 7974 SourceKind.LIBRARY) { |
| 7975 _errorListener.onError(new AnalysisError( |
| 7976 library.librarySource, |
| 7977 uriLiteral.offset, |
| 7978 uriLiteral.length, |
| 7979 CompileTimeErrorCode.EXPORT_OF_NON_LIBRARY, |
| 7980 [uriLiteral.toSource()])); |
| 7981 } |
| 7982 } |
| 7983 } |
| 7984 } |
| 7985 } |
| 7986 Source librarySource = library.librarySource; |
| 7987 if (!library.explicitlyImportsCore && |
| 7988 _coreLibrarySource != librarySource) { |
| 7989 ImportElementImpl importElement = new ImportElementImpl(-1); |
| 7990 importElement.importedLibrary = _coreLibrary.libraryElement; |
| 7991 importElement.synthetic = true; |
| 7992 imports.add(importElement); |
| 7993 } |
| 7994 LibraryElementImpl libraryElement = library.libraryElement; |
| 7995 libraryElement.imports = imports; |
| 7996 libraryElement.exports = exports; |
| 7997 if (libraryElement.entryPoint == null) { |
| 7998 Namespace namespace = new NamespaceBuilder() |
| 7999 .createExportNamespaceForLibrary(libraryElement); |
| 8000 Element element = namespace.get(FunctionElement.MAIN_FUNCTION_NAME); |
| 8001 if (element is FunctionElement) { |
| 8002 libraryElement.entryPoint = element; |
| 8003 } |
| 8004 } |
| 8005 } |
| 8006 } |
| 8007 |
| 8008 /** |
| 8009 * Build element models for all of the libraries in the current cycle. |
| 8010 * |
| 8011 * @throws AnalysisException if any of the element models cannot be built |
| 8012 */ |
| 8013 void _buildElementModels() { |
| 8014 for (Library library in _librariesInCycles) { |
| 8015 LibraryElementBuilder builder = |
| 8016 new LibraryElementBuilder(analysisContext, errorListener); |
| 8017 LibraryElementImpl libraryElement = builder.buildLibrary(library); |
| 8018 library.libraryElement = libraryElement; |
| 8019 } |
| 8020 } |
| 8021 |
| 8022 /** |
| 8023 * Build the members in enum declarations. This cannot be done while building
the rest of the |
| 8024 * element model because it depends on being able to access core types, which
cannot happen until |
| 8025 * the rest of the element model has been built (when resolving the core libra
ry). |
| 8026 * |
| 8027 * @throws AnalysisException if any of the enum members could not be built |
| 8028 */ |
| 8029 void _buildEnumMembers() { |
| 8030 PerformanceStatistics.resolve.makeCurrentWhile(() { |
| 8031 for (Library library in _librariesInCycles) { |
| 8032 for (Source source in library.compilationUnitSources) { |
| 8033 EnumMemberBuilder builder = new EnumMemberBuilder(_typeProvider); |
| 8034 library.getAST(source).accept(builder); |
| 8035 } |
| 8036 } |
| 8037 }); |
| 8038 } |
| 8039 |
| 8040 /** |
| 8041 * Resolve the type hierarchy across all of the types declared in the librarie
s in the current |
| 8042 * cycle. |
| 8043 * |
| 8044 * @throws AnalysisException if any of the type hierarchies could not be resol
ved |
| 8045 */ |
| 8046 void _buildTypeHierarchies() { |
| 8047 PerformanceStatistics.resolve.makeCurrentWhile(() { |
| 8048 for (Library library in _librariesInCycles) { |
| 8049 for (Source source in library.compilationUnitSources) { |
| 8050 TypeResolverVisitorFactory typeResolverVisitorFactory = |
| 8051 analysisContext.typeResolverVisitorFactory; |
| 8052 TypeResolverVisitor visitor = (typeResolverVisitorFactory == null) |
| 8053 ? new TypeResolverVisitor(library.libraryElement, source, |
| 8054 _typeProvider, library.errorListener, |
| 8055 nameScope: library.libraryScope) |
| 8056 : typeResolverVisitorFactory(library, source, _typeProvider); |
| 8057 library.getAST(source).accept(visitor); |
| 8058 } |
| 8059 } |
| 8060 }); |
| 8061 } |
| 8062 |
| 8063 /** |
| 8064 * Compute a dependency map of libraries reachable from the given library. A d
ependency map is a |
| 8065 * table that maps individual libraries to a list of the libraries that either
import or export |
| 8066 * those libraries. |
| 8067 * |
| 8068 * This map is used to compute all of the libraries involved in a cycle that i
nclude the root |
| 8069 * library. Given that we only add libraries that are reachable from the root
library, when we |
| 8070 * work backward we are guaranteed to only get libraries in the cycle. |
| 8071 * |
| 8072 * @param library the library currently being added to the dependency map |
| 8073 */ |
| 8074 HashMap<Library, List<Library>> _computeDependencyMap(Library library) { |
| 8075 HashMap<Library, List<Library>> dependencyMap = |
| 8076 new HashMap<Library, List<Library>>(); |
| 8077 _addToDependencyMap(library, dependencyMap, new HashSet<Library>()); |
| 8078 return dependencyMap; |
| 8079 } |
| 8080 |
| 8081 /** |
| 8082 * Recursively traverse the libraries reachable from the given library, creati
ng instances of the |
| 8083 * class [Library] to represent them, and record the references in the library
objects. |
| 8084 * |
| 8085 * @param library the library to be processed to find libraries that have not
yet been traversed |
| 8086 * @throws AnalysisException if some portion of the library graph could not be
traversed |
| 8087 */ |
| 8088 void _computeEmbeddedLibraryDependencies( |
| 8089 Library library, CompilationUnit unit) { |
| 8090 Source librarySource = library.librarySource; |
| 8091 HashSet<Source> exportedSources = new HashSet<Source>(); |
| 8092 HashSet<Source> importedSources = new HashSet<Source>(); |
| 8093 for (Directive directive in unit.directives) { |
| 8094 if (directive is ExportDirective) { |
| 8095 Source exportSource = _resolveSource(librarySource, directive); |
| 8096 if (exportSource != null) { |
| 8097 exportedSources.add(exportSource); |
| 8098 } |
| 8099 } else if (directive is ImportDirective) { |
| 8100 Source importSource = _resolveSource(librarySource, directive); |
| 8101 if (importSource != null) { |
| 8102 importedSources.add(importSource); |
| 8103 } |
| 8104 } |
| 8105 } |
| 8106 _computeLibraryDependenciesFromDirectives(library, |
| 8107 new List.from(importedSources), new List.from(exportedSources)); |
| 8108 } |
| 8109 |
| 8110 /** |
| 8111 * Return a collection containing all of the libraries reachable from the give
n library that are |
| 8112 * contained in a cycle that includes the given library. |
| 8113 * |
| 8114 * @param library the library that must be included in any cycles whose member
s are to be returned |
| 8115 * @return all of the libraries referenced by the given library that have a ci
rcular reference |
| 8116 * back to the given library |
| 8117 */ |
| 8118 Set<Library> _computeLibrariesInCycles(Library library) { |
| 8119 HashMap<Library, List<Library>> dependencyMap = |
| 8120 _computeDependencyMap(library); |
| 8121 Set<Library> librariesInCycle = new HashSet<Library>(); |
| 8122 _addLibrariesInCycle(library, librariesInCycle, dependencyMap); |
| 8123 return librariesInCycle; |
| 8124 } |
| 8125 |
| 8126 /** |
| 8127 * Recursively traverse the libraries reachable from the given library, creati
ng instances of the |
| 8128 * class [Library] to represent them, and record the references in the library
objects. |
| 8129 * |
| 8130 * @param library the library to be processed to find libraries that have not
yet been traversed |
| 8131 * @throws AnalysisException if some portion of the library graph could not be
traversed |
| 8132 */ |
| 8133 void _computeLibraryDependencies(Library library) { |
| 8134 Source librarySource = library.librarySource; |
| 8135 _computeLibraryDependenciesFromDirectives( |
| 8136 library, |
| 8137 analysisContext.computeImportedLibraries(librarySource), |
| 8138 analysisContext.computeExportedLibraries(librarySource)); |
| 8139 } |
| 8140 |
| 8141 /** |
| 8142 * Recursively traverse the libraries reachable from the given library, creati
ng instances of the |
| 8143 * class [Library] to represent them, and record the references in the library
objects. |
| 8144 * |
| 8145 * @param library the library to be processed to find libraries that have not
yet been traversed |
| 8146 * @param importedSources an array containing the sources that are imported in
to the given library |
| 8147 * @param exportedSources an array containing the sources that are exported fr
om the given library |
| 8148 * @throws AnalysisException if some portion of the library graph could not be
traversed |
| 8149 */ |
| 8150 void _computeLibraryDependenciesFromDirectives(Library library, |
| 8151 List<Source> importedSources, List<Source> exportedSources) { |
| 8152 List<Library> importedLibraries = new List<Library>(); |
| 8153 bool explicitlyImportsCore = false; |
| 8154 bool importsAsync = false; |
| 8155 for (Source importedSource in importedSources) { |
| 8156 if (importedSource == _coreLibrarySource) { |
| 8157 explicitlyImportsCore = true; |
| 8158 } |
| 8159 if (importedSource == _asyncLibrarySource) { |
| 8160 importsAsync = true; |
| 8161 } |
| 8162 Library importedLibrary = _libraryMap[importedSource]; |
| 8163 if (importedLibrary == null) { |
| 8164 importedLibrary = _createLibraryOrNull(importedSource); |
| 8165 if (importedLibrary != null) { |
| 8166 _computeLibraryDependencies(importedLibrary); |
| 8167 } |
| 8168 } |
| 8169 if (importedLibrary != null) { |
| 8170 importedLibraries.add(importedLibrary); |
| 8171 } |
| 8172 } |
| 8173 library.importedLibraries = importedLibraries; |
| 8174 List<Library> exportedLibraries = new List<Library>(); |
| 8175 for (Source exportedSource in exportedSources) { |
| 8176 Library exportedLibrary = _libraryMap[exportedSource]; |
| 8177 if (exportedLibrary == null) { |
| 8178 exportedLibrary = _createLibraryOrNull(exportedSource); |
| 8179 if (exportedLibrary != null) { |
| 8180 _computeLibraryDependencies(exportedLibrary); |
| 8181 } |
| 8182 } |
| 8183 if (exportedLibrary != null) { |
| 8184 exportedLibraries.add(exportedLibrary); |
| 8185 } |
| 8186 } |
| 8187 library.exportedLibraries = exportedLibraries; |
| 8188 library.explicitlyImportsCore = explicitlyImportsCore; |
| 8189 if (!explicitlyImportsCore && _coreLibrarySource != library.librarySource) { |
| 8190 Library importedLibrary = _libraryMap[_coreLibrarySource]; |
| 8191 if (importedLibrary == null) { |
| 8192 importedLibrary = _createLibraryOrNull(_coreLibrarySource); |
| 8193 if (importedLibrary != null) { |
| 8194 _computeLibraryDependencies(importedLibrary); |
| 8195 } |
| 8196 } |
| 8197 } |
| 8198 if (!importsAsync && _asyncLibrarySource != library.librarySource) { |
| 8199 Library importedLibrary = _libraryMap[_asyncLibrarySource]; |
| 8200 if (importedLibrary == null) { |
| 8201 importedLibrary = _createLibraryOrNull(_asyncLibrarySource); |
| 8202 if (importedLibrary != null) { |
| 8203 _computeLibraryDependencies(importedLibrary); |
| 8204 } |
| 8205 } |
| 8206 } |
| 8207 } |
| 8208 |
| 8209 /** |
| 8210 * Create an object to represent the information about the library defined by
the compilation unit |
| 8211 * with the given source. Return the library object that was created, or `null
` if the |
| 8212 * source is not valid. |
| 8213 * |
| 8214 * @param librarySource the source of the library's defining compilation unit |
| 8215 * @return the library object that was created |
| 8216 */ |
| 8217 Library _createLibraryOrNull(Source librarySource) { |
| 8218 if (!analysisContext.exists(librarySource)) { |
| 8219 return null; |
| 8220 } |
| 8221 Library library = |
| 8222 new Library(analysisContext, _errorListener, librarySource); |
| 8223 _libraryMap[librarySource] = library; |
| 8224 return library; |
| 8225 } |
| 8226 |
| 8227 /** |
| 8228 * Create an object to represent the information about the library defined by
the compilation unit |
| 8229 * with the given source. |
| 8230 * |
| 8231 * @param librarySource the source of the library's defining compilation unit |
| 8232 * @param unit the compilation unit that defines the library |
| 8233 * @return the library object that was created |
| 8234 * @throws AnalysisException if the library source is not valid |
| 8235 */ |
| 8236 Library _createLibraryWithUnit(Source librarySource, CompilationUnit unit) { |
| 8237 Library library = |
| 8238 new Library(analysisContext, _errorListener, librarySource); |
| 8239 library.setDefiningCompilationUnit(unit); |
| 8240 _libraryMap[librarySource] = library; |
| 8241 return library; |
| 8242 } |
| 8243 |
| 8244 /** |
| 8245 * Return an array containing the lexical identifiers associated with the node
s in the given list. |
| 8246 * |
| 8247 * @param names the AST nodes representing the identifiers |
| 8248 * @return the lexical identifiers associated with the nodes in the list |
| 8249 */ |
| 8250 List<String> _getIdentifiers(NodeList<SimpleIdentifier> names) { |
| 8251 int count = names.length; |
| 8252 List<String> identifiers = new List<String>(count); |
| 8253 for (int i = 0; i < count; i++) { |
| 8254 identifiers[i] = names[i].name; |
| 8255 } |
| 8256 return identifiers; |
| 8257 } |
| 8258 |
| 8259 /** |
| 8260 * Compute a value for all of the constants in the libraries being analyzed. |
| 8261 */ |
| 8262 void _performConstantEvaluation() { |
| 8263 PerformanceStatistics.resolve.makeCurrentWhile(() { |
| 8264 ConstantValueComputer computer = new ConstantValueComputer( |
| 8265 analysisContext, _typeProvider, analysisContext.declaredVariables); |
| 8266 for (Library library in _librariesInCycles) { |
| 8267 for (Source source in library.compilationUnitSources) { |
| 8268 try { |
| 8269 CompilationUnit unit = library.getAST(source); |
| 8270 if (unit != null) { |
| 8271 computer.add(unit, source, library.librarySource); |
| 8272 } |
| 8273 } on AnalysisException catch (exception, stackTrace) { |
| 8274 AnalysisEngine.instance.logger.logError( |
| 8275 "Internal Error: Could not access AST for ${source.fullName} dur
ing constant evaluation", |
| 8276 new CaughtException(exception, stackTrace)); |
| 8277 } |
| 8278 } |
| 8279 } |
| 8280 computer.computeValues(); |
| 8281 // As a temporary workaround for issue 21572, run ConstantVerifier now. |
| 8282 // TODO(paulberry): remove this workaround once issue 21572 is fixed. |
| 8283 for (Library library in _librariesInCycles) { |
| 8284 for (Source source in library.compilationUnitSources) { |
| 8285 try { |
| 8286 CompilationUnit unit = library.getAST(source); |
| 8287 ErrorReporter errorReporter = |
| 8288 new ErrorReporter(_errorListener, source); |
| 8289 ConstantVerifier constantVerifier = new ConstantVerifier( |
| 8290 errorReporter, |
| 8291 library.libraryElement, |
| 8292 _typeProvider, |
| 8293 analysisContext.declaredVariables); |
| 8294 unit.accept(constantVerifier); |
| 8295 } on AnalysisException catch (exception, stackTrace) { |
| 8296 AnalysisEngine.instance.logger.logError( |
| 8297 "Internal Error: Could not access AST for ${source.fullName} " |
| 8298 "during constant verification", |
| 8299 new CaughtException(exception, stackTrace)); |
| 8300 } |
| 8301 } |
| 8302 } |
| 8303 }); |
| 8304 } |
| 8305 |
| 8306 /** |
| 8307 * Resolve the identifiers and perform type analysis in the given library. |
| 8308 * |
| 8309 * @param library the library to be resolved |
| 8310 * @throws AnalysisException if any of the identifiers could not be resolved o
r if the types in |
| 8311 * the library cannot be analyzed |
| 8312 */ |
| 8313 void _resolveReferencesAndTypesInLibrary(Library library) { |
| 8314 PerformanceStatistics.resolve.makeCurrentWhile(() { |
| 8315 for (Source source in library.compilationUnitSources) { |
| 8316 CompilationUnit ast = library.getAST(source); |
| 8317 ast.accept(new VariableResolverVisitor(library.libraryElement, source, |
| 8318 _typeProvider, library.errorListener, |
| 8319 nameScope: library.libraryScope)); |
| 8320 ResolverVisitorFactory visitorFactory = |
| 8321 analysisContext.resolverVisitorFactory; |
| 8322 ResolverVisitor visitor = visitorFactory != null |
| 8323 ? visitorFactory(library, source, _typeProvider) |
| 8324 : new ResolverVisitor(library.libraryElement, source, _typeProvider, |
| 8325 library.errorListener, |
| 8326 nameScope: library.libraryScope, |
| 8327 inheritanceManager: library.inheritanceManager); |
| 8328 ast.accept(visitor); |
| 8329 } |
| 8330 }); |
| 8331 } |
| 8332 |
| 8333 /** |
| 8334 * Return the result of resolving the URI of the given URI-based directive aga
inst the URI of the |
| 8335 * given library, or `null` if the URI is not valid. |
| 8336 * |
| 8337 * @param librarySource the source representing the library containing the dir
ective |
| 8338 * @param directive the directive which URI should be resolved |
| 8339 * @return the result of resolving the URI against the URI of the library |
| 8340 */ |
| 8341 Source _resolveSource(Source librarySource, UriBasedDirective directive) { |
| 8342 StringLiteral uriLiteral = directive.uri; |
| 8343 if (uriLiteral is StringInterpolation) { |
| 8344 return null; |
| 8345 } |
| 8346 String uriContent = uriLiteral.stringValue.trim(); |
| 8347 if (uriContent == null || uriContent.isEmpty) { |
| 8348 return null; |
| 8349 } |
| 8350 uriContent = Uri.encodeFull(uriContent); |
| 8351 return analysisContext.sourceFactory.resolveUri(librarySource, uriContent); |
| 8352 } |
| 8353 } |
| 8354 |
| 8355 /** |
| 8356 * Instances of the class `LibraryResolver` are used to resolve one or more mutu
ally dependent |
| 8357 * libraries within a single context. |
| 8358 */ |
| 8359 class LibraryResolver2 { |
| 8360 /** |
| 8361 * The analysis context in which the libraries are being analyzed. |
| 8362 */ |
| 8363 final InternalAnalysisContext analysisContext; |
| 8364 |
| 8365 /** |
| 8366 * The listener to which analysis errors will be reported, this error listener
is either |
| 8367 * references [recordingErrorListener], or it unions the passed |
| 8368 * [AnalysisErrorListener] with the [recordingErrorListener]. |
| 8369 */ |
| 8370 RecordingErrorListener _errorListener; |
| 8371 |
| 8372 /** |
| 8373 * A source object representing the core library (dart:core). |
| 8374 */ |
| 8375 Source _coreLibrarySource; |
| 8376 |
| 8377 /** |
| 8378 * A source object representing the async library (dart:async). |
| 8379 */ |
| 8380 Source _asyncLibrarySource; |
| 8381 |
| 8382 /** |
| 8383 * The object representing the core library. |
| 8384 */ |
| 8385 ResolvableLibrary _coreLibrary; |
| 8386 |
| 8387 /** |
| 8388 * The object representing the async library. |
| 8389 */ |
| 8390 ResolvableLibrary _asyncLibrary; |
| 8391 |
| 8392 /** |
| 8393 * The object used to access the types from the core library. |
| 8394 */ |
| 8395 TypeProvider _typeProvider; |
| 8396 |
| 8397 /** |
| 8398 * A table mapping library sources to the information being maintained for tho
se libraries. |
| 8399 */ |
| 8400 HashMap<Source, ResolvableLibrary> _libraryMap = |
| 8401 new HashMap<Source, ResolvableLibrary>(); |
| 8402 |
| 8403 /** |
| 8404 * A collection containing the libraries that are being resolved together. |
| 8405 */ |
| 8406 List<ResolvableLibrary> _librariesInCycle; |
| 8407 |
| 8408 /** |
| 8409 * Initialize a newly created library resolver to resolve libraries within the
given context. |
| 8410 * |
| 8411 * @param analysisContext the analysis context in which the library is being a
nalyzed |
| 8412 */ |
| 8413 LibraryResolver2(this.analysisContext) { |
| 8414 this._errorListener = new RecordingErrorListener(); |
| 8415 _coreLibrarySource = |
| 8416 analysisContext.sourceFactory.forUri(DartSdk.DART_CORE); |
| 8417 _asyncLibrarySource = |
| 8418 analysisContext.sourceFactory.forUri(DartSdk.DART_ASYNC); |
| 8419 } |
| 8420 |
| 8421 /** |
| 8422 * Return the listener to which analysis errors will be reported. |
| 8423 * |
| 8424 * @return the listener to which analysis errors will be reported |
| 8425 */ |
| 8426 RecordingErrorListener get errorListener => _errorListener; |
| 8427 |
| 8428 /** |
| 8429 * Return an array containing information about all of the libraries that were
resolved. |
| 8430 * |
| 8431 * @return an array containing the libraries that were resolved |
| 8432 */ |
| 8433 List<ResolvableLibrary> get resolvedLibraries => _librariesInCycle; |
| 8434 |
| 8435 /** |
| 8436 * Resolve the library specified by the given source in the given context. |
| 8437 * |
| 8438 * Note that because Dart allows circular imports between libraries, it is pos
sible that more than |
| 8439 * one library will need to be resolved. In such cases the error listener can
receive errors from |
| 8440 * multiple libraries. |
| 8441 * |
| 8442 * @param librarySource the source specifying the defining compilation unit of
the library to be |
| 8443 * resolved |
| 8444 * @param fullAnalysis `true` if a full analysis should be performed |
| 8445 * @return the element representing the resolved library |
| 8446 * @throws AnalysisException if the library could not be resolved for some rea
son |
| 8447 */ |
| 8448 LibraryElement resolveLibrary( |
| 8449 Source librarySource, List<ResolvableLibrary> librariesInCycle) { |
| 8450 // |
| 8451 // Build the map of libraries that are known. |
| 8452 // |
| 8453 this._librariesInCycle = librariesInCycle; |
| 8454 _libraryMap = _buildLibraryMap(); |
| 8455 ResolvableLibrary targetLibrary = _libraryMap[librarySource]; |
| 8456 _coreLibrary = _libraryMap[_coreLibrarySource]; |
| 8457 _asyncLibrary = _libraryMap[_asyncLibrarySource]; |
| 8458 // |
| 8459 // Build the element models representing the libraries being resolved. |
| 8460 // This is done in three steps: |
| 8461 // |
| 8462 // 1. Build the basic element models without making any connections |
| 8463 // between elements other than the basic parent/child relationships. |
| 8464 // This includes building the elements representing the libraries, but |
| 8465 // excludes members defined in enums. |
| 8466 // 2. Build the elements for the import and export directives. This |
| 8467 // requires that we have the elements built for the referenced |
| 8468 // libraries, but because of the possibility of circular references |
| 8469 // needs to happen after all of the library elements have been created. |
| 8470 // 3. Build the members in enum declarations. |
| 8471 // 4. Build the rest of the type model by connecting superclasses, mixins, |
| 8472 // and interfaces. This requires that we be able to compute the names |
| 8473 // visible in the libraries being resolved, which in turn requires that |
| 8474 // we have resolved the import directives. |
| 8475 // |
| 8476 _buildElementModels(); |
| 8477 LibraryElement coreElement = _coreLibrary.libraryElement; |
| 8478 if (coreElement == null) { |
| 8479 missingCoreLibrary(analysisContext, _coreLibrarySource); |
| 8480 } |
| 8481 LibraryElement asyncElement = _asyncLibrary.libraryElement; |
| 8482 if (asyncElement == null) { |
| 8483 missingAsyncLibrary(analysisContext, _asyncLibrarySource); |
| 8484 } |
| 8485 _buildDirectiveModels(); |
| 8486 _typeProvider = new TypeProviderImpl(coreElement, asyncElement); |
| 8487 _buildEnumMembers(); |
| 8488 _buildTypeHierarchies(); |
| 8489 // |
| 8490 // Perform resolution and type analysis. |
| 8491 // |
| 8492 // TODO(brianwilkerson) Decide whether we want to resolve all of the |
| 8493 // libraries or whether we want to only resolve the target library. The |
| 8494 // advantage to resolving everything is that we have already done part of |
| 8495 // the work so we'll avoid duplicated effort. The disadvantage of |
| 8496 // resolving everything is that we might do extra work that we don't |
| 8497 // really care about. Another possibility is to add a parameter to this |
| 8498 // method and punt the decision to the clients. |
| 8499 // |
| 8500 //if (analyzeAll) { |
| 8501 _resolveReferencesAndTypes(); |
| 8502 //} else { |
| 8503 // resolveReferencesAndTypes(targetLibrary); |
| 8504 //} |
| 8505 _performConstantEvaluation(); |
| 8506 return targetLibrary.libraryElement; |
| 8507 } |
| 8508 |
| 8509 /** |
| 8510 * Build the element model representing the combinators declared by the given
directive. |
| 8511 * |
| 8512 * @param directive the directive that declares the combinators |
| 8513 * @return an array containing the import combinators that were built |
| 8514 */ |
| 8515 List<NamespaceCombinator> _buildCombinators(NamespaceDirective directive) { |
| 8516 List<NamespaceCombinator> combinators = new List<NamespaceCombinator>(); |
| 8517 for (Combinator combinator in directive.combinators) { |
| 8518 if (combinator is HideCombinator) { |
| 8519 HideElementCombinatorImpl hide = new HideElementCombinatorImpl(); |
| 8520 hide.hiddenNames = _getIdentifiers(combinator.hiddenNames); |
| 8521 combinators.add(hide); |
| 8522 } else { |
| 8523 ShowElementCombinatorImpl show = new ShowElementCombinatorImpl(); |
| 8524 show.offset = combinator.offset; |
| 8525 show.end = combinator.end; |
| 8526 show.shownNames = |
| 8527 _getIdentifiers((combinator as ShowCombinator).shownNames); |
| 8528 combinators.add(show); |
| 8529 } |
| 8530 } |
| 8531 return combinators; |
| 8532 } |
| 8533 |
| 8534 /** |
| 8535 * Every library now has a corresponding [LibraryElement], so it is now possib
le to resolve |
| 8536 * the import and export directives. |
| 8537 * |
| 8538 * @throws AnalysisException if the defining compilation unit for any of the l
ibraries could not |
| 8539 * be accessed |
| 8540 */ |
| 8541 void _buildDirectiveModels() { |
| 8542 for (ResolvableLibrary library in _librariesInCycle) { |
| 8543 HashMap<String, PrefixElementImpl> nameToPrefixMap = |
| 8544 new HashMap<String, PrefixElementImpl>(); |
| 8545 List<ImportElement> imports = new List<ImportElement>(); |
| 8546 List<ExportElement> exports = new List<ExportElement>(); |
| 8547 for (Directive directive in library.definingCompilationUnit.directives) { |
| 8548 if (directive is ImportDirective) { |
| 8549 ImportDirective importDirective = directive; |
| 8550 String uriContent = importDirective.uriContent; |
| 8551 if (DartUriResolver.isDartExtUri(uriContent)) { |
| 8552 library.libraryElement.hasExtUri = true; |
| 8553 } |
| 8554 Source importedSource = importDirective.source; |
| 8555 if (importedSource != null && |
| 8556 analysisContext.exists(importedSource)) { |
| 8557 // The imported source will be null if the URI in the import |
| 8558 // directive was invalid. |
| 8559 ResolvableLibrary importedLibrary = _libraryMap[importedSource]; |
| 8560 if (importedLibrary != null) { |
| 8561 ImportElementImpl importElement = |
| 8562 new ImportElementImpl(directive.offset); |
| 8563 StringLiteral uriLiteral = importDirective.uri; |
| 8564 if (uriLiteral != null) { |
| 8565 importElement.uriOffset = uriLiteral.offset; |
| 8566 importElement.uriEnd = uriLiteral.end; |
| 8567 } |
| 8568 importElement.uri = uriContent; |
| 8569 importElement.deferred = importDirective.deferredKeyword != null; |
| 8570 importElement.combinators = _buildCombinators(importDirective); |
| 8571 LibraryElement importedLibraryElement = |
| 8572 importedLibrary.libraryElement; |
| 8573 if (importedLibraryElement != null) { |
| 8574 importElement.importedLibrary = importedLibraryElement; |
| 8575 } |
| 8576 SimpleIdentifier prefixNode = directive.prefix; |
| 8577 if (prefixNode != null) { |
| 8578 importElement.prefixOffset = prefixNode.offset; |
| 8579 String prefixName = prefixNode.name; |
| 8580 PrefixElementImpl prefix = nameToPrefixMap[prefixName]; |
| 8581 if (prefix == null) { |
| 8582 prefix = new PrefixElementImpl.forNode(prefixNode); |
| 8583 nameToPrefixMap[prefixName] = prefix; |
| 8584 } |
| 8585 importElement.prefix = prefix; |
| 8586 prefixNode.staticElement = prefix; |
| 8587 } |
| 8588 directive.element = importElement; |
| 8589 imports.add(importElement); |
| 8590 if (analysisContext.computeKindOf(importedSource) != |
| 8591 SourceKind.LIBRARY) { |
| 8592 ErrorCode errorCode = (importElement.isDeferred |
| 8593 ? StaticWarningCode.IMPORT_OF_NON_LIBRARY |
| 8594 : CompileTimeErrorCode.IMPORT_OF_NON_LIBRARY); |
| 8595 _errorListener.onError(new AnalysisError( |
| 8596 library.librarySource, |
| 8597 uriLiteral.offset, |
| 8598 uriLiteral.length, |
| 8599 errorCode, |
| 8600 [uriLiteral.toSource()])); |
| 8601 } |
| 8602 } |
| 8603 } |
| 8604 } else if (directive is ExportDirective) { |
| 8605 ExportDirective exportDirective = directive; |
| 8606 Source exportedSource = exportDirective.source; |
| 8607 if (exportedSource != null && |
| 8608 analysisContext.exists(exportedSource)) { |
| 8609 // The exported source will be null if the URI in the export |
| 8610 // directive was invalid. |
| 8611 ResolvableLibrary exportedLibrary = _libraryMap[exportedSource]; |
| 8612 if (exportedLibrary != null) { |
| 8613 ExportElementImpl exportElement = |
| 8614 new ExportElementImpl(directive.offset); |
| 8615 StringLiteral uriLiteral = exportDirective.uri; |
| 8616 if (uriLiteral != null) { |
| 8617 exportElement.uriOffset = uriLiteral.offset; |
| 8618 exportElement.uriEnd = uriLiteral.end; |
| 8619 } |
| 8620 exportElement.uri = exportDirective.uriContent; |
| 8621 exportElement.combinators = _buildCombinators(exportDirective); |
| 8622 LibraryElement exportedLibraryElement = |
| 8623 exportedLibrary.libraryElement; |
| 8624 if (exportedLibraryElement != null) { |
| 8625 exportElement.exportedLibrary = exportedLibraryElement; |
| 8626 } |
| 8627 directive.element = exportElement; |
| 8628 exports.add(exportElement); |
| 8629 if (analysisContext.computeKindOf(exportedSource) != |
| 8630 SourceKind.LIBRARY) { |
| 8631 _errorListener.onError(new AnalysisError( |
| 8632 library.librarySource, |
| 8633 uriLiteral.offset, |
| 8634 uriLiteral.length, |
| 8635 CompileTimeErrorCode.EXPORT_OF_NON_LIBRARY, |
| 8636 [uriLiteral.toSource()])); |
| 8637 } |
| 8638 } |
| 8639 } |
| 8640 } |
| 8641 } |
| 8642 Source librarySource = library.librarySource; |
| 8643 if (!library.explicitlyImportsCore && |
| 8644 _coreLibrarySource != librarySource) { |
| 8645 ImportElementImpl importElement = new ImportElementImpl(-1); |
| 8646 importElement.importedLibrary = _coreLibrary.libraryElement; |
| 8647 importElement.synthetic = true; |
| 8648 imports.add(importElement); |
| 8649 } |
| 8650 LibraryElementImpl libraryElement = library.libraryElement; |
| 8651 libraryElement.imports = imports; |
| 8652 libraryElement.exports = exports; |
| 8653 if (libraryElement.entryPoint == null) { |
| 8654 Namespace namespace = new NamespaceBuilder() |
| 8655 .createExportNamespaceForLibrary(libraryElement); |
| 8656 Element element = namespace.get(FunctionElement.MAIN_FUNCTION_NAME); |
| 8657 if (element is FunctionElement) { |
| 8658 libraryElement.entryPoint = element; |
| 8659 } |
| 8660 } |
| 8661 } |
| 8662 } |
| 8663 |
| 8664 /** |
| 8665 * Build element models for all of the libraries in the current cycle. |
| 8666 * |
| 8667 * @throws AnalysisException if any of the element models cannot be built |
| 8668 */ |
| 8669 void _buildElementModels() { |
| 8670 for (ResolvableLibrary library in _librariesInCycle) { |
| 8671 LibraryElementBuilder builder = |
| 8672 new LibraryElementBuilder(analysisContext, errorListener); |
| 8673 builder.buildLibrary2(library); |
| 8674 } |
| 8675 } |
| 8676 |
| 8677 /** |
| 8678 * Build the members in enum declarations. This cannot be done while building
the rest of the |
| 8679 * element model because it depends on being able to access core types, which
cannot happen until |
| 8680 * the rest of the element model has been built (when resolving the core libra
ry). |
| 8681 * |
| 8682 * @throws AnalysisException if any of the enum members could not be built |
| 8683 */ |
| 8684 void _buildEnumMembers() { |
| 8685 PerformanceStatistics.resolve.makeCurrentWhile(() { |
| 8686 for (ResolvableLibrary library in _librariesInCycle) { |
| 8687 for (Source source in library.compilationUnitSources) { |
| 8688 EnumMemberBuilder builder = new EnumMemberBuilder(_typeProvider); |
| 8689 library.getAST(source).accept(builder); |
| 8690 } |
| 8691 } |
| 8692 }); |
| 8693 } |
| 8694 |
| 8695 HashMap<Source, ResolvableLibrary> _buildLibraryMap() { |
| 8696 HashMap<Source, ResolvableLibrary> libraryMap = |
| 8697 new HashMap<Source, ResolvableLibrary>(); |
| 8698 int libraryCount = _librariesInCycle.length; |
| 8699 for (int i = 0; i < libraryCount; i++) { |
| 8700 ResolvableLibrary library = _librariesInCycle[i]; |
| 8701 library.errorListener = _errorListener; |
| 8702 libraryMap[library.librarySource] = library; |
| 8703 List<ResolvableLibrary> dependencies = library.importsAndExports; |
| 8704 int dependencyCount = dependencies.length; |
| 8705 for (int j = 0; j < dependencyCount; j++) { |
| 8706 ResolvableLibrary dependency = dependencies[j]; |
| 8707 //dependency.setErrorListener(errorListener); |
| 8708 libraryMap[dependency.librarySource] = dependency; |
| 8709 } |
| 8710 } |
| 8711 return libraryMap; |
| 8712 } |
| 8713 |
| 8714 /** |
| 8715 * Resolve the type hierarchy across all of the types declared in the librarie
s in the current |
| 8716 * cycle. |
| 8717 * |
| 8718 * @throws AnalysisException if any of the type hierarchies could not be resol
ved |
| 8719 */ |
| 8720 void _buildTypeHierarchies() { |
| 8721 PerformanceStatistics.resolve.makeCurrentWhile(() { |
| 8722 for (ResolvableLibrary library in _librariesInCycle) { |
| 8723 for (ResolvableCompilationUnit unit |
| 8724 in library.resolvableCompilationUnits) { |
| 8725 Source source = unit.source; |
| 8726 CompilationUnit ast = unit.compilationUnit; |
| 8727 TypeResolverVisitor visitor = new TypeResolverVisitor( |
| 8728 library.libraryElement, |
| 8729 source, |
| 8730 _typeProvider, |
| 8731 library.libraryScope.errorListener, |
| 8732 nameScope: library.libraryScope); |
| 8733 ast.accept(visitor); |
| 8734 } |
| 8735 } |
| 8736 }); |
| 8737 } |
| 8738 |
| 8739 /** |
| 8740 * Return an array containing the lexical identifiers associated with the node
s in the given list. |
| 8741 * |
| 8742 * @param names the AST nodes representing the identifiers |
| 8743 * @return the lexical identifiers associated with the nodes in the list |
| 8744 */ |
| 8745 List<String> _getIdentifiers(NodeList<SimpleIdentifier> names) { |
| 8746 int count = names.length; |
| 8747 List<String> identifiers = new List<String>(count); |
| 8748 for (int i = 0; i < count; i++) { |
| 8749 identifiers[i] = names[i].name; |
| 8750 } |
| 8751 return identifiers; |
| 8752 } |
| 8753 |
| 8754 /** |
| 8755 * Compute a value for all of the constants in the libraries being analyzed. |
| 8756 */ |
| 8757 void _performConstantEvaluation() { |
| 8758 PerformanceStatistics.resolve.makeCurrentWhile(() { |
| 8759 ConstantValueComputer computer = new ConstantValueComputer( |
| 8760 analysisContext, _typeProvider, analysisContext.declaredVariables); |
| 8761 for (ResolvableLibrary library in _librariesInCycle) { |
| 8762 for (ResolvableCompilationUnit unit |
| 8763 in library.resolvableCompilationUnits) { |
| 8764 CompilationUnit ast = unit.compilationUnit; |
| 8765 if (ast != null) { |
| 8766 computer.add(ast, unit.source, library.librarySource); |
| 8767 } |
| 8768 } |
| 8769 } |
| 8770 computer.computeValues(); |
| 8771 // As a temporary workaround for issue 21572, run ConstantVerifier now. |
| 8772 // TODO(paulberry): remove this workaround once issue 21572 is fixed. |
| 8773 for (ResolvableLibrary library in _librariesInCycle) { |
| 8774 for (ResolvableCompilationUnit unit |
| 8775 in library.resolvableCompilationUnits) { |
| 8776 CompilationUnit ast = unit.compilationUnit; |
| 8777 ErrorReporter errorReporter = |
| 8778 new ErrorReporter(_errorListener, unit.source); |
| 8779 ConstantVerifier constantVerifier = new ConstantVerifier( |
| 8780 errorReporter, |
| 8781 library.libraryElement, |
| 8782 _typeProvider, |
| 8783 analysisContext.declaredVariables); |
| 8784 ast.accept(constantVerifier); |
| 8785 } |
| 8786 } |
| 8787 }); |
| 8788 } |
| 8789 |
| 8790 /** |
| 8791 * Resolve the identifiers and perform type analysis in the libraries in the c
urrent cycle. |
| 8792 * |
| 8793 * @throws AnalysisException if any of the identifiers could not be resolved o
r if any of the |
| 8794 * libraries could not have their types analyzed |
| 8795 */ |
| 8796 void _resolveReferencesAndTypes() { |
| 8797 for (ResolvableLibrary library in _librariesInCycle) { |
| 8798 _resolveReferencesAndTypesInLibrary(library); |
| 8799 } |
| 8800 } |
| 8801 |
| 8802 /** |
| 8803 * Resolve the identifiers and perform type analysis in the given library. |
| 8804 * |
| 8805 * @param library the library to be resolved |
| 8806 * @throws AnalysisException if any of the identifiers could not be resolved o
r if the types in |
| 8807 * the library cannot be analyzed |
| 8808 */ |
| 8809 void _resolveReferencesAndTypesInLibrary(ResolvableLibrary library) { |
| 8810 PerformanceStatistics.resolve.makeCurrentWhile(() { |
| 8811 for (ResolvableCompilationUnit unit |
| 8812 in library.resolvableCompilationUnits) { |
| 8813 Source source = unit.source; |
| 8814 CompilationUnit ast = unit.compilationUnit; |
| 8815 ast.accept(new VariableResolverVisitor(library.libraryElement, source, |
| 8816 _typeProvider, library.libraryScope.errorListener, |
| 8817 nameScope: library.libraryScope)); |
| 8818 ResolverVisitor visitor = new ResolverVisitor(library.libraryElement, |
| 8819 source, _typeProvider, library._libraryScope.errorListener, |
| 8820 nameScope: library._libraryScope, |
| 8821 inheritanceManager: library.inheritanceManager); |
| 8822 ast.accept(visitor); |
| 8823 } |
| 8824 }); |
| 8825 } |
| 8826 |
| 8827 /** |
| 8828 * Report that the async library could not be resolved in the given |
| 8829 * [analysisContext] and throw an exception. [asyncLibrarySource] is the sour
ce |
| 8830 * representing the async library. |
| 8831 */ |
| 8832 static void missingAsyncLibrary( |
| 8833 AnalysisContext analysisContext, Source asyncLibrarySource) { |
| 8834 throw new AnalysisException("Could not resolve dart:async"); |
| 8835 } |
| 8836 |
| 8837 /** |
| 8838 * Report that the core library could not be resolved in the given analysis co
ntext and throw an |
| 8839 * exception. |
| 8840 * |
| 8841 * @param analysisContext the analysis context in which the failure occurred |
| 8842 * @param coreLibrarySource the source representing the core library |
| 8843 * @throws AnalysisException always |
| 8844 */ |
| 8845 static void missingCoreLibrary( |
| 8846 AnalysisContext analysisContext, Source coreLibrarySource) { |
| 8847 throw new AnalysisException("Could not resolve dart:core"); |
| 8848 } |
| 8849 } |
| 8850 |
| 8851 /** |
| 8852 * Instances of the class `TypeAliasInfo` hold information about a [TypeAlias]. |
| 8853 */ |
| 8854 class LibraryResolver2_TypeAliasInfo { |
| 8855 final ResolvableLibrary _library; |
| 8856 |
| 8857 final Source _source; |
| 8858 |
| 8859 final FunctionTypeAlias _typeAlias; |
| 8860 |
| 8861 /** |
| 8862 * Initialize a newly created information holder with the given information. |
| 8863 * |
| 8864 * @param library the library containing the type alias |
| 8865 * @param source the source of the file containing the type alias |
| 8866 * @param typeAlias the type alias being remembered |
| 8867 */ |
| 8868 LibraryResolver2_TypeAliasInfo(this._library, this._source, this._typeAlias); |
| 8869 } |
| 8870 |
| 8871 /** |
| 8872 * Instances of the class `TypeAliasInfo` hold information about a [TypeAlias]. |
| 8873 */ |
| 8874 class LibraryResolver_TypeAliasInfo { |
| 8875 final Library _library; |
| 8876 |
| 8877 final Source _source; |
| 8878 |
| 8879 final FunctionTypeAlias _typeAlias; |
| 8880 |
| 8881 /** |
| 8882 * Initialize a newly created information holder with the given information. |
| 8883 * |
| 8884 * @param library the library containing the type alias |
| 8885 * @param source the source of the file containing the type alias |
| 8886 * @param typeAlias the type alias being remembered |
| 8887 */ |
| 8888 LibraryResolver_TypeAliasInfo(this._library, this._source, this._typeAlias); |
| 8889 } |
| 8890 |
| 8891 /** |
| 8892 * Instances of the class `LibraryScope` implement a scope containing all of the
names defined |
| 8893 * in a given library. |
| 8894 */ |
| 8895 class LibraryScope extends EnclosedScope { |
| 8896 /** |
| 8897 * Initialize a newly created scope representing the names defined in the give
n library. |
| 8898 * |
| 8899 * @param definingLibrary the element representing the library represented by
this scope |
| 8900 * @param errorListener the listener that is to be informed when an error is e
ncountered |
| 8901 */ |
| 8902 LibraryScope( |
| 8903 LibraryElement definingLibrary, AnalysisErrorListener errorListener) |
| 8904 : super(new LibraryImportScope(definingLibrary, errorListener)) { |
| 8905 _defineTopLevelNames(definingLibrary); |
| 8906 } |
| 8907 |
| 8908 @override |
| 8909 AnalysisError getErrorForDuplicate(Element existing, Element duplicate) { |
| 8910 if (existing is PrefixElement) { |
| 8911 // TODO(scheglov) consider providing actual 'nameOffset' from the |
| 8912 // synthetic accessor |
| 8913 int offset = duplicate.nameOffset; |
| 8914 if (duplicate is PropertyAccessorElement) { |
| 8915 PropertyAccessorElement accessor = duplicate; |
| 8916 if (accessor.isSynthetic) { |
| 8917 offset = accessor.variable.nameOffset; |
| 8918 } |
| 8919 } |
| 8920 return new AnalysisError( |
| 8921 duplicate.source, |
| 8922 offset, |
| 8923 duplicate.displayName.length, |
| 8924 CompileTimeErrorCode.PREFIX_COLLIDES_WITH_TOP_LEVEL_MEMBER, |
| 8925 [existing.displayName]); |
| 8926 } |
| 8927 return super.getErrorForDuplicate(existing, duplicate); |
| 8928 } |
| 8929 |
| 8930 /** |
| 8931 * Add to this scope all of the public top-level names that are defined in the
given compilation |
| 8932 * unit. |
| 8933 * |
| 8934 * @param compilationUnit the compilation unit defining the top-level names to
be added to this |
| 8935 * scope |
| 8936 */ |
| 8937 void _defineLocalNames(CompilationUnitElement compilationUnit) { |
| 8938 for (PropertyAccessorElement element in compilationUnit.accessors) { |
| 8939 define(element); |
| 8940 } |
| 8941 for (ClassElement element in compilationUnit.enums) { |
| 8942 define(element); |
| 8943 } |
| 8944 for (FunctionElement element in compilationUnit.functions) { |
| 8945 define(element); |
| 8946 } |
| 8947 for (FunctionTypeAliasElement element |
| 8948 in compilationUnit.functionTypeAliases) { |
| 8949 define(element); |
| 8950 } |
| 8951 for (ClassElement element in compilationUnit.types) { |
| 8952 define(element); |
| 8953 } |
| 8954 } |
| 8955 |
| 8956 /** |
| 8957 * Add to this scope all of the names that are explicitly defined in the given
library. |
| 8958 * |
| 8959 * @param definingLibrary the element representing the library that defines th
e names in this |
| 8960 * scope |
| 8961 */ |
| 8962 void _defineTopLevelNames(LibraryElement definingLibrary) { |
| 8963 for (PrefixElement prefix in definingLibrary.prefixes) { |
| 8964 define(prefix); |
| 8965 } |
| 8966 _defineLocalNames(definingLibrary.definingCompilationUnit); |
| 8967 for (CompilationUnitElement compilationUnit in definingLibrary.parts) { |
| 8968 _defineLocalNames(compilationUnit); |
| 8969 } |
| 8970 } |
| 8971 } |
| 8972 |
| 8973 /** |
| 8974 * This class is used to replace uses of `HashMap<String, ExecutableElement>` |
| 8975 * which are not as performant as this class. |
| 8976 */ |
| 8977 class MemberMap { |
| 8978 /** |
| 8979 * The current size of this map. |
| 8980 */ |
| 8981 int _size = 0; |
| 8982 |
| 8983 /** |
| 8984 * The array of keys. |
| 8985 */ |
| 8986 List<String> _keys; |
| 8987 |
| 8988 /** |
| 8989 * The array of ExecutableElement values. |
| 8990 */ |
| 8991 List<ExecutableElement> _values; |
| 8992 |
| 8993 /** |
| 8994 * Initialize a newly created member map to have the given [initialCapacity]. |
| 8995 * The map will grow if needed. |
| 8996 */ |
| 8997 MemberMap([int initialCapacity = 10]) { |
| 8998 _initArrays(initialCapacity); |
| 8999 } |
| 9000 |
| 9001 /** |
| 9002 * This constructor takes an initial capacity of the map. |
| 9003 * |
| 9004 * @param initialCapacity the initial capacity |
| 9005 */ |
| 9006 @deprecated // Use new MemberMap(initialCapacity) |
| 9007 MemberMap.con1(int initialCapacity) { |
| 9008 _initArrays(initialCapacity); |
| 9009 } |
| 9010 |
| 9011 /** |
| 9012 * Copy constructor. |
| 9013 */ |
| 9014 @deprecated // Use new MemberMap.from(memberMap) |
| 9015 MemberMap.con2(MemberMap memberMap) { |
| 9016 _initArrays(memberMap._size + 5); |
| 9017 for (int i = 0; i < memberMap._size; i++) { |
| 9018 _keys[i] = memberMap._keys[i]; |
| 9019 _values[i] = memberMap._values[i]; |
| 9020 } |
| 9021 _size = memberMap._size; |
| 9022 } |
| 9023 |
| 9024 /** |
| 9025 * Initialize a newly created member map to contain the same members as the |
| 9026 * given [memberMap]. |
| 9027 */ |
| 9028 MemberMap.from(MemberMap memberMap) { |
| 9029 _initArrays(memberMap._size + 5); |
| 9030 for (int i = 0; i < memberMap._size; i++) { |
| 9031 _keys[i] = memberMap._keys[i]; |
| 9032 _values[i] = memberMap._values[i]; |
| 9033 } |
| 9034 _size = memberMap._size; |
| 9035 } |
| 9036 |
| 9037 /** |
| 9038 * The size of the map. |
| 9039 * |
| 9040 * @return the size of the map. |
| 9041 */ |
| 9042 int get size => _size; |
| 9043 |
| 9044 /** |
| 9045 * Given some key, return the ExecutableElement value from the map, if the key
does not exist in |
| 9046 * the map, `null` is returned. |
| 9047 * |
| 9048 * @param key some key to look up in the map |
| 9049 * @return the associated ExecutableElement value from the map, if the key doe
s not exist in the |
| 9050 * map, `null` is returned |
| 9051 */ |
| 9052 ExecutableElement get(String key) { |
| 9053 for (int i = 0; i < _size; i++) { |
| 9054 if (_keys[i] != null && _keys[i] == key) { |
| 9055 return _values[i]; |
| 9056 } |
| 9057 } |
| 9058 return null; |
| 9059 } |
| 9060 |
| 9061 /** |
| 9062 * Get and return the key at the specified location. If the key/value pair has
been removed from |
| 9063 * the set, then `null` is returned. |
| 9064 * |
| 9065 * @param i some non-zero value less than size |
| 9066 * @return the key at the passed index |
| 9067 * @throw ArrayIndexOutOfBoundsException this exception is thrown if the passe
d index is less than |
| 9068 * zero or greater than or equal to the capacity of the arrays |
| 9069 */ |
| 9070 String getKey(int i) => _keys[i]; |
| 9071 |
| 9072 /** |
| 9073 * Get and return the ExecutableElement at the specified location. If the key/
value pair has been |
| 9074 * removed from the set, then then `null` is returned. |
| 9075 * |
| 9076 * @param i some non-zero value less than size |
| 9077 * @return the key at the passed index |
| 9078 * @throw ArrayIndexOutOfBoundsException this exception is thrown if the passe
d index is less than |
| 9079 * zero or greater than or equal to the capacity of the arrays |
| 9080 */ |
| 9081 ExecutableElement getValue(int i) => _values[i]; |
| 9082 |
| 9083 /** |
| 9084 * Given some key/value pair, store the pair in the map. If the key exists alr
eady, then the new |
| 9085 * value overrides the old value. |
| 9086 * |
| 9087 * @param key the key to store in the map |
| 9088 * @param value the ExecutableElement value to store in the map |
| 9089 */ |
| 9090 void put(String key, ExecutableElement value) { |
| 9091 // If we already have a value with this key, override the value |
| 9092 for (int i = 0; i < _size; i++) { |
| 9093 if (_keys[i] != null && _keys[i] == key) { |
| 9094 _values[i] = value; |
| 9095 return; |
| 9096 } |
| 9097 } |
| 9098 // If needed, double the size of our arrays and copy values over in both |
| 9099 // arrays |
| 9100 if (_size == _keys.length) { |
| 9101 int newArrayLength = _size * 2; |
| 9102 List<String> keys_new_array = new List<String>(newArrayLength); |
| 9103 List<ExecutableElement> values_new_array = |
| 9104 new List<ExecutableElement>(newArrayLength); |
| 9105 for (int i = 0; i < _size; i++) { |
| 9106 keys_new_array[i] = _keys[i]; |
| 9107 } |
| 9108 for (int i = 0; i < _size; i++) { |
| 9109 values_new_array[i] = _values[i]; |
| 9110 } |
| 9111 _keys = keys_new_array; |
| 9112 _values = values_new_array; |
| 9113 } |
| 9114 // Put new value at end of array |
| 9115 _keys[_size] = key; |
| 9116 _values[_size] = value; |
| 9117 _size++; |
| 9118 } |
| 9119 |
| 9120 /** |
| 9121 * Given some [String] key, this method replaces the associated key and value
pair with |
| 9122 * `null`. The size is not decremented with this call, instead it is expected
that the users |
| 9123 * check for `null`. |
| 9124 * |
| 9125 * @param key the key of the key/value pair to remove from the map |
| 9126 */ |
| 9127 void remove(String key) { |
| 9128 for (int i = 0; i < _size; i++) { |
| 9129 if (_keys[i] == key) { |
| 9130 _keys[i] = null; |
| 9131 _values[i] = null; |
| 9132 return; |
| 9133 } |
| 9134 } |
| 9135 } |
| 9136 |
| 9137 /** |
| 9138 * Sets the ExecutableElement at the specified location. |
| 9139 * |
| 9140 * @param i some non-zero value less than size |
| 9141 * @param value the ExecutableElement value to store in the map |
| 9142 */ |
| 9143 void setValue(int i, ExecutableElement value) { |
| 9144 _values[i] = value; |
| 9145 } |
| 9146 |
| 9147 /** |
| 9148 * Initializes [keys] and [values]. |
| 9149 */ |
| 9150 void _initArrays(int initialCapacity) { |
| 9151 _keys = new List<String>(initialCapacity); |
| 9152 _values = new List<ExecutableElement>(initialCapacity); |
| 9153 } |
| 9154 } |
| 9155 |
| 9156 /** |
| 9157 * Instances of the class `Namespace` implement a mapping of identifiers to the
elements |
| 9158 * represented by those identifiers. Namespaces are the building blocks for scop
es. |
| 9159 */ |
| 9160 class Namespace { |
| 9161 /** |
| 9162 * An empty namespace. |
| 9163 */ |
| 9164 static Namespace EMPTY = new Namespace(new HashMap<String, Element>()); |
| 9165 |
| 9166 /** |
| 9167 * A table mapping names that are defined in this namespace to the element rep
resenting the thing |
| 9168 * declared with that name. |
| 9169 */ |
| 9170 final HashMap<String, Element> _definedNames; |
| 9171 |
| 9172 /** |
| 9173 * Initialize a newly created namespace to have the given defined names. |
| 9174 * |
| 9175 * @param definedNames the mapping from names that are defined in this namespa
ce to the |
| 9176 * corresponding elements |
| 9177 */ |
| 9178 Namespace(this._definedNames); |
| 9179 |
| 9180 /** |
| 9181 * Return a table containing the same mappings as those defined by this namesp
ace. |
| 9182 * |
| 9183 * @return a table containing the same mappings as those defined by this names
pace |
| 9184 */ |
| 9185 Map<String, Element> get definedNames => |
| 9186 new HashMap<String, Element>.from(_definedNames); |
| 9187 |
| 9188 /** |
| 9189 * Return the element in this namespace that is available to the containing sc
ope using the given |
| 9190 * name. |
| 9191 * |
| 9192 * @param name the name used to reference the |
| 9193 * @return the element represented by the given identifier |
| 9194 */ |
| 9195 Element get(String name) => _definedNames[name]; |
| 9196 } |
| 9197 |
| 9198 /** |
| 9199 * Instances of the class `NamespaceBuilder` are used to build a `Namespace`. Na
mespace |
| 9200 * builders are thread-safe and re-usable. |
| 9201 */ |
| 9202 class NamespaceBuilder { |
| 9203 /** |
| 9204 * Create a namespace representing the export namespace of the given [ExportEl
ement]. |
| 9205 * |
| 9206 * @param element the export element whose export namespace is to be created |
| 9207 * @return the export namespace that was created |
| 9208 */ |
| 9209 Namespace createExportNamespaceForDirective(ExportElement element) { |
| 9210 LibraryElement exportedLibrary = element.exportedLibrary; |
| 9211 if (exportedLibrary == null) { |
| 9212 // |
| 9213 // The exported library will be null if the URI does not reference a valid |
| 9214 // library. |
| 9215 // |
| 9216 return Namespace.EMPTY; |
| 9217 } |
| 9218 HashMap<String, Element> definedNames = |
| 9219 _createExportMapping(exportedLibrary, new HashSet<LibraryElement>()); |
| 9220 definedNames = _applyCombinators(definedNames, element.combinators); |
| 9221 return new Namespace(definedNames); |
| 9222 } |
| 9223 |
| 9224 /** |
| 9225 * Create a namespace representing the export namespace of the given library. |
| 9226 * |
| 9227 * @param library the library whose export namespace is to be created |
| 9228 * @return the export namespace that was created |
| 9229 */ |
| 9230 Namespace createExportNamespaceForLibrary(LibraryElement library) => |
| 9231 new Namespace( |
| 9232 _createExportMapping(library, new HashSet<LibraryElement>())); |
| 9233 |
| 9234 /** |
| 9235 * Create a namespace representing the import namespace of the given library. |
| 9236 * |
| 9237 * @param library the library whose import namespace is to be created |
| 9238 * @return the import namespace that was created |
| 9239 */ |
| 9240 Namespace createImportNamespaceForDirective(ImportElement element) { |
| 9241 LibraryElement importedLibrary = element.importedLibrary; |
| 9242 if (importedLibrary == null) { |
| 9243 // |
| 9244 // The imported library will be null if the URI does not reference a valid |
| 9245 // library. |
| 9246 // |
| 9247 return Namespace.EMPTY; |
| 9248 } |
| 9249 HashMap<String, Element> definedNames = |
| 9250 _createExportMapping(importedLibrary, new HashSet<LibraryElement>()); |
| 9251 definedNames = _applyCombinators(definedNames, element.combinators); |
| 9252 definedNames = _applyPrefix(definedNames, element.prefix); |
| 9253 return new Namespace(definedNames); |
| 9254 } |
| 9255 |
| 9256 /** |
| 9257 * Create a namespace representing the public namespace of the given library. |
| 9258 * |
| 9259 * @param library the library whose public namespace is to be created |
| 9260 * @return the public namespace that was created |
| 9261 */ |
| 9262 Namespace createPublicNamespaceForLibrary(LibraryElement library) { |
| 9263 HashMap<String, Element> definedNames = new HashMap<String, Element>(); |
| 9264 _addPublicNames(definedNames, library.definingCompilationUnit); |
| 9265 for (CompilationUnitElement compilationUnit in library.parts) { |
| 9266 _addPublicNames(definedNames, compilationUnit); |
| 9267 } |
| 9268 return new Namespace(definedNames); |
| 9269 } |
| 9270 |
| 9271 /** |
| 9272 * Add all of the names in the given namespace to the given mapping table. |
| 9273 * |
| 9274 * @param definedNames the mapping table to which the names in the given names
pace are to be added |
| 9275 * @param namespace the namespace containing the names to be added to this nam
espace |
| 9276 */ |
| 9277 void _addAllFromNamespace( |
| 9278 Map<String, Element> definedNames, Namespace namespace) { |
| 9279 if (namespace != null) { |
| 9280 definedNames.addAll(namespace.definedNames); |
| 9281 } |
| 9282 } |
| 9283 |
| 9284 /** |
| 9285 * Add the given element to the given mapping table if it has a publicly visib
le name. |
| 9286 * |
| 9287 * @param definedNames the mapping table to which the public name is to be add
ed |
| 9288 * @param element the element to be added |
| 9289 */ |
| 9290 void _addIfPublic(Map<String, Element> definedNames, Element element) { |
| 9291 String name = element.name; |
| 9292 if (name != null && !Scope.isPrivateName(name)) { |
| 9293 definedNames[name] = element; |
| 9294 } |
| 9295 } |
| 9296 |
| 9297 /** |
| 9298 * Add to the given mapping table all of the public top-level names that are d
efined in the given |
| 9299 * compilation unit. |
| 9300 * |
| 9301 * @param definedNames the mapping table to which the public names are to be a
dded |
| 9302 * @param compilationUnit the compilation unit defining the top-level names to
be added to this |
| 9303 * namespace |
| 9304 */ |
| 9305 void _addPublicNames(Map<String, Element> definedNames, |
| 9306 CompilationUnitElement compilationUnit) { |
| 9307 for (PropertyAccessorElement element in compilationUnit.accessors) { |
| 9308 _addIfPublic(definedNames, element); |
| 9309 } |
| 9310 for (ClassElement element in compilationUnit.enums) { |
| 9311 _addIfPublic(definedNames, element); |
| 9312 } |
| 9313 for (FunctionElement element in compilationUnit.functions) { |
| 9314 _addIfPublic(definedNames, element); |
| 9315 } |
| 9316 for (FunctionTypeAliasElement element |
| 9317 in compilationUnit.functionTypeAliases) { |
| 9318 _addIfPublic(definedNames, element); |
| 9319 } |
| 9320 for (ClassElement element in compilationUnit.types) { |
| 9321 _addIfPublic(definedNames, element); |
| 9322 } |
| 9323 } |
| 9324 |
| 9325 /** |
| 9326 * Apply the given combinators to all of the names in the given mapping table. |
| 9327 * |
| 9328 * @param definedNames the mapping table to which the namespace operations are
to be applied |
| 9329 * @param combinators the combinators to be applied |
| 9330 */ |
| 9331 HashMap<String, Element> _applyCombinators( |
| 9332 HashMap<String, Element> definedNames, |
| 9333 List<NamespaceCombinator> combinators) { |
| 9334 for (NamespaceCombinator combinator in combinators) { |
| 9335 if (combinator is HideElementCombinator) { |
| 9336 _hide(definedNames, combinator.hiddenNames); |
| 9337 } else if (combinator is ShowElementCombinator) { |
| 9338 definedNames = _show(definedNames, combinator.shownNames); |
| 9339 } else { |
| 9340 // Internal error. |
| 9341 AnalysisEngine.instance.logger |
| 9342 .logError("Unknown type of combinator: ${combinator.runtimeType}"); |
| 9343 } |
| 9344 } |
| 9345 return definedNames; |
| 9346 } |
| 9347 |
| 9348 /** |
| 9349 * Apply the given prefix to all of the names in the table of defined names. |
| 9350 * |
| 9351 * @param definedNames the names that were defined before this operation |
| 9352 * @param prefixElement the element defining the prefix to be added to the nam
es |
| 9353 */ |
| 9354 HashMap<String, Element> _applyPrefix( |
| 9355 HashMap<String, Element> definedNames, PrefixElement prefixElement) { |
| 9356 if (prefixElement != null) { |
| 9357 String prefix = prefixElement.name; |
| 9358 HashMap<String, Element> newNames = new HashMap<String, Element>(); |
| 9359 definedNames.forEach((String name, Element element) { |
| 9360 newNames["$prefix.$name"] = element; |
| 9361 }); |
| 9362 return newNames; |
| 9363 } else { |
| 9364 return definedNames; |
| 9365 } |
| 9366 } |
| 9367 |
| 9368 /** |
| 9369 * Create a mapping table representing the export namespace of the given libra
ry. |
| 9370 * |
| 9371 * @param library the library whose public namespace is to be created |
| 9372 * @param visitedElements a set of libraries that do not need to be visited wh
en processing the |
| 9373 * export directives of the given library because all of the names de
fined by them will |
| 9374 * be added by another library |
| 9375 * @return the mapping table that was created |
| 9376 */ |
| 9377 HashMap<String, Element> _createExportMapping( |
| 9378 LibraryElement library, HashSet<LibraryElement> visitedElements) { |
| 9379 // Check if the export namespace has been already computed. |
| 9380 { |
| 9381 Namespace exportNamespace = library.exportNamespace; |
| 9382 if (exportNamespace != null) { |
| 9383 return exportNamespace.definedNames; |
| 9384 } |
| 9385 } |
| 9386 // TODO(scheglov) Remove this after switching to the new task model. |
| 9387 visitedElements.add(library); |
| 9388 try { |
| 9389 HashMap<String, Element> definedNames = new HashMap<String, Element>(); |
| 9390 for (ExportElement element in library.exports) { |
| 9391 LibraryElement exportedLibrary = element.exportedLibrary; |
| 9392 if (exportedLibrary != null && |
| 9393 !visitedElements.contains(exportedLibrary)) { |
| 9394 // |
| 9395 // The exported library will be null if the URI does not reference a |
| 9396 // valid library. |
| 9397 // |
| 9398 HashMap<String, Element> exportedNames = |
| 9399 _createExportMapping(exportedLibrary, visitedElements); |
| 9400 exportedNames = _applyCombinators(exportedNames, element.combinators); |
| 9401 definedNames.addAll(exportedNames); |
| 9402 } |
| 9403 } |
| 9404 _addAllFromNamespace( |
| 9405 definedNames, |
| 9406 (library.context as InternalAnalysisContext) |
| 9407 .getPublicNamespace(library)); |
| 9408 return definedNames; |
| 9409 } finally { |
| 9410 visitedElements.remove(library); |
| 9411 } |
| 9412 } |
| 9413 |
| 9414 /** |
| 9415 * Hide all of the given names by removing them from the given collection of d
efined names. |
| 9416 * |
| 9417 * @param definedNames the names that were defined before this operation |
| 9418 * @param hiddenNames the names to be hidden |
| 9419 */ |
| 9420 void _hide(HashMap<String, Element> definedNames, List<String> hiddenNames) { |
| 9421 for (String name in hiddenNames) { |
| 9422 definedNames.remove(name); |
| 9423 definedNames.remove("$name="); |
| 9424 } |
| 9425 } |
| 9426 |
| 9427 /** |
| 9428 * Show only the given names by removing all other names from the given collec
tion of defined |
| 9429 * names. |
| 9430 * |
| 9431 * @param definedNames the names that were defined before this operation |
| 9432 * @param shownNames the names to be shown |
| 9433 */ |
| 9434 HashMap<String, Element> _show( |
| 9435 HashMap<String, Element> definedNames, List<String> shownNames) { |
| 9436 HashMap<String, Element> newNames = new HashMap<String, Element>(); |
| 9437 for (String name in shownNames) { |
| 9438 Element element = definedNames[name]; |
| 9439 if (element != null) { |
| 9440 newNames[name] = element; |
| 9441 } |
| 9442 String setterName = "$name="; |
| 9443 element = definedNames[setterName]; |
| 9444 if (element != null) { |
| 9445 newNames[setterName] = element; |
| 9446 } |
| 9447 } |
| 9448 return newNames; |
| 9449 } |
| 9450 } |
| 9451 |
| 9452 /** |
| 9453 * Instances of the class `OverrideVerifier` visit all of the declarations in a
compilation |
| 9454 * unit to verify that if they have an override annotation it is being used corr
ectly. |
| 9455 */ |
| 9456 class OverrideVerifier extends RecursiveAstVisitor<Object> { |
| 9457 /** |
| 9458 * The error reporter used to report errors. |
| 9459 */ |
| 9460 final ErrorReporter _errorReporter; |
| 9461 |
| 9462 /** |
| 9463 * The inheritance manager used to find overridden methods. |
| 9464 */ |
| 9465 final InheritanceManager _manager; |
| 9466 |
| 9467 /** |
| 9468 * Initialize a newly created verifier to look for inappropriate uses of the o
verride annotation. |
| 9469 * |
| 9470 * @param errorReporter the error reporter used to report errors |
| 9471 * @param manager the inheritance manager used to find overridden methods |
| 9472 */ |
| 9473 OverrideVerifier(this._errorReporter, this._manager); |
| 9474 |
| 9475 @override |
| 9476 Object visitMethodDeclaration(MethodDeclaration node) { |
| 9477 ExecutableElement element = node.element; |
| 9478 if (_isOverride(element)) { |
| 9479 if (_getOverriddenMember(element) == null) { |
| 9480 if (element is MethodElement) { |
| 9481 _errorReporter.reportErrorForNode( |
| 9482 HintCode.OVERRIDE_ON_NON_OVERRIDING_METHOD, node.name); |
| 9483 } else if (element is PropertyAccessorElement) { |
| 9484 if (element.isGetter) { |
| 9485 _errorReporter.reportErrorForNode( |
| 9486 HintCode.OVERRIDE_ON_NON_OVERRIDING_GETTER, node.name); |
| 9487 } else { |
| 9488 _errorReporter.reportErrorForNode( |
| 9489 HintCode.OVERRIDE_ON_NON_OVERRIDING_SETTER, node.name); |
| 9490 } |
| 9491 } |
| 9492 } |
| 9493 } |
| 9494 return super.visitMethodDeclaration(node); |
| 9495 } |
| 9496 |
| 9497 /** |
| 9498 * Return the member that overrides the given member. |
| 9499 * |
| 9500 * @param member the member that overrides the returned member |
| 9501 * @return the member that overrides the given member |
| 9502 */ |
| 9503 ExecutableElement _getOverriddenMember(ExecutableElement member) { |
| 9504 LibraryElement library = member.library; |
| 9505 if (library == null) { |
| 9506 return null; |
| 9507 } |
| 9508 ClassElement classElement = |
| 9509 member.getAncestor((element) => element is ClassElement); |
| 9510 if (classElement == null) { |
| 9511 return null; |
| 9512 } |
| 9513 return _manager.lookupInheritance(classElement, member.name); |
| 9514 } |
| 9515 |
| 9516 /** |
| 9517 * Return `true` if the given element has an override annotation associated wi
th it. |
| 9518 * |
| 9519 * @param element the element being tested |
| 9520 * @return `true` if the element has an override annotation associated with it |
| 9521 */ |
| 9522 bool _isOverride(Element element) => element != null && element.isOverride; |
| 9523 } |
| 9524 |
| 9525 /** |
| 9526 * Instances of the class `PubVerifier` traverse an AST structure looking for de
viations from |
| 9527 * pub best practices. |
| 9528 */ |
| 9529 class PubVerifier extends RecursiveAstVisitor<Object> { |
| 9530 // static String _PUBSPEC_YAML = "pubspec.yaml"; |
| 9531 |
| 9532 /** |
| 9533 * The analysis context containing the sources to be analyzed |
| 9534 */ |
| 9535 final AnalysisContext _context; |
| 9536 |
| 9537 /** |
| 9538 * The error reporter by which errors will be reported. |
| 9539 */ |
| 9540 final ErrorReporter _errorReporter; |
| 9541 |
| 9542 PubVerifier(this._context, this._errorReporter); |
| 9543 |
| 9544 @override |
| 9545 Object visitImportDirective(ImportDirective directive) { |
| 9546 return null; |
| 9547 } |
| 9548 |
| 9549 // /** |
| 9550 // * This verifies that the passed file import directive is not contained in a
source inside a |
| 9551 // * package "lib" directory hierarchy referencing a source outside that packa
ge "lib" directory |
| 9552 // * hierarchy. |
| 9553 // * |
| 9554 // * @param uriLiteral the import URL (not `null`) |
| 9555 // * @param path the file path being verified (not `null`) |
| 9556 // * @return `true` if and only if an error code is generated on the passed no
de |
| 9557 // * See [PubSuggestionCode.FILE_IMPORT_INSIDE_LIB_REFERENCES_FILE_OUTSIDE]. |
| 9558 // */ |
| 9559 // bool |
| 9560 // _checkForFileImportInsideLibReferencesFileOutside(StringLiteral uriLiter
al, |
| 9561 // String path) { |
| 9562 // Source source = _getSource(uriLiteral); |
| 9563 // String fullName = _getSourceFullName(source); |
| 9564 // if (fullName != null) { |
| 9565 // int pathIndex = 0; |
| 9566 // int fullNameIndex = fullName.length; |
| 9567 // while (pathIndex < path.length && |
| 9568 // StringUtilities.startsWith3(path, pathIndex, 0x2E, 0x2E, 0x2F)) { |
| 9569 // fullNameIndex = JavaString.lastIndexOf(fullName, '/', fullNameIndex); |
| 9570 // if (fullNameIndex < 4) { |
| 9571 // return false; |
| 9572 // } |
| 9573 // // Check for "/lib" at a specified place in the fullName |
| 9574 // if (StringUtilities.startsWith4( |
| 9575 // fullName, |
| 9576 // fullNameIndex - 4, |
| 9577 // 0x2F, |
| 9578 // 0x6C, |
| 9579 // 0x69, |
| 9580 // 0x62)) { |
| 9581 // String relativePubspecPath = |
| 9582 // path.substring(0, pathIndex + 3) + |
| 9583 // _PUBSPEC_YAML; |
| 9584 // Source pubspecSource = |
| 9585 // _context.sourceFactory.resolveUri(source, relativePubspecPath); |
| 9586 // if (_context.exists(pubspecSource)) { |
| 9587 // // Files inside the lib directory hierarchy should not reference |
| 9588 // // files outside |
| 9589 // _errorReporter.reportErrorForNode( |
| 9590 // HintCode.FILE_IMPORT_INSIDE_LIB_REFERENCES_FILE_OUTSIDE, |
| 9591 // uriLiteral); |
| 9592 // } |
| 9593 // return true; |
| 9594 // } |
| 9595 // pathIndex += 3; |
| 9596 // } |
| 9597 // } |
| 9598 // return false; |
| 9599 // } |
| 9600 |
| 9601 // /** |
| 9602 // * This verifies that the passed file import directive is not contained in a
source outside a |
| 9603 // * package "lib" directory hierarchy referencing a source inside that packag
e "lib" directory |
| 9604 // * hierarchy. |
| 9605 // * |
| 9606 // * @param uriLiteral the import URL (not `null`) |
| 9607 // * @param path the file path being verified (not `null`) |
| 9608 // * @return `true` if and only if an error code is generated on the passed no
de |
| 9609 // * See [PubSuggestionCode.FILE_IMPORT_OUTSIDE_LIB_REFERENCES_FILE_INSIDE]. |
| 9610 // */ |
| 9611 // bool |
| 9612 // _checkForFileImportOutsideLibReferencesFileInside(StringLiteral uriLiter
al, |
| 9613 // String path) { |
| 9614 // if (StringUtilities.startsWith4(path, 0, 0x6C, 0x69, 0x62, 0x2F)) { |
| 9615 // if (_checkForFileImportOutsideLibReferencesFileInsideAtIndex( |
| 9616 // uriLiteral, |
| 9617 // path, |
| 9618 // 0)) { |
| 9619 // return true; |
| 9620 // } |
| 9621 // } |
| 9622 // int pathIndex = |
| 9623 // StringUtilities.indexOf5(path, 0, 0x2F, 0x6C, 0x69, 0x62, 0x2F); |
| 9624 // while (pathIndex != -1) { |
| 9625 // if (_checkForFileImportOutsideLibReferencesFileInsideAtIndex( |
| 9626 // uriLiteral, |
| 9627 // path, |
| 9628 // pathIndex + 1)) { |
| 9629 // return true; |
| 9630 // } |
| 9631 // pathIndex = |
| 9632 // StringUtilities.indexOf5(path, pathIndex + 4, 0x2F, 0x6C, 0x69, 0x62
, 0x2F); |
| 9633 // } |
| 9634 // return false; |
| 9635 // } |
| 9636 |
| 9637 // bool |
| 9638 // _checkForFileImportOutsideLibReferencesFileInsideAtIndex(StringLiteral u
riLiteral, |
| 9639 // String path, int pathIndex) { |
| 9640 // Source source = _getSource(uriLiteral); |
| 9641 // String relativePubspecPath = path.substring(0, pathIndex) + _PUBSPEC_YAML; |
| 9642 // Source pubspecSource = |
| 9643 // _context.sourceFactory.resolveUri(source, relativePubspecPath); |
| 9644 // if (!_context.exists(pubspecSource)) { |
| 9645 // return false; |
| 9646 // } |
| 9647 // String fullName = _getSourceFullName(source); |
| 9648 // if (fullName != null) { |
| 9649 // if (StringUtilities.indexOf5(fullName, 0, 0x2F, 0x6C, 0x69, 0x62, 0x2F)
< |
| 9650 // 0) { |
| 9651 // // Files outside the lib directory hierarchy should not reference file
s |
| 9652 // // inside ... use package: url instead |
| 9653 // _errorReporter.reportErrorForNode( |
| 9654 // HintCode.FILE_IMPORT_OUTSIDE_LIB_REFERENCES_FILE_INSIDE, |
| 9655 // uriLiteral); |
| 9656 // return true; |
| 9657 // } |
| 9658 // } |
| 9659 // return false; |
| 9660 // } |
| 9661 |
| 9662 // /** |
| 9663 // * This verifies that the passed package import directive does not contain "
.." |
| 9664 // * |
| 9665 // * @param uriLiteral the import URL (not `null`) |
| 9666 // * @param path the path to be validated (not `null`) |
| 9667 // * @return `true` if and only if an error code is generated on the passed no
de |
| 9668 // * See [PubSuggestionCode.PACKAGE_IMPORT_CONTAINS_DOT_DOT]. |
| 9669 // */ |
| 9670 // bool _checkForPackageImportContainsDotDot(StringLiteral uriLiteral, |
| 9671 // String path) { |
| 9672 // if (StringUtilities.startsWith3(path, 0, 0x2E, 0x2E, 0x2F) || |
| 9673 // StringUtilities.indexOf4(path, 0, 0x2F, 0x2E, 0x2E, 0x2F) >= 0) { |
| 9674 // // Package import should not to contain ".." |
| 9675 // _errorReporter.reportErrorForNode( |
| 9676 // HintCode.PACKAGE_IMPORT_CONTAINS_DOT_DOT, |
| 9677 // uriLiteral); |
| 9678 // return true; |
| 9679 // } |
| 9680 // return false; |
| 9681 // } |
| 9682 |
| 9683 // /** |
| 9684 // * Answer the source associated with the compilation unit containing the giv
en AST node. |
| 9685 // * |
| 9686 // * @param node the node (not `null`) |
| 9687 // * @return the source or `null` if it could not be determined |
| 9688 // */ |
| 9689 // Source _getSource(AstNode node) { |
| 9690 // Source source = null; |
| 9691 // CompilationUnit unit = node.getAncestor((node) => node is CompilationUnit)
; |
| 9692 // if (unit != null) { |
| 9693 // CompilationUnitElement element = unit.element; |
| 9694 // if (element != null) { |
| 9695 // source = element.source; |
| 9696 // } |
| 9697 // } |
| 9698 // return source; |
| 9699 // } |
| 9700 |
| 9701 // /** |
| 9702 // * Answer the full name of the given source. The returned value will have al
l |
| 9703 // * [File.separatorChar] replace by '/'. |
| 9704 // * |
| 9705 // * @param source the source |
| 9706 // * @return the full name or `null` if it could not be determined |
| 9707 // */ |
| 9708 // String _getSourceFullName(Source source) { |
| 9709 // if (source != null) { |
| 9710 // String fullName = source.fullName; |
| 9711 // if (fullName != null) { |
| 9712 // return fullName.replaceAll(r'\', '/'); |
| 9713 // } |
| 9714 // } |
| 9715 // return null; |
| 9716 // } |
| 9717 } |
| 9718 |
| 9719 /** |
| 9720 * Kind of the redirecting constructor. |
| 9721 */ |
| 9722 class RedirectingConstructorKind extends Enum<RedirectingConstructorKind> { |
| 9723 static const RedirectingConstructorKind CONST = |
| 9724 const RedirectingConstructorKind('CONST', 0); |
| 9725 |
| 9726 static const RedirectingConstructorKind NORMAL = |
| 9727 const RedirectingConstructorKind('NORMAL', 1); |
| 9728 |
| 9729 static const List<RedirectingConstructorKind> values = const [CONST, NORMAL]; |
| 9730 |
| 9731 const RedirectingConstructorKind(String name, int ordinal) |
| 9732 : super(name, ordinal); |
| 9733 } |
| 9734 |
| 9735 /** |
| 9736 * A `ResolvableLibrary` represents a single library during the resolution of |
| 9737 * some (possibly different) library. They are not intended to be used except |
| 9738 * during the resolution process. |
| 9739 */ |
| 9740 class ResolvableLibrary { |
| 9741 /** |
| 9742 * An empty array that can be used to initialize lists of libraries. |
| 9743 */ |
| 9744 static List<ResolvableLibrary> _EMPTY_ARRAY = new List<ResolvableLibrary>(0); |
| 9745 |
| 9746 /** |
| 9747 * The next artificial hash code. |
| 9748 */ |
| 9749 static int _NEXT_HASH_CODE = 0; |
| 9750 |
| 9751 /** |
| 9752 * The artifitial hash code for this object. |
| 9753 */ |
| 9754 final int _hashCode = _nextHashCode(); |
| 9755 |
| 9756 /** |
| 9757 * The source specifying the defining compilation unit of this library. |
| 9758 */ |
| 9759 final Source librarySource; |
| 9760 |
| 9761 /** |
| 9762 * A list containing all of the libraries that are imported into this library. |
| 9763 */ |
| 9764 List<ResolvableLibrary> _importedLibraries = _EMPTY_ARRAY; |
| 9765 |
| 9766 /** |
| 9767 * A flag indicating whether this library explicitly imports core. |
| 9768 */ |
| 9769 bool explicitlyImportsCore = false; |
| 9770 |
| 9771 /** |
| 9772 * An array containing all of the libraries that are exported from this librar
y. |
| 9773 */ |
| 9774 List<ResolvableLibrary> _exportedLibraries = _EMPTY_ARRAY; |
| 9775 |
| 9776 /** |
| 9777 * An array containing the compilation units that comprise this library. The |
| 9778 * defining compilation unit is always first. |
| 9779 */ |
| 9780 List<ResolvableCompilationUnit> _compilationUnits; |
| 9781 |
| 9782 /** |
| 9783 * The library element representing this library. |
| 9784 */ |
| 9785 LibraryElementImpl _libraryElement; |
| 9786 |
| 9787 /** |
| 9788 * The listener to which analysis errors will be reported. |
| 9789 */ |
| 9790 AnalysisErrorListener _errorListener; |
| 9791 |
| 9792 /** |
| 9793 * The inheritance manager which is used for member lookups in this library. |
| 9794 */ |
| 9795 InheritanceManager _inheritanceManager; |
| 9796 |
| 9797 /** |
| 9798 * The library scope used when resolving elements within this library's compil
ation units. |
| 9799 */ |
| 9800 LibraryScope _libraryScope; |
| 9801 |
| 9802 /** |
| 9803 * Initialize a newly created data holder that can maintain the data associate
d with a library. |
| 9804 * |
| 9805 * @param librarySource the source specifying the defining compilation unit of
this library |
| 9806 * @param errorListener the listener to which analysis errors will be reported |
| 9807 */ |
| 9808 ResolvableLibrary(this.librarySource); |
| 9809 |
| 9810 /** |
| 9811 * Return an array of the [CompilationUnit]s that make up the library. The fir
st unit is |
| 9812 * always the defining unit. |
| 9813 * |
| 9814 * @return an array of the [CompilationUnit]s that make up the library. The fi
rst unit is |
| 9815 * always the defining unit |
| 9816 */ |
| 9817 List<CompilationUnit> get compilationUnits { |
| 9818 int count = _compilationUnits.length; |
| 9819 List<CompilationUnit> units = new List<CompilationUnit>(count); |
| 9820 for (int i = 0; i < count; i++) { |
| 9821 units[i] = _compilationUnits[i].compilationUnit; |
| 9822 } |
| 9823 return units; |
| 9824 } |
| 9825 |
| 9826 /** |
| 9827 * Return an array containing the sources for the compilation units in this li
brary, including the |
| 9828 * defining compilation unit. |
| 9829 * |
| 9830 * @return the sources for the compilation units in this library |
| 9831 */ |
| 9832 List<Source> get compilationUnitSources { |
| 9833 int count = _compilationUnits.length; |
| 9834 List<Source> sources = new List<Source>(count); |
| 9835 for (int i = 0; i < count; i++) { |
| 9836 sources[i] = _compilationUnits[i].source; |
| 9837 } |
| 9838 return sources; |
| 9839 } |
| 9840 |
| 9841 /** |
| 9842 * Return the AST structure associated with the defining compilation unit for
this library. |
| 9843 * |
| 9844 * @return the AST structure associated with the defining compilation unit for
this library |
| 9845 * @throws AnalysisException if an AST structure could not be created for the
defining compilation |
| 9846 * unit |
| 9847 */ |
| 9848 CompilationUnit get definingCompilationUnit => |
| 9849 _compilationUnits[0].compilationUnit; |
| 9850 |
| 9851 /** |
| 9852 * Set the listener to which analysis errors will be reported to be the given
listener. |
| 9853 * |
| 9854 * @param errorListener the listener to which analysis errors will be reported |
| 9855 */ |
| 9856 void set errorListener(AnalysisErrorListener errorListener) { |
| 9857 this._errorListener = errorListener; |
| 9858 } |
| 9859 |
| 9860 /** |
| 9861 * Set the libraries that are exported by this library to be those in the give
n array. |
| 9862 * |
| 9863 * @param exportedLibraries the libraries that are exported by this library |
| 9864 */ |
| 9865 void set exportedLibraries(List<ResolvableLibrary> exportedLibraries) { |
| 9866 this._exportedLibraries = exportedLibraries; |
| 9867 } |
| 9868 |
| 9869 /** |
| 9870 * Return an array containing the libraries that are exported from this librar
y. |
| 9871 * |
| 9872 * @return an array containing the libraries that are exported from this libra
ry |
| 9873 */ |
| 9874 List<ResolvableLibrary> get exports => _exportedLibraries; |
| 9875 |
| 9876 @override |
| 9877 int get hashCode => _hashCode; |
| 9878 |
| 9879 /** |
| 9880 * Set the libraries that are imported into this library to be those in the gi
ven array. |
| 9881 * |
| 9882 * @param importedLibraries the libraries that are imported into this library |
| 9883 */ |
| 9884 void set importedLibraries(List<ResolvableLibrary> importedLibraries) { |
| 9885 this._importedLibraries = importedLibraries; |
| 9886 } |
| 9887 |
| 9888 /** |
| 9889 * Return an array containing the libraries that are imported into this librar
y. |
| 9890 * |
| 9891 * @return an array containing the libraries that are imported into this libra
ry |
| 9892 */ |
| 9893 List<ResolvableLibrary> get imports => _importedLibraries; |
| 9894 |
| 9895 /** |
| 9896 * Return an array containing the libraries that are either imported or export
ed from this |
| 9897 * library. |
| 9898 * |
| 9899 * @return the libraries that are either imported or exported from this librar
y |
| 9900 */ |
| 9901 List<ResolvableLibrary> get importsAndExports { |
| 9902 HashSet<ResolvableLibrary> libraries = new HashSet<ResolvableLibrary>(); |
| 9903 for (ResolvableLibrary library in _importedLibraries) { |
| 9904 libraries.add(library); |
| 9905 } |
| 9906 for (ResolvableLibrary library in _exportedLibraries) { |
| 9907 libraries.add(library); |
| 9908 } |
| 9909 return new List.from(libraries); |
| 9910 } |
| 9911 |
| 9912 /** |
| 9913 * Return the inheritance manager for this library. |
| 9914 * |
| 9915 * @return the inheritance manager for this library |
| 9916 */ |
| 9917 InheritanceManager get inheritanceManager { |
| 9918 if (_inheritanceManager == null) { |
| 9919 return _inheritanceManager = new InheritanceManager(_libraryElement); |
| 9920 } |
| 9921 return _inheritanceManager; |
| 9922 } |
| 9923 |
| 9924 /** |
| 9925 * Return the library element representing this library, creating it if necess
ary. |
| 9926 * |
| 9927 * @return the library element representing this library |
| 9928 */ |
| 9929 LibraryElementImpl get libraryElement => _libraryElement; |
| 9930 |
| 9931 /** |
| 9932 * Set the library element representing this library to the given library elem
ent. |
| 9933 * |
| 9934 * @param libraryElement the library element representing this library |
| 9935 */ |
| 9936 void set libraryElement(LibraryElementImpl libraryElement) { |
| 9937 this._libraryElement = libraryElement; |
| 9938 if (_inheritanceManager != null) { |
| 9939 _inheritanceManager.libraryElement = libraryElement; |
| 9940 } |
| 9941 } |
| 9942 |
| 9943 /** |
| 9944 * Return the library scope used when resolving elements within this library's
compilation units. |
| 9945 * |
| 9946 * @return the library scope used when resolving elements within this library'
s compilation units |
| 9947 */ |
| 9948 LibraryScope get libraryScope { |
| 9949 if (_libraryScope == null) { |
| 9950 _libraryScope = new LibraryScope(_libraryElement, _errorListener); |
| 9951 } |
| 9952 return _libraryScope; |
| 9953 } |
| 9954 |
| 9955 /** |
| 9956 * Return an array containing the compilation units that comprise this library
. The defining |
| 9957 * compilation unit is always first. |
| 9958 * |
| 9959 * @return the compilation units that comprise this library |
| 9960 */ |
| 9961 List<ResolvableCompilationUnit> get resolvableCompilationUnits => |
| 9962 _compilationUnits; |
| 9963 |
| 9964 /** |
| 9965 * Set the compilation unit in this library to the given compilation units. Th
e defining |
| 9966 * compilation unit must be the first element of the array. |
| 9967 * |
| 9968 * @param units the compilation units in this library |
| 9969 */ |
| 9970 void set resolvableCompilationUnits(List<ResolvableCompilationUnit> units) { |
| 9971 _compilationUnits = units; |
| 9972 } |
| 9973 |
| 9974 /** |
| 9975 * Return the AST structure associated with the given source, or `null` if the
source does |
| 9976 * not represent a compilation unit that is included in this library. |
| 9977 * |
| 9978 * @param source the source representing the compilation unit whose AST is to
be returned |
| 9979 * @return the AST structure associated with the given source |
| 9980 * @throws AnalysisException if an AST structure could not be created for the
compilation unit |
| 9981 */ |
| 9982 CompilationUnit getAST(Source source) { |
| 9983 int count = _compilationUnits.length; |
| 9984 for (int i = 0; i < count; i++) { |
| 9985 if (_compilationUnits[i].source == source) { |
| 9986 return _compilationUnits[i].compilationUnit; |
| 9987 } |
| 9988 } |
| 9989 return null; |
| 9990 } |
| 9991 |
| 9992 @override |
| 9993 String toString() => librarySource.shortName; |
| 9994 |
| 9995 static int _nextHashCode() { |
| 9996 int next = (_NEXT_HASH_CODE + 1) & 0xFFFFFF; |
| 9997 _NEXT_HASH_CODE = next; |
| 9998 return next; |
| 9999 } |
| 10000 } |
| 10001 |
| 10002 /** |
| 10003 * The enumeration `ResolverErrorCode` defines the error codes used for errors |
| 10004 * detected by the resolver. The convention for this class is for the name of |
| 10005 * the error code to indicate the problem that caused the error to be generated |
| 10006 * and for the error message to explain what is wrong and, when appropriate, how |
| 10007 * the problem can be corrected. |
| 10008 */ |
| 10009 class ResolverErrorCode extends ErrorCode { |
| 10010 static const ResolverErrorCode BREAK_LABEL_ON_SWITCH_MEMBER = |
| 10011 const ResolverErrorCode('BREAK_LABEL_ON_SWITCH_MEMBER', |
| 10012 "Break label resolves to case or default statement"); |
| 10013 |
| 10014 static const ResolverErrorCode CONTINUE_LABEL_ON_SWITCH = |
| 10015 const ResolverErrorCode('CONTINUE_LABEL_ON_SWITCH', |
| 10016 "A continue label resolves to switch, must be loop or switch member"); |
| 10017 |
| 10018 static const ResolverErrorCode MISSING_LIBRARY_DIRECTIVE_WITH_PART = |
| 10019 const ResolverErrorCode('MISSING_LIBRARY_DIRECTIVE_WITH_PART', |
| 10020 "Libraries that have parts must have a library directive"); |
| 10021 |
| 10022 /** |
| 10023 * Initialize a newly created error code to have the given [name]. The message |
| 10024 * associated with the error will be created from the given [message] |
| 10025 * template. The correction associated with the error will be created from the |
| 10026 * given [correction] template. |
| 10027 */ |
| 10028 const ResolverErrorCode(String name, String message, [String correction]) |
| 10029 : super(name, message, correction); |
| 10030 |
| 10031 @override |
| 10032 ErrorSeverity get errorSeverity => type.severity; |
| 10033 |
| 10034 @override |
| 10035 ErrorType get type => ErrorType.COMPILE_TIME_ERROR; |
| 10036 } |
| 10037 |
| 10038 /** |
| 10039 * Instances of the class `ResolverVisitor` are used to resolve the nodes within
a single |
| 10040 * compilation unit. |
| 10041 */ |
| 10042 class ResolverVisitor extends ScopedVisitor { |
| 10043 /** |
| 10044 * The manager for the inheritance mappings. |
| 10045 */ |
| 10046 InheritanceManager _inheritanceManager; |
| 10047 |
| 10048 /** |
| 10049 * The object used to resolve the element associated with the current node. |
| 10050 */ |
| 10051 ElementResolver elementResolver; |
| 10052 |
| 10053 /** |
| 10054 * The object used to compute the type associated with the current node. |
| 10055 */ |
| 10056 StaticTypeAnalyzer typeAnalyzer; |
| 10057 |
| 10058 /** |
| 10059 * The class element representing the class containing the current node, |
| 10060 * or `null` if the current node is not contained in a class. |
| 10061 */ |
| 10062 ClassElement enclosingClass = null; |
| 10063 |
| 10064 /** |
| 10065 * The class declaration representing the class containing the current node, o
r `null` if |
| 10066 * the current node is not contained in a class. |
| 10067 */ |
| 10068 ClassDeclaration _enclosingClassDeclaration = null; |
| 10069 |
| 10070 /** |
| 10071 * The function type alias representing the function type containing the curre
nt node, or |
| 10072 * `null` if the current node is not contained in a function type alias. |
| 10073 */ |
| 10074 FunctionTypeAlias _enclosingFunctionTypeAlias = null; |
| 10075 |
| 10076 /** |
| 10077 * The element representing the function containing the current node, or `null
` if the |
| 10078 * current node is not contained in a function. |
| 10079 */ |
| 10080 ExecutableElement _enclosingFunction = null; |
| 10081 |
| 10082 /** |
| 10083 * The [Comment] before a [FunctionDeclaration] or a [MethodDeclaration] that |
| 10084 * cannot be resolved where we visited it, because it should be resolved in th
e scope of the body. |
| 10085 */ |
| 10086 Comment _commentBeforeFunction = null; |
| 10087 |
| 10088 /** |
| 10089 * The object keeping track of which elements have had their types overridden. |
| 10090 */ |
| 10091 TypeOverrideManager _overrideManager = new TypeOverrideManager(); |
| 10092 |
| 10093 /** |
| 10094 * The object keeping track of which elements have had their types promoted. |
| 10095 */ |
| 10096 TypePromotionManager _promoteManager = new TypePromotionManager(); |
| 10097 |
| 10098 /** |
| 10099 * A comment before a function should be resolved in the context of the |
| 10100 * function. But when we incrementally resolve a comment, we don't want to |
| 10101 * resolve the whole function. |
| 10102 * |
| 10103 * So, this flag is set to `true`, when just context of the function should |
| 10104 * be built and the comment resolved. |
| 10105 */ |
| 10106 bool resolveOnlyCommentInFunctionBody = false; |
| 10107 |
| 10108 /** |
| 10109 * Initialize a newly created visitor to resolve the nodes in an AST node. |
| 10110 * |
| 10111 * [definingLibrary] is the element for the library containing the node being |
| 10112 * visited. |
| 10113 * [source] is the source representing the compilation unit containing the |
| 10114 * node being visited. |
| 10115 * [typeProvider] the object used to access the types from the core library. |
| 10116 * [errorListener] the error listener that will be informed of any errors |
| 10117 * that are found during resolution. |
| 10118 * [nameScope] is the scope used to resolve identifiers in the node that will |
| 10119 * first be visited. If `null` or unspecified, a new [LibraryScope] will be |
| 10120 * created based on [definingLibrary] and [typeProvider]. |
| 10121 * [inheritanceManager] is used to perform inheritance lookups. If `null` or |
| 10122 * unspecified, a new [InheritanceManager] will be created based on |
| 10123 * [definingLibrary]. |
| 10124 * [typeAnalyzerFactory] is used to create the type analyzer. If `null` or |
| 10125 * unspecified, a type analyzer of type [StaticTypeAnalyzer] will be created. |
| 10126 */ |
| 10127 ResolverVisitor(LibraryElement definingLibrary, Source source, |
| 10128 TypeProvider typeProvider, AnalysisErrorListener errorListener, |
| 10129 {Scope nameScope, |
| 10130 InheritanceManager inheritanceManager, |
| 10131 StaticTypeAnalyzerFactory typeAnalyzerFactory}) |
| 10132 : super(definingLibrary, source, typeProvider, errorListener, |
| 10133 nameScope: nameScope) { |
| 10134 if (inheritanceManager == null) { |
| 10135 this._inheritanceManager = new InheritanceManager(definingLibrary); |
| 10136 } else { |
| 10137 this._inheritanceManager = inheritanceManager; |
| 10138 } |
| 10139 this.elementResolver = new ElementResolver(this); |
| 10140 if (typeAnalyzerFactory == null) { |
| 10141 this.typeAnalyzer = new StaticTypeAnalyzer(this); |
| 10142 } else { |
| 10143 this.typeAnalyzer = typeAnalyzerFactory(this); |
| 10144 } |
| 10145 } |
| 10146 |
| 10147 /** |
| 10148 * Initialize a newly created visitor to resolve the nodes in a compilation un
it. |
| 10149 * |
| 10150 * @param library the library containing the compilation unit being resolved |
| 10151 * @param source the source representing the compilation unit being visited |
| 10152 * @param typeProvider the object used to access the types from the core libra
ry |
| 10153 * |
| 10154 * Deprecated. Please use unnamed constructor instead. |
| 10155 */ |
| 10156 @deprecated |
| 10157 ResolverVisitor.con1( |
| 10158 Library library, Source source, TypeProvider typeProvider, |
| 10159 {StaticTypeAnalyzerFactory typeAnalyzerFactory}) |
| 10160 : this( |
| 10161 library.libraryElement, source, typeProvider, library.errorListener, |
| 10162 nameScope: library.libraryScope, |
| 10163 inheritanceManager: library.inheritanceManager, |
| 10164 typeAnalyzerFactory: typeAnalyzerFactory); |
| 10165 |
| 10166 /** |
| 10167 * Return the element representing the function containing the current node, o
r `null` if |
| 10168 * the current node is not contained in a function. |
| 10169 * |
| 10170 * @return the element representing the function containing the current node |
| 10171 */ |
| 10172 ExecutableElement get enclosingFunction => _enclosingFunction; |
| 10173 |
| 10174 /** |
| 10175 * Return the object keeping track of which elements have had their types over
ridden. |
| 10176 * |
| 10177 * @return the object keeping track of which elements have had their types ove
rridden |
| 10178 */ |
| 10179 TypeOverrideManager get overrideManager => _overrideManager; |
| 10180 |
| 10181 /** |
| 10182 * Return the object keeping track of which elements have had their types prom
oted. |
| 10183 * |
| 10184 * @return the object keeping track of which elements have had their types pro
moted |
| 10185 */ |
| 10186 TypePromotionManager get promoteManager => _promoteManager; |
| 10187 |
| 10188 /** |
| 10189 * Return the propagated element associated with the given expression whose ty
pe can be |
| 10190 * overridden, or `null` if there is no element whose type can be overridden. |
| 10191 * |
| 10192 * @param expression the expression with which the element is associated |
| 10193 * @return the element associated with the given expression |
| 10194 */ |
| 10195 VariableElement getOverridablePropagatedElement(Expression expression) { |
| 10196 Element element = null; |
| 10197 if (expression is SimpleIdentifier) { |
| 10198 element = expression.propagatedElement; |
| 10199 } else if (expression is PrefixedIdentifier) { |
| 10200 element = expression.propagatedElement; |
| 10201 } else if (expression is PropertyAccess) { |
| 10202 element = expression.propertyName.propagatedElement; |
| 10203 } |
| 10204 if (element is VariableElement) { |
| 10205 return element; |
| 10206 } |
| 10207 return null; |
| 10208 } |
| 10209 |
| 10210 /** |
| 10211 * Return the static element associated with the given expression whose type c
an be overridden, or |
| 10212 * `null` if there is no element whose type can be overridden. |
| 10213 * |
| 10214 * @param expression the expression with which the element is associated |
| 10215 * @return the element associated with the given expression |
| 10216 */ |
| 10217 VariableElement getOverridableStaticElement(Expression expression) { |
| 10218 Element element = null; |
| 10219 if (expression is SimpleIdentifier) { |
| 10220 element = expression.staticElement; |
| 10221 } else if (expression is PrefixedIdentifier) { |
| 10222 element = expression.staticElement; |
| 10223 } else if (expression is PropertyAccess) { |
| 10224 element = expression.propertyName.staticElement; |
| 10225 } |
| 10226 if (element is VariableElement) { |
| 10227 return element; |
| 10228 } |
| 10229 return null; |
| 10230 } |
| 10231 |
| 10232 /** |
| 10233 * Return the static element associated with the given expression whose type c
an be promoted, or |
| 10234 * `null` if there is no element whose type can be promoted. |
| 10235 * |
| 10236 * @param expression the expression with which the element is associated |
| 10237 * @return the element associated with the given expression |
| 10238 */ |
| 10239 VariableElement getPromotionStaticElement(Expression expression) { |
| 10240 while (expression is ParenthesizedExpression) { |
| 10241 expression = (expression as ParenthesizedExpression).expression; |
| 10242 } |
| 10243 if (expression is! SimpleIdentifier) { |
| 10244 return null; |
| 10245 } |
| 10246 SimpleIdentifier identifier = expression as SimpleIdentifier; |
| 10247 Element element = identifier.staticElement; |
| 10248 if (element is! VariableElement) { |
| 10249 return null; |
| 10250 } |
| 10251 ElementKind kind = element.kind; |
| 10252 if (kind == ElementKind.LOCAL_VARIABLE) { |
| 10253 return element as VariableElement; |
| 10254 } |
| 10255 if (kind == ElementKind.PARAMETER) { |
| 10256 return element as VariableElement; |
| 10257 } |
| 10258 return null; |
| 10259 } |
| 10260 |
| 10261 /** |
| 10262 * Prepares this [ResolverVisitor] to using it for incremental resolution. |
| 10263 */ |
| 10264 void initForIncrementalResolution() { |
| 10265 _overrideManager.enterScope(); |
| 10266 } |
| 10267 |
| 10268 /** |
| 10269 * If it is appropriate to do so, override the current type of the static and
propagated elements |
| 10270 * associated with the given expression with the given type. Generally speakin
g, it is appropriate |
| 10271 * if the given type is more specific than the current type. |
| 10272 * |
| 10273 * @param expression the expression used to access the static and propagated e
lements whose types |
| 10274 * might be overridden |
| 10275 * @param potentialType the potential type of the elements |
| 10276 * @param allowPrecisionLoss see @{code overrideVariable} docs |
| 10277 */ |
| 10278 void overrideExpression(Expression expression, DartType potentialType, |
| 10279 bool allowPrecisionLoss, bool setExpressionType) { |
| 10280 VariableElement element = getOverridableStaticElement(expression); |
| 10281 if (element != null) { |
| 10282 DartType newBestType = |
| 10283 overrideVariable(element, potentialType, allowPrecisionLoss); |
| 10284 if (setExpressionType) { |
| 10285 recordPropagatedTypeIfBetter(expression, newBestType); |
| 10286 } |
| 10287 } |
| 10288 element = getOverridablePropagatedElement(expression); |
| 10289 if (element != null) { |
| 10290 overrideVariable(element, potentialType, allowPrecisionLoss); |
| 10291 } |
| 10292 } |
| 10293 |
| 10294 /** |
| 10295 * If it is appropriate to do so, override the current type of the given eleme
nt with the given |
| 10296 * type. |
| 10297 * |
| 10298 * @param element the element whose type might be overridden |
| 10299 * @param potentialType the potential type of the element |
| 10300 * @param allowPrecisionLoss true if `potentialType` is allowed to be less pre
cise than the |
| 10301 * current best type |
| 10302 * |
| 10303 * Return a new better [DartType], or `null` if [potentialType] is not better |
| 10304 * than the current [element] type. |
| 10305 */ |
| 10306 DartType overrideVariable(VariableElement element, DartType potentialType, |
| 10307 bool allowPrecisionLoss) { |
| 10308 if (potentialType == null || potentialType.isBottom) { |
| 10309 return null; |
| 10310 } |
| 10311 DartType currentType = _overrideManager.getBestType(element); |
| 10312 |
| 10313 if (potentialType == currentType) { |
| 10314 return null; |
| 10315 } |
| 10316 |
| 10317 // If we aren't allowing precision loss then the third and fourth conditions |
| 10318 // check that we aren't losing precision. |
| 10319 // |
| 10320 // Let [C] be the current type and [P] be the potential type. When we |
| 10321 // aren't allowing precision loss -- which is the case for is-checks -- we |
| 10322 // check that [! (C << P)] or [P << C]. The second check, that [P << C], is |
| 10323 // analogous to part of the Dart Language Spec rule for type promotion under |
| 10324 // is-checks (in the analogy [T] is [P] and [S] is [C]): |
| 10325 // |
| 10326 // An is-expression of the form [v is T] shows that [v] has type [T] iff |
| 10327 // [T] is more specific than the type [S] of the expression [v] and both |
| 10328 // [T != dynamic] and [S != dynamic]. |
| 10329 // |
| 10330 // It also covers an important case that is not applicable in the spec: |
| 10331 // for union types, we want an is-check to promote from an union type to |
| 10332 // (a subtype of) any of its members. |
| 10333 // |
| 10334 // The first check, that [! (C << P)], covers the case where [P] and [C] are |
| 10335 // unrelated types; This case is not addressed in the spec for static types. |
| 10336 if (currentType == null || |
| 10337 allowPrecisionLoss || |
| 10338 !currentType.isMoreSpecificThan(potentialType) || |
| 10339 potentialType.isMoreSpecificThan(currentType)) { |
| 10340 // TODO(scheglov) type propagation for instance/top-level fields |
| 10341 // was disabled because it depends on the order or visiting. |
| 10342 // If both field and its client are in the same unit, and we visit |
| 10343 // the client before the field, then propagated type is not set yet. |
| 10344 // if (element is PropertyInducingElement) { |
| 10345 // PropertyInducingElement variable = element; |
| 10346 // if (!variable.isConst && !variable.isFinal) { |
| 10347 // return; |
| 10348 // } |
| 10349 // (variable as PropertyInducingElementImpl).propagatedType = |
| 10350 // potentialType; |
| 10351 // } |
| 10352 _overrideManager.setType(element, potentialType); |
| 10353 return potentialType; |
| 10354 } |
| 10355 return null; |
| 10356 } |
| 10357 |
| 10358 /** |
| 10359 * A client is about to resolve a member in the given class declaration. |
| 10360 */ |
| 10361 void prepareToResolveMembersInClass(ClassDeclaration node) { |
| 10362 _enclosingClassDeclaration = node; |
| 10363 enclosingClass = node.element; |
| 10364 typeAnalyzer.thisType = enclosingClass == null ? null : enclosingClass.type; |
| 10365 } |
| 10366 |
| 10367 /** |
| 10368 * If the given [type] is valid, strongly more specific than the |
| 10369 * existing static type of the given [expression], record it as a propagated |
| 10370 * type of the given [expression]. Otherwise, reset it to `null`. |
| 10371 * |
| 10372 * If [hasOldPropagatedType] is `true` then the existing propagated type |
| 10373 * should also is checked. |
| 10374 */ |
| 10375 void recordPropagatedTypeIfBetter(Expression expression, DartType type, |
| 10376 [bool hasOldPropagatedType = false]) { |
| 10377 // Ensure that propagated type invalid. |
| 10378 if (type == null || type.isDynamic || type.isBottom) { |
| 10379 if (!hasOldPropagatedType) { |
| 10380 expression.propagatedType = null; |
| 10381 } |
| 10382 return; |
| 10383 } |
| 10384 // Ensure that propagated type is more specific than the static type. |
| 10385 DartType staticType = expression.staticType; |
| 10386 if (type == staticType || !type.isMoreSpecificThan(staticType)) { |
| 10387 expression.propagatedType = null; |
| 10388 return; |
| 10389 } |
| 10390 // Ensure that the new propagated type is more specific than the old one. |
| 10391 if (hasOldPropagatedType) { |
| 10392 DartType oldPropagatedType = expression.propagatedType; |
| 10393 if (oldPropagatedType != null && |
| 10394 !type.isMoreSpecificThan(oldPropagatedType)) { |
| 10395 return; |
| 10396 } |
| 10397 } |
| 10398 // OK |
| 10399 expression.propagatedType = type; |
| 10400 } |
| 10401 |
| 10402 @override |
| 10403 Object visitAnnotation(Annotation node) { |
| 10404 AstNode parent = node.parent; |
| 10405 if (identical(parent, _enclosingClassDeclaration) || |
| 10406 identical(parent, _enclosingFunctionTypeAlias)) { |
| 10407 return null; |
| 10408 } |
| 10409 return super.visitAnnotation(node); |
| 10410 } |
| 10411 |
| 10412 @override |
| 10413 Object visitAsExpression(AsExpression node) { |
| 10414 super.visitAsExpression(node); |
| 10415 // Since an as-statement doesn't actually change the type, we don't |
| 10416 // let it affect the propagated type when it would result in a loss |
| 10417 // of precision. |
| 10418 overrideExpression(node.expression, node.type.type, false, false); |
| 10419 return null; |
| 10420 } |
| 10421 |
| 10422 @override |
| 10423 Object visitAssertStatement(AssertStatement node) { |
| 10424 super.visitAssertStatement(node); |
| 10425 _propagateTrueState(node.condition); |
| 10426 return null; |
| 10427 } |
| 10428 |
| 10429 @override |
| 10430 Object visitBinaryExpression(BinaryExpression node) { |
| 10431 sc.TokenType operatorType = node.operator.type; |
| 10432 Expression leftOperand = node.leftOperand; |
| 10433 Expression rightOperand = node.rightOperand; |
| 10434 if (operatorType == sc.TokenType.AMPERSAND_AMPERSAND) { |
| 10435 safelyVisit(leftOperand); |
| 10436 if (rightOperand != null) { |
| 10437 _overrideManager.enterScope(); |
| 10438 try { |
| 10439 _promoteManager.enterScope(); |
| 10440 try { |
| 10441 _propagateTrueState(leftOperand); |
| 10442 // Type promotion. |
| 10443 _promoteTypes(leftOperand); |
| 10444 _clearTypePromotionsIfPotentiallyMutatedIn(leftOperand); |
| 10445 _clearTypePromotionsIfPotentiallyMutatedIn(rightOperand); |
| 10446 _clearTypePromotionsIfAccessedInClosureAndProtentiallyMutated( |
| 10447 rightOperand); |
| 10448 // Visit right operand. |
| 10449 rightOperand.accept(this); |
| 10450 } finally { |
| 10451 _promoteManager.exitScope(); |
| 10452 } |
| 10453 } finally { |
| 10454 _overrideManager.exitScope(); |
| 10455 } |
| 10456 } |
| 10457 } else if (operatorType == sc.TokenType.BAR_BAR) { |
| 10458 safelyVisit(leftOperand); |
| 10459 if (rightOperand != null) { |
| 10460 _overrideManager.enterScope(); |
| 10461 try { |
| 10462 _propagateFalseState(leftOperand); |
| 10463 rightOperand.accept(this); |
| 10464 } finally { |
| 10465 _overrideManager.exitScope(); |
| 10466 } |
| 10467 } |
| 10468 } else { |
| 10469 safelyVisit(leftOperand); |
| 10470 safelyVisit(rightOperand); |
| 10471 } |
| 10472 node.accept(elementResolver); |
| 10473 node.accept(typeAnalyzer); |
| 10474 return null; |
| 10475 } |
| 10476 |
| 10477 @override |
| 10478 Object visitBlockFunctionBody(BlockFunctionBody node) { |
| 10479 safelyVisit(_commentBeforeFunction); |
| 10480 _overrideManager.enterScope(); |
| 10481 try { |
| 10482 super.visitBlockFunctionBody(node); |
| 10483 } finally { |
| 10484 _overrideManager.exitScope(); |
| 10485 } |
| 10486 return null; |
| 10487 } |
| 10488 |
| 10489 @override |
| 10490 Object visitBreakStatement(BreakStatement node) { |
| 10491 // |
| 10492 // We do not visit the label because it needs to be visited in the context |
| 10493 // of the statement. |
| 10494 // |
| 10495 node.accept(elementResolver); |
| 10496 node.accept(typeAnalyzer); |
| 10497 return null; |
| 10498 } |
| 10499 |
| 10500 @override |
| 10501 Object visitClassDeclaration(ClassDeclaration node) { |
| 10502 // |
| 10503 // Resolve the metadata in the library scope. |
| 10504 // |
| 10505 if (node.metadata != null) { |
| 10506 node.metadata.accept(this); |
| 10507 } |
| 10508 _enclosingClassDeclaration = node; |
| 10509 // |
| 10510 // Continue the class resolution. |
| 10511 // |
| 10512 ClassElement outerType = enclosingClass; |
| 10513 try { |
| 10514 enclosingClass = node.element; |
| 10515 typeAnalyzer.thisType = |
| 10516 enclosingClass == null ? null : enclosingClass.type; |
| 10517 super.visitClassDeclaration(node); |
| 10518 node.accept(elementResolver); |
| 10519 node.accept(typeAnalyzer); |
| 10520 } finally { |
| 10521 typeAnalyzer.thisType = outerType == null ? null : outerType.type; |
| 10522 enclosingClass = outerType; |
| 10523 _enclosingClassDeclaration = null; |
| 10524 } |
| 10525 return null; |
| 10526 } |
| 10527 |
| 10528 /** |
| 10529 * Implementation of this method should be synchronized with |
| 10530 * [visitClassDeclaration]. |
| 10531 */ |
| 10532 visitClassDeclarationIncrementally(ClassDeclaration node) { |
| 10533 // |
| 10534 // Resolve the metadata in the library scope. |
| 10535 // |
| 10536 if (node.metadata != null) { |
| 10537 node.metadata.accept(this); |
| 10538 } |
| 10539 _enclosingClassDeclaration = node; |
| 10540 // |
| 10541 // Continue the class resolution. |
| 10542 // |
| 10543 enclosingClass = node.element; |
| 10544 typeAnalyzer.thisType = enclosingClass == null ? null : enclosingClass.type; |
| 10545 node.accept(elementResolver); |
| 10546 node.accept(typeAnalyzer); |
| 10547 } |
| 10548 |
| 10549 @override |
| 10550 Object visitComment(Comment node) { |
| 10551 if (node.parent is FunctionDeclaration || |
| 10552 node.parent is ConstructorDeclaration || |
| 10553 node.parent is MethodDeclaration) { |
| 10554 if (!identical(node, _commentBeforeFunction)) { |
| 10555 _commentBeforeFunction = node; |
| 10556 return null; |
| 10557 } |
| 10558 } |
| 10559 super.visitComment(node); |
| 10560 _commentBeforeFunction = null; |
| 10561 return null; |
| 10562 } |
| 10563 |
| 10564 @override |
| 10565 Object visitCommentReference(CommentReference node) { |
| 10566 // |
| 10567 // We do not visit the identifier because it needs to be visited in the |
| 10568 // context of the reference. |
| 10569 // |
| 10570 node.accept(elementResolver); |
| 10571 node.accept(typeAnalyzer); |
| 10572 return null; |
| 10573 } |
| 10574 |
| 10575 @override |
| 10576 Object visitCompilationUnit(CompilationUnit node) { |
| 10577 // |
| 10578 // TODO(brianwilkerson) The goal of the code below is to visit the |
| 10579 // declarations in such an order that we can infer type information for |
| 10580 // top-level variables before we visit references to them. This is better |
| 10581 // than making no effort, but still doesn't completely satisfy that goal |
| 10582 // (consider for example "final var a = b; final var b = 0;"; we'll infer a |
| 10583 // type of 'int' for 'b', but not for 'a' because of the order of the |
| 10584 // visits). Ideally we would create a dependency graph, but that would |
| 10585 // require references to be resolved, which they are not. |
| 10586 // |
| 10587 _overrideManager.enterScope(); |
| 10588 try { |
| 10589 NodeList<Directive> directives = node.directives; |
| 10590 int directiveCount = directives.length; |
| 10591 for (int i = 0; i < directiveCount; i++) { |
| 10592 directives[i].accept(this); |
| 10593 } |
| 10594 NodeList<CompilationUnitMember> declarations = node.declarations; |
| 10595 int declarationCount = declarations.length; |
| 10596 for (int i = 0; i < declarationCount; i++) { |
| 10597 CompilationUnitMember declaration = declarations[i]; |
| 10598 if (declaration is! ClassDeclaration) { |
| 10599 declaration.accept(this); |
| 10600 } |
| 10601 } |
| 10602 for (int i = 0; i < declarationCount; i++) { |
| 10603 CompilationUnitMember declaration = declarations[i]; |
| 10604 if (declaration is ClassDeclaration) { |
| 10605 declaration.accept(this); |
| 10606 } |
| 10607 } |
| 10608 } finally { |
| 10609 _overrideManager.exitScope(); |
| 10610 } |
| 10611 node.accept(elementResolver); |
| 10612 node.accept(typeAnalyzer); |
| 10613 return null; |
| 10614 } |
| 10615 |
| 10616 @override |
| 10617 Object visitConditionalExpression(ConditionalExpression node) { |
| 10618 Expression condition = node.condition; |
| 10619 safelyVisit(condition); |
| 10620 Expression thenExpression = node.thenExpression; |
| 10621 if (thenExpression != null) { |
| 10622 _overrideManager.enterScope(); |
| 10623 try { |
| 10624 _promoteManager.enterScope(); |
| 10625 try { |
| 10626 _propagateTrueState(condition); |
| 10627 // Type promotion. |
| 10628 _promoteTypes(condition); |
| 10629 _clearTypePromotionsIfPotentiallyMutatedIn(thenExpression); |
| 10630 _clearTypePromotionsIfAccessedInClosureAndProtentiallyMutated( |
| 10631 thenExpression); |
| 10632 // Visit "then" expression. |
| 10633 thenExpression.accept(this); |
| 10634 } finally { |
| 10635 _promoteManager.exitScope(); |
| 10636 } |
| 10637 } finally { |
| 10638 _overrideManager.exitScope(); |
| 10639 } |
| 10640 } |
| 10641 Expression elseExpression = node.elseExpression; |
| 10642 if (elseExpression != null) { |
| 10643 _overrideManager.enterScope(); |
| 10644 try { |
| 10645 _propagateFalseState(condition); |
| 10646 elseExpression.accept(this); |
| 10647 } finally { |
| 10648 _overrideManager.exitScope(); |
| 10649 } |
| 10650 } |
| 10651 node.accept(elementResolver); |
| 10652 node.accept(typeAnalyzer); |
| 10653 bool thenIsAbrupt = _isAbruptTerminationExpression(thenExpression); |
| 10654 bool elseIsAbrupt = _isAbruptTerminationExpression(elseExpression); |
| 10655 if (elseIsAbrupt && !thenIsAbrupt) { |
| 10656 _propagateTrueState(condition); |
| 10657 _propagateState(thenExpression); |
| 10658 } else if (thenIsAbrupt && !elseIsAbrupt) { |
| 10659 _propagateFalseState(condition); |
| 10660 _propagateState(elseExpression); |
| 10661 } |
| 10662 return null; |
| 10663 } |
| 10664 |
| 10665 @override |
| 10666 Object visitConstructorDeclaration(ConstructorDeclaration node) { |
| 10667 ExecutableElement outerFunction = _enclosingFunction; |
| 10668 try { |
| 10669 _enclosingFunction = node.element; |
| 10670 super.visitConstructorDeclaration(node); |
| 10671 } finally { |
| 10672 _enclosingFunction = outerFunction; |
| 10673 } |
| 10674 ConstructorElementImpl constructor = node.element; |
| 10675 constructor.constantInitializers = |
| 10676 new ConstantAstCloner().cloneNodeList(node.initializers); |
| 10677 return null; |
| 10678 } |
| 10679 |
| 10680 @override |
| 10681 Object visitConstructorFieldInitializer(ConstructorFieldInitializer node) { |
| 10682 // |
| 10683 // We visit the expression, but do not visit the field name because it needs |
| 10684 // to be visited in the context of the constructor field initializer node. |
| 10685 // |
| 10686 safelyVisit(node.expression); |
| 10687 node.accept(elementResolver); |
| 10688 node.accept(typeAnalyzer); |
| 10689 return null; |
| 10690 } |
| 10691 |
| 10692 @override |
| 10693 Object visitConstructorName(ConstructorName node) { |
| 10694 // |
| 10695 // We do not visit either the type name, because it won't be visited anyway, |
| 10696 // or the name, because it needs to be visited in the context of the |
| 10697 // constructor name. |
| 10698 // |
| 10699 node.accept(elementResolver); |
| 10700 node.accept(typeAnalyzer); |
| 10701 return null; |
| 10702 } |
| 10703 |
| 10704 @override |
| 10705 Object visitContinueStatement(ContinueStatement node) { |
| 10706 // |
| 10707 // We do not visit the label because it needs to be visited in the context |
| 10708 // of the statement. |
| 10709 // |
| 10710 node.accept(elementResolver); |
| 10711 node.accept(typeAnalyzer); |
| 10712 return null; |
| 10713 } |
| 10714 |
| 10715 @override |
| 10716 Object visitDefaultFormalParameter(DefaultFormalParameter node) { |
| 10717 super.visitDefaultFormalParameter(node); |
| 10718 ParameterElement element = node.element; |
| 10719 if (element.initializer != null && node.defaultValue != null) { |
| 10720 (element.initializer as FunctionElementImpl).returnType = |
| 10721 node.defaultValue.staticType; |
| 10722 } |
| 10723 FormalParameterList parent = node.parent; |
| 10724 AstNode grandparent = parent.parent; |
| 10725 if (grandparent is ConstructorDeclaration && |
| 10726 grandparent.constKeyword != null) { |
| 10727 // For const constructors, we need to clone the ASTs for default formal |
| 10728 // parameters, so that we can use them during constant evaluation. |
| 10729 ParameterElement element = node.element; |
| 10730 (element as ConstVariableElement).constantInitializer = |
| 10731 new ConstantAstCloner().cloneNode(node.defaultValue); |
| 10732 } |
| 10733 return null; |
| 10734 } |
| 10735 |
| 10736 @override |
| 10737 Object visitDoStatement(DoStatement node) { |
| 10738 _overrideManager.enterScope(); |
| 10739 try { |
| 10740 super.visitDoStatement(node); |
| 10741 } finally { |
| 10742 _overrideManager.exitScope(); |
| 10743 } |
| 10744 // TODO(brianwilkerson) If the loop can only be exited because the condition |
| 10745 // is false, then propagateFalseState(node.getCondition()); |
| 10746 return null; |
| 10747 } |
| 10748 |
| 10749 @override |
| 10750 Object visitEmptyFunctionBody(EmptyFunctionBody node) { |
| 10751 safelyVisit(_commentBeforeFunction); |
| 10752 if (resolveOnlyCommentInFunctionBody) { |
| 10753 return null; |
| 10754 } |
| 10755 return super.visitEmptyFunctionBody(node); |
| 10756 } |
| 10757 |
| 10758 @override |
| 10759 Object visitEnumDeclaration(EnumDeclaration node) { |
| 10760 // |
| 10761 // Resolve the metadata in the library scope |
| 10762 // and associate the annotations with the element. |
| 10763 // |
| 10764 if (node.metadata != null) { |
| 10765 node.metadata.accept(this); |
| 10766 ElementResolver.setMetadata(node.element, node); |
| 10767 } |
| 10768 // |
| 10769 // Continue the enum resolution. |
| 10770 // |
| 10771 ClassElement outerType = enclosingClass; |
| 10772 try { |
| 10773 enclosingClass = node.element; |
| 10774 typeAnalyzer.thisType = |
| 10775 enclosingClass == null ? null : enclosingClass.type; |
| 10776 super.visitEnumDeclaration(node); |
| 10777 node.accept(elementResolver); |
| 10778 node.accept(typeAnalyzer); |
| 10779 } finally { |
| 10780 typeAnalyzer.thisType = outerType == null ? null : outerType.type; |
| 10781 enclosingClass = outerType; |
| 10782 _enclosingClassDeclaration = null; |
| 10783 } |
| 10784 return null; |
| 10785 } |
| 10786 |
| 10787 @override |
| 10788 Object visitExpressionFunctionBody(ExpressionFunctionBody node) { |
| 10789 safelyVisit(_commentBeforeFunction); |
| 10790 if (resolveOnlyCommentInFunctionBody) { |
| 10791 return null; |
| 10792 } |
| 10793 _overrideManager.enterScope(); |
| 10794 try { |
| 10795 super.visitExpressionFunctionBody(node); |
| 10796 } finally { |
| 10797 _overrideManager.exitScope(); |
| 10798 } |
| 10799 return null; |
| 10800 } |
| 10801 |
| 10802 @override |
| 10803 Object visitFieldDeclaration(FieldDeclaration node) { |
| 10804 _overrideManager.enterScope(); |
| 10805 try { |
| 10806 super.visitFieldDeclaration(node); |
| 10807 } finally { |
| 10808 Map<VariableElement, DartType> overrides = |
| 10809 _overrideManager.captureOverrides(node.fields); |
| 10810 _overrideManager.exitScope(); |
| 10811 _overrideManager.applyOverrides(overrides); |
| 10812 } |
| 10813 return null; |
| 10814 } |
| 10815 |
| 10816 @override |
| 10817 Object visitForEachStatement(ForEachStatement node) { |
| 10818 _overrideManager.enterScope(); |
| 10819 try { |
| 10820 super.visitForEachStatement(node); |
| 10821 } finally { |
| 10822 _overrideManager.exitScope(); |
| 10823 } |
| 10824 return null; |
| 10825 } |
| 10826 |
| 10827 @override |
| 10828 void visitForEachStatementInScope(ForEachStatement node) { |
| 10829 // |
| 10830 // We visit the iterator before the loop variable because the loop variable |
| 10831 // cannot be in scope while visiting the iterator. |
| 10832 // |
| 10833 Expression iterable = node.iterable; |
| 10834 safelyVisit(iterable); |
| 10835 DeclaredIdentifier loopVariable = node.loopVariable; |
| 10836 SimpleIdentifier identifier = node.identifier; |
| 10837 safelyVisit(loopVariable); |
| 10838 safelyVisit(identifier); |
| 10839 Statement body = node.body; |
| 10840 if (body != null) { |
| 10841 _overrideManager.enterScope(); |
| 10842 try { |
| 10843 if (loopVariable != null && iterable != null) { |
| 10844 LocalVariableElement loopElement = loopVariable.element; |
| 10845 if (loopElement != null) { |
| 10846 DartType propagatedType = null; |
| 10847 if (node.awaitKeyword == null) { |
| 10848 propagatedType = _getIteratorElementType(iterable); |
| 10849 } else { |
| 10850 propagatedType = _getStreamElementType(iterable); |
| 10851 } |
| 10852 if (propagatedType != null) { |
| 10853 overrideVariable(loopElement, propagatedType, true); |
| 10854 _recordPropagatedType(loopVariable.identifier, propagatedType); |
| 10855 } |
| 10856 } |
| 10857 } else if (identifier != null && iterable != null) { |
| 10858 Element identifierElement = identifier.staticElement; |
| 10859 if (identifierElement is VariableElement) { |
| 10860 DartType iteratorElementType = _getIteratorElementType(iterable); |
| 10861 overrideVariable(identifierElement, iteratorElementType, true); |
| 10862 _recordPropagatedType(identifier, iteratorElementType); |
| 10863 } |
| 10864 } |
| 10865 visitStatementInScope(body); |
| 10866 } finally { |
| 10867 _overrideManager.exitScope(); |
| 10868 } |
| 10869 } |
| 10870 node.accept(elementResolver); |
| 10871 node.accept(typeAnalyzer); |
| 10872 } |
| 10873 |
| 10874 @override |
| 10875 Object visitForStatement(ForStatement node) { |
| 10876 _overrideManager.enterScope(); |
| 10877 try { |
| 10878 super.visitForStatement(node); |
| 10879 } finally { |
| 10880 _overrideManager.exitScope(); |
| 10881 } |
| 10882 return null; |
| 10883 } |
| 10884 |
| 10885 @override |
| 10886 void visitForStatementInScope(ForStatement node) { |
| 10887 safelyVisit(node.variables); |
| 10888 safelyVisit(node.initialization); |
| 10889 safelyVisit(node.condition); |
| 10890 _overrideManager.enterScope(); |
| 10891 try { |
| 10892 _propagateTrueState(node.condition); |
| 10893 visitStatementInScope(node.body); |
| 10894 node.updaters.accept(this); |
| 10895 } finally { |
| 10896 _overrideManager.exitScope(); |
| 10897 } |
| 10898 // TODO(brianwilkerson) If the loop can only be exited because the condition |
| 10899 // is false, then propagateFalseState(condition); |
| 10900 } |
| 10901 |
| 10902 @override |
| 10903 Object visitFunctionDeclaration(FunctionDeclaration node) { |
| 10904 ExecutableElement outerFunction = _enclosingFunction; |
| 10905 try { |
| 10906 SimpleIdentifier functionName = node.name; |
| 10907 _enclosingFunction = functionName.staticElement as ExecutableElement; |
| 10908 super.visitFunctionDeclaration(node); |
| 10909 } finally { |
| 10910 _enclosingFunction = outerFunction; |
| 10911 } |
| 10912 return null; |
| 10913 } |
| 10914 |
| 10915 @override |
| 10916 Object visitFunctionExpression(FunctionExpression node) { |
| 10917 ExecutableElement outerFunction = _enclosingFunction; |
| 10918 try { |
| 10919 _enclosingFunction = node.element; |
| 10920 _overrideManager.enterScope(); |
| 10921 try { |
| 10922 super.visitFunctionExpression(node); |
| 10923 } finally { |
| 10924 _overrideManager.exitScope(); |
| 10925 } |
| 10926 } finally { |
| 10927 _enclosingFunction = outerFunction; |
| 10928 } |
| 10929 return null; |
| 10930 } |
| 10931 |
| 10932 @override |
| 10933 Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { |
| 10934 safelyVisit(node.function); |
| 10935 node.accept(elementResolver); |
| 10936 _inferFunctionExpressionsParametersTypes(node.argumentList); |
| 10937 safelyVisit(node.argumentList); |
| 10938 node.accept(typeAnalyzer); |
| 10939 return null; |
| 10940 } |
| 10941 |
| 10942 @override |
| 10943 Object visitFunctionTypeAlias(FunctionTypeAlias node) { |
| 10944 // Resolve the metadata in the library scope. |
| 10945 if (node.metadata != null) { |
| 10946 node.metadata.accept(this); |
| 10947 } |
| 10948 FunctionTypeAlias outerAlias = _enclosingFunctionTypeAlias; |
| 10949 _enclosingFunctionTypeAlias = node; |
| 10950 try { |
| 10951 super.visitFunctionTypeAlias(node); |
| 10952 } finally { |
| 10953 _enclosingFunctionTypeAlias = outerAlias; |
| 10954 } |
| 10955 return null; |
| 10956 } |
| 10957 |
| 10958 @override |
| 10959 Object visitHideCombinator(HideCombinator node) => null; |
| 10960 |
| 10961 @override |
| 10962 Object visitIfStatement(IfStatement node) { |
| 10963 Expression condition = node.condition; |
| 10964 safelyVisit(condition); |
| 10965 Map<VariableElement, DartType> thenOverrides = |
| 10966 new HashMap<VariableElement, DartType>(); |
| 10967 Statement thenStatement = node.thenStatement; |
| 10968 if (thenStatement != null) { |
| 10969 _overrideManager.enterScope(); |
| 10970 try { |
| 10971 _promoteManager.enterScope(); |
| 10972 try { |
| 10973 _propagateTrueState(condition); |
| 10974 // Type promotion. |
| 10975 _promoteTypes(condition); |
| 10976 _clearTypePromotionsIfPotentiallyMutatedIn(thenStatement); |
| 10977 _clearTypePromotionsIfAccessedInClosureAndProtentiallyMutated( |
| 10978 thenStatement); |
| 10979 // Visit "then". |
| 10980 visitStatementInScope(thenStatement); |
| 10981 } finally { |
| 10982 _promoteManager.exitScope(); |
| 10983 } |
| 10984 } finally { |
| 10985 thenOverrides = _overrideManager.captureLocalOverrides(); |
| 10986 _overrideManager.exitScope(); |
| 10987 } |
| 10988 } |
| 10989 Map<VariableElement, DartType> elseOverrides = |
| 10990 new HashMap<VariableElement, DartType>(); |
| 10991 Statement elseStatement = node.elseStatement; |
| 10992 if (elseStatement != null) { |
| 10993 _overrideManager.enterScope(); |
| 10994 try { |
| 10995 _propagateFalseState(condition); |
| 10996 visitStatementInScope(elseStatement); |
| 10997 } finally { |
| 10998 elseOverrides = _overrideManager.captureLocalOverrides(); |
| 10999 _overrideManager.exitScope(); |
| 11000 } |
| 11001 } |
| 11002 node.accept(elementResolver); |
| 11003 node.accept(typeAnalyzer); |
| 11004 // Join overrides. |
| 11005 bool thenIsAbrupt = _isAbruptTerminationStatement(thenStatement); |
| 11006 bool elseIsAbrupt = _isAbruptTerminationStatement(elseStatement); |
| 11007 if (elseIsAbrupt && !thenIsAbrupt) { |
| 11008 _propagateTrueState(condition); |
| 11009 _overrideManager.applyOverrides(thenOverrides); |
| 11010 } else if (thenIsAbrupt && !elseIsAbrupt) { |
| 11011 _propagateFalseState(condition); |
| 11012 _overrideManager.applyOverrides(elseOverrides); |
| 11013 } else if (!thenIsAbrupt && !elseIsAbrupt) { |
| 11014 List<Map<VariableElement, DartType>> perBranchOverrides = |
| 11015 new List<Map<VariableElement, DartType>>(); |
| 11016 perBranchOverrides.add(thenOverrides); |
| 11017 perBranchOverrides.add(elseOverrides); |
| 11018 _overrideManager.mergeOverrides(perBranchOverrides); |
| 11019 } |
| 11020 return null; |
| 11021 } |
| 11022 |
| 11023 @override |
| 11024 Object visitLabel(Label node) => null; |
| 11025 |
| 11026 @override |
| 11027 Object visitLibraryIdentifier(LibraryIdentifier node) => null; |
| 11028 |
| 11029 @override |
| 11030 Object visitMethodDeclaration(MethodDeclaration node) { |
| 11031 ExecutableElement outerFunction = _enclosingFunction; |
| 11032 try { |
| 11033 _enclosingFunction = node.element; |
| 11034 super.visitMethodDeclaration(node); |
| 11035 } finally { |
| 11036 _enclosingFunction = outerFunction; |
| 11037 } |
| 11038 return null; |
| 11039 } |
| 11040 |
| 11041 @override |
| 11042 Object visitMethodInvocation(MethodInvocation node) { |
| 11043 // |
| 11044 // We visit the target and argument list, but do not visit the method name |
| 11045 // because it needs to be visited in the context of the invocation. |
| 11046 // |
| 11047 safelyVisit(node.target); |
| 11048 node.accept(elementResolver); |
| 11049 _inferFunctionExpressionsParametersTypes(node.argumentList); |
| 11050 safelyVisit(node.argumentList); |
| 11051 node.accept(typeAnalyzer); |
| 11052 return null; |
| 11053 } |
| 11054 |
| 11055 @override |
| 11056 Object visitNode(AstNode node) { |
| 11057 node.visitChildren(this); |
| 11058 node.accept(elementResolver); |
| 11059 node.accept(typeAnalyzer); |
| 11060 return null; |
| 11061 } |
| 11062 |
| 11063 @override |
| 11064 Object visitPrefixedIdentifier(PrefixedIdentifier node) { |
| 11065 // |
| 11066 // We visit the prefix, but do not visit the identifier because it needs to |
| 11067 // be visited in the context of the prefix. |
| 11068 // |
| 11069 safelyVisit(node.prefix); |
| 11070 node.accept(elementResolver); |
| 11071 node.accept(typeAnalyzer); |
| 11072 return null; |
| 11073 } |
| 11074 |
| 11075 @override |
| 11076 Object visitPropertyAccess(PropertyAccess node) { |
| 11077 // |
| 11078 // We visit the target, but do not visit the property name because it needs |
| 11079 // to be visited in the context of the property access node. |
| 11080 // |
| 11081 safelyVisit(node.target); |
| 11082 node.accept(elementResolver); |
| 11083 node.accept(typeAnalyzer); |
| 11084 return null; |
| 11085 } |
| 11086 |
| 11087 @override |
| 11088 Object visitRedirectingConstructorInvocation( |
| 11089 RedirectingConstructorInvocation node) { |
| 11090 // |
| 11091 // We visit the argument list, but do not visit the optional identifier |
| 11092 // because it needs to be visited in the context of the constructor |
| 11093 // invocation. |
| 11094 // |
| 11095 safelyVisit(node.argumentList); |
| 11096 node.accept(elementResolver); |
| 11097 node.accept(typeAnalyzer); |
| 11098 return null; |
| 11099 } |
| 11100 |
| 11101 @override |
| 11102 Object visitShowCombinator(ShowCombinator node) => null; |
| 11103 |
| 11104 @override |
| 11105 Object visitSuperConstructorInvocation(SuperConstructorInvocation node) { |
| 11106 // |
| 11107 // We visit the argument list, but do not visit the optional identifier |
| 11108 // because it needs to be visited in the context of the constructor |
| 11109 // invocation. |
| 11110 // |
| 11111 safelyVisit(node.argumentList); |
| 11112 node.accept(elementResolver); |
| 11113 node.accept(typeAnalyzer); |
| 11114 return null; |
| 11115 } |
| 11116 |
| 11117 @override |
| 11118 Object visitSwitchCase(SwitchCase node) { |
| 11119 _overrideManager.enterScope(); |
| 11120 try { |
| 11121 super.visitSwitchCase(node); |
| 11122 } finally { |
| 11123 _overrideManager.exitScope(); |
| 11124 } |
| 11125 return null; |
| 11126 } |
| 11127 |
| 11128 @override |
| 11129 Object visitSwitchDefault(SwitchDefault node) { |
| 11130 _overrideManager.enterScope(); |
| 11131 try { |
| 11132 super.visitSwitchDefault(node); |
| 11133 } finally { |
| 11134 _overrideManager.exitScope(); |
| 11135 } |
| 11136 return null; |
| 11137 } |
| 11138 |
| 11139 @override |
| 11140 Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
| 11141 _overrideManager.enterScope(); |
| 11142 try { |
| 11143 super.visitTopLevelVariableDeclaration(node); |
| 11144 } finally { |
| 11145 Map<VariableElement, DartType> overrides = |
| 11146 _overrideManager.captureOverrides(node.variables); |
| 11147 _overrideManager.exitScope(); |
| 11148 _overrideManager.applyOverrides(overrides); |
| 11149 } |
| 11150 return null; |
| 11151 } |
| 11152 |
| 11153 @override |
| 11154 Object visitTypeName(TypeName node) => null; |
| 11155 |
| 11156 @override |
| 11157 Object visitVariableDeclaration(VariableDeclaration node) { |
| 11158 super.visitVariableDeclaration(node); |
| 11159 VariableElement element = node.element; |
| 11160 if (element.initializer != null && node.initializer != null) { |
| 11161 (element.initializer as FunctionElementImpl).returnType = |
| 11162 node.initializer.staticType; |
| 11163 } |
| 11164 // Note: in addition to cloning the initializers for const variables, we |
| 11165 // have to clone the initializers for non-static final fields (because if |
| 11166 // they occur in a class with a const constructor, they will be needed to |
| 11167 // evaluate the const constructor). |
| 11168 if ((element.isConst || |
| 11169 (element is FieldElement && |
| 11170 element.isFinal && |
| 11171 !element.isStatic)) && |
| 11172 node.initializer != null) { |
| 11173 (element as ConstVariableElement).constantInitializer = |
| 11174 new ConstantAstCloner().cloneNode(node.initializer); |
| 11175 } |
| 11176 return null; |
| 11177 } |
| 11178 |
| 11179 @override |
| 11180 Object visitWhileStatement(WhileStatement node) { |
| 11181 // Note: since we don't call the base class, we have to maintain |
| 11182 // _implicitLabelScope ourselves. |
| 11183 ImplicitLabelScope outerImplicitScope = _implicitLabelScope; |
| 11184 try { |
| 11185 _implicitLabelScope = _implicitLabelScope.nest(node); |
| 11186 Expression condition = node.condition; |
| 11187 safelyVisit(condition); |
| 11188 Statement body = node.body; |
| 11189 if (body != null) { |
| 11190 _overrideManager.enterScope(); |
| 11191 try { |
| 11192 _propagateTrueState(condition); |
| 11193 visitStatementInScope(body); |
| 11194 } finally { |
| 11195 _overrideManager.exitScope(); |
| 11196 } |
| 11197 } |
| 11198 } finally { |
| 11199 _implicitLabelScope = outerImplicitScope; |
| 11200 } |
| 11201 // TODO(brianwilkerson) If the loop can only be exited because the condition |
| 11202 // is false, then propagateFalseState(condition); |
| 11203 node.accept(elementResolver); |
| 11204 node.accept(typeAnalyzer); |
| 11205 return null; |
| 11206 } |
| 11207 |
| 11208 /** |
| 11209 * Checks each promoted variable in the current scope for compliance with the
following |
| 11210 * specification statement: |
| 11211 * |
| 11212 * If the variable <i>v</i> is accessed by a closure in <i>s<sub>1</sub></i> t
hen the variable |
| 11213 * <i>v</i> is not potentially mutated anywhere in the scope of <i>v</i>. |
| 11214 */ |
| 11215 void _clearTypePromotionsIfAccessedInClosureAndProtentiallyMutated( |
| 11216 AstNode target) { |
| 11217 for (Element element in _promoteManager.promotedElements) { |
| 11218 if ((element as VariableElementImpl).isPotentiallyMutatedInScope) { |
| 11219 if (_isVariableAccessedInClosure(element, target)) { |
| 11220 _promoteManager.setType(element, null); |
| 11221 } |
| 11222 } |
| 11223 } |
| 11224 } |
| 11225 |
| 11226 /** |
| 11227 * Checks each promoted variable in the current scope for compliance with the
following |
| 11228 * specification statement: |
| 11229 * |
| 11230 * <i>v</i> is not potentially mutated in <i>s<sub>1</sub></i> or within a clo
sure. |
| 11231 */ |
| 11232 void _clearTypePromotionsIfPotentiallyMutatedIn(AstNode target) { |
| 11233 for (Element element in _promoteManager.promotedElements) { |
| 11234 if (_isVariablePotentiallyMutatedIn(element, target)) { |
| 11235 _promoteManager.setType(element, null); |
| 11236 } |
| 11237 } |
| 11238 } |
| 11239 |
| 11240 /** |
| 11241 * The given expression is the expression used to compute the iterator for a |
| 11242 * for-each statement. Attempt to compute the type of objects that will be |
| 11243 * assigned to the loop variable and return that type. Return `null` if the |
| 11244 * type could not be determined. The [iteratorExpression] is the expression |
| 11245 * that will return the Iterable being iterated over. |
| 11246 */ |
| 11247 DartType _getIteratorElementType(Expression iteratorExpression) { |
| 11248 DartType expressionType = iteratorExpression.bestType; |
| 11249 if (expressionType is InterfaceType) { |
| 11250 InterfaceType interfaceType = expressionType; |
| 11251 FunctionType iteratorFunction = |
| 11252 _inheritanceManager.lookupMemberType(interfaceType, "iterator"); |
| 11253 if (iteratorFunction == null) { |
| 11254 // TODO(brianwilkerson) Should we report this error? |
| 11255 return null; |
| 11256 } |
| 11257 DartType iteratorType = iteratorFunction.returnType; |
| 11258 if (iteratorType is InterfaceType) { |
| 11259 InterfaceType iteratorInterfaceType = iteratorType; |
| 11260 FunctionType currentFunction = _inheritanceManager.lookupMemberType( |
| 11261 iteratorInterfaceType, "current"); |
| 11262 if (currentFunction == null) { |
| 11263 // TODO(brianwilkerson) Should we report this error? |
| 11264 return null; |
| 11265 } |
| 11266 return currentFunction.returnType; |
| 11267 } |
| 11268 } |
| 11269 return null; |
| 11270 } |
| 11271 |
| 11272 /** |
| 11273 * The given expression is the expression used to compute the stream for an |
| 11274 * asyncronous for-each statement. Attempt to compute the type of objects that |
| 11275 * will be assigned to the loop variable and return that type. Return `null` |
| 11276 * if the type could not be determined. The [streamExpression] is the |
| 11277 * expression that will return the stream being iterated over. |
| 11278 */ |
| 11279 DartType _getStreamElementType(Expression streamExpression) { |
| 11280 DartType streamType = streamExpression.bestType; |
| 11281 if (streamType is InterfaceType) { |
| 11282 FunctionType listenFunction = |
| 11283 _inheritanceManager.lookupMemberType(streamType, "listen"); |
| 11284 if (listenFunction == null) { |
| 11285 return null; |
| 11286 } |
| 11287 List<ParameterElement> listenParameters = listenFunction.parameters; |
| 11288 if (listenParameters == null || listenParameters.length < 1) { |
| 11289 return null; |
| 11290 } |
| 11291 DartType onDataType = listenParameters[0].type; |
| 11292 if (onDataType is FunctionType) { |
| 11293 List<ParameterElement> onDataParameters = onDataType.parameters; |
| 11294 if (onDataParameters == null || onDataParameters.length < 1) { |
| 11295 return null; |
| 11296 } |
| 11297 DartType eventType = onDataParameters[0].type; |
| 11298 // TODO(paulberry): checking that typeParameters.isNotEmpty is a |
| 11299 // band-aid fix for dartbug.com/24191. Figure out what the correct |
| 11300 // logic should be. |
| 11301 if (streamType.typeParameters.isNotEmpty && |
| 11302 eventType.element == streamType.typeParameters[0]) { |
| 11303 return streamType.typeArguments[0]; |
| 11304 } |
| 11305 } |
| 11306 } |
| 11307 return null; |
| 11308 } |
| 11309 |
| 11310 /** |
| 11311 * If given "mayBeClosure" is [FunctionExpression] without explicit parameters
types and its |
| 11312 * required type is [FunctionType], then infer parameters types from [Function
Type]. |
| 11313 */ |
| 11314 void _inferFunctionExpressionParametersTypes( |
| 11315 Expression mayBeClosure, DartType mayByFunctionType) { |
| 11316 // prepare closure |
| 11317 if (mayBeClosure is! FunctionExpression) { |
| 11318 return; |
| 11319 } |
| 11320 FunctionExpression closure = mayBeClosure as FunctionExpression; |
| 11321 // prepare expected closure type |
| 11322 if (mayByFunctionType is! FunctionType) { |
| 11323 return; |
| 11324 } |
| 11325 FunctionType expectedClosureType = mayByFunctionType as FunctionType; |
| 11326 // If the expectedClosureType is not more specific than the static type, |
| 11327 // return. |
| 11328 DartType staticClosureType = |
| 11329 closure.element != null ? closure.element.type : null; |
| 11330 if (staticClosureType != null && |
| 11331 !expectedClosureType.isMoreSpecificThan(staticClosureType)) { |
| 11332 return; |
| 11333 } |
| 11334 // set propagated type for the closure |
| 11335 closure.propagatedType = expectedClosureType; |
| 11336 // set inferred types for parameters |
| 11337 NodeList<FormalParameter> parameters = closure.parameters.parameters; |
| 11338 List<ParameterElement> expectedParameters = expectedClosureType.parameters; |
| 11339 for (int i = 0; |
| 11340 i < parameters.length && i < expectedParameters.length; |
| 11341 i++) { |
| 11342 FormalParameter parameter = parameters[i]; |
| 11343 ParameterElement element = parameter.element; |
| 11344 DartType currentType = _overrideManager.getBestType(element); |
| 11345 // may be override the type |
| 11346 DartType expectedType = expectedParameters[i].type; |
| 11347 if (currentType == null || expectedType.isMoreSpecificThan(currentType)) { |
| 11348 _overrideManager.setType(element, expectedType); |
| 11349 } |
| 11350 } |
| 11351 } |
| 11352 |
| 11353 /** |
| 11354 * Try to infer types of parameters of the [FunctionExpression] arguments. |
| 11355 */ |
| 11356 void _inferFunctionExpressionsParametersTypes(ArgumentList argumentList) { |
| 11357 for (Expression argument in argumentList.arguments) { |
| 11358 ParameterElement parameter = argument.propagatedParameterElement; |
| 11359 if (parameter == null) { |
| 11360 parameter = argument.staticParameterElement; |
| 11361 } |
| 11362 if (parameter != null) { |
| 11363 _inferFunctionExpressionParametersTypes(argument, parameter.type); |
| 11364 } |
| 11365 } |
| 11366 } |
| 11367 |
| 11368 /** |
| 11369 * Return `true` if the given expression terminates abruptly (that is, if any
expression |
| 11370 * following the given expression will not be reached). |
| 11371 * |
| 11372 * @param expression the expression being tested |
| 11373 * @return `true` if the given expression terminates abruptly |
| 11374 */ |
| 11375 bool _isAbruptTerminationExpression(Expression expression) { |
| 11376 // TODO(brianwilkerson) This needs to be significantly improved. Ideally we |
| 11377 // would eventually turn this into a method on Expression that returns a |
| 11378 // termination indication (normal, abrupt with no exception, abrupt with an |
| 11379 // exception). |
| 11380 while (expression is ParenthesizedExpression) { |
| 11381 expression = (expression as ParenthesizedExpression).expression; |
| 11382 } |
| 11383 return expression is ThrowExpression || expression is RethrowExpression; |
| 11384 } |
| 11385 |
| 11386 /** |
| 11387 * Return `true` if the given statement terminates abruptly (that is, if any s
tatement |
| 11388 * following the given statement will not be reached). |
| 11389 * |
| 11390 * @param statement the statement being tested |
| 11391 * @return `true` if the given statement terminates abruptly |
| 11392 */ |
| 11393 bool _isAbruptTerminationStatement(Statement statement) { |
| 11394 // TODO(brianwilkerson) This needs to be significantly improved. Ideally we |
| 11395 // would eventually turn this into a method on Statement that returns a |
| 11396 // termination indication (normal, abrupt with no exception, abrupt with an |
| 11397 // exception). |
| 11398 // |
| 11399 // collinsn: it is unsound to assume that [break] and [continue] are |
| 11400 // "abrupt". See: https://code.google.com/p/dart/issues/detail?id=19929#c4 |
| 11401 // (tests are included in TypePropagationTest.java). |
| 11402 // In general, the difficulty is loopy control flow. |
| 11403 // |
| 11404 // In the presence of exceptions things become much more complicated, but |
| 11405 // while we only use this to propagate at [if]-statement join points, |
| 11406 // checking for [return] may work well enough in the common case. |
| 11407 if (statement is ReturnStatement) { |
| 11408 return true; |
| 11409 } else if (statement is ExpressionStatement) { |
| 11410 return _isAbruptTerminationExpression(statement.expression); |
| 11411 } else if (statement is Block) { |
| 11412 NodeList<Statement> statements = statement.statements; |
| 11413 int size = statements.length; |
| 11414 if (size == 0) { |
| 11415 return false; |
| 11416 } |
| 11417 |
| 11418 // This last-statement-is-return heuristic is unsound for adversarial |
| 11419 // code, but probably works well in the common case: |
| 11420 // |
| 11421 // var x = 123; |
| 11422 // var c = true; |
| 11423 // L: if (c) { |
| 11424 // x = "hello"; |
| 11425 // c = false; |
| 11426 // break L; |
| 11427 // return; |
| 11428 // } |
| 11429 // print(x); |
| 11430 // |
| 11431 // Unsound to assume that [x = "hello";] never executed after the |
| 11432 // if-statement. Of course, a dead-code analysis could point out that |
| 11433 // [return] here is dead. |
| 11434 return _isAbruptTerminationStatement(statements[size - 1]); |
| 11435 } |
| 11436 return false; |
| 11437 } |
| 11438 |
| 11439 /** |
| 11440 * Return `true` if the given variable is accessed within a closure in the giv
en |
| 11441 * [AstNode] and also mutated somewhere in variable scope. This information is
only |
| 11442 * available for local variables (including parameters). |
| 11443 * |
| 11444 * @param variable the variable to check |
| 11445 * @param target the [AstNode] to check within |
| 11446 * @return `true` if this variable is potentially mutated somewhere in the giv
en ASTNode |
| 11447 */ |
| 11448 bool _isVariableAccessedInClosure(Element variable, AstNode target) { |
| 11449 _ResolverVisitor_isVariableAccessedInClosure visitor = |
| 11450 new _ResolverVisitor_isVariableAccessedInClosure(variable); |
| 11451 target.accept(visitor); |
| 11452 return visitor.result; |
| 11453 } |
| 11454 |
| 11455 /** |
| 11456 * Return `true` if the given variable is potentially mutated somewhere in the
given |
| 11457 * [AstNode]. This information is only available for local variables (includin
g parameters). |
| 11458 * |
| 11459 * @param variable the variable to check |
| 11460 * @param target the [AstNode] to check within |
| 11461 * @return `true` if this variable is potentially mutated somewhere in the giv
en ASTNode |
| 11462 */ |
| 11463 bool _isVariablePotentiallyMutatedIn(Element variable, AstNode target) { |
| 11464 _ResolverVisitor_isVariablePotentiallyMutatedIn visitor = |
| 11465 new _ResolverVisitor_isVariablePotentiallyMutatedIn(variable); |
| 11466 target.accept(visitor); |
| 11467 return visitor.result; |
| 11468 } |
| 11469 |
| 11470 /** |
| 11471 * If it is appropriate to do so, promotes the current type of the static elem
ent associated with |
| 11472 * the given expression with the given type. Generally speaking, it is appropr
iate if the given |
| 11473 * type is more specific than the current type. |
| 11474 * |
| 11475 * @param expression the expression used to access the static element whose ty
pes might be |
| 11476 * promoted |
| 11477 * @param potentialType the potential type of the elements |
| 11478 */ |
| 11479 void _promote(Expression expression, DartType potentialType) { |
| 11480 VariableElement element = getPromotionStaticElement(expression); |
| 11481 if (element != null) { |
| 11482 // may be mutated somewhere in closure |
| 11483 if (element.isPotentiallyMutatedInClosure) { |
| 11484 return; |
| 11485 } |
| 11486 // prepare current variable type |
| 11487 DartType type = _promoteManager.getType(element); |
| 11488 if (type == null) { |
| 11489 type = expression.staticType; |
| 11490 } |
| 11491 // Declared type should not be "dynamic". |
| 11492 if (type == null || type.isDynamic) { |
| 11493 return; |
| 11494 } |
| 11495 // Promoted type should not be "dynamic". |
| 11496 if (potentialType == null || potentialType.isDynamic) { |
| 11497 return; |
| 11498 } |
| 11499 // Promoted type should be more specific than declared. |
| 11500 if (!potentialType.isMoreSpecificThan(type)) { |
| 11501 return; |
| 11502 } |
| 11503 // Do promote type of variable. |
| 11504 _promoteManager.setType(element, potentialType); |
| 11505 } |
| 11506 } |
| 11507 |
| 11508 /** |
| 11509 * Promotes type information using given condition. |
| 11510 */ |
| 11511 void _promoteTypes(Expression condition) { |
| 11512 if (condition is BinaryExpression) { |
| 11513 BinaryExpression binary = condition; |
| 11514 if (binary.operator.type == sc.TokenType.AMPERSAND_AMPERSAND) { |
| 11515 Expression left = binary.leftOperand; |
| 11516 Expression right = binary.rightOperand; |
| 11517 _promoteTypes(left); |
| 11518 _promoteTypes(right); |
| 11519 _clearTypePromotionsIfPotentiallyMutatedIn(right); |
| 11520 } |
| 11521 } else if (condition is IsExpression) { |
| 11522 IsExpression is2 = condition; |
| 11523 if (is2.notOperator == null) { |
| 11524 _promote(is2.expression, is2.type.type); |
| 11525 } |
| 11526 } else if (condition is ParenthesizedExpression) { |
| 11527 _promoteTypes(condition.expression); |
| 11528 } |
| 11529 } |
| 11530 |
| 11531 /** |
| 11532 * Propagate any type information that results from knowing that the given con
dition will have |
| 11533 * been evaluated to 'false'. |
| 11534 * |
| 11535 * @param condition the condition that will have evaluated to 'false' |
| 11536 */ |
| 11537 void _propagateFalseState(Expression condition) { |
| 11538 if (condition is BinaryExpression) { |
| 11539 BinaryExpression binary = condition; |
| 11540 if (binary.operator.type == sc.TokenType.BAR_BAR) { |
| 11541 _propagateFalseState(binary.leftOperand); |
| 11542 _propagateFalseState(binary.rightOperand); |
| 11543 } |
| 11544 } else if (condition is IsExpression) { |
| 11545 IsExpression is2 = condition; |
| 11546 if (is2.notOperator != null) { |
| 11547 // Since an is-statement doesn't actually change the type, we don't |
| 11548 // let it affect the propagated type when it would result in a loss |
| 11549 // of precision. |
| 11550 overrideExpression(is2.expression, is2.type.type, false, false); |
| 11551 } |
| 11552 } else if (condition is PrefixExpression) { |
| 11553 PrefixExpression prefix = condition; |
| 11554 if (prefix.operator.type == sc.TokenType.BANG) { |
| 11555 _propagateTrueState(prefix.operand); |
| 11556 } |
| 11557 } else if (condition is ParenthesizedExpression) { |
| 11558 _propagateFalseState(condition.expression); |
| 11559 } |
| 11560 } |
| 11561 |
| 11562 /** |
| 11563 * Propagate any type information that results from knowing that the given exp
ression will have |
| 11564 * been evaluated without altering the flow of execution. |
| 11565 * |
| 11566 * @param expression the expression that will have been evaluated |
| 11567 */ |
| 11568 void _propagateState(Expression expression) { |
| 11569 // TODO(brianwilkerson) Implement this. |
| 11570 } |
| 11571 |
| 11572 /** |
| 11573 * Propagate any type information that results from knowing that the given con
dition will have |
| 11574 * been evaluated to 'true'. |
| 11575 * |
| 11576 * @param condition the condition that will have evaluated to 'true' |
| 11577 */ |
| 11578 void _propagateTrueState(Expression condition) { |
| 11579 if (condition is BinaryExpression) { |
| 11580 BinaryExpression binary = condition; |
| 11581 if (binary.operator.type == sc.TokenType.AMPERSAND_AMPERSAND) { |
| 11582 _propagateTrueState(binary.leftOperand); |
| 11583 _propagateTrueState(binary.rightOperand); |
| 11584 } |
| 11585 } else if (condition is IsExpression) { |
| 11586 IsExpression is2 = condition; |
| 11587 if (is2.notOperator == null) { |
| 11588 // Since an is-statement doesn't actually change the type, we don't |
| 11589 // let it affect the propagated type when it would result in a loss |
| 11590 // of precision. |
| 11591 overrideExpression(is2.expression, is2.type.type, false, false); |
| 11592 } |
| 11593 } else if (condition is PrefixExpression) { |
| 11594 PrefixExpression prefix = condition; |
| 11595 if (prefix.operator.type == sc.TokenType.BANG) { |
| 11596 _propagateFalseState(prefix.operand); |
| 11597 } |
| 11598 } else if (condition is ParenthesizedExpression) { |
| 11599 _propagateTrueState(condition.expression); |
| 11600 } |
| 11601 } |
| 11602 |
| 11603 /** |
| 11604 * Record that the propagated type of the given node is the given type. |
| 11605 * |
| 11606 * @param expression the node whose type is to be recorded |
| 11607 * @param type the propagated type of the node |
| 11608 */ |
| 11609 void _recordPropagatedType(Expression expression, DartType type) { |
| 11610 if (type != null && !type.isDynamic) { |
| 11611 expression.propagatedType = type; |
| 11612 } |
| 11613 } |
| 11614 } |
| 11615 |
| 11616 /** |
| 11617 * The abstract class `Scope` defines the behavior common to name scopes used by
the resolver |
| 11618 * to determine which names are visible at any given point in the code. |
| 11619 */ |
| 11620 abstract class Scope { |
| 11621 /** |
| 11622 * The prefix used to mark an identifier as being private to its library. |
| 11623 */ |
| 11624 static int PRIVATE_NAME_PREFIX = 0x5F; |
| 11625 |
| 11626 /** |
| 11627 * The suffix added to the declared name of a setter when looking up the sette
r. Used to |
| 11628 * disambiguate between a getter and a setter that have the same name. |
| 11629 */ |
| 11630 static String SETTER_SUFFIX = "="; |
| 11631 |
| 11632 /** |
| 11633 * The name used to look up the method used to implement the unary minus opera
tor. Used to |
| 11634 * disambiguate between the unary and binary operators. |
| 11635 */ |
| 11636 static String UNARY_MINUS = "unary-"; |
| 11637 |
| 11638 /** |
| 11639 * A table mapping names that are defined in this scope to the element represe
nting the thing |
| 11640 * declared with that name. |
| 11641 */ |
| 11642 HashMap<String, Element> _definedNames = new HashMap<String, Element>(); |
| 11643 |
| 11644 /** |
| 11645 * A flag indicating whether there are any names defined in this scope. |
| 11646 */ |
| 11647 bool _hasName = false; |
| 11648 |
| 11649 /** |
| 11650 * Return the scope in which this scope is lexically enclosed. |
| 11651 * |
| 11652 * @return the scope in which this scope is lexically enclosed |
| 11653 */ |
| 11654 Scope get enclosingScope => null; |
| 11655 |
| 11656 /** |
| 11657 * Return the listener that is to be informed when an error is encountered. |
| 11658 * |
| 11659 * @return the listener that is to be informed when an error is encountered |
| 11660 */ |
| 11661 AnalysisErrorListener get errorListener; |
| 11662 |
| 11663 /** |
| 11664 * Add the given element to this scope. If there is already an element with th
e given name defined |
| 11665 * in this scope, then an error will be generated and the original element wil
l continue to be |
| 11666 * mapped to the name. If there is an element with the given name in an enclos
ing scope, then a |
| 11667 * warning will be generated but the given element will hide the inherited ele
ment. |
| 11668 * |
| 11669 * @param element the element to be added to this scope |
| 11670 */ |
| 11671 void define(Element element) { |
| 11672 String name = _getName(element); |
| 11673 if (name != null && !name.isEmpty) { |
| 11674 if (_definedNames.containsKey(name)) { |
| 11675 errorListener |
| 11676 .onError(getErrorForDuplicate(_definedNames[name], element)); |
| 11677 } else { |
| 11678 _definedNames[name] = element; |
| 11679 _hasName = true; |
| 11680 } |
| 11681 } |
| 11682 } |
| 11683 |
| 11684 /** |
| 11685 * Add the given element to this scope without checking for duplication or hid
ing. |
| 11686 * |
| 11687 * @param name the name of the element to be added |
| 11688 * @param element the element to be added to this scope |
| 11689 */ |
| 11690 void defineNameWithoutChecking(String name, Element element) { |
| 11691 _definedNames[name] = element; |
| 11692 _hasName = true; |
| 11693 } |
| 11694 |
| 11695 /** |
| 11696 * Add the given element to this scope without checking for duplication or hid
ing. |
| 11697 * |
| 11698 * @param element the element to be added to this scope |
| 11699 */ |
| 11700 void defineWithoutChecking(Element element) { |
| 11701 _definedNames[_getName(element)] = element; |
| 11702 _hasName = true; |
| 11703 } |
| 11704 |
| 11705 /** |
| 11706 * Return the error code to be used when reporting that a name being defined l
ocally conflicts |
| 11707 * with another element of the same name in the local scope. |
| 11708 * |
| 11709 * @param existing the first element to be declared with the conflicting name |
| 11710 * @param duplicate another element declared with the conflicting name |
| 11711 * @return the error code used to report duplicate names within a scope |
| 11712 */ |
| 11713 AnalysisError getErrorForDuplicate(Element existing, Element duplicate) { |
| 11714 // TODO(brianwilkerson) Customize the error message based on the types of |
| 11715 // elements that share the same name. |
| 11716 // TODO(jwren) There are 4 error codes for duplicate, but only 1 is being |
| 11717 // generated. |
| 11718 Source source = duplicate.source; |
| 11719 return new AnalysisError( |
| 11720 source, |
| 11721 duplicate.nameOffset, |
| 11722 duplicate.displayName.length, |
| 11723 CompileTimeErrorCode.DUPLICATE_DEFINITION, |
| 11724 [existing.displayName]); |
| 11725 } |
| 11726 |
| 11727 /** |
| 11728 * Return the source that contains the given identifier, or the source associa
ted with this scope |
| 11729 * if the source containing the identifier could not be determined. |
| 11730 * |
| 11731 * @param identifier the identifier whose source is to be returned |
| 11732 * @return the source that contains the given identifier |
| 11733 */ |
| 11734 Source getSource(AstNode node) { |
| 11735 CompilationUnit unit = node.getAncestor((node) => node is CompilationUnit); |
| 11736 if (unit != null) { |
| 11737 CompilationUnitElement unitElement = unit.element; |
| 11738 if (unitElement != null) { |
| 11739 return unitElement.source; |
| 11740 } |
| 11741 } |
| 11742 return null; |
| 11743 } |
| 11744 |
| 11745 /** |
| 11746 * Return the element with which the given name is associated, or `null` if th
e name is not |
| 11747 * defined within this scope. |
| 11748 * |
| 11749 * @param identifier the identifier node to lookup element for, used to report
correct kind of a |
| 11750 * problem and associate problem with |
| 11751 * @param name the name associated with the element to be returned |
| 11752 * @param referencingLibrary the library that contains the reference to the na
me, used to |
| 11753 * implement library-level privacy |
| 11754 * @return the element with which the given name is associated |
| 11755 */ |
| 11756 Element internalLookup( |
| 11757 Identifier identifier, String name, LibraryElement referencingLibrary); |
| 11758 |
| 11759 /** |
| 11760 * Return the element with which the given name is associated, or `null` if th
e name is not |
| 11761 * defined within this scope. This method only returns elements that are direc
tly defined within |
| 11762 * this scope, not elements that are defined in an enclosing scope. |
| 11763 * |
| 11764 * @param name the name associated with the element to be returned |
| 11765 * @param referencingLibrary the library that contains the reference to the na
me, used to |
| 11766 * implement library-level privacy |
| 11767 * @return the element with which the given name is associated |
| 11768 */ |
| 11769 Element localLookup(String name, LibraryElement referencingLibrary) { |
| 11770 if (_hasName) { |
| 11771 return _definedNames[name]; |
| 11772 } |
| 11773 return null; |
| 11774 } |
| 11775 |
| 11776 /** |
| 11777 * Return the element with which the given identifier is associated, or `null`
if the name |
| 11778 * is not defined within this scope. |
| 11779 * |
| 11780 * @param identifier the identifier associated with the element to be returned |
| 11781 * @param referencingLibrary the library that contains the reference to the na
me, used to |
| 11782 * implement library-level privacy |
| 11783 * @return the element with which the given identifier is associated |
| 11784 */ |
| 11785 Element lookup(Identifier identifier, LibraryElement referencingLibrary) => |
| 11786 internalLookup(identifier, identifier.name, referencingLibrary); |
| 11787 |
| 11788 /** |
| 11789 * Return the name that will be used to look up the given element. |
| 11790 * |
| 11791 * @param element the element whose look-up name is to be returned |
| 11792 * @return the name that will be used to look up the given element |
| 11793 */ |
| 11794 String _getName(Element element) { |
| 11795 if (element is MethodElement) { |
| 11796 MethodElement method = element; |
| 11797 if (method.name == "-" && method.parameters.length == 0) { |
| 11798 return UNARY_MINUS; |
| 11799 } |
| 11800 } |
| 11801 return element.name; |
| 11802 } |
| 11803 |
| 11804 /** |
| 11805 * Return `true` if the given name is a library-private name. |
| 11806 * |
| 11807 * @param name the name being tested |
| 11808 * @return `true` if the given name is a library-private name |
| 11809 */ |
| 11810 static bool isPrivateName(String name) => |
| 11811 name != null && StringUtilities.startsWithChar(name, PRIVATE_NAME_PREFIX); |
| 11812 } |
| 11813 |
| 11814 /** |
| 11815 * The abstract class `ScopedVisitor` maintains name and label scopes as an AST
structure is |
| 11816 * being visited. |
| 11817 */ |
| 11818 abstract class ScopedVisitor extends UnifyingAstVisitor<Object> { |
| 11819 /** |
| 11820 * The element for the library containing the compilation unit being visited. |
| 11821 */ |
| 11822 final LibraryElement definingLibrary; |
| 11823 |
| 11824 /** |
| 11825 * The source representing the compilation unit being visited. |
| 11826 */ |
| 11827 final Source source; |
| 11828 |
| 11829 /** |
| 11830 * The error listener that will be informed of any errors that are found durin
g resolution. |
| 11831 */ |
| 11832 final AnalysisErrorListener errorListener; |
| 11833 |
| 11834 /** |
| 11835 * The scope used to resolve identifiers. |
| 11836 */ |
| 11837 Scope nameScope; |
| 11838 |
| 11839 /** |
| 11840 * The object used to access the types from the core library. |
| 11841 */ |
| 11842 final TypeProvider typeProvider; |
| 11843 |
| 11844 /** |
| 11845 * The scope used to resolve unlabeled `break` and `continue` statements. |
| 11846 */ |
| 11847 ImplicitLabelScope _implicitLabelScope = ImplicitLabelScope.ROOT; |
| 11848 |
| 11849 /** |
| 11850 * The scope used to resolve labels for `break` and `continue` statements, or |
| 11851 * `null` if no labels have been defined in the current context. |
| 11852 */ |
| 11853 LabelScope labelScope; |
| 11854 |
| 11855 /** |
| 11856 * The class containing the AST nodes being visited, |
| 11857 * or `null` if we are not in the scope of a class. |
| 11858 */ |
| 11859 ClassElement enclosingClass; |
| 11860 |
| 11861 /** |
| 11862 * Initialize a newly created visitor to resolve the nodes in a compilation |
| 11863 * unit. |
| 11864 * |
| 11865 * [definingLibrary] is the element for the library containing the |
| 11866 * compilation unit being visited. |
| 11867 * [source] is the source representing the compilation unit being visited. |
| 11868 * [typeProvider] is the object used to access the types from the core |
| 11869 * library. |
| 11870 * [errorListener] is the error listener that will be informed of any errors |
| 11871 * that are found during resolution. |
| 11872 * [nameScope] is the scope used to resolve identifiers in the node that will |
| 11873 * first be visited. If `null` or unspecified, a new [LibraryScope] will be |
| 11874 * created based on [definingLibrary] and [typeProvider]. |
| 11875 */ |
| 11876 ScopedVisitor( |
| 11877 this.definingLibrary, this.source, this.typeProvider, this.errorListener, |
| 11878 {Scope nameScope}) { |
| 11879 if (nameScope == null) { |
| 11880 this.nameScope = new LibraryScope(definingLibrary, errorListener); |
| 11881 } else { |
| 11882 this.nameScope = nameScope; |
| 11883 } |
| 11884 } |
| 11885 |
| 11886 /** |
| 11887 * Return the implicit label scope in which the current node is being |
| 11888 * resolved. |
| 11889 */ |
| 11890 ImplicitLabelScope get implicitLabelScope => _implicitLabelScope; |
| 11891 |
| 11892 /** |
| 11893 * Replaces the current [Scope] with the enclosing [Scope]. |
| 11894 * |
| 11895 * @return the enclosing [Scope]. |
| 11896 */ |
| 11897 Scope popNameScope() { |
| 11898 nameScope = nameScope.enclosingScope; |
| 11899 return nameScope; |
| 11900 } |
| 11901 |
| 11902 /** |
| 11903 * Pushes a new [Scope] into the visitor. |
| 11904 * |
| 11905 * @return the new [Scope]. |
| 11906 */ |
| 11907 Scope pushNameScope() { |
| 11908 Scope newScope = new EnclosedScope(nameScope); |
| 11909 nameScope = newScope; |
| 11910 return nameScope; |
| 11911 } |
| 11912 |
| 11913 /** |
| 11914 * Report an error with the given error code and arguments. |
| 11915 * |
| 11916 * @param errorCode the error code of the error to be reported |
| 11917 * @param node the node specifying the location of the error |
| 11918 * @param arguments the arguments to the error, used to compose the error mess
age |
| 11919 */ |
| 11920 void reportErrorForNode(ErrorCode errorCode, AstNode node, |
| 11921 [List<Object> arguments]) { |
| 11922 errorListener.onError(new AnalysisError( |
| 11923 source, node.offset, node.length, errorCode, arguments)); |
| 11924 } |
| 11925 |
| 11926 /** |
| 11927 * Report an error with the given error code and arguments. |
| 11928 * |
| 11929 * @param errorCode the error code of the error to be reported |
| 11930 * @param offset the offset of the location of the error |
| 11931 * @param length the length of the location of the error |
| 11932 * @param arguments the arguments to the error, used to compose the error mess
age |
| 11933 */ |
| 11934 void reportErrorForOffset(ErrorCode errorCode, int offset, int length, |
| 11935 [List<Object> arguments]) { |
| 11936 errorListener.onError( |
| 11937 new AnalysisError(source, offset, length, errorCode, arguments)); |
| 11938 } |
| 11939 |
| 11940 /** |
| 11941 * Report an error with the given error code and arguments. |
| 11942 * |
| 11943 * @param errorCode the error code of the error to be reported |
| 11944 * @param token the token specifying the location of the error |
| 11945 * @param arguments the arguments to the error, used to compose the error mess
age |
| 11946 */ |
| 11947 void reportErrorForToken(ErrorCode errorCode, sc.Token token, |
| 11948 [List<Object> arguments]) { |
| 11949 errorListener.onError(new AnalysisError( |
| 11950 source, token.offset, token.length, errorCode, arguments)); |
| 11951 } |
| 11952 |
| 11953 /** |
| 11954 * Visit the given AST node if it is not null. |
| 11955 * |
| 11956 * @param node the node to be visited |
| 11957 */ |
| 11958 void safelyVisit(AstNode node) { |
| 11959 if (node != null) { |
| 11960 node.accept(this); |
| 11961 } |
| 11962 } |
| 11963 |
| 11964 @override |
| 11965 Object visitBlock(Block node) { |
| 11966 Scope outerScope = nameScope; |
| 11967 try { |
| 11968 EnclosedScope enclosedScope = new EnclosedScope(nameScope); |
| 11969 _hideNamesDefinedInBlock(enclosedScope, node); |
| 11970 nameScope = enclosedScope; |
| 11971 super.visitBlock(node); |
| 11972 } finally { |
| 11973 nameScope = outerScope; |
| 11974 } |
| 11975 return null; |
| 11976 } |
| 11977 |
| 11978 @override |
| 11979 Object visitBlockFunctionBody(BlockFunctionBody node) { |
| 11980 ImplicitLabelScope implicitOuterScope = _implicitLabelScope; |
| 11981 try { |
| 11982 _implicitLabelScope = ImplicitLabelScope.ROOT; |
| 11983 super.visitBlockFunctionBody(node); |
| 11984 } finally { |
| 11985 _implicitLabelScope = implicitOuterScope; |
| 11986 } |
| 11987 return null; |
| 11988 } |
| 11989 |
| 11990 @override |
| 11991 Object visitCatchClause(CatchClause node) { |
| 11992 SimpleIdentifier exception = node.exceptionParameter; |
| 11993 if (exception != null) { |
| 11994 Scope outerScope = nameScope; |
| 11995 try { |
| 11996 nameScope = new EnclosedScope(nameScope); |
| 11997 nameScope.define(exception.staticElement); |
| 11998 SimpleIdentifier stackTrace = node.stackTraceParameter; |
| 11999 if (stackTrace != null) { |
| 12000 nameScope.define(stackTrace.staticElement); |
| 12001 } |
| 12002 super.visitCatchClause(node); |
| 12003 } finally { |
| 12004 nameScope = outerScope; |
| 12005 } |
| 12006 } else { |
| 12007 super.visitCatchClause(node); |
| 12008 } |
| 12009 return null; |
| 12010 } |
| 12011 |
| 12012 @override |
| 12013 Object visitClassDeclaration(ClassDeclaration node) { |
| 12014 ClassElement classElement = node.element; |
| 12015 Scope outerScope = nameScope; |
| 12016 try { |
| 12017 if (classElement == null) { |
| 12018 AnalysisEngine.instance.logger.logInformation( |
| 12019 "Missing element for class declaration ${node.name.name} in ${defini
ngLibrary.source.fullName}", |
| 12020 new CaughtException(new AnalysisException(), null)); |
| 12021 super.visitClassDeclaration(node); |
| 12022 } else { |
| 12023 ClassElement outerClass = enclosingClass; |
| 12024 try { |
| 12025 enclosingClass = node.element; |
| 12026 nameScope = new TypeParameterScope(nameScope, classElement); |
| 12027 visitClassDeclarationInScope(node); |
| 12028 nameScope = new ClassScope(nameScope, classElement); |
| 12029 visitClassMembersInScope(node); |
| 12030 } finally { |
| 12031 enclosingClass = outerClass; |
| 12032 } |
| 12033 } |
| 12034 } finally { |
| 12035 nameScope = outerScope; |
| 12036 } |
| 12037 return null; |
| 12038 } |
| 12039 |
| 12040 void visitClassDeclarationInScope(ClassDeclaration node) { |
| 12041 safelyVisit(node.name); |
| 12042 safelyVisit(node.typeParameters); |
| 12043 safelyVisit(node.extendsClause); |
| 12044 safelyVisit(node.withClause); |
| 12045 safelyVisit(node.implementsClause); |
| 12046 safelyVisit(node.nativeClause); |
| 12047 } |
| 12048 |
| 12049 void visitClassMembersInScope(ClassDeclaration node) { |
| 12050 safelyVisit(node.documentationComment); |
| 12051 node.metadata.accept(this); |
| 12052 node.members.accept(this); |
| 12053 } |
| 12054 |
| 12055 @override |
| 12056 Object visitClassTypeAlias(ClassTypeAlias node) { |
| 12057 Scope outerScope = nameScope; |
| 12058 try { |
| 12059 ClassElement element = node.element; |
| 12060 nameScope = |
| 12061 new ClassScope(new TypeParameterScope(nameScope, element), element); |
| 12062 super.visitClassTypeAlias(node); |
| 12063 } finally { |
| 12064 nameScope = outerScope; |
| 12065 } |
| 12066 return null; |
| 12067 } |
| 12068 |
| 12069 @override |
| 12070 Object visitConstructorDeclaration(ConstructorDeclaration node) { |
| 12071 ConstructorElement constructorElement = node.element; |
| 12072 Scope outerScope = nameScope; |
| 12073 try { |
| 12074 if (constructorElement == null) { |
| 12075 StringBuffer buffer = new StringBuffer(); |
| 12076 buffer.write("Missing element for constructor "); |
| 12077 buffer.write(node.returnType.name); |
| 12078 if (node.name != null) { |
| 12079 buffer.write("."); |
| 12080 buffer.write(node.name.name); |
| 12081 } |
| 12082 buffer.write(" in "); |
| 12083 buffer.write(definingLibrary.source.fullName); |
| 12084 AnalysisEngine.instance.logger.logInformation(buffer.toString(), |
| 12085 new CaughtException(new AnalysisException(), null)); |
| 12086 } else { |
| 12087 nameScope = new FunctionScope(nameScope, constructorElement); |
| 12088 } |
| 12089 super.visitConstructorDeclaration(node); |
| 12090 } finally { |
| 12091 nameScope = outerScope; |
| 12092 } |
| 12093 return null; |
| 12094 } |
| 12095 |
| 12096 @override |
| 12097 Object visitDeclaredIdentifier(DeclaredIdentifier node) { |
| 12098 VariableElement element = node.element; |
| 12099 if (element != null) { |
| 12100 nameScope.define(element); |
| 12101 } |
| 12102 super.visitDeclaredIdentifier(node); |
| 12103 return null; |
| 12104 } |
| 12105 |
| 12106 @override |
| 12107 Object visitDoStatement(DoStatement node) { |
| 12108 ImplicitLabelScope outerImplicitScope = _implicitLabelScope; |
| 12109 try { |
| 12110 _implicitLabelScope = _implicitLabelScope.nest(node); |
| 12111 visitStatementInScope(node.body); |
| 12112 safelyVisit(node.condition); |
| 12113 } finally { |
| 12114 _implicitLabelScope = outerImplicitScope; |
| 12115 } |
| 12116 return null; |
| 12117 } |
| 12118 |
| 12119 @override |
| 12120 Object visitEnumDeclaration(EnumDeclaration node) { |
| 12121 ClassElement classElement = node.element; |
| 12122 Scope outerScope = nameScope; |
| 12123 try { |
| 12124 if (classElement == null) { |
| 12125 AnalysisEngine.instance.logger.logInformation( |
| 12126 "Missing element for enum declaration ${node.name.name} in ${definin
gLibrary.source.fullName}", |
| 12127 new CaughtException(new AnalysisException(), null)); |
| 12128 super.visitEnumDeclaration(node); |
| 12129 } else { |
| 12130 ClassElement outerClass = enclosingClass; |
| 12131 try { |
| 12132 enclosingClass = node.element; |
| 12133 nameScope = new ClassScope(nameScope, classElement); |
| 12134 visitEnumMembersInScope(node); |
| 12135 } finally { |
| 12136 enclosingClass = outerClass; |
| 12137 } |
| 12138 } |
| 12139 } finally { |
| 12140 nameScope = outerScope; |
| 12141 } |
| 12142 return null; |
| 12143 } |
| 12144 |
| 12145 void visitEnumMembersInScope(EnumDeclaration node) { |
| 12146 safelyVisit(node.documentationComment); |
| 12147 node.metadata.accept(this); |
| 12148 node.constants.accept(this); |
| 12149 } |
| 12150 |
| 12151 @override |
| 12152 Object visitForEachStatement(ForEachStatement node) { |
| 12153 Scope outerNameScope = nameScope; |
| 12154 ImplicitLabelScope outerImplicitScope = _implicitLabelScope; |
| 12155 try { |
| 12156 nameScope = new EnclosedScope(nameScope); |
| 12157 _implicitLabelScope = _implicitLabelScope.nest(node); |
| 12158 visitForEachStatementInScope(node); |
| 12159 } finally { |
| 12160 nameScope = outerNameScope; |
| 12161 _implicitLabelScope = outerImplicitScope; |
| 12162 } |
| 12163 return null; |
| 12164 } |
| 12165 |
| 12166 /** |
| 12167 * Visit the given statement after it's scope has been created. This replaces
the normal call to |
| 12168 * the inherited visit method so that ResolverVisitor can intervene when type
propagation is |
| 12169 * enabled. |
| 12170 * |
| 12171 * @param node the statement to be visited |
| 12172 */ |
| 12173 void visitForEachStatementInScope(ForEachStatement node) { |
| 12174 // |
| 12175 // We visit the iterator before the loop variable because the loop variable |
| 12176 // cannot be in scope while visiting the iterator. |
| 12177 // |
| 12178 safelyVisit(node.identifier); |
| 12179 safelyVisit(node.iterable); |
| 12180 safelyVisit(node.loopVariable); |
| 12181 visitStatementInScope(node.body); |
| 12182 } |
| 12183 |
| 12184 @override |
| 12185 Object visitFormalParameterList(FormalParameterList node) { |
| 12186 super.visitFormalParameterList(node); |
| 12187 // We finished resolving function signature, now include formal parameters |
| 12188 // scope. Note: we must not do this if the parent is a |
| 12189 // FunctionTypedFormalParameter, because in that case we aren't finished |
| 12190 // resolving the full function signature, just a part of it. |
| 12191 if (nameScope is FunctionScope && |
| 12192 node.parent is! FunctionTypedFormalParameter) { |
| 12193 (nameScope as FunctionScope).defineParameters(); |
| 12194 } |
| 12195 if (nameScope is FunctionTypeScope) { |
| 12196 (nameScope as FunctionTypeScope).defineParameters(); |
| 12197 } |
| 12198 return null; |
| 12199 } |
| 12200 |
| 12201 @override |
| 12202 Object visitForStatement(ForStatement node) { |
| 12203 Scope outerNameScope = nameScope; |
| 12204 ImplicitLabelScope outerImplicitScope = _implicitLabelScope; |
| 12205 try { |
| 12206 nameScope = new EnclosedScope(nameScope); |
| 12207 _implicitLabelScope = _implicitLabelScope.nest(node); |
| 12208 visitForStatementInScope(node); |
| 12209 } finally { |
| 12210 nameScope = outerNameScope; |
| 12211 _implicitLabelScope = outerImplicitScope; |
| 12212 } |
| 12213 return null; |
| 12214 } |
| 12215 |
| 12216 /** |
| 12217 * Visit the given statement after it's scope has been created. This replaces
the normal call to |
| 12218 * the inherited visit method so that ResolverVisitor can intervene when type
propagation is |
| 12219 * enabled. |
| 12220 * |
| 12221 * @param node the statement to be visited |
| 12222 */ |
| 12223 void visitForStatementInScope(ForStatement node) { |
| 12224 safelyVisit(node.variables); |
| 12225 safelyVisit(node.initialization); |
| 12226 safelyVisit(node.condition); |
| 12227 node.updaters.accept(this); |
| 12228 visitStatementInScope(node.body); |
| 12229 } |
| 12230 |
| 12231 @override |
| 12232 Object visitFunctionDeclaration(FunctionDeclaration node) { |
| 12233 ExecutableElement functionElement = node.element; |
| 12234 if (functionElement != null && |
| 12235 functionElement.enclosingElement is! CompilationUnitElement) { |
| 12236 nameScope.define(functionElement); |
| 12237 } |
| 12238 Scope outerScope = nameScope; |
| 12239 try { |
| 12240 if (functionElement == null) { |
| 12241 AnalysisEngine.instance.logger.logInformation( |
| 12242 "Missing element for top-level function ${node.name.name} in ${defin
ingLibrary.source.fullName}", |
| 12243 new CaughtException(new AnalysisException(), null)); |
| 12244 } else { |
| 12245 nameScope = new FunctionScope(nameScope, functionElement); |
| 12246 } |
| 12247 super.visitFunctionDeclaration(node); |
| 12248 } finally { |
| 12249 nameScope = outerScope; |
| 12250 } |
| 12251 return null; |
| 12252 } |
| 12253 |
| 12254 @override |
| 12255 Object visitFunctionExpression(FunctionExpression node) { |
| 12256 if (node.parent is FunctionDeclaration) { |
| 12257 // We have already created a function scope and don't need to do so again. |
| 12258 super.visitFunctionExpression(node); |
| 12259 } else { |
| 12260 Scope outerScope = nameScope; |
| 12261 try { |
| 12262 ExecutableElement functionElement = node.element; |
| 12263 if (functionElement == null) { |
| 12264 StringBuffer buffer = new StringBuffer(); |
| 12265 buffer.write("Missing element for function "); |
| 12266 AstNode parent = node.parent; |
| 12267 while (parent != null) { |
| 12268 if (parent is Declaration) { |
| 12269 Element parentElement = parent.element; |
| 12270 buffer.write(parentElement == null |
| 12271 ? "<unknown> " |
| 12272 : "${parentElement.name} "); |
| 12273 } |
| 12274 parent = parent.parent; |
| 12275 } |
| 12276 buffer.write("in "); |
| 12277 buffer.write(definingLibrary.source.fullName); |
| 12278 AnalysisEngine.instance.logger.logInformation(buffer.toString(), |
| 12279 new CaughtException(new AnalysisException(), null)); |
| 12280 } else { |
| 12281 nameScope = new FunctionScope(nameScope, functionElement); |
| 12282 } |
| 12283 super.visitFunctionExpression(node); |
| 12284 } finally { |
| 12285 nameScope = outerScope; |
| 12286 } |
| 12287 } |
| 12288 return null; |
| 12289 } |
| 12290 |
| 12291 @override |
| 12292 Object visitFunctionTypeAlias(FunctionTypeAlias node) { |
| 12293 Scope outerScope = nameScope; |
| 12294 try { |
| 12295 nameScope = new FunctionTypeScope(nameScope, node.element); |
| 12296 super.visitFunctionTypeAlias(node); |
| 12297 } finally { |
| 12298 nameScope = outerScope; |
| 12299 } |
| 12300 return null; |
| 12301 } |
| 12302 |
| 12303 @override |
| 12304 Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { |
| 12305 Scope outerScope = nameScope; |
| 12306 try { |
| 12307 ParameterElement parameterElement = node.element; |
| 12308 if (parameterElement == null) { |
| 12309 AnalysisEngine.instance.logger.logInformation( |
| 12310 "Missing element for function typed formal parameter ${node.identifi
er.name} in ${definingLibrary.source.fullName}", |
| 12311 new CaughtException(new AnalysisException(), null)); |
| 12312 } else { |
| 12313 nameScope = new EnclosedScope(nameScope); |
| 12314 for (TypeParameterElement typeParameter |
| 12315 in parameterElement.typeParameters) { |
| 12316 nameScope.define(typeParameter); |
| 12317 } |
| 12318 } |
| 12319 super.visitFunctionTypedFormalParameter(node); |
| 12320 } finally { |
| 12321 nameScope = outerScope; |
| 12322 } |
| 12323 return null; |
| 12324 } |
| 12325 |
| 12326 @override |
| 12327 Object visitIfStatement(IfStatement node) { |
| 12328 safelyVisit(node.condition); |
| 12329 visitStatementInScope(node.thenStatement); |
| 12330 visitStatementInScope(node.elseStatement); |
| 12331 return null; |
| 12332 } |
| 12333 |
| 12334 @override |
| 12335 Object visitLabeledStatement(LabeledStatement node) { |
| 12336 LabelScope outerScope = _addScopesFor(node.labels, node.unlabeled); |
| 12337 try { |
| 12338 super.visitLabeledStatement(node); |
| 12339 } finally { |
| 12340 labelScope = outerScope; |
| 12341 } |
| 12342 return null; |
| 12343 } |
| 12344 |
| 12345 @override |
| 12346 Object visitMethodDeclaration(MethodDeclaration node) { |
| 12347 Scope outerScope = nameScope; |
| 12348 try { |
| 12349 ExecutableElement methodElement = node.element; |
| 12350 if (methodElement == null) { |
| 12351 AnalysisEngine.instance.logger.logInformation( |
| 12352 "Missing element for method ${node.name.name} in ${definingLibrary.s
ource.fullName}", |
| 12353 new CaughtException(new AnalysisException(), null)); |
| 12354 } else { |
| 12355 nameScope = new FunctionScope(nameScope, methodElement); |
| 12356 } |
| 12357 super.visitMethodDeclaration(node); |
| 12358 } finally { |
| 12359 nameScope = outerScope; |
| 12360 } |
| 12361 return null; |
| 12362 } |
| 12363 |
| 12364 /** |
| 12365 * Visit the given statement after it's scope has been created. This is used b
y ResolverVisitor to |
| 12366 * correctly visit the 'then' and 'else' statements of an 'if' statement. |
| 12367 * |
| 12368 * @param node the statement to be visited |
| 12369 */ |
| 12370 void visitStatementInScope(Statement node) { |
| 12371 if (node is Block) { |
| 12372 // Don't create a scope around a block because the block will create it's |
| 12373 // own scope. |
| 12374 visitBlock(node); |
| 12375 } else if (node != null) { |
| 12376 Scope outerNameScope = nameScope; |
| 12377 try { |
| 12378 nameScope = new EnclosedScope(nameScope); |
| 12379 node.accept(this); |
| 12380 } finally { |
| 12381 nameScope = outerNameScope; |
| 12382 } |
| 12383 } |
| 12384 } |
| 12385 |
| 12386 @override |
| 12387 Object visitSwitchCase(SwitchCase node) { |
| 12388 node.expression.accept(this); |
| 12389 Scope outerNameScope = nameScope; |
| 12390 try { |
| 12391 nameScope = new EnclosedScope(nameScope); |
| 12392 node.statements.accept(this); |
| 12393 } finally { |
| 12394 nameScope = outerNameScope; |
| 12395 } |
| 12396 return null; |
| 12397 } |
| 12398 |
| 12399 @override |
| 12400 Object visitSwitchDefault(SwitchDefault node) { |
| 12401 Scope outerNameScope = nameScope; |
| 12402 try { |
| 12403 nameScope = new EnclosedScope(nameScope); |
| 12404 node.statements.accept(this); |
| 12405 } finally { |
| 12406 nameScope = outerNameScope; |
| 12407 } |
| 12408 return null; |
| 12409 } |
| 12410 |
| 12411 @override |
| 12412 Object visitSwitchStatement(SwitchStatement node) { |
| 12413 LabelScope outerScope = labelScope; |
| 12414 ImplicitLabelScope outerImplicitScope = _implicitLabelScope; |
| 12415 try { |
| 12416 _implicitLabelScope = _implicitLabelScope.nest(node); |
| 12417 for (SwitchMember member in node.members) { |
| 12418 for (Label label in member.labels) { |
| 12419 SimpleIdentifier labelName = label.label; |
| 12420 LabelElement labelElement = labelName.staticElement as LabelElement; |
| 12421 labelScope = |
| 12422 new LabelScope(labelScope, labelName.name, member, labelElement); |
| 12423 } |
| 12424 } |
| 12425 super.visitSwitchStatement(node); |
| 12426 } finally { |
| 12427 labelScope = outerScope; |
| 12428 _implicitLabelScope = outerImplicitScope; |
| 12429 } |
| 12430 return null; |
| 12431 } |
| 12432 |
| 12433 @override |
| 12434 Object visitVariableDeclaration(VariableDeclaration node) { |
| 12435 super.visitVariableDeclaration(node); |
| 12436 if (node.parent.parent is! TopLevelVariableDeclaration && |
| 12437 node.parent.parent is! FieldDeclaration) { |
| 12438 VariableElement element = node.element; |
| 12439 if (element != null) { |
| 12440 nameScope.define(element); |
| 12441 } |
| 12442 } |
| 12443 return null; |
| 12444 } |
| 12445 |
| 12446 @override |
| 12447 Object visitWhileStatement(WhileStatement node) { |
| 12448 safelyVisit(node.condition); |
| 12449 ImplicitLabelScope outerImplicitScope = _implicitLabelScope; |
| 12450 try { |
| 12451 _implicitLabelScope = _implicitLabelScope.nest(node); |
| 12452 visitStatementInScope(node.body); |
| 12453 } finally { |
| 12454 _implicitLabelScope = outerImplicitScope; |
| 12455 } |
| 12456 return null; |
| 12457 } |
| 12458 |
| 12459 /** |
| 12460 * Add scopes for each of the given labels. |
| 12461 * |
| 12462 * @param labels the labels for which new scopes are to be added |
| 12463 * @return the scope that was in effect before the new scopes were added |
| 12464 */ |
| 12465 LabelScope _addScopesFor(NodeList<Label> labels, AstNode node) { |
| 12466 LabelScope outerScope = labelScope; |
| 12467 for (Label label in labels) { |
| 12468 SimpleIdentifier labelNameNode = label.label; |
| 12469 String labelName = labelNameNode.name; |
| 12470 LabelElement labelElement = labelNameNode.staticElement as LabelElement; |
| 12471 labelScope = new LabelScope(labelScope, labelName, node, labelElement); |
| 12472 } |
| 12473 return outerScope; |
| 12474 } |
| 12475 |
| 12476 /** |
| 12477 * Marks the local declarations of the given [Block] hidden in the enclosing s
cope. |
| 12478 * According to the scoping rules name is hidden if block defines it, but name
is defined after |
| 12479 * its declaration statement. |
| 12480 */ |
| 12481 void _hideNamesDefinedInBlock(EnclosedScope scope, Block block) { |
| 12482 NodeList<Statement> statements = block.statements; |
| 12483 int statementCount = statements.length; |
| 12484 for (int i = 0; i < statementCount; i++) { |
| 12485 Statement statement = statements[i]; |
| 12486 if (statement is VariableDeclarationStatement) { |
| 12487 VariableDeclarationStatement vds = statement; |
| 12488 NodeList<VariableDeclaration> variables = vds.variables.variables; |
| 12489 int variableCount = variables.length; |
| 12490 for (int j = 0; j < variableCount; j++) { |
| 12491 scope.hide(variables[j].element); |
| 12492 } |
| 12493 } else if (statement is FunctionDeclarationStatement) { |
| 12494 FunctionDeclarationStatement fds = statement; |
| 12495 scope.hide(fds.functionDeclaration.element); |
| 12496 } |
| 12497 } |
| 12498 } |
| 12499 } |
| 12500 |
| 12501 /** |
| 12502 * Instances of this class manage the knowledge of what the set of subtypes are
for a given type. |
| 12503 */ |
| 12504 class SubtypeManager { |
| 12505 /** |
| 12506 * A map between [ClassElement]s and a set of [ClassElement]s that are subtype
s of the |
| 12507 * key. |
| 12508 */ |
| 12509 HashMap<ClassElement, HashSet<ClassElement>> _subtypeMap = |
| 12510 new HashMap<ClassElement, HashSet<ClassElement>>(); |
| 12511 |
| 12512 /** |
| 12513 * The set of all [LibraryElement]s that have been visited by the manager. Thi
s is used both |
| 12514 * to prevent infinite loops in the recursive methods, and also as a marker fo
r the scope of the |
| 12515 * libraries visited by this manager. |
| 12516 */ |
| 12517 HashSet<LibraryElement> _visitedLibraries = new HashSet<LibraryElement>(); |
| 12518 |
| 12519 /** |
| 12520 * Given some [ClassElement], return the set of all subtypes, and subtypes of
subtypes. |
| 12521 * |
| 12522 * @param classElement the class to recursively return the set of subtypes of |
| 12523 */ |
| 12524 HashSet<ClassElement> computeAllSubtypes(ClassElement classElement) { |
| 12525 // Ensure that we have generated the subtype map for the library |
| 12526 _computeSubtypesInLibrary(classElement.library); |
| 12527 // use the subtypeMap to compute the set of all subtypes and subtype's |
| 12528 // subtypes |
| 12529 HashSet<ClassElement> allSubtypes = new HashSet<ClassElement>(); |
| 12530 _safelyComputeAllSubtypes( |
| 12531 classElement, new HashSet<ClassElement>(), allSubtypes); |
| 12532 return allSubtypes; |
| 12533 } |
| 12534 |
| 12535 /** |
| 12536 * Given some [LibraryElement], visit all of the types in the library, the pas
sed library, |
| 12537 * and any imported libraries, will be in the [visitedLibraries] set. |
| 12538 * |
| 12539 * @param libraryElement the library to visit, it it hasn't been visited alrea
dy |
| 12540 */ |
| 12541 void ensureLibraryVisited(LibraryElement libraryElement) { |
| 12542 _computeSubtypesInLibrary(libraryElement); |
| 12543 } |
| 12544 |
| 12545 /** |
| 12546 * Given some [ClassElement], this method adds all of the pairs combinations o
f itself and |
| 12547 * all of its supertypes to the [subtypeMap] map. |
| 12548 * |
| 12549 * @param classElement the class element |
| 12550 */ |
| 12551 void _computeSubtypesInClass(ClassElement classElement) { |
| 12552 InterfaceType supertypeType = classElement.supertype; |
| 12553 if (supertypeType != null) { |
| 12554 ClassElement supertypeElement = supertypeType.element; |
| 12555 if (supertypeElement != null) { |
| 12556 _putInSubtypeMap(supertypeElement, classElement); |
| 12557 } |
| 12558 } |
| 12559 List<InterfaceType> interfaceTypes = classElement.interfaces; |
| 12560 for (InterfaceType interfaceType in interfaceTypes) { |
| 12561 ClassElement interfaceElement = interfaceType.element; |
| 12562 if (interfaceElement != null) { |
| 12563 _putInSubtypeMap(interfaceElement, classElement); |
| 12564 } |
| 12565 } |
| 12566 List<InterfaceType> mixinTypes = classElement.mixins; |
| 12567 for (InterfaceType mixinType in mixinTypes) { |
| 12568 ClassElement mixinElement = mixinType.element; |
| 12569 if (mixinElement != null) { |
| 12570 _putInSubtypeMap(mixinElement, classElement); |
| 12571 } |
| 12572 } |
| 12573 } |
| 12574 |
| 12575 /** |
| 12576 * Given some [CompilationUnitElement], this method calls |
| 12577 * [computeAllSubtypes] on all of the [ClassElement]s in the |
| 12578 * compilation unit. |
| 12579 * |
| 12580 * @param unitElement the compilation unit element |
| 12581 */ |
| 12582 void _computeSubtypesInCompilationUnit(CompilationUnitElement unitElement) { |
| 12583 List<ClassElement> classElements = unitElement.types; |
| 12584 for (ClassElement classElement in classElements) { |
| 12585 _computeSubtypesInClass(classElement); |
| 12586 } |
| 12587 } |
| 12588 |
| 12589 /** |
| 12590 * Given some [LibraryElement], this method calls |
| 12591 * [computeAllSubtypes] on all of the [ClassElement]s in the |
| 12592 * compilation unit, and itself for all imported and exported libraries. All v
isited libraries are |
| 12593 * added to the [visitedLibraries] set. |
| 12594 * |
| 12595 * @param libraryElement the library element |
| 12596 */ |
| 12597 void _computeSubtypesInLibrary(LibraryElement libraryElement) { |
| 12598 if (libraryElement == null || _visitedLibraries.contains(libraryElement)) { |
| 12599 return; |
| 12600 } |
| 12601 _visitedLibraries.add(libraryElement); |
| 12602 _computeSubtypesInCompilationUnit(libraryElement.definingCompilationUnit); |
| 12603 List<CompilationUnitElement> parts = libraryElement.parts; |
| 12604 for (CompilationUnitElement part in parts) { |
| 12605 _computeSubtypesInCompilationUnit(part); |
| 12606 } |
| 12607 List<LibraryElement> imports = libraryElement.importedLibraries; |
| 12608 for (LibraryElement importElt in imports) { |
| 12609 _computeSubtypesInLibrary(importElt.library); |
| 12610 } |
| 12611 List<LibraryElement> exports = libraryElement.exportedLibraries; |
| 12612 for (LibraryElement exportElt in exports) { |
| 12613 _computeSubtypesInLibrary(exportElt.library); |
| 12614 } |
| 12615 } |
| 12616 |
| 12617 /** |
| 12618 * Add some key/ value pair into the [subtypeMap] map. |
| 12619 * |
| 12620 * @param supertypeElement the key for the [subtypeMap] map |
| 12621 * @param subtypeElement the value for the [subtypeMap] map |
| 12622 */ |
| 12623 void _putInSubtypeMap( |
| 12624 ClassElement supertypeElement, ClassElement subtypeElement) { |
| 12625 HashSet<ClassElement> subtypes = _subtypeMap[supertypeElement]; |
| 12626 if (subtypes == null) { |
| 12627 subtypes = new HashSet<ClassElement>(); |
| 12628 _subtypeMap[supertypeElement] = subtypes; |
| 12629 } |
| 12630 subtypes.add(subtypeElement); |
| 12631 } |
| 12632 |
| 12633 /** |
| 12634 * Given some [ClassElement] and a [HashSet<ClassElement>], this method recurs
ively |
| 12635 * adds all of the subtypes of the [ClassElement] to the passed array. |
| 12636 * |
| 12637 * @param classElement the type to compute the set of subtypes of |
| 12638 * @param visitedClasses the set of class elements that this method has alread
y recursively seen |
| 12639 * @param allSubtypes the computed set of subtypes of the passed class element |
| 12640 */ |
| 12641 void _safelyComputeAllSubtypes(ClassElement classElement, |
| 12642 HashSet<ClassElement> visitedClasses, HashSet<ClassElement> allSubtypes) { |
| 12643 if (!visitedClasses.add(classElement)) { |
| 12644 // if this class has already been called on this class element |
| 12645 return; |
| 12646 } |
| 12647 HashSet<ClassElement> subtypes = _subtypeMap[classElement]; |
| 12648 if (subtypes == null) { |
| 12649 return; |
| 12650 } |
| 12651 for (ClassElement subtype in subtypes) { |
| 12652 _safelyComputeAllSubtypes(subtype, visitedClasses, allSubtypes); |
| 12653 } |
| 12654 allSubtypes.addAll(subtypes); |
| 12655 } |
| 12656 } |
| 12657 |
| 12658 /** |
| 12659 * Instances of the class `ToDoFinder` find to-do comments in Dart code. |
| 12660 */ |
| 12661 class ToDoFinder { |
| 12662 /** |
| 12663 * The error reporter by which to-do comments will be reported. |
| 12664 */ |
| 12665 final ErrorReporter _errorReporter; |
| 12666 |
| 12667 /** |
| 12668 * Initialize a newly created to-do finder to report to-do comments to the giv
en reporter. |
| 12669 * |
| 12670 * @param errorReporter the error reporter by which to-do comments will be rep
orted |
| 12671 */ |
| 12672 ToDoFinder(this._errorReporter); |
| 12673 |
| 12674 /** |
| 12675 * Search the comments in the given compilation unit for to-do comments and re
port an error for |
| 12676 * each. |
| 12677 * |
| 12678 * @param unit the compilation unit containing the to-do comments |
| 12679 */ |
| 12680 void findIn(CompilationUnit unit) { |
| 12681 _gatherTodoComments(unit.beginToken); |
| 12682 } |
| 12683 |
| 12684 /** |
| 12685 * Search the comment tokens reachable from the given token and create errors
for each to-do |
| 12686 * comment. |
| 12687 * |
| 12688 * @param token the head of the list of tokens being searched |
| 12689 */ |
| 12690 void _gatherTodoComments(sc.Token token) { |
| 12691 while (token != null && token.type != sc.TokenType.EOF) { |
| 12692 sc.Token commentToken = token.precedingComments; |
| 12693 while (commentToken != null) { |
| 12694 if (commentToken.type == sc.TokenType.SINGLE_LINE_COMMENT || |
| 12695 commentToken.type == sc.TokenType.MULTI_LINE_COMMENT) { |
| 12696 _scrapeTodoComment(commentToken); |
| 12697 } |
| 12698 commentToken = commentToken.next; |
| 12699 } |
| 12700 token = token.next; |
| 12701 } |
| 12702 } |
| 12703 |
| 12704 /** |
| 12705 * Look for user defined tasks in comments and convert them into info level an
alysis issues. |
| 12706 * |
| 12707 * @param commentToken the comment token to analyze |
| 12708 */ |
| 12709 void _scrapeTodoComment(sc.Token commentToken) { |
| 12710 JavaPatternMatcher matcher = |
| 12711 new JavaPatternMatcher(TodoCode.TODO_REGEX, commentToken.lexeme); |
| 12712 if (matcher.find()) { |
| 12713 int offset = |
| 12714 commentToken.offset + matcher.start() + matcher.group(1).length; |
| 12715 int length = matcher.group(2).length; |
| 12716 _errorReporter.reportErrorForOffset( |
| 12717 TodoCode.TODO, offset, length, [matcher.group(2)]); |
| 12718 } |
| 12719 } |
| 12720 } |
| 12721 |
| 12722 /** |
| 12723 * Instances of the class `TypeOverrideManager` manage the ability to override t
he type of an |
| 12724 * element within a given context. |
| 12725 */ |
| 12726 class TypeOverrideManager { |
| 12727 /** |
| 12728 * The current override scope, or `null` if no scope has been entered. |
| 12729 */ |
| 12730 TypeOverrideManager_TypeOverrideScope currentScope; |
| 12731 |
| 12732 /** |
| 12733 * Apply a set of overrides that were previously captured. |
| 12734 * |
| 12735 * @param overrides the overrides to be applied |
| 12736 */ |
| 12737 void applyOverrides(Map<VariableElement, DartType> overrides) { |
| 12738 if (currentScope == null) { |
| 12739 throw new IllegalStateException("Cannot apply overrides without a scope"); |
| 12740 } |
| 12741 currentScope.applyOverrides(overrides); |
| 12742 } |
| 12743 |
| 12744 /** |
| 12745 * Return a table mapping the elements whose type is overridden in the current
scope to the |
| 12746 * overriding type. |
| 12747 * |
| 12748 * @return the overrides in the current scope |
| 12749 */ |
| 12750 Map<VariableElement, DartType> captureLocalOverrides() { |
| 12751 if (currentScope == null) { |
| 12752 throw new IllegalStateException( |
| 12753 "Cannot capture local overrides without a scope"); |
| 12754 } |
| 12755 return currentScope.captureLocalOverrides(); |
| 12756 } |
| 12757 |
| 12758 /** |
| 12759 * Return a map from the elements for the variables in the given list that hav
e their types |
| 12760 * overridden to the overriding type. |
| 12761 * |
| 12762 * @param variableList the list of variables whose overriding types are to be
captured |
| 12763 * @return a table mapping elements to their overriding types |
| 12764 */ |
| 12765 Map<VariableElement, DartType> captureOverrides( |
| 12766 VariableDeclarationList variableList) { |
| 12767 if (currentScope == null) { |
| 12768 throw new IllegalStateException( |
| 12769 "Cannot capture overrides without a scope"); |
| 12770 } |
| 12771 return currentScope.captureOverrides(variableList); |
| 12772 } |
| 12773 |
| 12774 /** |
| 12775 * Enter a new override scope. |
| 12776 */ |
| 12777 void enterScope() { |
| 12778 currentScope = new TypeOverrideManager_TypeOverrideScope(currentScope); |
| 12779 } |
| 12780 |
| 12781 /** |
| 12782 * Exit the current override scope. |
| 12783 */ |
| 12784 void exitScope() { |
| 12785 if (currentScope == null) { |
| 12786 throw new IllegalStateException("No scope to exit"); |
| 12787 } |
| 12788 currentScope = currentScope._outerScope; |
| 12789 } |
| 12790 |
| 12791 /** |
| 12792 * Return the best type information available for the given element. If the ty
pe of the element |
| 12793 * has been overridden, then return the overriding type. Otherwise, return the
static type. |
| 12794 * |
| 12795 * @param element the element for which type information is to be returned |
| 12796 * @return the best type information available for the given element |
| 12797 */ |
| 12798 DartType getBestType(VariableElement element) { |
| 12799 DartType bestType = getType(element); |
| 12800 return bestType == null ? element.type : bestType; |
| 12801 } |
| 12802 |
| 12803 /** |
| 12804 * Return the overridden type of the given element, or `null` if the type of t
he element has |
| 12805 * not been overridden. |
| 12806 * |
| 12807 * @param element the element whose type might have been overridden |
| 12808 * @return the overridden type of the given element |
| 12809 */ |
| 12810 DartType getType(Element element) { |
| 12811 if (currentScope == null) { |
| 12812 return null; |
| 12813 } |
| 12814 return currentScope.getType(element); |
| 12815 } |
| 12816 |
| 12817 /** |
| 12818 * Update overrides assuming [perBranchOverrides] is the collection of |
| 12819 * per-branch overrides for *all* branches flowing into a join point. |
| 12820 * |
| 12821 * If a variable type in any of branches is not the same as its type before |
| 12822 * the branching, then its propagated type is reset to `null`. |
| 12823 */ |
| 12824 void mergeOverrides(List<Map<VariableElement, DartType>> perBranchOverrides) { |
| 12825 for (Map<VariableElement, DartType> branch in perBranchOverrides) { |
| 12826 branch.forEach((VariableElement variable, DartType branchType) { |
| 12827 DartType currentType = currentScope.getType(variable); |
| 12828 if (currentType != branchType) { |
| 12829 currentScope.resetType(variable); |
| 12830 } |
| 12831 }); |
| 12832 } |
| 12833 } |
| 12834 |
| 12835 /** |
| 12836 * Set the overridden type of the given element to the given type |
| 12837 * |
| 12838 * @param element the element whose type might have been overridden |
| 12839 * @param type the overridden type of the given element |
| 12840 */ |
| 12841 void setType(VariableElement element, DartType type) { |
| 12842 if (currentScope == null) { |
| 12843 throw new IllegalStateException("Cannot override without a scope"); |
| 12844 } |
| 12845 currentScope.setType(element, type); |
| 12846 } |
| 12847 } |
| 12848 |
| 12849 /** |
| 12850 * Instances of the class `TypeOverrideScope` represent a scope in which the typ
es of |
| 12851 * elements can be overridden. |
| 12852 */ |
| 12853 class TypeOverrideManager_TypeOverrideScope { |
| 12854 /** |
| 12855 * The outer scope in which types might be overridden. |
| 12856 */ |
| 12857 final TypeOverrideManager_TypeOverrideScope _outerScope; |
| 12858 |
| 12859 /** |
| 12860 * A table mapping elements to the overridden type of that element. |
| 12861 */ |
| 12862 Map<VariableElement, DartType> _overridenTypes = |
| 12863 new HashMap<VariableElement, DartType>(); |
| 12864 |
| 12865 /** |
| 12866 * Initialize a newly created scope to be an empty child of the given scope. |
| 12867 * |
| 12868 * @param outerScope the outer scope in which types might be overridden |
| 12869 */ |
| 12870 TypeOverrideManager_TypeOverrideScope(this._outerScope); |
| 12871 |
| 12872 /** |
| 12873 * Apply a set of overrides that were previously captured. |
| 12874 * |
| 12875 * @param overrides the overrides to be applied |
| 12876 */ |
| 12877 void applyOverrides(Map<VariableElement, DartType> overrides) { |
| 12878 _overridenTypes.addAll(overrides); |
| 12879 } |
| 12880 |
| 12881 /** |
| 12882 * Return a table mapping the elements whose type is overridden in the current
scope to the |
| 12883 * overriding type. |
| 12884 * |
| 12885 * @return the overrides in the current scope |
| 12886 */ |
| 12887 Map<VariableElement, DartType> captureLocalOverrides() => _overridenTypes; |
| 12888 |
| 12889 /** |
| 12890 * Return a map from the elements for the variables in the given list that hav
e their types |
| 12891 * overridden to the overriding type. |
| 12892 * |
| 12893 * @param variableList the list of variables whose overriding types are to be
captured |
| 12894 * @return a table mapping elements to their overriding types |
| 12895 */ |
| 12896 Map<VariableElement, DartType> captureOverrides( |
| 12897 VariableDeclarationList variableList) { |
| 12898 Map<VariableElement, DartType> overrides = |
| 12899 new HashMap<VariableElement, DartType>(); |
| 12900 if (variableList.isConst || variableList.isFinal) { |
| 12901 for (VariableDeclaration variable in variableList.variables) { |
| 12902 VariableElement element = variable.element; |
| 12903 if (element != null) { |
| 12904 DartType type = _overridenTypes[element]; |
| 12905 if (type != null) { |
| 12906 overrides[element] = type; |
| 12907 } |
| 12908 } |
| 12909 } |
| 12910 } |
| 12911 return overrides; |
| 12912 } |
| 12913 |
| 12914 /** |
| 12915 * Return the overridden type of the given element, or `null` if the type of t
he element |
| 12916 * has not been overridden. |
| 12917 * |
| 12918 * @param element the element whose type might have been overridden |
| 12919 * @return the overridden type of the given element |
| 12920 */ |
| 12921 DartType getType(Element element) { |
| 12922 if (element is PropertyAccessorElement) { |
| 12923 element = (element as PropertyAccessorElement).variable; |
| 12924 } |
| 12925 DartType type = _overridenTypes[element]; |
| 12926 if (_overridenTypes.containsKey(element)) { |
| 12927 return type; |
| 12928 } |
| 12929 if (type != null) { |
| 12930 return type; |
| 12931 } else if (_outerScope != null) { |
| 12932 return _outerScope.getType(element); |
| 12933 } |
| 12934 return null; |
| 12935 } |
| 12936 |
| 12937 /** |
| 12938 * Clears the overridden type of the given [element]. |
| 12939 */ |
| 12940 void resetType(VariableElement element) { |
| 12941 _overridenTypes[element] = null; |
| 12942 } |
| 12943 |
| 12944 /** |
| 12945 * Set the overridden type of the given element to the given type |
| 12946 * |
| 12947 * @param element the element whose type might have been overridden |
| 12948 * @param type the overridden type of the given element |
| 12949 */ |
| 12950 void setType(VariableElement element, DartType type) { |
| 12951 _overridenTypes[element] = type; |
| 12952 } |
| 12953 } |
| 12954 |
| 12955 /** |
| 12956 * Instances of the class `TypeParameterScope` implement the scope defined by th
e type |
| 12957 * parameters in a class. |
| 12958 */ |
| 12959 class TypeParameterScope extends EnclosedScope { |
| 12960 /** |
| 12961 * Initialize a newly created scope enclosed within another scope. |
| 12962 * |
| 12963 * @param enclosingScope the scope in which this scope is lexically enclosed |
| 12964 * @param typeElement the element representing the type represented by this sc
ope |
| 12965 */ |
| 12966 TypeParameterScope(Scope enclosingScope, ClassElement typeElement) |
| 12967 : super(enclosingScope) { |
| 12968 if (typeElement == null) { |
| 12969 throw new IllegalArgumentException("class element cannot be null"); |
| 12970 } |
| 12971 _defineTypeParameters(typeElement); |
| 12972 } |
| 12973 |
| 12974 /** |
| 12975 * Define the type parameters for the class. |
| 12976 * |
| 12977 * @param typeElement the element representing the type represented by this sc
ope |
| 12978 */ |
| 12979 void _defineTypeParameters(ClassElement typeElement) { |
| 12980 for (TypeParameterElement typeParameter in typeElement.typeParameters) { |
| 12981 define(typeParameter); |
| 12982 } |
| 12983 } |
| 12984 } |
| 12985 |
| 12986 /** |
| 12987 * Instances of the class `TypePromotionManager` manage the ability to promote t
ypes of local |
| 12988 * variables and formal parameters from their declared types based on control fl
ow. |
| 12989 */ |
| 12990 class TypePromotionManager { |
| 12991 /** |
| 12992 * The current promotion scope, or `null` if no scope has been entered. |
| 12993 */ |
| 12994 TypePromotionManager_TypePromoteScope currentScope; |
| 12995 |
| 12996 /** |
| 12997 * Returns the elements with promoted types. |
| 12998 */ |
| 12999 Iterable<Element> get promotedElements => currentScope.promotedElements; |
| 13000 |
| 13001 /** |
| 13002 * Enter a new promotions scope. |
| 13003 */ |
| 13004 void enterScope() { |
| 13005 currentScope = new TypePromotionManager_TypePromoteScope(currentScope); |
| 13006 } |
| 13007 |
| 13008 /** |
| 13009 * Exit the current promotion scope. |
| 13010 */ |
| 13011 void exitScope() { |
| 13012 if (currentScope == null) { |
| 13013 throw new IllegalStateException("No scope to exit"); |
| 13014 } |
| 13015 currentScope = currentScope._outerScope; |
| 13016 } |
| 13017 |
| 13018 /** |
| 13019 * Returns static type of the given variable - declared or promoted. |
| 13020 * |
| 13021 * @return the static type of the given variable - declared or promoted |
| 13022 */ |
| 13023 DartType getStaticType(VariableElement variable) { |
| 13024 DartType staticType = getType(variable); |
| 13025 if (staticType == null) { |
| 13026 staticType = variable.type; |
| 13027 } |
| 13028 return staticType; |
| 13029 } |
| 13030 |
| 13031 /** |
| 13032 * Return the promoted type of the given element, or `null` if the type of the
element has |
| 13033 * not been promoted. |
| 13034 * |
| 13035 * @param element the element whose type might have been promoted |
| 13036 * @return the promoted type of the given element |
| 13037 */ |
| 13038 DartType getType(Element element) { |
| 13039 if (currentScope == null) { |
| 13040 return null; |
| 13041 } |
| 13042 return currentScope.getType(element); |
| 13043 } |
| 13044 |
| 13045 /** |
| 13046 * Set the promoted type of the given element to the given type. |
| 13047 * |
| 13048 * @param element the element whose type might have been promoted |
| 13049 * @param type the promoted type of the given element |
| 13050 */ |
| 13051 void setType(Element element, DartType type) { |
| 13052 if (currentScope == null) { |
| 13053 throw new IllegalStateException("Cannot promote without a scope"); |
| 13054 } |
| 13055 currentScope.setType(element, type); |
| 13056 } |
| 13057 } |
| 13058 |
| 13059 /** |
| 13060 * Instances of the class `TypePromoteScope` represent a scope in which the type
s of |
| 13061 * elements can be promoted. |
| 13062 */ |
| 13063 class TypePromotionManager_TypePromoteScope { |
| 13064 /** |
| 13065 * The outer scope in which types might be promoter. |
| 13066 */ |
| 13067 final TypePromotionManager_TypePromoteScope _outerScope; |
| 13068 |
| 13069 /** |
| 13070 * A table mapping elements to the promoted type of that element. |
| 13071 */ |
| 13072 HashMap<Element, DartType> _promotedTypes = new HashMap<Element, DartType>(); |
| 13073 |
| 13074 /** |
| 13075 * Initialize a newly created scope to be an empty child of the given scope. |
| 13076 * |
| 13077 * @param outerScope the outer scope in which types might be promoted |
| 13078 */ |
| 13079 TypePromotionManager_TypePromoteScope(this._outerScope); |
| 13080 |
| 13081 /** |
| 13082 * Returns the elements with promoted types. |
| 13083 */ |
| 13084 Iterable<Element> get promotedElements => _promotedTypes.keys.toSet(); |
| 13085 |
| 13086 /** |
| 13087 * Return the promoted type of the given element, or `null` if the type of the
element has |
| 13088 * not been promoted. |
| 13089 * |
| 13090 * @param element the element whose type might have been promoted |
| 13091 * @return the promoted type of the given element |
| 13092 */ |
| 13093 DartType getType(Element element) { |
| 13094 DartType type = _promotedTypes[element]; |
| 13095 if (type == null && element is PropertyAccessorElement) { |
| 13096 type = _promotedTypes[element.variable]; |
| 13097 } |
| 13098 if (type != null) { |
| 13099 return type; |
| 13100 } else if (_outerScope != null) { |
| 13101 return _outerScope.getType(element); |
| 13102 } |
| 13103 return null; |
| 13104 } |
| 13105 |
| 13106 /** |
| 13107 * Set the promoted type of the given element to the given type. |
| 13108 * |
| 13109 * @param element the element whose type might have been promoted |
| 13110 * @param type the promoted type of the given element |
| 13111 */ |
| 13112 void setType(Element element, DartType type) { |
| 13113 _promotedTypes[element] = type; |
| 13114 } |
| 13115 } |
| 13116 |
| 13117 /** |
| 13118 * The interface `TypeProvider` defines the behavior of objects that provide acc
ess to types |
| 13119 * defined by the language. |
| 13120 */ |
| 13121 abstract class TypeProvider { |
| 13122 /** |
| 13123 * Return the type representing the built-in type 'bool'. |
| 13124 */ |
| 13125 InterfaceType get boolType; |
| 13126 |
| 13127 /** |
| 13128 * Return the type representing the type 'bottom'. |
| 13129 */ |
| 13130 DartType get bottomType; |
| 13131 |
| 13132 /** |
| 13133 * Return the type representing the built-in type 'Deprecated'. |
| 13134 */ |
| 13135 InterfaceType get deprecatedType; |
| 13136 |
| 13137 /** |
| 13138 * Return the type representing the built-in type 'double'. |
| 13139 */ |
| 13140 InterfaceType get doubleType; |
| 13141 |
| 13142 /** |
| 13143 * Return the type representing the built-in type 'dynamic'. |
| 13144 */ |
| 13145 DartType get dynamicType; |
| 13146 |
| 13147 /** |
| 13148 * Return the type representing the built-in type 'Function'. |
| 13149 */ |
| 13150 InterfaceType get functionType; |
| 13151 |
| 13152 /** |
| 13153 * Return the type representing 'Future<dynamic>'. |
| 13154 */ |
| 13155 InterfaceType get futureDynamicType; |
| 13156 |
| 13157 /** |
| 13158 * Return the type representing 'Future<Null>'. |
| 13159 */ |
| 13160 InterfaceType get futureNullType; |
| 13161 |
| 13162 /** |
| 13163 * Return the type representing the built-in type 'Future'. |
| 13164 */ |
| 13165 InterfaceType get futureType; |
| 13166 |
| 13167 /** |
| 13168 * Return the type representing the built-in type 'int'. |
| 13169 */ |
| 13170 InterfaceType get intType; |
| 13171 |
| 13172 /** |
| 13173 * Return the type representing the type 'Iterable<dynamic>'. |
| 13174 */ |
| 13175 InterfaceType get iterableDynamicType; |
| 13176 |
| 13177 /** |
| 13178 * Return the type representing the built-in type 'Iterable'. |
| 13179 */ |
| 13180 InterfaceType get iterableType; |
| 13181 |
| 13182 /** |
| 13183 * Return the type representing the built-in type 'List'. |
| 13184 */ |
| 13185 InterfaceType get listType; |
| 13186 |
| 13187 /** |
| 13188 * Return the type representing the built-in type 'Map'. |
| 13189 */ |
| 13190 InterfaceType get mapType; |
| 13191 |
| 13192 /** |
| 13193 * Return a list containing all of the types that cannot be either extended or |
| 13194 * implemented. |
| 13195 */ |
| 13196 List<InterfaceType> get nonSubtypableTypes; |
| 13197 |
| 13198 /** |
| 13199 * Return a [DartObjectImpl] representing the `null` object. |
| 13200 */ |
| 13201 DartObjectImpl get nullObject; |
| 13202 |
| 13203 /** |
| 13204 * Return the type representing the built-in type 'Null'. |
| 13205 */ |
| 13206 InterfaceType get nullType; |
| 13207 |
| 13208 /** |
| 13209 * Return the type representing the built-in type 'num'. |
| 13210 */ |
| 13211 InterfaceType get numType; |
| 13212 |
| 13213 /** |
| 13214 * Return the type representing the built-in type 'Object'. |
| 13215 */ |
| 13216 InterfaceType get objectType; |
| 13217 |
| 13218 /** |
| 13219 * Return the type representing the built-in type 'StackTrace'. |
| 13220 */ |
| 13221 InterfaceType get stackTraceType; |
| 13222 |
| 13223 /** |
| 13224 * Return the type representing 'Stream<dynamic>'. |
| 13225 */ |
| 13226 InterfaceType get streamDynamicType; |
| 13227 |
| 13228 /** |
| 13229 * Return the type representing the built-in type 'Stream'. |
| 13230 */ |
| 13231 InterfaceType get streamType; |
| 13232 |
| 13233 /** |
| 13234 * Return the type representing the built-in type 'String'. |
| 13235 */ |
| 13236 InterfaceType get stringType; |
| 13237 |
| 13238 /** |
| 13239 * Return the type representing the built-in type 'Symbol'. |
| 13240 */ |
| 13241 InterfaceType get symbolType; |
| 13242 |
| 13243 /** |
| 13244 * Return the type representing the built-in type 'Type'. |
| 13245 */ |
| 13246 InterfaceType get typeType; |
| 13247 |
| 13248 /** |
| 13249 * Return the type representing typenames that can't be resolved. |
| 13250 */ |
| 13251 DartType get undefinedType; |
| 13252 } |
| 13253 |
| 13254 /** |
| 13255 * Instances of the class `TypeProviderImpl` provide access to types defined by
the language |
| 13256 * by looking for those types in the element model for the core library. |
| 13257 */ |
| 13258 class TypeProviderImpl implements TypeProvider { |
| 13259 /** |
| 13260 * The type representing the built-in type 'bool'. |
| 13261 */ |
| 13262 InterfaceType _boolType; |
| 13263 |
| 13264 /** |
| 13265 * The type representing the type 'bottom'. |
| 13266 */ |
| 13267 DartType _bottomType; |
| 13268 |
| 13269 /** |
| 13270 * The type representing the built-in type 'double'. |
| 13271 */ |
| 13272 InterfaceType _doubleType; |
| 13273 |
| 13274 /** |
| 13275 * The type representing the built-in type 'Deprecated'. |
| 13276 */ |
| 13277 InterfaceType _deprecatedType; |
| 13278 |
| 13279 /** |
| 13280 * The type representing the built-in type 'dynamic'. |
| 13281 */ |
| 13282 DartType _dynamicType; |
| 13283 |
| 13284 /** |
| 13285 * The type representing the built-in type 'Function'. |
| 13286 */ |
| 13287 InterfaceType _functionType; |
| 13288 |
| 13289 /** |
| 13290 * The type representing 'Future<dynamic>'. |
| 13291 */ |
| 13292 InterfaceType _futureDynamicType; |
| 13293 |
| 13294 /** |
| 13295 * The type representing 'Future<Null>'. |
| 13296 */ |
| 13297 InterfaceType _futureNullType; |
| 13298 |
| 13299 /** |
| 13300 * The type representing the built-in type 'Future'. |
| 13301 */ |
| 13302 InterfaceType _futureType; |
| 13303 |
| 13304 /** |
| 13305 * The type representing the built-in type 'int'. |
| 13306 */ |
| 13307 InterfaceType _intType; |
| 13308 |
| 13309 /** |
| 13310 * The type representing 'Iterable<dynamic>'. |
| 13311 */ |
| 13312 InterfaceType _iterableDynamicType; |
| 13313 |
| 13314 /** |
| 13315 * The type representing the built-in type 'Iterable'. |
| 13316 */ |
| 13317 InterfaceType _iterableType; |
| 13318 |
| 13319 /** |
| 13320 * The type representing the built-in type 'List'. |
| 13321 */ |
| 13322 InterfaceType _listType; |
| 13323 |
| 13324 /** |
| 13325 * The type representing the built-in type 'Map'. |
| 13326 */ |
| 13327 InterfaceType _mapType; |
| 13328 |
| 13329 /** |
| 13330 * An shared object representing the value 'null'. |
| 13331 */ |
| 13332 DartObjectImpl _nullObject; |
| 13333 |
| 13334 /** |
| 13335 * The type representing the type 'Null'. |
| 13336 */ |
| 13337 InterfaceType _nullType; |
| 13338 |
| 13339 /** |
| 13340 * The type representing the built-in type 'num'. |
| 13341 */ |
| 13342 InterfaceType _numType; |
| 13343 |
| 13344 /** |
| 13345 * The type representing the built-in type 'Object'. |
| 13346 */ |
| 13347 InterfaceType _objectType; |
| 13348 |
| 13349 /** |
| 13350 * The type representing the built-in type 'StackTrace'. |
| 13351 */ |
| 13352 InterfaceType _stackTraceType; |
| 13353 |
| 13354 /** |
| 13355 * The type representing 'Stream<dynamic>'. |
| 13356 */ |
| 13357 InterfaceType _streamDynamicType; |
| 13358 |
| 13359 /** |
| 13360 * The type representing the built-in type 'Stream'. |
| 13361 */ |
| 13362 InterfaceType _streamType; |
| 13363 |
| 13364 /** |
| 13365 * The type representing the built-in type 'String'. |
| 13366 */ |
| 13367 InterfaceType _stringType; |
| 13368 |
| 13369 /** |
| 13370 * The type representing the built-in type 'Symbol'. |
| 13371 */ |
| 13372 InterfaceType _symbolType; |
| 13373 |
| 13374 /** |
| 13375 * The type representing the built-in type 'Type'. |
| 13376 */ |
| 13377 InterfaceType _typeType; |
| 13378 |
| 13379 /** |
| 13380 * The type representing typenames that can't be resolved. |
| 13381 */ |
| 13382 DartType _undefinedType; |
| 13383 |
| 13384 /** |
| 13385 * Initialize a newly created type provider to provide the types defined in |
| 13386 * the given [coreLibrary] and [asyncLibrary]. |
| 13387 */ |
| 13388 TypeProviderImpl(LibraryElement coreLibrary, LibraryElement asyncLibrary) { |
| 13389 Namespace coreNamespace = |
| 13390 new NamespaceBuilder().createPublicNamespaceForLibrary(coreLibrary); |
| 13391 Namespace asyncNamespace = |
| 13392 new NamespaceBuilder().createPublicNamespaceForLibrary(asyncLibrary); |
| 13393 _initializeFrom(coreNamespace, asyncNamespace); |
| 13394 } |
| 13395 |
| 13396 /** |
| 13397 * Initialize a newly created type provider to provide the types defined in |
| 13398 * the given [Namespace]s. |
| 13399 */ |
| 13400 TypeProviderImpl.forNamespaces( |
| 13401 Namespace coreNamespace, Namespace asyncNamespace) { |
| 13402 _initializeFrom(coreNamespace, asyncNamespace); |
| 13403 } |
| 13404 |
| 13405 @override |
| 13406 InterfaceType get boolType => _boolType; |
| 13407 |
| 13408 @override |
| 13409 DartType get bottomType => _bottomType; |
| 13410 |
| 13411 @override |
| 13412 InterfaceType get deprecatedType => _deprecatedType; |
| 13413 |
| 13414 @override |
| 13415 InterfaceType get doubleType => _doubleType; |
| 13416 |
| 13417 @override |
| 13418 DartType get dynamicType => _dynamicType; |
| 13419 |
| 13420 @override |
| 13421 InterfaceType get functionType => _functionType; |
| 13422 |
| 13423 @override |
| 13424 InterfaceType get futureDynamicType => _futureDynamicType; |
| 13425 |
| 13426 @override |
| 13427 InterfaceType get futureNullType => _futureNullType; |
| 13428 |
| 13429 @override |
| 13430 InterfaceType get futureType => _futureType; |
| 13431 |
| 13432 @override |
| 13433 InterfaceType get intType => _intType; |
| 13434 |
| 13435 @override |
| 13436 InterfaceType get iterableDynamicType => _iterableDynamicType; |
| 13437 |
| 13438 @override |
| 13439 InterfaceType get iterableType => _iterableType; |
| 13440 |
| 13441 @override |
| 13442 InterfaceType get listType => _listType; |
| 13443 |
| 13444 @override |
| 13445 InterfaceType get mapType => _mapType; |
| 13446 |
| 13447 @override |
| 13448 List<InterfaceType> get nonSubtypableTypes => <InterfaceType>[ |
| 13449 nullType, |
| 13450 numType, |
| 13451 intType, |
| 13452 doubleType, |
| 13453 boolType, |
| 13454 stringType |
| 13455 ]; |
| 13456 |
| 13457 @override |
| 13458 DartObjectImpl get nullObject { |
| 13459 if (_nullObject == null) { |
| 13460 _nullObject = new DartObjectImpl(nullType, NullState.NULL_STATE); |
| 13461 } |
| 13462 return _nullObject; |
| 13463 } |
| 13464 |
| 13465 @override |
| 13466 InterfaceType get nullType => _nullType; |
| 13467 |
| 13468 @override |
| 13469 InterfaceType get numType => _numType; |
| 13470 |
| 13471 @override |
| 13472 InterfaceType get objectType => _objectType; |
| 13473 |
| 13474 @override |
| 13475 InterfaceType get stackTraceType => _stackTraceType; |
| 13476 |
| 13477 @override |
| 13478 InterfaceType get streamDynamicType => _streamDynamicType; |
| 13479 |
| 13480 @override |
| 13481 InterfaceType get streamType => _streamType; |
| 13482 |
| 13483 @override |
| 13484 InterfaceType get stringType => _stringType; |
| 13485 |
| 13486 @override |
| 13487 InterfaceType get symbolType => _symbolType; |
| 13488 |
| 13489 @override |
| 13490 InterfaceType get typeType => _typeType; |
| 13491 |
| 13492 @override |
| 13493 DartType get undefinedType => _undefinedType; |
| 13494 |
| 13495 /** |
| 13496 * Return the type with the given name from the given namespace, or `null` if
there is no |
| 13497 * class with the given name. |
| 13498 * |
| 13499 * @param namespace the namespace in which to search for the given name |
| 13500 * @param typeName the name of the type being searched for |
| 13501 * @return the type that was found |
| 13502 */ |
| 13503 InterfaceType _getType(Namespace namespace, String typeName) { |
| 13504 Element element = namespace.get(typeName); |
| 13505 if (element == null) { |
| 13506 AnalysisEngine.instance.logger |
| 13507 .logInformation("No definition of type $typeName"); |
| 13508 return null; |
| 13509 } |
| 13510 return (element as ClassElement).type; |
| 13511 } |
| 13512 |
| 13513 /** |
| 13514 * Initialize the types provided by this type provider from the given |
| 13515 * [Namespace]s. |
| 13516 */ |
| 13517 void _initializeFrom(Namespace coreNamespace, Namespace asyncNamespace) { |
| 13518 _boolType = _getType(coreNamespace, "bool"); |
| 13519 _bottomType = BottomTypeImpl.instance; |
| 13520 _deprecatedType = _getType(coreNamespace, "Deprecated"); |
| 13521 _doubleType = _getType(coreNamespace, "double"); |
| 13522 _dynamicType = DynamicTypeImpl.instance; |
| 13523 _functionType = _getType(coreNamespace, "Function"); |
| 13524 _futureType = _getType(asyncNamespace, "Future"); |
| 13525 _intType = _getType(coreNamespace, "int"); |
| 13526 _iterableType = _getType(coreNamespace, "Iterable"); |
| 13527 _listType = _getType(coreNamespace, "List"); |
| 13528 _mapType = _getType(coreNamespace, "Map"); |
| 13529 _nullType = _getType(coreNamespace, "Null"); |
| 13530 _numType = _getType(coreNamespace, "num"); |
| 13531 _objectType = _getType(coreNamespace, "Object"); |
| 13532 _stackTraceType = _getType(coreNamespace, "StackTrace"); |
| 13533 _streamType = _getType(asyncNamespace, "Stream"); |
| 13534 _stringType = _getType(coreNamespace, "String"); |
| 13535 _symbolType = _getType(coreNamespace, "Symbol"); |
| 13536 _typeType = _getType(coreNamespace, "Type"); |
| 13537 _undefinedType = UndefinedTypeImpl.instance; |
| 13538 _futureDynamicType = _futureType.substitute4(<DartType>[_dynamicType]); |
| 13539 _futureNullType = _futureType.substitute4(<DartType>[_nullType]); |
| 13540 _iterableDynamicType = _iterableType.substitute4(<DartType>[_dynamicType]); |
| 13541 _streamDynamicType = _streamType.substitute4(<DartType>[_dynamicType]); |
| 13542 } |
| 13543 } |
| 13544 |
| 13545 /** |
| 13546 * Instances of the class `TypeResolverVisitor` are used to resolve the types as
sociated with |
| 13547 * the elements in the element model. This includes the types of superclasses, m
ixins, interfaces, |
| 13548 * fields, methods, parameters, and local variables. As a side-effect, this also
finishes building |
| 13549 * the type hierarchy. |
| 13550 */ |
| 13551 class TypeResolverVisitor extends ScopedVisitor { |
| 13552 /** |
| 13553 * The type representing the type 'dynamic'. |
| 13554 */ |
| 13555 DartType _dynamicType; |
| 13556 |
| 13557 /** |
| 13558 * The type representing typenames that can't be resolved. |
| 13559 */ |
| 13560 DartType _undefinedType; |
| 13561 |
| 13562 /** |
| 13563 * The flag specifying if currently visited class references 'super' expressio
n. |
| 13564 */ |
| 13565 bool _hasReferenceToSuper = false; |
| 13566 |
| 13567 /** |
| 13568 * Initialize a newly created visitor to resolve the nodes in an AST node. |
| 13569 * |
| 13570 * [definingLibrary] is the element for the library containing the node being |
| 13571 * visited. |
| 13572 * [source] is the source representing the compilation unit containing the |
| 13573 * node being visited. |
| 13574 * [typeProvider] is the object used to access the types from the core |
| 13575 * library. |
| 13576 * [errorListener] is the error listener that will be informed of any errors |
| 13577 * that are found during resolution. |
| 13578 * [nameScope] is the scope used to resolve identifiers in the node that will |
| 13579 * first be visited. If `null` or unspecified, a new [LibraryScope] will be |
| 13580 * created based on [definingLibrary] and [typeProvider]. |
| 13581 */ |
| 13582 TypeResolverVisitor(LibraryElement definingLibrary, Source source, |
| 13583 TypeProvider typeProvider, AnalysisErrorListener errorListener, |
| 13584 {Scope nameScope}) |
| 13585 : super(definingLibrary, source, typeProvider, errorListener, |
| 13586 nameScope: nameScope) { |
| 13587 _dynamicType = typeProvider.dynamicType; |
| 13588 _undefinedType = typeProvider.undefinedType; |
| 13589 } |
| 13590 |
| 13591 @override |
| 13592 Object visitAnnotation(Annotation node) { |
| 13593 // |
| 13594 // Visit annotations, if the annotation is @proxy, on a class, and "proxy" |
| 13595 // resolves to the proxy annotation in dart.core, then create create the |
| 13596 // ElementAnnotationImpl and set it as the metadata on the enclosing class. |
| 13597 // |
| 13598 // Element resolution is done in the ElementResolver, and this work will be |
| 13599 // done in the general case for all annotations in the ElementResolver. |
| 13600 // The reason we resolve this particular element early is so that |
| 13601 // ClassElement.isProxy() returns the correct information during all |
| 13602 // phases of the ElementResolver. |
| 13603 // |
| 13604 super.visitAnnotation(node); |
| 13605 Identifier identifier = node.name; |
| 13606 if (identifier.name.endsWith(ElementAnnotationImpl.PROXY_VARIABLE_NAME) && |
| 13607 node.parent is ClassDeclaration) { |
| 13608 Element element = nameScope.lookup(identifier, definingLibrary); |
| 13609 if (element != null && |
| 13610 element.library.isDartCore && |
| 13611 element is PropertyAccessorElement) { |
| 13612 // This is the @proxy from dart.core |
| 13613 ClassDeclaration classDeclaration = node.parent as ClassDeclaration; |
| 13614 ElementAnnotationImpl elementAnnotation = |
| 13615 new ElementAnnotationImpl(element); |
| 13616 node.elementAnnotation = elementAnnotation; |
| 13617 (classDeclaration.element as ClassElementImpl).metadata = |
| 13618 <ElementAnnotationImpl>[elementAnnotation]; |
| 13619 } |
| 13620 } |
| 13621 return null; |
| 13622 } |
| 13623 |
| 13624 @override |
| 13625 Object visitCatchClause(CatchClause node) { |
| 13626 super.visitCatchClause(node); |
| 13627 SimpleIdentifier exception = node.exceptionParameter; |
| 13628 if (exception != null) { |
| 13629 // If an 'on' clause is provided the type of the exception parameter is |
| 13630 // the type in the 'on' clause. Otherwise, the type of the exception |
| 13631 // parameter is 'Object'. |
| 13632 TypeName exceptionTypeName = node.exceptionType; |
| 13633 DartType exceptionType; |
| 13634 if (exceptionTypeName == null) { |
| 13635 exceptionType = typeProvider.dynamicType; |
| 13636 } else { |
| 13637 exceptionType = _getType(exceptionTypeName); |
| 13638 } |
| 13639 _recordType(exception, exceptionType); |
| 13640 Element element = exception.staticElement; |
| 13641 if (element is VariableElementImpl) { |
| 13642 element.type = exceptionType; |
| 13643 } else { |
| 13644 // TODO(brianwilkerson) Report the internal error |
| 13645 } |
| 13646 } |
| 13647 SimpleIdentifier stackTrace = node.stackTraceParameter; |
| 13648 if (stackTrace != null) { |
| 13649 _recordType(stackTrace, typeProvider.stackTraceType); |
| 13650 Element element = stackTrace.staticElement; |
| 13651 if (element is VariableElementImpl) { |
| 13652 element.type = typeProvider.stackTraceType; |
| 13653 } else { |
| 13654 // TODO(brianwilkerson) Report the internal error |
| 13655 } |
| 13656 } |
| 13657 return null; |
| 13658 } |
| 13659 |
| 13660 @override |
| 13661 Object visitClassDeclaration(ClassDeclaration node) { |
| 13662 _hasReferenceToSuper = false; |
| 13663 super.visitClassDeclaration(node); |
| 13664 ClassElementImpl classElement = _getClassElement(node.name); |
| 13665 if (classElement != null) { |
| 13666 classElement.hasReferenceToSuper = _hasReferenceToSuper; |
| 13667 } |
| 13668 return null; |
| 13669 } |
| 13670 |
| 13671 @override |
| 13672 void visitClassDeclarationInScope(ClassDeclaration node) { |
| 13673 super.visitClassDeclarationInScope(node); |
| 13674 ExtendsClause extendsClause = node.extendsClause; |
| 13675 WithClause withClause = node.withClause; |
| 13676 ImplementsClause implementsClause = node.implementsClause; |
| 13677 ClassElementImpl classElement = _getClassElement(node.name); |
| 13678 InterfaceType superclassType = null; |
| 13679 if (extendsClause != null) { |
| 13680 ErrorCode errorCode = (withClause == null |
| 13681 ? CompileTimeErrorCode.EXTENDS_NON_CLASS |
| 13682 : CompileTimeErrorCode.MIXIN_WITH_NON_CLASS_SUPERCLASS); |
| 13683 superclassType = _resolveType(extendsClause.superclass, errorCode, |
| 13684 CompileTimeErrorCode.EXTENDS_ENUM, errorCode); |
| 13685 if (!identical(superclassType, typeProvider.objectType)) { |
| 13686 classElement.validMixin = false; |
| 13687 } |
| 13688 } |
| 13689 if (classElement != null) { |
| 13690 if (superclassType == null) { |
| 13691 InterfaceType objectType = typeProvider.objectType; |
| 13692 if (!identical(classElement.type, objectType)) { |
| 13693 superclassType = objectType; |
| 13694 } |
| 13695 } |
| 13696 classElement.supertype = superclassType; |
| 13697 } |
| 13698 _resolve(classElement, withClause, implementsClause); |
| 13699 return null; |
| 13700 } |
| 13701 |
| 13702 @override |
| 13703 void visitClassMembersInScope(ClassDeclaration node) { |
| 13704 // |
| 13705 // Process field declarations before constructors and methods so that the |
| 13706 // types of field formal parameters can be correctly resolved. |
| 13707 // |
| 13708 List<ClassMember> nonFields = new List<ClassMember>(); |
| 13709 node.visitChildren( |
| 13710 new _TypeResolverVisitor_visitClassMembersInScope(this, nonFields)); |
| 13711 int count = nonFields.length; |
| 13712 for (int i = 0; i < count; i++) { |
| 13713 nonFields[i].accept(this); |
| 13714 } |
| 13715 } |
| 13716 |
| 13717 @override |
| 13718 Object visitClassTypeAlias(ClassTypeAlias node) { |
| 13719 super.visitClassTypeAlias(node); |
| 13720 ErrorCode errorCode = CompileTimeErrorCode.MIXIN_WITH_NON_CLASS_SUPERCLASS; |
| 13721 InterfaceType superclassType = _resolveType(node.superclass, errorCode, |
| 13722 CompileTimeErrorCode.EXTENDS_ENUM, errorCode); |
| 13723 if (superclassType == null) { |
| 13724 superclassType = typeProvider.objectType; |
| 13725 } |
| 13726 ClassElementImpl classElement = _getClassElement(node.name); |
| 13727 if (classElement != null) { |
| 13728 classElement.supertype = superclassType; |
| 13729 } |
| 13730 _resolve(classElement, node.withClause, node.implementsClause); |
| 13731 return null; |
| 13732 } |
| 13733 |
| 13734 @override |
| 13735 Object visitConstructorDeclaration(ConstructorDeclaration node) { |
| 13736 super.visitConstructorDeclaration(node); |
| 13737 ExecutableElementImpl element = node.element as ExecutableElementImpl; |
| 13738 if (element == null) { |
| 13739 ClassDeclaration classNode = |
| 13740 node.getAncestor((node) => node is ClassDeclaration); |
| 13741 StringBuffer buffer = new StringBuffer(); |
| 13742 buffer.write("The element for the constructor "); |
| 13743 buffer.write(node.name == null ? "<unnamed>" : node.name.name); |
| 13744 buffer.write(" in "); |
| 13745 if (classNode == null) { |
| 13746 buffer.write("<unknown class>"); |
| 13747 } else { |
| 13748 buffer.write(classNode.name.name); |
| 13749 } |
| 13750 buffer.write(" in "); |
| 13751 buffer.write(source.fullName); |
| 13752 buffer.write(" was not set while trying to resolve types."); |
| 13753 AnalysisEngine.instance.logger.logError(buffer.toString(), |
| 13754 new CaughtException(new AnalysisException(), null)); |
| 13755 } else { |
| 13756 ClassElement definingClass = element.enclosingElement as ClassElement; |
| 13757 element.returnType = definingClass.type; |
| 13758 FunctionTypeImpl type = new FunctionTypeImpl(element); |
| 13759 type.typeArguments = definingClass.type.typeArguments; |
| 13760 element.type = type; |
| 13761 } |
| 13762 return null; |
| 13763 } |
| 13764 |
| 13765 @override |
| 13766 Object visitDeclaredIdentifier(DeclaredIdentifier node) { |
| 13767 super.visitDeclaredIdentifier(node); |
| 13768 DartType declaredType; |
| 13769 TypeName typeName = node.type; |
| 13770 if (typeName == null) { |
| 13771 declaredType = _dynamicType; |
| 13772 } else { |
| 13773 declaredType = _getType(typeName); |
| 13774 } |
| 13775 LocalVariableElementImpl element = node.element as LocalVariableElementImpl; |
| 13776 element.type = declaredType; |
| 13777 return null; |
| 13778 } |
| 13779 |
| 13780 @override |
| 13781 Object visitFieldFormalParameter(FieldFormalParameter node) { |
| 13782 super.visitFieldFormalParameter(node); |
| 13783 Element element = node.identifier.staticElement; |
| 13784 if (element is ParameterElementImpl) { |
| 13785 ParameterElementImpl parameter = element; |
| 13786 FormalParameterList parameterList = node.parameters; |
| 13787 if (parameterList == null) { |
| 13788 DartType type; |
| 13789 TypeName typeName = node.type; |
| 13790 if (typeName == null) { |
| 13791 type = _dynamicType; |
| 13792 if (parameter is FieldFormalParameterElement) { |
| 13793 FieldElement fieldElement = |
| 13794 (parameter as FieldFormalParameterElement).field; |
| 13795 if (fieldElement != null) { |
| 13796 type = fieldElement.type; |
| 13797 } |
| 13798 } |
| 13799 } else { |
| 13800 type = _getType(typeName); |
| 13801 } |
| 13802 parameter.type = type; |
| 13803 } else { |
| 13804 _setFunctionTypedParameterType(parameter, node.type, node.parameters); |
| 13805 } |
| 13806 } else { |
| 13807 // TODO(brianwilkerson) Report this internal error |
| 13808 } |
| 13809 return null; |
| 13810 } |
| 13811 |
| 13812 @override |
| 13813 Object visitFunctionDeclaration(FunctionDeclaration node) { |
| 13814 super.visitFunctionDeclaration(node); |
| 13815 ExecutableElementImpl element = node.element as ExecutableElementImpl; |
| 13816 if (element == null) { |
| 13817 StringBuffer buffer = new StringBuffer(); |
| 13818 buffer.write("The element for the top-level function "); |
| 13819 buffer.write(node.name); |
| 13820 buffer.write(" in "); |
| 13821 buffer.write(source.fullName); |
| 13822 buffer.write(" was not set while trying to resolve types."); |
| 13823 AnalysisEngine.instance.logger.logError(buffer.toString(), |
| 13824 new CaughtException(new AnalysisException(), null)); |
| 13825 } |
| 13826 element.returnType = _computeReturnType(node.returnType); |
| 13827 FunctionTypeImpl type = new FunctionTypeImpl(element); |
| 13828 ClassElement definingClass = |
| 13829 element.getAncestor((element) => element is ClassElement); |
| 13830 if (definingClass != null) { |
| 13831 type.typeArguments = definingClass.type.typeArguments; |
| 13832 } |
| 13833 element.type = type; |
| 13834 return null; |
| 13835 } |
| 13836 |
| 13837 @override |
| 13838 Object visitFunctionTypeAlias(FunctionTypeAlias node) { |
| 13839 FunctionTypeAliasElementImpl element = |
| 13840 node.element as FunctionTypeAliasElementImpl; |
| 13841 super.visitFunctionTypeAlias(node); |
| 13842 element.returnType = _computeReturnType(node.returnType); |
| 13843 return null; |
| 13844 } |
| 13845 |
| 13846 @override |
| 13847 Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { |
| 13848 super.visitFunctionTypedFormalParameter(node); |
| 13849 Element element = node.identifier.staticElement; |
| 13850 if (element is ParameterElementImpl) { |
| 13851 _setFunctionTypedParameterType(element, node.returnType, node.parameters); |
| 13852 } else { |
| 13853 // TODO(brianwilkerson) Report this internal error |
| 13854 } |
| 13855 return null; |
| 13856 } |
| 13857 |
| 13858 @override |
| 13859 Object visitMethodDeclaration(MethodDeclaration node) { |
| 13860 super.visitMethodDeclaration(node); |
| 13861 ExecutableElementImpl element = node.element as ExecutableElementImpl; |
| 13862 if (element == null) { |
| 13863 ClassDeclaration classNode = |
| 13864 node.getAncestor((node) => node is ClassDeclaration); |
| 13865 StringBuffer buffer = new StringBuffer(); |
| 13866 buffer.write("The element for the method "); |
| 13867 buffer.write(node.name.name); |
| 13868 buffer.write(" in "); |
| 13869 if (classNode == null) { |
| 13870 buffer.write("<unknown class>"); |
| 13871 } else { |
| 13872 buffer.write(classNode.name.name); |
| 13873 } |
| 13874 buffer.write(" in "); |
| 13875 buffer.write(source.fullName); |
| 13876 buffer.write(" was not set while trying to resolve types."); |
| 13877 AnalysisEngine.instance.logger.logError(buffer.toString(), |
| 13878 new CaughtException(new AnalysisException(), null)); |
| 13879 } |
| 13880 element.returnType = _computeReturnType(node.returnType); |
| 13881 FunctionTypeImpl type = new FunctionTypeImpl(element); |
| 13882 ClassElement definingClass = |
| 13883 element.getAncestor((element) => element is ClassElement); |
| 13884 if (definingClass != null) { |
| 13885 type.typeArguments = definingClass.type.typeArguments; |
| 13886 } |
| 13887 element.type = type; |
| 13888 if (element is PropertyAccessorElement) { |
| 13889 PropertyAccessorElement accessor = element as PropertyAccessorElement; |
| 13890 PropertyInducingElementImpl variable = |
| 13891 accessor.variable as PropertyInducingElementImpl; |
| 13892 if (accessor.isGetter) { |
| 13893 variable.type = type.returnType; |
| 13894 } else if (variable.type == null) { |
| 13895 List<DartType> parameterTypes = type.normalParameterTypes; |
| 13896 if (parameterTypes != null && parameterTypes.length > 0) { |
| 13897 variable.type = parameterTypes[0]; |
| 13898 } |
| 13899 } |
| 13900 } |
| 13901 return null; |
| 13902 } |
| 13903 |
| 13904 @override |
| 13905 Object visitSimpleFormalParameter(SimpleFormalParameter node) { |
| 13906 super.visitSimpleFormalParameter(node); |
| 13907 DartType declaredType; |
| 13908 TypeName typeName = node.type; |
| 13909 if (typeName == null) { |
| 13910 declaredType = _dynamicType; |
| 13911 } else { |
| 13912 declaredType = _getType(typeName); |
| 13913 } |
| 13914 Element element = node.identifier.staticElement; |
| 13915 if (element is ParameterElement) { |
| 13916 (element as ParameterElementImpl).type = declaredType; |
| 13917 } else { |
| 13918 // TODO(brianwilkerson) Report the internal error. |
| 13919 } |
| 13920 return null; |
| 13921 } |
| 13922 |
| 13923 @override |
| 13924 Object visitSuperExpression(SuperExpression node) { |
| 13925 _hasReferenceToSuper = true; |
| 13926 return super.visitSuperExpression(node); |
| 13927 } |
| 13928 |
| 13929 @override |
| 13930 Object visitTypeName(TypeName node) { |
| 13931 super.visitTypeName(node); |
| 13932 Identifier typeName = node.name; |
| 13933 TypeArgumentList argumentList = node.typeArguments; |
| 13934 Element element = nameScope.lookup(typeName, definingLibrary); |
| 13935 if (element == null) { |
| 13936 // |
| 13937 // Check to see whether the type name is either 'dynamic' or 'void', |
| 13938 // neither of which are in the name scope and hence will not be found by |
| 13939 // normal means. |
| 13940 // |
| 13941 if (typeName.name == _dynamicType.name) { |
| 13942 _setElement(typeName, _dynamicType.element); |
| 13943 if (argumentList != null) { |
| 13944 // TODO(brianwilkerson) Report this error |
| 13945 // reporter.reportError(StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGU
MENTS, node, dynamicType.getName(), 0, argumentList.getArguments().size()); |
| 13946 } |
| 13947 typeName.staticType = _dynamicType; |
| 13948 node.type = _dynamicType; |
| 13949 return null; |
| 13950 } |
| 13951 VoidTypeImpl voidType = VoidTypeImpl.instance; |
| 13952 if (typeName.name == voidType.name) { |
| 13953 // There is no element for 'void'. |
| 13954 if (argumentList != null) { |
| 13955 // TODO(brianwilkerson) Report this error |
| 13956 // reporter.reportError(StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGU
MENTS, node, voidType.getName(), 0, argumentList.getArguments().size()); |
| 13957 } |
| 13958 typeName.staticType = voidType; |
| 13959 node.type = voidType; |
| 13960 return null; |
| 13961 } |
| 13962 // |
| 13963 // If not, the look to see whether we might have created the wrong AST |
| 13964 // structure for a constructor name. If so, fix the AST structure and then |
| 13965 // proceed. |
| 13966 // |
| 13967 AstNode parent = node.parent; |
| 13968 if (typeName is PrefixedIdentifier && |
| 13969 parent is ConstructorName && |
| 13970 argumentList == null) { |
| 13971 ConstructorName name = parent; |
| 13972 if (name.name == null) { |
| 13973 PrefixedIdentifier prefixedIdentifier = |
| 13974 typeName as PrefixedIdentifier; |
| 13975 SimpleIdentifier prefix = prefixedIdentifier.prefix; |
| 13976 element = nameScope.lookup(prefix, definingLibrary); |
| 13977 if (element is PrefixElement) { |
| 13978 if (parent.parent is InstanceCreationExpression && |
| 13979 (parent.parent as InstanceCreationExpression).isConst) { |
| 13980 // If, if this is a const expression, then generate a |
| 13981 // CompileTimeErrorCode.CONST_WITH_NON_TYPE error. |
| 13982 reportErrorForNode( |
| 13983 CompileTimeErrorCode.CONST_WITH_NON_TYPE, |
| 13984 prefixedIdentifier.identifier, |
| 13985 [prefixedIdentifier.identifier.name]); |
| 13986 } else { |
| 13987 // Else, if this expression is a new expression, report a |
| 13988 // NEW_WITH_NON_TYPE warning. |
| 13989 reportErrorForNode( |
| 13990 StaticWarningCode.NEW_WITH_NON_TYPE, |
| 13991 prefixedIdentifier.identifier, |
| 13992 [prefixedIdentifier.identifier.name]); |
| 13993 } |
| 13994 _setElement(prefix, element); |
| 13995 return null; |
| 13996 } else if (element != null) { |
| 13997 // |
| 13998 // Rewrite the constructor name. The parser, when it sees a |
| 13999 // constructor named "a.b", cannot tell whether "a" is a prefix and |
| 14000 // "b" is a class name, or whether "a" is a class name and "b" is a |
| 14001 // constructor name. It arbitrarily chooses the former, but in this |
| 14002 // case was wrong. |
| 14003 // |
| 14004 name.name = prefixedIdentifier.identifier; |
| 14005 name.period = prefixedIdentifier.period; |
| 14006 node.name = prefix; |
| 14007 typeName = prefix; |
| 14008 } |
| 14009 } |
| 14010 } |
| 14011 } |
| 14012 // check element |
| 14013 bool elementValid = element is! MultiplyDefinedElement; |
| 14014 if (elementValid && |
| 14015 element is! ClassElement && |
| 14016 _isTypeNameInInstanceCreationExpression(node)) { |
| 14017 SimpleIdentifier typeNameSimple = _getTypeSimpleIdentifier(typeName); |
| 14018 InstanceCreationExpression creation = |
| 14019 node.parent.parent as InstanceCreationExpression; |
| 14020 if (creation.isConst) { |
| 14021 if (element == null) { |
| 14022 reportErrorForNode( |
| 14023 CompileTimeErrorCode.UNDEFINED_CLASS, typeNameSimple, [typeName]); |
| 14024 } else { |
| 14025 reportErrorForNode(CompileTimeErrorCode.CONST_WITH_NON_TYPE, |
| 14026 typeNameSimple, [typeName]); |
| 14027 } |
| 14028 elementValid = false; |
| 14029 } else { |
| 14030 if (element != null) { |
| 14031 reportErrorForNode( |
| 14032 StaticWarningCode.NEW_WITH_NON_TYPE, typeNameSimple, [typeName]); |
| 14033 elementValid = false; |
| 14034 } |
| 14035 } |
| 14036 } |
| 14037 if (elementValid && element == null) { |
| 14038 // We couldn't resolve the type name. |
| 14039 // TODO(jwren) Consider moving the check for |
| 14040 // CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE from the |
| 14041 // ErrorVerifier, so that we don't have two errors on a built in |
| 14042 // identifier being used as a class name. |
| 14043 // See CompileTimeErrorCodeTest.test_builtInIdentifierAsType(). |
| 14044 SimpleIdentifier typeNameSimple = _getTypeSimpleIdentifier(typeName); |
| 14045 RedirectingConstructorKind redirectingConstructorKind; |
| 14046 if (_isBuiltInIdentifier(node) && _isTypeAnnotation(node)) { |
| 14047 reportErrorForNode(CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE, |
| 14048 typeName, [typeName.name]); |
| 14049 } else if (typeNameSimple.name == "boolean") { |
| 14050 reportErrorForNode( |
| 14051 StaticWarningCode.UNDEFINED_CLASS_BOOLEAN, typeNameSimple, []); |
| 14052 } else if (_isTypeNameInCatchClause(node)) { |
| 14053 reportErrorForNode(StaticWarningCode.NON_TYPE_IN_CATCH_CLAUSE, typeName, |
| 14054 [typeName.name]); |
| 14055 } else if (_isTypeNameInAsExpression(node)) { |
| 14056 reportErrorForNode( |
| 14057 StaticWarningCode.CAST_TO_NON_TYPE, typeName, [typeName.name]); |
| 14058 } else if (_isTypeNameInIsExpression(node)) { |
| 14059 reportErrorForNode(StaticWarningCode.TYPE_TEST_WITH_UNDEFINED_NAME, |
| 14060 typeName, [typeName.name]); |
| 14061 } else if ((redirectingConstructorKind = |
| 14062 _getRedirectingConstructorKind(node)) != |
| 14063 null) { |
| 14064 ErrorCode errorCode = (redirectingConstructorKind == |
| 14065 RedirectingConstructorKind.CONST |
| 14066 ? CompileTimeErrorCode.REDIRECT_TO_NON_CLASS |
| 14067 : StaticWarningCode.REDIRECT_TO_NON_CLASS); |
| 14068 reportErrorForNode(errorCode, typeName, [typeName.name]); |
| 14069 } else if (_isTypeNameInTypeArgumentList(node)) { |
| 14070 reportErrorForNode(StaticTypeWarningCode.NON_TYPE_AS_TYPE_ARGUMENT, |
| 14071 typeName, [typeName.name]); |
| 14072 } else { |
| 14073 reportErrorForNode( |
| 14074 StaticWarningCode.UNDEFINED_CLASS, typeName, [typeName.name]); |
| 14075 } |
| 14076 elementValid = false; |
| 14077 } |
| 14078 if (!elementValid) { |
| 14079 if (element is MultiplyDefinedElement) { |
| 14080 _setElement(typeName, element); |
| 14081 } else { |
| 14082 _setElement(typeName, _dynamicType.element); |
| 14083 } |
| 14084 typeName.staticType = _undefinedType; |
| 14085 node.type = _undefinedType; |
| 14086 return null; |
| 14087 } |
| 14088 DartType type = null; |
| 14089 if (element is ClassElement) { |
| 14090 _setElement(typeName, element); |
| 14091 type = element.type; |
| 14092 } else if (element is FunctionTypeAliasElement) { |
| 14093 _setElement(typeName, element); |
| 14094 type = element.type; |
| 14095 } else if (element is TypeParameterElement) { |
| 14096 _setElement(typeName, element); |
| 14097 type = element.type; |
| 14098 if (argumentList != null) { |
| 14099 // Type parameters cannot have type arguments. |
| 14100 // TODO(brianwilkerson) Report this error. |
| 14101 // resolver.reportError(ResolverErrorCode.?, keyType); |
| 14102 } |
| 14103 } else if (element is MultiplyDefinedElement) { |
| 14104 List<Element> elements = element.conflictingElements; |
| 14105 type = _getTypeWhenMultiplyDefined(elements); |
| 14106 if (type != null) { |
| 14107 node.type = type; |
| 14108 } |
| 14109 } else { |
| 14110 // The name does not represent a type. |
| 14111 RedirectingConstructorKind redirectingConstructorKind; |
| 14112 if (_isTypeNameInCatchClause(node)) { |
| 14113 reportErrorForNode(StaticWarningCode.NON_TYPE_IN_CATCH_CLAUSE, typeName, |
| 14114 [typeName.name]); |
| 14115 } else if (_isTypeNameInAsExpression(node)) { |
| 14116 reportErrorForNode( |
| 14117 StaticWarningCode.CAST_TO_NON_TYPE, typeName, [typeName.name]); |
| 14118 } else if (_isTypeNameInIsExpression(node)) { |
| 14119 reportErrorForNode(StaticWarningCode.TYPE_TEST_WITH_NON_TYPE, typeName, |
| 14120 [typeName.name]); |
| 14121 } else if ((redirectingConstructorKind = |
| 14122 _getRedirectingConstructorKind(node)) != |
| 14123 null) { |
| 14124 ErrorCode errorCode = (redirectingConstructorKind == |
| 14125 RedirectingConstructorKind.CONST |
| 14126 ? CompileTimeErrorCode.REDIRECT_TO_NON_CLASS |
| 14127 : StaticWarningCode.REDIRECT_TO_NON_CLASS); |
| 14128 reportErrorForNode(errorCode, typeName, [typeName.name]); |
| 14129 } else if (_isTypeNameInTypeArgumentList(node)) { |
| 14130 reportErrorForNode(StaticTypeWarningCode.NON_TYPE_AS_TYPE_ARGUMENT, |
| 14131 typeName, [typeName.name]); |
| 14132 } else { |
| 14133 AstNode parent = typeName.parent; |
| 14134 while (parent is TypeName) { |
| 14135 parent = parent.parent; |
| 14136 } |
| 14137 if (parent is ExtendsClause || |
| 14138 parent is ImplementsClause || |
| 14139 parent is WithClause || |
| 14140 parent is ClassTypeAlias) { |
| 14141 // Ignored. The error will be reported elsewhere. |
| 14142 } else { |
| 14143 reportErrorForNode( |
| 14144 StaticWarningCode.NOT_A_TYPE, typeName, [typeName.name]); |
| 14145 } |
| 14146 } |
| 14147 _setElement(typeName, _dynamicType.element); |
| 14148 typeName.staticType = _dynamicType; |
| 14149 node.type = _dynamicType; |
| 14150 return null; |
| 14151 } |
| 14152 if (argumentList != null) { |
| 14153 NodeList<TypeName> arguments = argumentList.arguments; |
| 14154 int argumentCount = arguments.length; |
| 14155 List<DartType> parameters = _getTypeArguments(type); |
| 14156 int parameterCount = parameters.length; |
| 14157 List<DartType> typeArguments = new List<DartType>(parameterCount); |
| 14158 if (argumentCount == parameterCount) { |
| 14159 for (int i = 0; i < parameterCount; i++) { |
| 14160 TypeName argumentTypeName = arguments[i]; |
| 14161 DartType argumentType = _getType(argumentTypeName); |
| 14162 if (argumentType == null) { |
| 14163 argumentType = _dynamicType; |
| 14164 } |
| 14165 typeArguments[i] = argumentType; |
| 14166 } |
| 14167 } else { |
| 14168 reportErrorForNode(_getInvalidTypeParametersErrorCode(node), node, |
| 14169 [typeName.name, parameterCount, argumentCount]); |
| 14170 for (int i = 0; i < parameterCount; i++) { |
| 14171 typeArguments[i] = _dynamicType; |
| 14172 } |
| 14173 } |
| 14174 if (type is InterfaceTypeImpl) { |
| 14175 InterfaceTypeImpl interfaceType = type as InterfaceTypeImpl; |
| 14176 type = interfaceType.substitute4(typeArguments); |
| 14177 } else if (type is FunctionTypeImpl) { |
| 14178 FunctionTypeImpl functionType = type as FunctionTypeImpl; |
| 14179 type = functionType.substitute3(typeArguments); |
| 14180 } else { |
| 14181 // TODO(brianwilkerson) Report this internal error. |
| 14182 } |
| 14183 } else { |
| 14184 // |
| 14185 // Check for the case where there are no type arguments given for a |
| 14186 // parameterized type. |
| 14187 // |
| 14188 List<DartType> parameters = _getTypeArguments(type); |
| 14189 int parameterCount = parameters.length; |
| 14190 if (parameterCount > 0) { |
| 14191 DynamicTypeImpl dynamicType = DynamicTypeImpl.instance; |
| 14192 List<DartType> arguments = new List<DartType>(parameterCount); |
| 14193 for (int i = 0; i < parameterCount; i++) { |
| 14194 arguments[i] = dynamicType; |
| 14195 } |
| 14196 type = type.substitute2(arguments, parameters); |
| 14197 } |
| 14198 } |
| 14199 typeName.staticType = type; |
| 14200 node.type = type; |
| 14201 return null; |
| 14202 } |
| 14203 |
| 14204 @override |
| 14205 Object visitTypeParameter(TypeParameter node) { |
| 14206 super.visitTypeParameter(node); |
| 14207 TypeName bound = node.bound; |
| 14208 if (bound != null) { |
| 14209 TypeParameterElementImpl typeParameter = |
| 14210 node.name.staticElement as TypeParameterElementImpl; |
| 14211 if (typeParameter != null) { |
| 14212 typeParameter.bound = bound.type; |
| 14213 } |
| 14214 } |
| 14215 return null; |
| 14216 } |
| 14217 |
| 14218 @override |
| 14219 Object visitVariableDeclaration(VariableDeclaration node) { |
| 14220 super.visitVariableDeclaration(node); |
| 14221 DartType declaredType; |
| 14222 TypeName typeName = (node.parent as VariableDeclarationList).type; |
| 14223 if (typeName == null) { |
| 14224 declaredType = _dynamicType; |
| 14225 } else { |
| 14226 declaredType = _getType(typeName); |
| 14227 } |
| 14228 Element element = node.name.staticElement; |
| 14229 if (element is VariableElement) { |
| 14230 (element as VariableElementImpl).type = declaredType; |
| 14231 if (element is PropertyInducingElement) { |
| 14232 PropertyInducingElement variableElement = element; |
| 14233 PropertyAccessorElementImpl getter = |
| 14234 variableElement.getter as PropertyAccessorElementImpl; |
| 14235 getter.returnType = declaredType; |
| 14236 FunctionTypeImpl getterType = new FunctionTypeImpl(getter); |
| 14237 ClassElement definingClass = |
| 14238 element.getAncestor((element) => element is ClassElement); |
| 14239 if (definingClass != null) { |
| 14240 getterType.typeArguments = definingClass.type.typeArguments; |
| 14241 } |
| 14242 getter.type = getterType; |
| 14243 PropertyAccessorElementImpl setter = |
| 14244 variableElement.setter as PropertyAccessorElementImpl; |
| 14245 if (setter != null) { |
| 14246 List<ParameterElement> parameters = setter.parameters; |
| 14247 if (parameters.length > 0) { |
| 14248 (parameters[0] as ParameterElementImpl).type = declaredType; |
| 14249 } |
| 14250 setter.returnType = VoidTypeImpl.instance; |
| 14251 FunctionTypeImpl setterType = new FunctionTypeImpl(setter); |
| 14252 if (definingClass != null) { |
| 14253 setterType.typeArguments = definingClass.type.typeArguments; |
| 14254 } |
| 14255 setter.type = setterType; |
| 14256 } |
| 14257 } |
| 14258 } else { |
| 14259 // TODO(brianwilkerson) Report the internal error. |
| 14260 } |
| 14261 return null; |
| 14262 } |
| 14263 |
| 14264 /** |
| 14265 * Given a type name representing the return type of a function, compute the r
eturn type of the |
| 14266 * function. |
| 14267 * |
| 14268 * @param returnType the type name representing the return type of the functio
n |
| 14269 * @return the return type that was computed |
| 14270 */ |
| 14271 DartType _computeReturnType(TypeName returnType) { |
| 14272 if (returnType == null) { |
| 14273 return _dynamicType; |
| 14274 } else { |
| 14275 return returnType.type; |
| 14276 } |
| 14277 } |
| 14278 |
| 14279 /** |
| 14280 * Return the class element that represents the class whose name was provided. |
| 14281 * |
| 14282 * @param identifier the name from the declaration of a class |
| 14283 * @return the class element that represents the class |
| 14284 */ |
| 14285 ClassElementImpl _getClassElement(SimpleIdentifier identifier) { |
| 14286 // TODO(brianwilkerson) Seems like we should be using |
| 14287 // ClassDeclaration.getElement(). |
| 14288 if (identifier == null) { |
| 14289 // TODO(brianwilkerson) Report this |
| 14290 // Internal error: We should never build a class declaration without a |
| 14291 // name. |
| 14292 return null; |
| 14293 } |
| 14294 Element element = identifier.staticElement; |
| 14295 if (element is! ClassElementImpl) { |
| 14296 // TODO(brianwilkerson) Report this |
| 14297 // Internal error: Failed to create an element for a class declaration. |
| 14298 return null; |
| 14299 } |
| 14300 return element as ClassElementImpl; |
| 14301 } |
| 14302 |
| 14303 /** |
| 14304 * Return an array containing all of the elements associated with the paramete
rs in the given |
| 14305 * list. |
| 14306 * |
| 14307 * @param parameterList the list of parameters whose elements are to be return
ed |
| 14308 * @return the elements associated with the parameters |
| 14309 */ |
| 14310 List<ParameterElement> _getElements(FormalParameterList parameterList) { |
| 14311 List<ParameterElement> elements = new List<ParameterElement>(); |
| 14312 for (FormalParameter parameter in parameterList.parameters) { |
| 14313 ParameterElement element = |
| 14314 parameter.identifier.staticElement as ParameterElement; |
| 14315 // TODO(brianwilkerson) Understand why the element would be null. |
| 14316 if (element != null) { |
| 14317 elements.add(element); |
| 14318 } |
| 14319 } |
| 14320 return elements; |
| 14321 } |
| 14322 |
| 14323 /** |
| 14324 * The number of type arguments in the given type name does not match the numb
er of parameters in |
| 14325 * the corresponding class element. Return the error code that should be used
to report this |
| 14326 * error. |
| 14327 * |
| 14328 * @param node the type name with the wrong number of type arguments |
| 14329 * @return the error code that should be used to report that the wrong number
of type arguments |
| 14330 * were provided |
| 14331 */ |
| 14332 ErrorCode _getInvalidTypeParametersErrorCode(TypeName node) { |
| 14333 AstNode parent = node.parent; |
| 14334 if (parent is ConstructorName) { |
| 14335 parent = parent.parent; |
| 14336 if (parent is InstanceCreationExpression) { |
| 14337 if (parent.isConst) { |
| 14338 return CompileTimeErrorCode.CONST_WITH_INVALID_TYPE_PARAMETERS; |
| 14339 } else { |
| 14340 return StaticWarningCode.NEW_WITH_INVALID_TYPE_PARAMETERS; |
| 14341 } |
| 14342 } |
| 14343 } |
| 14344 return StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS; |
| 14345 } |
| 14346 |
| 14347 /** |
| 14348 * Checks if the given type name is the target in a redirected constructor. |
| 14349 * |
| 14350 * @param typeName the type name to analyze |
| 14351 * @return some [RedirectingConstructorKind] if the given type name is used as
the type in a |
| 14352 * redirected constructor, or `null` otherwise |
| 14353 */ |
| 14354 RedirectingConstructorKind _getRedirectingConstructorKind(TypeName typeName) { |
| 14355 AstNode parent = typeName.parent; |
| 14356 if (parent is ConstructorName) { |
| 14357 ConstructorName constructorName = parent as ConstructorName; |
| 14358 parent = constructorName.parent; |
| 14359 if (parent is ConstructorDeclaration) { |
| 14360 if (identical(parent.redirectedConstructor, constructorName)) { |
| 14361 if (parent.constKeyword != null) { |
| 14362 return RedirectingConstructorKind.CONST; |
| 14363 } |
| 14364 return RedirectingConstructorKind.NORMAL; |
| 14365 } |
| 14366 } |
| 14367 } |
| 14368 return null; |
| 14369 } |
| 14370 |
| 14371 /** |
| 14372 * Return the type represented by the given type name. |
| 14373 * |
| 14374 * @param typeName the type name representing the type to be returned |
| 14375 * @return the type represented by the type name |
| 14376 */ |
| 14377 DartType _getType(TypeName typeName) { |
| 14378 DartType type = typeName.type; |
| 14379 if (type == null) { |
| 14380 return _undefinedType; |
| 14381 } |
| 14382 return type; |
| 14383 } |
| 14384 |
| 14385 /** |
| 14386 * Return the type arguments associated with the given type. |
| 14387 * |
| 14388 * @param type the type whole type arguments are to be returned |
| 14389 * @return the type arguments associated with the given type |
| 14390 */ |
| 14391 List<DartType> _getTypeArguments(DartType type) { |
| 14392 if (type is InterfaceType) { |
| 14393 return type.typeArguments; |
| 14394 } else if (type is FunctionType) { |
| 14395 return type.typeArguments; |
| 14396 } |
| 14397 return DartType.EMPTY_LIST; |
| 14398 } |
| 14399 |
| 14400 /** |
| 14401 * Returns the simple identifier of the given (may be qualified) type name. |
| 14402 * |
| 14403 * @param typeName the (may be qualified) qualified type name |
| 14404 * @return the simple identifier of the given (may be qualified) type name. |
| 14405 */ |
| 14406 SimpleIdentifier _getTypeSimpleIdentifier(Identifier typeName) { |
| 14407 if (typeName is SimpleIdentifier) { |
| 14408 return typeName; |
| 14409 } else { |
| 14410 return (typeName as PrefixedIdentifier).identifier; |
| 14411 } |
| 14412 } |
| 14413 |
| 14414 /** |
| 14415 * Given the multiple elements to which a single name could potentially be res
olved, return the |
| 14416 * single interface type that should be used, or `null` if there is no clear c
hoice. |
| 14417 * |
| 14418 * @param elements the elements to which a single name could potentially be re
solved |
| 14419 * @return the single interface type that should be used for the type name |
| 14420 */ |
| 14421 InterfaceType _getTypeWhenMultiplyDefined(List<Element> elements) { |
| 14422 InterfaceType type = null; |
| 14423 for (Element element in elements) { |
| 14424 if (element is ClassElement) { |
| 14425 if (type != null) { |
| 14426 return null; |
| 14427 } |
| 14428 type = element.type; |
| 14429 } |
| 14430 } |
| 14431 return type; |
| 14432 } |
| 14433 |
| 14434 /** |
| 14435 * Checks if the given type name is used as the type in an as expression. |
| 14436 * |
| 14437 * @param typeName the type name to analyzer |
| 14438 * @return `true` if the given type name is used as the type in an as expressi
on |
| 14439 */ |
| 14440 bool _isTypeNameInAsExpression(TypeName typeName) { |
| 14441 AstNode parent = typeName.parent; |
| 14442 if (parent is AsExpression) { |
| 14443 AsExpression asExpression = parent; |
| 14444 return identical(asExpression.type, typeName); |
| 14445 } |
| 14446 return false; |
| 14447 } |
| 14448 |
| 14449 /** |
| 14450 * Checks if the given type name is used as the exception type in a catch clau
se. |
| 14451 * |
| 14452 * @param typeName the type name to analyzer |
| 14453 * @return `true` if the given type name is used as the exception type in a ca
tch clause |
| 14454 */ |
| 14455 bool _isTypeNameInCatchClause(TypeName typeName) { |
| 14456 AstNode parent = typeName.parent; |
| 14457 if (parent is CatchClause) { |
| 14458 CatchClause catchClause = parent; |
| 14459 return identical(catchClause.exceptionType, typeName); |
| 14460 } |
| 14461 return false; |
| 14462 } |
| 14463 |
| 14464 /** |
| 14465 * Checks if the given type name is used as the type in an instance creation e
xpression. |
| 14466 * |
| 14467 * @param typeName the type name to analyzer |
| 14468 * @return `true` if the given type name is used as the type in an instance cr
eation |
| 14469 * expression |
| 14470 */ |
| 14471 bool _isTypeNameInInstanceCreationExpression(TypeName typeName) { |
| 14472 AstNode parent = typeName.parent; |
| 14473 if (parent is ConstructorName && |
| 14474 parent.parent is InstanceCreationExpression) { |
| 14475 ConstructorName constructorName = parent; |
| 14476 return constructorName != null && |
| 14477 identical(constructorName.type, typeName); |
| 14478 } |
| 14479 return false; |
| 14480 } |
| 14481 |
| 14482 /** |
| 14483 * Checks if the given type name is used as the type in an is expression. |
| 14484 * |
| 14485 * @param typeName the type name to analyzer |
| 14486 * @return `true` if the given type name is used as the type in an is expressi
on |
| 14487 */ |
| 14488 bool _isTypeNameInIsExpression(TypeName typeName) { |
| 14489 AstNode parent = typeName.parent; |
| 14490 if (parent is IsExpression) { |
| 14491 IsExpression isExpression = parent; |
| 14492 return identical(isExpression.type, typeName); |
| 14493 } |
| 14494 return false; |
| 14495 } |
| 14496 |
| 14497 /** |
| 14498 * Checks if the given type name used in a type argument list. |
| 14499 * |
| 14500 * @param typeName the type name to analyzer |
| 14501 * @return `true` if the given type name is in a type argument list |
| 14502 */ |
| 14503 bool _isTypeNameInTypeArgumentList(TypeName typeName) => |
| 14504 typeName.parent is TypeArgumentList; |
| 14505 |
| 14506 /** |
| 14507 * Record that the static type of the given node is the given type. |
| 14508 * |
| 14509 * @param expression the node whose type is to be recorded |
| 14510 * @param type the static type of the node |
| 14511 */ |
| 14512 Object _recordType(Expression expression, DartType type) { |
| 14513 if (type == null) { |
| 14514 expression.staticType = _dynamicType; |
| 14515 } else { |
| 14516 expression.staticType = type; |
| 14517 } |
| 14518 return null; |
| 14519 } |
| 14520 |
| 14521 /** |
| 14522 * Resolve the types in the given with and implements clauses and associate th
ose types with the |
| 14523 * given class element. |
| 14524 * |
| 14525 * @param classElement the class element with which the mixin and interface ty
pes are to be |
| 14526 * associated |
| 14527 * @param withClause the with clause to be resolved |
| 14528 * @param implementsClause the implements clause to be resolved |
| 14529 */ |
| 14530 void _resolve(ClassElementImpl classElement, WithClause withClause, |
| 14531 ImplementsClause implementsClause) { |
| 14532 if (withClause != null) { |
| 14533 List<InterfaceType> mixinTypes = _resolveTypes( |
| 14534 withClause.mixinTypes, |
| 14535 CompileTimeErrorCode.MIXIN_OF_NON_CLASS, |
| 14536 CompileTimeErrorCode.MIXIN_OF_ENUM, |
| 14537 CompileTimeErrorCode.MIXIN_OF_NON_CLASS); |
| 14538 if (classElement != null) { |
| 14539 classElement.mixins = mixinTypes; |
| 14540 classElement.withClauseRange = |
| 14541 new SourceRange(withClause.offset, withClause.length); |
| 14542 } |
| 14543 } |
| 14544 if (implementsClause != null) { |
| 14545 NodeList<TypeName> interfaces = implementsClause.interfaces; |
| 14546 List<InterfaceType> interfaceTypes = _resolveTypes( |
| 14547 interfaces, |
| 14548 CompileTimeErrorCode.IMPLEMENTS_NON_CLASS, |
| 14549 CompileTimeErrorCode.IMPLEMENTS_ENUM, |
| 14550 CompileTimeErrorCode.IMPLEMENTS_DYNAMIC); |
| 14551 if (classElement != null) { |
| 14552 classElement.interfaces = interfaceTypes; |
| 14553 } |
| 14554 // TODO(brianwilkerson) Move the following checks to ErrorVerifier. |
| 14555 int count = interfaces.length; |
| 14556 List<bool> detectedRepeatOnIndex = new List<bool>.filled(count, false); |
| 14557 for (int i = 0; i < detectedRepeatOnIndex.length; i++) { |
| 14558 detectedRepeatOnIndex[i] = false; |
| 14559 } |
| 14560 for (int i = 0; i < count; i++) { |
| 14561 TypeName typeName = interfaces[i]; |
| 14562 if (!detectedRepeatOnIndex[i]) { |
| 14563 Element element = typeName.name.staticElement; |
| 14564 for (int j = i + 1; j < count; j++) { |
| 14565 TypeName typeName2 = interfaces[j]; |
| 14566 Identifier identifier2 = typeName2.name; |
| 14567 String name2 = identifier2.name; |
| 14568 Element element2 = identifier2.staticElement; |
| 14569 if (element != null && element == element2) { |
| 14570 detectedRepeatOnIndex[j] = true; |
| 14571 reportErrorForNode( |
| 14572 CompileTimeErrorCode.IMPLEMENTS_REPEATED, typeName2, [name2]); |
| 14573 } |
| 14574 } |
| 14575 } |
| 14576 } |
| 14577 } |
| 14578 } |
| 14579 |
| 14580 /** |
| 14581 * Return the type specified by the given name. |
| 14582 * |
| 14583 * @param typeName the type name specifying the type to be returned |
| 14584 * @param nonTypeError the error to produce if the type name is defined to be
something other than |
| 14585 * a type |
| 14586 * @param enumTypeError the error to produce if the type name is defined to be
an enum |
| 14587 * @param dynamicTypeError the error to produce if the type name is "dynamic" |
| 14588 * @return the type specified by the type name |
| 14589 */ |
| 14590 InterfaceType _resolveType(TypeName typeName, ErrorCode nonTypeError, |
| 14591 ErrorCode enumTypeError, ErrorCode dynamicTypeError) { |
| 14592 DartType type = typeName.type; |
| 14593 if (type is InterfaceType) { |
| 14594 ClassElement element = type.element; |
| 14595 if (element != null && element.isEnum) { |
| 14596 reportErrorForNode(enumTypeError, typeName); |
| 14597 return null; |
| 14598 } |
| 14599 return type; |
| 14600 } |
| 14601 // If the type is not an InterfaceType, then visitTypeName() sets the type |
| 14602 // to be a DynamicTypeImpl |
| 14603 Identifier name = typeName.name; |
| 14604 if (name.name == sc.Keyword.DYNAMIC.syntax) { |
| 14605 reportErrorForNode(dynamicTypeError, name, [name.name]); |
| 14606 } else { |
| 14607 reportErrorForNode(nonTypeError, name, [name.name]); |
| 14608 } |
| 14609 return null; |
| 14610 } |
| 14611 |
| 14612 /** |
| 14613 * Resolve the types in the given list of type names. |
| 14614 * |
| 14615 * @param typeNames the type names to be resolved |
| 14616 * @param nonTypeError the error to produce if the type name is defined to be
something other than |
| 14617 * a type |
| 14618 * @param enumTypeError the error to produce if the type name is defined to be
an enum |
| 14619 * @param dynamicTypeError the error to produce if the type name is "dynamic" |
| 14620 * @return an array containing all of the types that were resolved. |
| 14621 */ |
| 14622 List<InterfaceType> _resolveTypes( |
| 14623 NodeList<TypeName> typeNames, |
| 14624 ErrorCode nonTypeError, |
| 14625 ErrorCode enumTypeError, |
| 14626 ErrorCode dynamicTypeError) { |
| 14627 List<InterfaceType> types = new List<InterfaceType>(); |
| 14628 for (TypeName typeName in typeNames) { |
| 14629 InterfaceType type = |
| 14630 _resolveType(typeName, nonTypeError, enumTypeError, dynamicTypeError); |
| 14631 if (type != null) { |
| 14632 types.add(type); |
| 14633 } |
| 14634 } |
| 14635 return types; |
| 14636 } |
| 14637 |
| 14638 void _setElement(Identifier typeName, Element element) { |
| 14639 if (element != null) { |
| 14640 if (typeName is SimpleIdentifier) { |
| 14641 typeName.staticElement = element; |
| 14642 } else if (typeName is PrefixedIdentifier) { |
| 14643 PrefixedIdentifier identifier = typeName; |
| 14644 identifier.identifier.staticElement = element; |
| 14645 SimpleIdentifier prefix = identifier.prefix; |
| 14646 Element prefixElement = nameScope.lookup(prefix, definingLibrary); |
| 14647 if (prefixElement != null) { |
| 14648 prefix.staticElement = prefixElement; |
| 14649 } |
| 14650 } |
| 14651 } |
| 14652 } |
| 14653 |
| 14654 /** |
| 14655 * Given a parameter element, create a function type based on the given return
type and parameter |
| 14656 * list and associate the created type with the element. |
| 14657 * |
| 14658 * @param element the parameter element whose type is to be set |
| 14659 * @param returnType the (possibly `null`) return type of the function |
| 14660 * @param parameterList the list of parameters to the function |
| 14661 */ |
| 14662 void _setFunctionTypedParameterType(ParameterElementImpl element, |
| 14663 TypeName returnType, FormalParameterList parameterList) { |
| 14664 List<ParameterElement> parameters = _getElements(parameterList); |
| 14665 FunctionTypeAliasElementImpl aliasElement = |
| 14666 new FunctionTypeAliasElementImpl.forNode(null); |
| 14667 aliasElement.synthetic = true; |
| 14668 aliasElement.shareParameters(parameters); |
| 14669 aliasElement.returnType = _computeReturnType(returnType); |
| 14670 // FunctionTypeAliasElementImpl assumes the enclosing element is a |
| 14671 // CompilationUnitElement (because non-synthetic function types can only be |
| 14672 // declared at top level), so to avoid breaking things, go find the |
| 14673 // compilation unit element. |
| 14674 aliasElement.enclosingElement = |
| 14675 element.getAncestor((element) => element is CompilationUnitElement); |
| 14676 FunctionTypeImpl type = new FunctionTypeImpl.forTypedef(aliasElement); |
| 14677 ClassElement definingClass = |
| 14678 element.getAncestor((element) => element is ClassElement); |
| 14679 if (definingClass != null) { |
| 14680 aliasElement.shareTypeParameters(definingClass.typeParameters); |
| 14681 type.typeArguments = definingClass.type.typeArguments; |
| 14682 } else { |
| 14683 FunctionTypeAliasElement alias = |
| 14684 element.getAncestor((element) => element is FunctionTypeAliasElement); |
| 14685 while (alias != null && alias.isSynthetic) { |
| 14686 alias = |
| 14687 alias.getAncestor((element) => element is FunctionTypeAliasElement); |
| 14688 } |
| 14689 if (alias != null) { |
| 14690 aliasElement.typeParameters = alias.typeParameters; |
| 14691 type.typeArguments = alias.type.typeArguments; |
| 14692 } else { |
| 14693 type.typeArguments = DartType.EMPTY_LIST; |
| 14694 } |
| 14695 } |
| 14696 element.type = type; |
| 14697 } |
| 14698 |
| 14699 /** |
| 14700 * @return `true` if the name of the given [TypeName] is an built-in identifie
r. |
| 14701 */ |
| 14702 static bool _isBuiltInIdentifier(TypeName node) { |
| 14703 sc.Token token = node.name.beginToken; |
| 14704 return token.type == sc.TokenType.KEYWORD; |
| 14705 } |
| 14706 |
| 14707 /** |
| 14708 * @return `true` if given [TypeName] is used as a type annotation. |
| 14709 */ |
| 14710 static bool _isTypeAnnotation(TypeName node) { |
| 14711 AstNode parent = node.parent; |
| 14712 if (parent is VariableDeclarationList) { |
| 14713 return identical(parent.type, node); |
| 14714 } |
| 14715 if (parent is FieldFormalParameter) { |
| 14716 return identical(parent.type, node); |
| 14717 } |
| 14718 if (parent is SimpleFormalParameter) { |
| 14719 return identical(parent.type, node); |
| 14720 } |
| 14721 return false; |
| 14722 } |
| 14723 } |
| 14724 |
| 14725 /** |
| 14726 * The interface `TypeSystem` defines the behavior of an object representing |
| 14727 * the type system. This provides a common location to put methods that act on |
| 14728 * types but may need access to more global data structures, and it paves the |
| 14729 * way for a possible future where we may wish to make the type system |
| 14730 * pluggable. |
| 14731 */ |
| 14732 abstract class TypeSystem { |
| 14733 /** |
| 14734 * Return the [TypeProvider] associated with this [TypeSystem]. |
| 14735 */ |
| 14736 TypeProvider get typeProvider; |
| 14737 |
| 14738 /** |
| 14739 * Compute the least upper bound of two types. |
| 14740 */ |
| 14741 DartType getLeastUpperBound(DartType type1, DartType type2); |
| 14742 |
| 14743 /** |
| 14744 * Return `true` if the [leftType] is a subtype of the [rightType] (that is, |
| 14745 * if leftType <: rightType). |
| 14746 */ |
| 14747 bool isSubtypeOf(DartType leftType, DartType rightType); |
| 14748 } |
| 14749 |
| 14750 /** |
| 14751 * Implementation of [TypeSystem] using the rules in the Dart specification. |
| 14752 */ |
| 14753 class TypeSystemImpl implements TypeSystem { |
| 14754 @override |
| 14755 final TypeProvider typeProvider; |
| 14756 |
| 14757 TypeSystemImpl(this.typeProvider); |
| 14758 |
| 14759 @override |
| 14760 DartType getLeastUpperBound(DartType type1, DartType type2) { |
| 14761 // The least upper bound relation is reflexive. |
| 14762 if (identical(type1, type2)) { |
| 14763 return type1; |
| 14764 } |
| 14765 // The least upper bound of dynamic and any type T is dynamic. |
| 14766 if (type1.isDynamic) { |
| 14767 return type1; |
| 14768 } |
| 14769 if (type2.isDynamic) { |
| 14770 return type2; |
| 14771 } |
| 14772 // The least upper bound of void and any type T != dynamic is void. |
| 14773 if (type1.isVoid) { |
| 14774 return type1; |
| 14775 } |
| 14776 if (type2.isVoid) { |
| 14777 return type2; |
| 14778 } |
| 14779 // The least upper bound of bottom and any type T is T. |
| 14780 if (type1.isBottom) { |
| 14781 return type2; |
| 14782 } |
| 14783 if (type2.isBottom) { |
| 14784 return type1; |
| 14785 } |
| 14786 // Let U be a type variable with upper bound B. The least upper bound of U |
| 14787 // and a type T is the least upper bound of B and T. |
| 14788 while (type1 is TypeParameterType) { |
| 14789 // TODO(paulberry): is this correct in the complex of F-bounded |
| 14790 // polymorphism? |
| 14791 DartType bound = (type1 as TypeParameterType).element.bound; |
| 14792 if (bound == null) { |
| 14793 bound = typeProvider.objectType; |
| 14794 } |
| 14795 type1 = bound; |
| 14796 } |
| 14797 while (type2 is TypeParameterType) { |
| 14798 // TODO(paulberry): is this correct in the context of F-bounded |
| 14799 // polymorphism? |
| 14800 DartType bound = (type2 as TypeParameterType).element.bound; |
| 14801 if (bound == null) { |
| 14802 bound = typeProvider.objectType; |
| 14803 } |
| 14804 type2 = bound; |
| 14805 } |
| 14806 // The least upper bound of a function type and an interface type T is the |
| 14807 // least upper bound of Function and T. |
| 14808 if (type1 is FunctionType && type2 is InterfaceType) { |
| 14809 type1 = typeProvider.functionType; |
| 14810 } |
| 14811 if (type2 is FunctionType && type1 is InterfaceType) { |
| 14812 type2 = typeProvider.functionType; |
| 14813 } |
| 14814 |
| 14815 // At this point type1 and type2 should both either be interface types or |
| 14816 // function types. |
| 14817 if (type1 is InterfaceType && type2 is InterfaceType) { |
| 14818 InterfaceType result = |
| 14819 InterfaceTypeImpl.computeLeastUpperBound(type1, type2); |
| 14820 if (result == null) { |
| 14821 return typeProvider.dynamicType; |
| 14822 } |
| 14823 return result; |
| 14824 } else if (type1 is FunctionType && type2 is FunctionType) { |
| 14825 FunctionType result = |
| 14826 FunctionTypeImpl.computeLeastUpperBound(type1, type2); |
| 14827 if (result == null) { |
| 14828 return typeProvider.functionType; |
| 14829 } |
| 14830 return result; |
| 14831 } else { |
| 14832 // Should never happen. As a defensive measure, return the dynamic type. |
| 14833 assert(false); |
| 14834 return typeProvider.dynamicType; |
| 14835 } |
| 14836 } |
| 14837 |
| 14838 @override |
| 14839 bool isSubtypeOf(DartType leftType, DartType rightType) { |
| 14840 return leftType.isSubtypeOf(rightType); |
| 14841 } |
| 14842 } |
| 14843 |
| 14844 /** |
| 14845 * Instances of the class [UnusedLocalElementsVerifier] traverse an element |
| 14846 * structure looking for cases of [HintCode.UNUSED_ELEMENT], |
| 14847 * [HintCode.UNUSED_FIELD], [HintCode.UNUSED_LOCAL_VARIABLE], etc. |
| 14848 */ |
| 14849 class UnusedLocalElementsVerifier extends RecursiveElementVisitor { |
| 14850 /** |
| 14851 * The error listener to which errors will be reported. |
| 14852 */ |
| 14853 final AnalysisErrorListener _errorListener; |
| 14854 |
| 14855 /** |
| 14856 * The elements know to be used. |
| 14857 */ |
| 14858 final UsedLocalElements _usedElements; |
| 14859 |
| 14860 /** |
| 14861 * Create a new instance of the [UnusedLocalElementsVerifier]. |
| 14862 */ |
| 14863 UnusedLocalElementsVerifier(this._errorListener, this._usedElements); |
| 14864 |
| 14865 @override |
| 14866 visitClassElement(ClassElement element) { |
| 14867 if (!_isUsedElement(element)) { |
| 14868 _reportErrorForElement(HintCode.UNUSED_ELEMENT, element, |
| 14869 [element.kind.displayName, element.displayName]); |
| 14870 } |
| 14871 super.visitClassElement(element); |
| 14872 } |
| 14873 |
| 14874 @override |
| 14875 visitFieldElement(FieldElement element) { |
| 14876 if (!_isReadMember(element)) { |
| 14877 _reportErrorForElement( |
| 14878 HintCode.UNUSED_FIELD, element, [element.displayName]); |
| 14879 } |
| 14880 super.visitFieldElement(element); |
| 14881 } |
| 14882 |
| 14883 @override |
| 14884 visitFunctionElement(FunctionElement element) { |
| 14885 if (!_isUsedElement(element)) { |
| 14886 _reportErrorForElement(HintCode.UNUSED_ELEMENT, element, |
| 14887 [element.kind.displayName, element.displayName]); |
| 14888 } |
| 14889 super.visitFunctionElement(element); |
| 14890 } |
| 14891 |
| 14892 @override |
| 14893 visitFunctionTypeAliasElement(FunctionTypeAliasElement element) { |
| 14894 if (!_isUsedElement(element)) { |
| 14895 _reportErrorForElement(HintCode.UNUSED_ELEMENT, element, |
| 14896 [element.kind.displayName, element.displayName]); |
| 14897 } |
| 14898 super.visitFunctionTypeAliasElement(element); |
| 14899 } |
| 14900 |
| 14901 @override |
| 14902 visitLocalVariableElement(LocalVariableElement element) { |
| 14903 if (!_isUsedElement(element) && !_isNamedUnderscore(element)) { |
| 14904 HintCode errorCode; |
| 14905 if (_usedElements.isCatchException(element)) { |
| 14906 errorCode = HintCode.UNUSED_CATCH_CLAUSE; |
| 14907 } else if (_usedElements.isCatchStackTrace(element)) { |
| 14908 errorCode = HintCode.UNUSED_CATCH_STACK; |
| 14909 } else { |
| 14910 errorCode = HintCode.UNUSED_LOCAL_VARIABLE; |
| 14911 } |
| 14912 _reportErrorForElement(errorCode, element, [element.displayName]); |
| 14913 } |
| 14914 } |
| 14915 |
| 14916 @override |
| 14917 visitMethodElement(MethodElement element) { |
| 14918 if (!_isUsedMember(element)) { |
| 14919 _reportErrorForElement(HintCode.UNUSED_ELEMENT, element, |
| 14920 [element.kind.displayName, element.displayName]); |
| 14921 } |
| 14922 super.visitMethodElement(element); |
| 14923 } |
| 14924 |
| 14925 @override |
| 14926 visitPropertyAccessorElement(PropertyAccessorElement element) { |
| 14927 if (!_isUsedMember(element)) { |
| 14928 _reportErrorForElement(HintCode.UNUSED_ELEMENT, element, |
| 14929 [element.kind.displayName, element.displayName]); |
| 14930 } |
| 14931 super.visitPropertyAccessorElement(element); |
| 14932 } |
| 14933 |
| 14934 bool _isNamedUnderscore(LocalVariableElement element) { |
| 14935 String name = element.name; |
| 14936 if (name != null) { |
| 14937 for (int index = name.length - 1; index >= 0; --index) { |
| 14938 if (name.codeUnitAt(index) != 0x5F) { |
| 14939 // 0x5F => '_' |
| 14940 return false; |
| 14941 } |
| 14942 } |
| 14943 return true; |
| 14944 } |
| 14945 return false; |
| 14946 } |
| 14947 |
| 14948 bool _isReadMember(Element element) { |
| 14949 if (element.isPublic) { |
| 14950 return true; |
| 14951 } |
| 14952 if (element.isSynthetic) { |
| 14953 return true; |
| 14954 } |
| 14955 return _usedElements.readMembers.contains(element.displayName); |
| 14956 } |
| 14957 |
| 14958 bool _isUsedElement(Element element) { |
| 14959 if (element.isSynthetic) { |
| 14960 return true; |
| 14961 } |
| 14962 if (element is LocalVariableElement || |
| 14963 element is FunctionElement && !element.isStatic) { |
| 14964 // local variable or function |
| 14965 } else { |
| 14966 if (element.isPublic) { |
| 14967 return true; |
| 14968 } |
| 14969 } |
| 14970 return _usedElements.elements.contains(element); |
| 14971 } |
| 14972 |
| 14973 bool _isUsedMember(Element element) { |
| 14974 if (element.isPublic) { |
| 14975 return true; |
| 14976 } |
| 14977 if (element.isSynthetic) { |
| 14978 return true; |
| 14979 } |
| 14980 if (_usedElements.members.contains(element.displayName)) { |
| 14981 return true; |
| 14982 } |
| 14983 return _usedElements.elements.contains(element); |
| 14984 } |
| 14985 |
| 14986 void _reportErrorForElement( |
| 14987 ErrorCode errorCode, Element element, List<Object> arguments) { |
| 14988 if (element != null) { |
| 14989 _errorListener.onError(new AnalysisError( |
| 14990 element.source, |
| 14991 element.nameOffset, |
| 14992 element.displayName.length, |
| 14993 errorCode, |
| 14994 arguments)); |
| 14995 } |
| 14996 } |
| 14997 } |
| 14998 |
| 14999 /** |
| 15000 * A container with information about used imports prefixes and used imported |
| 15001 * elements. |
| 15002 */ |
| 15003 class UsedImportedElements { |
| 15004 /** |
| 15005 * The set of referenced [PrefixElement]s. |
| 15006 */ |
| 15007 final Set<PrefixElement> prefixes = new HashSet<PrefixElement>(); |
| 15008 |
| 15009 /** |
| 15010 * The set of referenced top-level [Element]s. |
| 15011 */ |
| 15012 final Set<Element> elements = new HashSet<Element>(); |
| 15013 } |
| 15014 |
| 15015 /** |
| 15016 * A container with sets of used [Element]s. |
| 15017 * All these elements are defined in a single compilation unit or a library. |
| 15018 */ |
| 15019 class UsedLocalElements { |
| 15020 /** |
| 15021 * Resolved, locally defined elements that are used or potentially can be |
| 15022 * used. |
| 15023 */ |
| 15024 final HashSet<Element> elements = new HashSet<Element>(); |
| 15025 |
| 15026 /** |
| 15027 * [LocalVariableElement]s that represent exceptions in [CatchClause]s. |
| 15028 */ |
| 15029 final HashSet<LocalVariableElement> catchExceptionElements = |
| 15030 new HashSet<LocalVariableElement>(); |
| 15031 |
| 15032 /** |
| 15033 * [LocalVariableElement]s that represent stack traces in [CatchClause]s. |
| 15034 */ |
| 15035 final HashSet<LocalVariableElement> catchStackTraceElements = |
| 15036 new HashSet<LocalVariableElement>(); |
| 15037 |
| 15038 /** |
| 15039 * Names of resolved or unresolved class members that are referenced in the |
| 15040 * library. |
| 15041 */ |
| 15042 final HashSet<String> members = new HashSet<String>(); |
| 15043 |
| 15044 /** |
| 15045 * Names of resolved or unresolved class members that are read in the |
| 15046 * library. |
| 15047 */ |
| 15048 final HashSet<String> readMembers = new HashSet<String>(); |
| 15049 |
| 15050 UsedLocalElements(); |
| 15051 |
| 15052 factory UsedLocalElements.merge(List<UsedLocalElements> parts) { |
| 15053 UsedLocalElements result = new UsedLocalElements(); |
| 15054 for (UsedLocalElements part in parts) { |
| 15055 result.elements.addAll(part.elements); |
| 15056 result.catchExceptionElements.addAll(part.catchExceptionElements); |
| 15057 result.catchStackTraceElements.addAll(part.catchStackTraceElements); |
| 15058 result.members.addAll(part.members); |
| 15059 result.readMembers.addAll(part.readMembers); |
| 15060 } |
| 15061 return result; |
| 15062 } |
| 15063 |
| 15064 void addCatchException(LocalVariableElement element) { |
| 15065 if (element != null) { |
| 15066 catchExceptionElements.add(element); |
| 15067 } |
| 15068 } |
| 15069 |
| 15070 void addCatchStackTrace(LocalVariableElement element) { |
| 15071 if (element != null) { |
| 15072 catchStackTraceElements.add(element); |
| 15073 } |
| 15074 } |
| 15075 |
| 15076 void addElement(Element element) { |
| 15077 if (element != null) { |
| 15078 elements.add(element); |
| 15079 } |
| 15080 } |
| 15081 |
| 15082 bool isCatchException(LocalVariableElement element) { |
| 15083 return catchExceptionElements.contains(element); |
| 15084 } |
| 15085 |
| 15086 bool isCatchStackTrace(LocalVariableElement element) { |
| 15087 return catchStackTraceElements.contains(element); |
| 15088 } |
| 15089 } |
| 15090 |
| 15091 /** |
| 15092 * Instances of the class `VariableResolverVisitor` are used to resolve |
| 15093 * [SimpleIdentifier]s to local variables and formal parameters. |
| 15094 */ |
| 15095 class VariableResolverVisitor extends ScopedVisitor { |
| 15096 /** |
| 15097 * The method or function that we are currently visiting, or `null` if we are
not inside a |
| 15098 * method or function. |
| 15099 */ |
| 15100 ExecutableElement _enclosingFunction; |
| 15101 |
| 15102 /** |
| 15103 * Initialize a newly created visitor to resolve the nodes in an AST node. |
| 15104 * |
| 15105 * [definingLibrary] is the element for the library containing the node being |
| 15106 * visited. |
| 15107 * [source] is the source representing the compilation unit containing the |
| 15108 * node being visited |
| 15109 * [typeProvider] is the object used to access the types from the core |
| 15110 * library. |
| 15111 * [errorListener] is the error listener that will be informed of any errors |
| 15112 * that are found during resolution. |
| 15113 * [nameScope] is the scope used to resolve identifiers in the node that will |
| 15114 * first be visited. If `null` or unspecified, a new [LibraryScope] will be |
| 15115 * created based on [definingLibrary] and [typeProvider]. |
| 15116 */ |
| 15117 VariableResolverVisitor(LibraryElement definingLibrary, Source source, |
| 15118 TypeProvider typeProvider, AnalysisErrorListener errorListener, |
| 15119 {Scope nameScope}) |
| 15120 : super(definingLibrary, source, typeProvider, errorListener, |
| 15121 nameScope: nameScope); |
| 15122 |
| 15123 /** |
| 15124 * Initialize a newly created visitor to resolve the nodes in a compilation un
it. |
| 15125 * |
| 15126 * @param library the library containing the compilation unit being resolved |
| 15127 * @param source the source representing the compilation unit being visited |
| 15128 * @param typeProvider the object used to access the types from the core libra
ry |
| 15129 * |
| 15130 * Deprecated. Please use unnamed constructor instead. |
| 15131 */ |
| 15132 @deprecated |
| 15133 VariableResolverVisitor.con1( |
| 15134 Library library, Source source, TypeProvider typeProvider) |
| 15135 : this( |
| 15136 library.libraryElement, source, typeProvider, library.errorListener, |
| 15137 nameScope: library.libraryScope); |
| 15138 |
| 15139 @override |
| 15140 Object visitExportDirective(ExportDirective node) => null; |
| 15141 |
| 15142 @override |
| 15143 Object visitFunctionDeclaration(FunctionDeclaration node) { |
| 15144 ExecutableElement outerFunction = _enclosingFunction; |
| 15145 try { |
| 15146 _enclosingFunction = node.element; |
| 15147 return super.visitFunctionDeclaration(node); |
| 15148 } finally { |
| 15149 _enclosingFunction = outerFunction; |
| 15150 } |
| 15151 } |
| 15152 |
| 15153 @override |
| 15154 Object visitFunctionExpression(FunctionExpression node) { |
| 15155 if (node.parent is! FunctionDeclaration) { |
| 15156 ExecutableElement outerFunction = _enclosingFunction; |
| 15157 try { |
| 15158 _enclosingFunction = node.element; |
| 15159 return super.visitFunctionExpression(node); |
| 15160 } finally { |
| 15161 _enclosingFunction = outerFunction; |
| 15162 } |
| 15163 } else { |
| 15164 return super.visitFunctionExpression(node); |
| 15165 } |
| 15166 } |
| 15167 |
| 15168 @override |
| 15169 Object visitImportDirective(ImportDirective node) => null; |
| 15170 |
| 15171 @override |
| 15172 Object visitMethodDeclaration(MethodDeclaration node) { |
| 15173 ExecutableElement outerFunction = _enclosingFunction; |
| 15174 try { |
| 15175 _enclosingFunction = node.element; |
| 15176 return super.visitMethodDeclaration(node); |
| 15177 } finally { |
| 15178 _enclosingFunction = outerFunction; |
| 15179 } |
| 15180 } |
| 15181 |
| 15182 @override |
| 15183 Object visitSimpleIdentifier(SimpleIdentifier node) { |
| 15184 // Ignore if already resolved - declaration or type. |
| 15185 if (node.staticElement != null) { |
| 15186 return null; |
| 15187 } |
| 15188 // Ignore if qualified. |
| 15189 AstNode parent = node.parent; |
| 15190 if (parent is PrefixedIdentifier && identical(parent.identifier, node)) { |
| 15191 return null; |
| 15192 } |
| 15193 if (parent is PropertyAccess && identical(parent.propertyName, node)) { |
| 15194 return null; |
| 15195 } |
| 15196 if (parent is MethodInvocation && |
| 15197 identical(parent.methodName, node) && |
| 15198 parent.realTarget != null) { |
| 15199 return null; |
| 15200 } |
| 15201 if (parent is ConstructorName) { |
| 15202 return null; |
| 15203 } |
| 15204 if (parent is Label) { |
| 15205 return null; |
| 15206 } |
| 15207 // Prepare VariableElement. |
| 15208 Element element = nameScope.lookup(node, definingLibrary); |
| 15209 if (element is! VariableElement) { |
| 15210 return null; |
| 15211 } |
| 15212 // Must be local or parameter. |
| 15213 ElementKind kind = element.kind; |
| 15214 if (kind == ElementKind.LOCAL_VARIABLE) { |
| 15215 node.staticElement = element; |
| 15216 LocalVariableElementImpl variableImpl = |
| 15217 element as LocalVariableElementImpl; |
| 15218 if (node.inSetterContext()) { |
| 15219 variableImpl.markPotentiallyMutatedInScope(); |
| 15220 if (element.enclosingElement != _enclosingFunction) { |
| 15221 variableImpl.markPotentiallyMutatedInClosure(); |
| 15222 } |
| 15223 } |
| 15224 } else if (kind == ElementKind.PARAMETER) { |
| 15225 node.staticElement = element; |
| 15226 if (node.inSetterContext()) { |
| 15227 ParameterElementImpl parameterImpl = element as ParameterElementImpl; |
| 15228 parameterImpl.markPotentiallyMutatedInScope(); |
| 15229 // If we are in some closure, check if it is not the same as where |
| 15230 // variable is declared. |
| 15231 if (_enclosingFunction != null && |
| 15232 (element.enclosingElement != _enclosingFunction)) { |
| 15233 parameterImpl.markPotentiallyMutatedInClosure(); |
| 15234 } |
| 15235 } |
| 15236 } |
| 15237 return null; |
| 15238 } |
| 15239 } |
| 15240 |
| 15241 class _ConstantVerifier_validateInitializerExpression extends ConstantVisitor { |
| 15242 final ConstantVerifier verifier; |
| 15243 |
| 15244 List<ParameterElement> parameterElements; |
| 15245 |
| 15246 _ConstantVerifier_validateInitializerExpression( |
| 15247 TypeProvider typeProvider, |
| 15248 ErrorReporter errorReporter, |
| 15249 this.verifier, |
| 15250 this.parameterElements, |
| 15251 DeclaredVariables declaredVariables) |
| 15252 : super(new ConstantEvaluationEngine(typeProvider, declaredVariables), |
| 15253 errorReporter); |
| 15254 |
| 15255 @override |
| 15256 DartObjectImpl visitSimpleIdentifier(SimpleIdentifier node) { |
| 15257 Element element = node.staticElement; |
| 15258 for (ParameterElement parameterElement in parameterElements) { |
| 15259 if (identical(parameterElement, element) && parameterElement != null) { |
| 15260 DartType type = parameterElement.type; |
| 15261 if (type != null) { |
| 15262 if (type.isDynamic) { |
| 15263 return new DartObjectImpl( |
| 15264 verifier._typeProvider.objectType, DynamicState.DYNAMIC_STATE); |
| 15265 } else if (type.isSubtypeOf(verifier._boolType)) { |
| 15266 return new DartObjectImpl( |
| 15267 verifier._typeProvider.boolType, BoolState.UNKNOWN_VALUE); |
| 15268 } else if (type.isSubtypeOf(verifier._typeProvider.doubleType)) { |
| 15269 return new DartObjectImpl( |
| 15270 verifier._typeProvider.doubleType, DoubleState.UNKNOWN_VALUE); |
| 15271 } else if (type.isSubtypeOf(verifier._intType)) { |
| 15272 return new DartObjectImpl( |
| 15273 verifier._typeProvider.intType, IntState.UNKNOWN_VALUE); |
| 15274 } else if (type.isSubtypeOf(verifier._numType)) { |
| 15275 return new DartObjectImpl( |
| 15276 verifier._typeProvider.numType, NumState.UNKNOWN_VALUE); |
| 15277 } else if (type.isSubtypeOf(verifier._stringType)) { |
| 15278 return new DartObjectImpl( |
| 15279 verifier._typeProvider.stringType, StringState.UNKNOWN_VALUE); |
| 15280 } |
| 15281 // |
| 15282 // We don't test for other types of objects (such as List, Map, |
| 15283 // Function or Type) because there are no operations allowed on such |
| 15284 // types other than '==' and '!=', which means that we don't need to |
| 15285 // know the type when there is no specific data about the state of |
| 15286 // such objects. |
| 15287 // |
| 15288 } |
| 15289 return new DartObjectImpl( |
| 15290 type is InterfaceType ? type : verifier._typeProvider.objectType, |
| 15291 GenericState.UNKNOWN_VALUE); |
| 15292 } |
| 15293 } |
| 15294 return super.visitSimpleIdentifier(node); |
| 15295 } |
| 15296 } |
| 15297 |
| 15298 class _ElementBuilder_visitClassDeclaration extends UnifyingAstVisitor<Object> { |
| 15299 final ElementBuilder builder; |
| 15300 |
| 15301 List<ClassMember> nonFields; |
| 15302 |
| 15303 _ElementBuilder_visitClassDeclaration(this.builder, this.nonFields) : super(); |
| 15304 |
| 15305 @override |
| 15306 Object visitConstructorDeclaration(ConstructorDeclaration node) { |
| 15307 nonFields.add(node); |
| 15308 return null; |
| 15309 } |
| 15310 |
| 15311 @override |
| 15312 Object visitMethodDeclaration(MethodDeclaration node) { |
| 15313 nonFields.add(node); |
| 15314 return null; |
| 15315 } |
| 15316 |
| 15317 @override |
| 15318 Object visitNode(AstNode node) => node.accept(builder); |
| 15319 } |
| 15320 |
| 15321 class _ResolverVisitor_isVariableAccessedInClosure |
| 15322 extends RecursiveAstVisitor<Object> { |
| 15323 final Element variable; |
| 15324 |
| 15325 bool result = false; |
| 15326 |
| 15327 bool _inClosure = false; |
| 15328 |
| 15329 _ResolverVisitor_isVariableAccessedInClosure(this.variable); |
| 15330 |
| 15331 @override |
| 15332 Object visitFunctionExpression(FunctionExpression node) { |
| 15333 bool inClosure = this._inClosure; |
| 15334 try { |
| 15335 this._inClosure = true; |
| 15336 return super.visitFunctionExpression(node); |
| 15337 } finally { |
| 15338 this._inClosure = inClosure; |
| 15339 } |
| 15340 } |
| 15341 |
| 15342 @override |
| 15343 Object visitSimpleIdentifier(SimpleIdentifier node) { |
| 15344 if (result) { |
| 15345 return null; |
| 15346 } |
| 15347 if (_inClosure && identical(node.staticElement, variable)) { |
| 15348 result = true; |
| 15349 } |
| 15350 return null; |
| 15351 } |
| 15352 } |
| 15353 |
| 15354 class _ResolverVisitor_isVariablePotentiallyMutatedIn |
| 15355 extends RecursiveAstVisitor<Object> { |
| 15356 final Element variable; |
| 15357 |
| 15358 bool result = false; |
| 15359 |
| 15360 _ResolverVisitor_isVariablePotentiallyMutatedIn(this.variable); |
| 15361 |
| 15362 @override |
| 15363 Object visitSimpleIdentifier(SimpleIdentifier node) { |
| 15364 if (result) { |
| 15365 return null; |
| 15366 } |
| 15367 if (identical(node.staticElement, variable)) { |
| 15368 if (node.inSetterContext()) { |
| 15369 result = true; |
| 15370 } |
| 15371 } |
| 15372 return null; |
| 15373 } |
| 15374 } |
| 15375 |
| 15376 class _TypeResolverVisitor_visitClassMembersInScope |
| 15377 extends UnifyingAstVisitor<Object> { |
| 15378 final TypeResolverVisitor TypeResolverVisitor_this; |
| 15379 |
| 15380 List<ClassMember> nonFields; |
| 15381 |
| 15382 _TypeResolverVisitor_visitClassMembersInScope( |
| 15383 this.TypeResolverVisitor_this, this.nonFields) |
| 15384 : super(); |
| 15385 |
| 15386 @override |
| 15387 Object visitConstructorDeclaration(ConstructorDeclaration node) { |
| 15388 nonFields.add(node); |
| 15389 return null; |
| 15390 } |
| 15391 |
| 15392 @override |
| 15393 Object visitExtendsClause(ExtendsClause node) => null; |
| 15394 |
| 15395 @override |
| 15396 Object visitImplementsClause(ImplementsClause node) => null; |
| 15397 |
| 15398 @override |
| 15399 Object visitMethodDeclaration(MethodDeclaration node) { |
| 15400 nonFields.add(node); |
| 15401 return null; |
| 15402 } |
| 15403 |
| 15404 @override |
| 15405 Object visitNode(AstNode node) => node.accept(TypeResolverVisitor_this); |
| 15406 |
| 15407 @override |
| 15408 Object visitWithClause(WithClause node) => null; |
| 15409 } |
OLD | NEW |