| 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(Expression expression, | |
| 297 DartType expectedStaticType, DartType actualStaticType, | |
| 298 DartType expectedPropagatedType, DartType actualPropagatedType, | |
| 299 ErrorCode hintCode) { | |
| 300 // | |
| 301 // Warning case: test static type information | |
| 302 // | |
| 303 if (actualStaticType != null && expectedStaticType != null) { | |
| 304 if (!actualStaticType.isAssignableTo(expectedStaticType)) { | |
| 305 // A warning was created in the ErrorVerifier, return false, don't | |
| 306 // create a hint when a warning has already been created. | |
| 307 return false; | |
| 308 } | |
| 309 } | |
| 310 // | |
| 311 // Hint case: test propagated type information | |
| 312 // | |
| 313 // Compute the best types to use. | |
| 314 DartType expectedBestType = expectedPropagatedType != null | |
| 315 ? expectedPropagatedType | |
| 316 : expectedStaticType; | |
| 317 DartType actualBestType = | |
| 318 actualPropagatedType != null ? actualPropagatedType : actualStaticType; | |
| 319 if (actualBestType != null && expectedBestType != null) { | |
| 320 if (!actualBestType.isAssignableTo(expectedBestType)) { | |
| 321 _errorReporter.reportTypeErrorForNode( | |
| 322 hintCode, expression, [actualBestType, expectedBestType]); | |
| 323 return true; | |
| 324 } | |
| 325 } | |
| 326 return false; | |
| 327 } | |
| 328 | |
| 329 /** | |
| 330 * This verifies that the passed argument can be assigned to its corresponding
parameter. | |
| 331 * | |
| 332 * This method corresponds to ErrorCode.checkForArgumentTypeNotAssignableForAr
gument. | |
| 333 * | |
| 334 * @param argument the argument to evaluate | |
| 335 * @return `true` if and only if an hint code is generated on the passed node | |
| 336 * See [HintCode.ARGUMENT_TYPE_NOT_ASSIGNABLE]. | |
| 337 */ | |
| 338 bool _checkForArgumentTypeNotAssignableForArgument(Expression argument) { | |
| 339 if (argument == null) { | |
| 340 return false; | |
| 341 } | |
| 342 ParameterElement staticParameterElement = argument.staticParameterElement; | |
| 343 DartType staticParameterType = | |
| 344 staticParameterElement == null ? null : staticParameterElement.type; | |
| 345 ParameterElement propagatedParameterElement = | |
| 346 argument.propagatedParameterElement; | |
| 347 DartType propagatedParameterType = propagatedParameterElement == null | |
| 348 ? null | |
| 349 : propagatedParameterElement.type; | |
| 350 return _checkForArgumentTypeNotAssignableWithExpectedTypes(argument, | |
| 351 staticParameterType, propagatedParameterType, | |
| 352 HintCode.ARGUMENT_TYPE_NOT_ASSIGNABLE); | |
| 353 } | |
| 354 | |
| 355 /** | |
| 356 * This verifies that the passed expression can be assigned to its correspondi
ng parameters. | |
| 357 * | |
| 358 * This method corresponds to ErrorCode.checkForArgumentTypeNotAssignableWithE
xpectedTypes. | |
| 359 * | |
| 360 * @param expression the expression to evaluate | |
| 361 * @param expectedStaticType the expected static type | |
| 362 * @param expectedPropagatedType the expected propagated type, may be `null` | |
| 363 * @return `true` if and only if an hint code is generated on the passed node | |
| 364 * See [HintCode.ARGUMENT_TYPE_NOT_ASSIGNABLE]. | |
| 365 */ | |
| 366 bool _checkForArgumentTypeNotAssignableWithExpectedTypes( | |
| 367 Expression expression, DartType expectedStaticType, | |
| 368 DartType expectedPropagatedType, ErrorCode errorCode) => | |
| 369 _checkForArgumentTypeNotAssignable(expression, expectedStaticType, | |
| 370 expression.staticType, expectedPropagatedType, | |
| 371 expression.propagatedType, errorCode); | |
| 372 | |
| 373 /** | |
| 374 * This verifies that the passed arguments can be assigned to their correspond
ing parameters. | |
| 375 * | |
| 376 * This method corresponds to ErrorCode.checkForArgumentTypesNotAssignableInLi
st. | |
| 377 * | |
| 378 * @param node the arguments to evaluate | |
| 379 * @return `true` if and only if an hint code is generated on the passed node | |
| 380 * See [HintCode.ARGUMENT_TYPE_NOT_ASSIGNABLE]. | |
| 381 */ | |
| 382 bool _checkForArgumentTypesNotAssignableInList(ArgumentList argumentList) { | |
| 383 if (argumentList == null) { | |
| 384 return false; | |
| 385 } | |
| 386 bool problemReported = false; | |
| 387 for (Expression argument in argumentList.arguments) { | |
| 388 if (_checkForArgumentTypeNotAssignableForArgument(argument)) { | |
| 389 problemReported = true; | |
| 390 } | |
| 391 } | |
| 392 return problemReported; | |
| 393 } | |
| 394 | |
| 395 /** | |
| 396 * Given some [Element], look at the associated metadata and report the use of
the member if | |
| 397 * it is declared as deprecated. | |
| 398 * | |
| 399 * @param element some element to check for deprecated use of | |
| 400 * @param node the node use for the location of the error | |
| 401 * @return `true` if and only if a hint code is generated on the passed node | |
| 402 * See [HintCode.DEPRECATED_MEMBER_USE]. | |
| 403 */ | |
| 404 bool _checkForDeprecatedMemberUse(Element element, AstNode node) { | |
| 405 if (element != null && element.isDeprecated) { | |
| 406 String displayName = element.displayName; | |
| 407 if (element is ConstructorElement) { | |
| 408 // TODO(jwren) We should modify ConstructorElement.getDisplayName(), | |
| 409 // or have the logic centralized elsewhere, instead of doing this logic | |
| 410 // here. | |
| 411 ConstructorElement constructorElement = element; | |
| 412 displayName = constructorElement.enclosingElement.displayName; | |
| 413 if (!constructorElement.displayName.isEmpty) { | |
| 414 displayName = "$displayName.${constructorElement.displayName}"; | |
| 415 } | |
| 416 } | |
| 417 _errorReporter.reportErrorForNode( | |
| 418 HintCode.DEPRECATED_MEMBER_USE, node, [displayName]); | |
| 419 return true; | |
| 420 } | |
| 421 return false; | |
| 422 } | |
| 423 | |
| 424 /** | |
| 425 * For [SimpleIdentifier]s, only call [checkForDeprecatedMemberUse] | |
| 426 * if the node is not in a declaration context. | |
| 427 * | |
| 428 * Also, if the identifier is a constructor name in a constructor invocation,
then calls to the | |
| 429 * deprecated constructor will be caught by | |
| 430 * [visitInstanceCreationExpression] and | |
| 431 * [visitSuperConstructorInvocation], and can be ignored by | |
| 432 * this visit method. | |
| 433 * | |
| 434 * @param identifier some simple identifier to check for deprecated use of | |
| 435 * @return `true` if and only if a hint code is generated on the passed node | |
| 436 * See [HintCode.DEPRECATED_MEMBER_USE]. | |
| 437 */ | |
| 438 bool _checkForDeprecatedMemberUseAtIdentifier(SimpleIdentifier identifier) { | |
| 439 if (identifier.inDeclarationContext()) { | |
| 440 return false; | |
| 441 } | |
| 442 AstNode parent = identifier.parent; | |
| 443 if ((parent is ConstructorName && identical(identifier, parent.name)) || | |
| 444 (parent is SuperConstructorInvocation && | |
| 445 identical(identifier, parent.constructorName)) || | |
| 446 parent is HideCombinator) { | |
| 447 return false; | |
| 448 } | |
| 449 return _checkForDeprecatedMemberUse(identifier.bestElement, identifier); | |
| 450 } | |
| 451 | |
| 452 /** | |
| 453 * Check for the passed binary expression for the [HintCode.DIVISION_OPTIMIZAT
ION]. | |
| 454 * | |
| 455 * @param node the binary expression to check | |
| 456 * @return `true` if and only if a hint code is generated on the passed node | |
| 457 * See [HintCode.DIVISION_OPTIMIZATION]. | |
| 458 */ | |
| 459 bool _checkForDivisionOptimizationHint(BinaryExpression node) { | |
| 460 // Return if the operator is not '/' | |
| 461 if (node.operator.type != sc.TokenType.SLASH) { | |
| 462 return false; | |
| 463 } | |
| 464 // Return if the '/' operator is not defined in core, or if we don't know | |
| 465 // its static or propagated type | |
| 466 MethodElement methodElement = node.bestElement; | |
| 467 if (methodElement == null) { | |
| 468 return false; | |
| 469 } | |
| 470 LibraryElement libraryElement = methodElement.library; | |
| 471 if (libraryElement != null && !libraryElement.isDartCore) { | |
| 472 return false; | |
| 473 } | |
| 474 // Report error if the (x/y) has toInt() invoked on it | |
| 475 if (node.parent is ParenthesizedExpression) { | |
| 476 ParenthesizedExpression parenthesizedExpression = | |
| 477 _wrapParenthesizedExpression(node.parent as ParenthesizedExpression); | |
| 478 if (parenthesizedExpression.parent is MethodInvocation) { | |
| 479 MethodInvocation methodInvocation = | |
| 480 parenthesizedExpression.parent as MethodInvocation; | |
| 481 if (_TO_INT_METHOD_NAME == methodInvocation.methodName.name && | |
| 482 methodInvocation.argumentList.arguments.isEmpty) { | |
| 483 _errorReporter.reportErrorForNode( | |
| 484 HintCode.DIVISION_OPTIMIZATION, methodInvocation); | |
| 485 return true; | |
| 486 } | |
| 487 } | |
| 488 } | |
| 489 return false; | |
| 490 } | |
| 491 | |
| 492 /** | |
| 493 * This verifies that the passed left hand side and right hand side represent
a valid assignment. | |
| 494 * | |
| 495 * This method corresponds to ErrorVerifier.checkForInvalidAssignment. | |
| 496 * | |
| 497 * @param lhs the left hand side expression | |
| 498 * @param rhs the right hand side expression | |
| 499 * @return `true` if and only if an error code is generated on the passed node | |
| 500 * See [HintCode.INVALID_ASSIGNMENT]. | |
| 501 */ | |
| 502 bool _checkForInvalidAssignment(Expression lhs, Expression rhs) { | |
| 503 if (lhs == null || rhs == null) { | |
| 504 return false; | |
| 505 } | |
| 506 VariableElement leftVariableElement = ErrorVerifier.getVariableElement(lhs); | |
| 507 DartType leftType = (leftVariableElement == null) | |
| 508 ? ErrorVerifier.getStaticType(lhs) | |
| 509 : leftVariableElement.type; | |
| 510 DartType staticRightType = ErrorVerifier.getStaticType(rhs); | |
| 511 if (!staticRightType.isAssignableTo(leftType)) { | |
| 512 // The warning was generated on this rhs | |
| 513 return false; | |
| 514 } | |
| 515 // Test for, and then generate the hint | |
| 516 DartType bestRightType = rhs.bestType; | |
| 517 if (leftType != null && bestRightType != null) { | |
| 518 if (!bestRightType.isAssignableTo(leftType)) { | |
| 519 _errorReporter.reportTypeErrorForNode( | |
| 520 HintCode.INVALID_ASSIGNMENT, rhs, [bestRightType, leftType]); | |
| 521 return true; | |
| 522 } | |
| 523 } | |
| 524 return false; | |
| 525 } | |
| 526 | |
| 527 /** | |
| 528 * Check that the imported library does not define a loadLibrary function. The
import has already | |
| 529 * been determined to be deferred when this is called. | |
| 530 * | |
| 531 * @param node the import directive to evaluate | |
| 532 * @param importElement the [ImportElement] retrieved from the node | |
| 533 * @return `true` if and only if an error code is generated on the passed node | |
| 534 * See [CompileTimeErrorCode.IMPORT_DEFERRED_LIBRARY_WITH_LOAD_FUNCTION]. | |
| 535 */ | |
| 536 bool _checkForLoadLibraryFunction( | |
| 537 ImportDirective node, ImportElement importElement) { | |
| 538 LibraryElement importedLibrary = importElement.importedLibrary; | |
| 539 if (importedLibrary == null) { | |
| 540 return false; | |
| 541 } | |
| 542 if (importedLibrary.hasLoadLibraryFunction) { | |
| 543 _errorReporter.reportErrorForNode( | |
| 544 HintCode.IMPORT_DEFERRED_LIBRARY_WITH_LOAD_FUNCTION, node, | |
| 545 [importedLibrary.name]); | |
| 546 return true; | |
| 547 } | |
| 548 return false; | |
| 549 } | |
| 550 | |
| 551 /** | |
| 552 * Generate a hint for functions or methods that have a return type, but do no
t have a return | |
| 553 * statement on all branches. At the end of blocks with no return, Dart implic
itly returns | |
| 554 * `null`, avoiding these implicit returns is considered a best practice. | |
| 555 * | |
| 556 * Note: for async functions/methods, this hint only applies when the | |
| 557 * function has a return type that Future<Null> is not assignable to. | |
| 558 * | |
| 559 * @param node the binary expression to check | |
| 560 * @param body the function body | |
| 561 * @return `true` if and only if a hint code is generated on the passed node | |
| 562 * See [HintCode.MISSING_RETURN]. | |
| 563 */ | |
| 564 bool _checkForMissingReturn(TypeName returnType, FunctionBody body) { | |
| 565 // Check that the method or function has a return type, and a function body | |
| 566 if (returnType == null || body == null) { | |
| 567 return false; | |
| 568 } | |
| 569 // Check that the body is a BlockFunctionBody | |
| 570 if (body is! BlockFunctionBody) { | |
| 571 return false; | |
| 572 } | |
| 573 // Generators are never required to have a return statement. | |
| 574 if (body.isGenerator) { | |
| 575 return false; | |
| 576 } | |
| 577 // Check that the type is resolvable, and is not "void" | |
| 578 DartType returnTypeType = returnType.type; | |
| 579 if (returnTypeType == null || returnTypeType.isVoid) { | |
| 580 return false; | |
| 581 } | |
| 582 // For async, give no hint if Future<Null> is assignable to the return | |
| 583 // type. | |
| 584 if (body.isAsynchronous && _futureNullType.isAssignableTo(returnTypeType)) { | |
| 585 return false; | |
| 586 } | |
| 587 // Check the block for a return statement, if not, create the hint | |
| 588 BlockFunctionBody blockFunctionBody = body as BlockFunctionBody; | |
| 589 if (!ExitDetector.exits(blockFunctionBody)) { | |
| 590 _errorReporter.reportErrorForNode( | |
| 591 HintCode.MISSING_RETURN, returnType, [returnTypeType.displayName]); | |
| 592 return true; | |
| 593 } | |
| 594 return false; | |
| 595 } | |
| 596 | |
| 597 /** | |
| 598 * Check for the passed class declaration for the | |
| 599 * [HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE] hint code. | |
| 600 * | |
| 601 * @param node the class declaration to check | |
| 602 * @return `true` if and only if a hint code is generated on the passed node | |
| 603 * See [HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE]. | |
| 604 */ | |
| 605 // bool _checkForOverrideEqualsButNotHashCode(ClassDeclaration node) { | |
| 606 // ClassElement classElement = node.element; | |
| 607 // if (classElement == null) { | |
| 608 // return false; | |
| 609 // } | |
| 610 // MethodElement equalsOperatorMethodElement = | |
| 611 // classElement.getMethod(sc.TokenType.EQ_EQ.lexeme); | |
| 612 // if (equalsOperatorMethodElement != null) { | |
| 613 // PropertyAccessorElement hashCodeElement = | |
| 614 // classElement.getGetter(_HASHCODE_GETTER_NAME); | |
| 615 // if (hashCodeElement == null) { | |
| 616 // _errorReporter.reportErrorForNode( | |
| 617 // HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE, | |
| 618 // node.name, | |
| 619 // [classElement.displayName]); | |
| 620 // return true; | |
| 621 // } | |
| 622 // } | |
| 623 // return false; | |
| 624 // } | |
| 625 | |
| 626 /** | |
| 627 * Check for the passed as expression for the [HintCode.UNNECESSARY_CAST] hint
code. | |
| 628 * | |
| 629 * @param node the as expression to check | |
| 630 * @return `true` if and only if a hint code is generated on the passed node | |
| 631 * See [HintCode.UNNECESSARY_CAST]. | |
| 632 */ | |
| 633 bool _checkForUnnecessaryCast(AsExpression node) { | |
| 634 // TODO(jwren) After dartbug.com/13732, revisit this, we should be able to | |
| 635 // remove the (x is! TypeParameterType) checks. | |
| 636 AstNode parent = node.parent; | |
| 637 if (parent is ConditionalExpression && | |
| 638 (node == parent.thenExpression || node == parent.elseExpression)) { | |
| 639 Expression thenExpression = parent.thenExpression; | |
| 640 DartType thenType; | |
| 641 if (thenExpression is AsExpression) { | |
| 642 thenType = thenExpression.expression.staticType; | |
| 643 } else { | |
| 644 thenType = thenExpression.staticType; | |
| 645 } | |
| 646 Expression elseExpression = parent.elseExpression; | |
| 647 DartType elseType; | |
| 648 if (elseExpression is AsExpression) { | |
| 649 elseType = elseExpression.expression.staticType; | |
| 650 } else { | |
| 651 elseType = elseExpression.staticType; | |
| 652 } | |
| 653 if (thenType != null && | |
| 654 elseType != null && | |
| 655 !thenType.isDynamic && | |
| 656 !elseType.isDynamic && | |
| 657 !thenType.isMoreSpecificThan(elseType) && | |
| 658 !elseType.isMoreSpecificThan(thenType)) { | |
| 659 return false; | |
| 660 } | |
| 661 } | |
| 662 DartType lhsType = node.expression.staticType; | |
| 663 DartType rhsType = node.type.type; | |
| 664 if (lhsType != null && | |
| 665 rhsType != null && | |
| 666 !lhsType.isDynamic && | |
| 667 !rhsType.isDynamic && | |
| 668 lhsType.isMoreSpecificThan(rhsType)) { | |
| 669 _errorReporter.reportErrorForNode(HintCode.UNNECESSARY_CAST, node); | |
| 670 return true; | |
| 671 } | |
| 672 return false; | |
| 673 } | |
| 674 | |
| 675 /** | |
| 676 * Check for situations where the result of a method or function is used, when
it returns 'void'. | |
| 677 * | |
| 678 * TODO(jwren) Many other situations of use could be covered. We currently cov
er the cases var x = | |
| 679 * m() and x = m(), but we could also cover cases such as m().x, m()[k], a + m
(), f(m()), return | |
| 680 * m(). | |
| 681 * | |
| 682 * @param node expression on the RHS of some assignment | |
| 683 * @return `true` if and only if a hint code is generated on the passed node | |
| 684 * See [HintCode.USE_OF_VOID_RESULT]. | |
| 685 */ | |
| 686 bool _checkForUseOfVoidResult(Expression expression) { | |
| 687 if (expression == null || expression is! MethodInvocation) { | |
| 688 return false; | |
| 689 } | |
| 690 MethodInvocation methodInvocation = expression as MethodInvocation; | |
| 691 if (identical(methodInvocation.staticType, VoidTypeImpl.instance)) { | |
| 692 SimpleIdentifier methodName = methodInvocation.methodName; | |
| 693 _errorReporter.reportErrorForNode( | |
| 694 HintCode.USE_OF_VOID_RESULT, methodName, [methodName.name]); | |
| 695 return true; | |
| 696 } | |
| 697 return false; | |
| 698 } | |
| 699 | |
| 700 /** | |
| 701 * Given a parenthesized expression, this returns the parent (or recursively g
rand-parent) of the | |
| 702 * expression that is a parenthesized expression, but whose parent is not a pa
renthesized | |
| 703 * expression. | |
| 704 * | |
| 705 * For example given the code `(((e)))`: `(e) -> (((e)))`. | |
| 706 * | |
| 707 * @param parenthesizedExpression some expression whose parent is a parenthesi
zed expression | |
| 708 * @return the first parent or grand-parent that is a parenthesized expression
, that does not have | |
| 709 * a parenthesized expression parent | |
| 710 */ | |
| 711 static ParenthesizedExpression _wrapParenthesizedExpression( | |
| 712 ParenthesizedExpression parenthesizedExpression) { | |
| 713 if (parenthesizedExpression.parent is ParenthesizedExpression) { | |
| 714 return _wrapParenthesizedExpression( | |
| 715 parenthesizedExpression.parent as ParenthesizedExpression); | |
| 716 } | |
| 717 return parenthesizedExpression; | |
| 718 } | |
| 719 } | |
| 720 | |
| 721 /** | |
| 722 * Instances of the class `ClassScope` implement the scope defined by a class. | |
| 723 */ | |
| 724 class ClassScope extends EnclosedScope { | |
| 725 /** | |
| 726 * Initialize a newly created scope enclosed within another scope. | |
| 727 * | |
| 728 * @param enclosingScope the scope in which this scope is lexically enclosed | |
| 729 * @param typeElement the element representing the type represented by this sc
ope | |
| 730 */ | |
| 731 ClassScope(Scope enclosingScope, ClassElement typeElement) | |
| 732 : super(enclosingScope) { | |
| 733 if (typeElement == null) { | |
| 734 throw new IllegalArgumentException("class element cannot be null"); | |
| 735 } | |
| 736 _defineMembers(typeElement); | |
| 737 } | |
| 738 | |
| 739 @override | |
| 740 AnalysisError getErrorForDuplicate(Element existing, Element duplicate) { | |
| 741 if (existing is PropertyAccessorElement && duplicate is MethodElement) { | |
| 742 if (existing.nameOffset < duplicate.nameOffset) { | |
| 743 return new AnalysisError(duplicate.source, duplicate.nameOffset, | |
| 744 duplicate.displayName.length, | |
| 745 CompileTimeErrorCode.METHOD_AND_GETTER_WITH_SAME_NAME, | |
| 746 [existing.displayName]); | |
| 747 } else { | |
| 748 return new AnalysisError(existing.source, existing.nameOffset, | |
| 749 existing.displayName.length, | |
| 750 CompileTimeErrorCode.GETTER_AND_METHOD_WITH_SAME_NAME, | |
| 751 [existing.displayName]); | |
| 752 } | |
| 753 } | |
| 754 return super.getErrorForDuplicate(existing, duplicate); | |
| 755 } | |
| 756 | |
| 757 /** | |
| 758 * Define the instance members defined by the class. | |
| 759 * | |
| 760 * @param typeElement the element representing the type represented by this sc
ope | |
| 761 */ | |
| 762 void _defineMembers(ClassElement typeElement) { | |
| 763 for (PropertyAccessorElement accessor in typeElement.accessors) { | |
| 764 define(accessor); | |
| 765 } | |
| 766 for (MethodElement method in typeElement.methods) { | |
| 767 define(method); | |
| 768 } | |
| 769 } | |
| 770 } | |
| 771 | |
| 772 /** | |
| 773 * A `CompilationUnitBuilder` builds an element model for a single compilation | |
| 774 * unit. | |
| 775 */ | |
| 776 class CompilationUnitBuilder { | |
| 777 /** | |
| 778 * Build the compilation unit element for the given [source] based on the | |
| 779 * compilation [unit] associated with the source. Throw an AnalysisException | |
| 780 * if the element could not be built. [librarySource] is the source for the | |
| 781 * containing library. | |
| 782 */ | |
| 783 CompilationUnitElementImpl buildCompilationUnit( | |
| 784 Source source, CompilationUnit unit, Source librarySource) { | |
| 785 return PerformanceStatistics.resolve.makeCurrentWhile(() { | |
| 786 if (unit == null) { | |
| 787 return null; | |
| 788 } | |
| 789 ElementHolder holder = new ElementHolder(); | |
| 790 ElementBuilder builder = new ElementBuilder(holder); | |
| 791 unit.accept(builder); | |
| 792 CompilationUnitElementImpl element = | |
| 793 new CompilationUnitElementImpl(source.shortName); | |
| 794 element.accessors = holder.accessors; | |
| 795 element.enums = holder.enums; | |
| 796 element.functions = holder.functions; | |
| 797 element.source = source; | |
| 798 element.librarySource = librarySource; | |
| 799 element.typeAliases = holder.typeAliases; | |
| 800 element.types = holder.types; | |
| 801 element.topLevelVariables = holder.topLevelVariables; | |
| 802 unit.element = element; | |
| 803 holder.validate(); | |
| 804 return element; | |
| 805 }); | |
| 806 } | |
| 807 } | |
| 808 | |
| 809 /** | |
| 810 * Instances of the class `ConstantVerifier` traverse an AST structure looking f
or additional | |
| 811 * errors and warnings not covered by the parser and resolver. In particular, it
looks for errors | |
| 812 * and warnings related to constant expressions. | |
| 813 */ | |
| 814 class ConstantVerifier extends RecursiveAstVisitor<Object> { | |
| 815 /** | |
| 816 * The error reporter by which errors will be reported. | |
| 817 */ | |
| 818 final ErrorReporter _errorReporter; | |
| 819 | |
| 820 /** | |
| 821 * The type provider used to access the known types. | |
| 822 */ | |
| 823 final TypeProvider _typeProvider; | |
| 824 | |
| 825 /** | |
| 826 * The set of variables declared using '-D' on the command line. | |
| 827 */ | |
| 828 final DeclaredVariables declaredVariables; | |
| 829 | |
| 830 /** | |
| 831 * The type representing the type 'bool'. | |
| 832 */ | |
| 833 InterfaceType _boolType; | |
| 834 | |
| 835 /** | |
| 836 * The type representing the type 'int'. | |
| 837 */ | |
| 838 InterfaceType _intType; | |
| 839 | |
| 840 /** | |
| 841 * The type representing the type 'num'. | |
| 842 */ | |
| 843 InterfaceType _numType; | |
| 844 | |
| 845 /** | |
| 846 * The type representing the type 'string'. | |
| 847 */ | |
| 848 InterfaceType _stringType; | |
| 849 | |
| 850 /** | |
| 851 * The current library that is being analyzed. | |
| 852 */ | |
| 853 final LibraryElement _currentLibrary; | |
| 854 | |
| 855 /** | |
| 856 * Initialize a newly created constant verifier. | |
| 857 * | |
| 858 * @param errorReporter the error reporter by which errors will be reported | |
| 859 */ | |
| 860 ConstantVerifier(this._errorReporter, this._currentLibrary, | |
| 861 this._typeProvider, this.declaredVariables) { | |
| 862 this._boolType = _typeProvider.boolType; | |
| 863 this._intType = _typeProvider.intType; | |
| 864 this._numType = _typeProvider.numType; | |
| 865 this._stringType = _typeProvider.stringType; | |
| 866 } | |
| 867 | |
| 868 @override | |
| 869 Object visitAnnotation(Annotation node) { | |
| 870 super.visitAnnotation(node); | |
| 871 // check annotation creation | |
| 872 Element element = node.element; | |
| 873 if (element is ConstructorElement) { | |
| 874 ConstructorElement constructorElement = element; | |
| 875 // should 'const' constructor | |
| 876 if (!constructorElement.isConst) { | |
| 877 _errorReporter.reportErrorForNode( | |
| 878 CompileTimeErrorCode.NON_CONSTANT_ANNOTATION_CONSTRUCTOR, node); | |
| 879 return null; | |
| 880 } | |
| 881 // should have arguments | |
| 882 ArgumentList argumentList = node.arguments; | |
| 883 if (argumentList == null) { | |
| 884 _errorReporter.reportErrorForNode( | |
| 885 CompileTimeErrorCode.NO_ANNOTATION_CONSTRUCTOR_ARGUMENTS, node); | |
| 886 return null; | |
| 887 } | |
| 888 // arguments should be constants | |
| 889 _validateConstantArguments(argumentList); | |
| 890 } | |
| 891 return null; | |
| 892 } | |
| 893 | |
| 894 @override | |
| 895 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
| 896 if (node.constKeyword != null) { | |
| 897 _validateConstructorInitializers(node); | |
| 898 _validateFieldInitializers(node.parent as ClassDeclaration, node); | |
| 899 } | |
| 900 _validateDefaultValues(node.parameters); | |
| 901 return super.visitConstructorDeclaration(node); | |
| 902 } | |
| 903 | |
| 904 @override | |
| 905 Object visitFunctionExpression(FunctionExpression node) { | |
| 906 super.visitFunctionExpression(node); | |
| 907 _validateDefaultValues(node.parameters); | |
| 908 return null; | |
| 909 } | |
| 910 | |
| 911 @override | |
| 912 Object visitInstanceCreationExpression(InstanceCreationExpression node) { | |
| 913 if (node.isConst) { | |
| 914 // We need to evaluate the constant to see if any errors occur during its | |
| 915 // evaluation. | |
| 916 ConstructorElement constructor = node.staticElement; | |
| 917 if (constructor != null) { | |
| 918 ConstantEvaluationEngine evaluationEngine = | |
| 919 new ConstantEvaluationEngine(_typeProvider, declaredVariables); | |
| 920 ConstantVisitor constantVisitor = | |
| 921 new ConstantVisitor(evaluationEngine, _errorReporter); | |
| 922 evaluationEngine.evaluateConstructorCall(node, | |
| 923 node.argumentList.arguments, constructor, constantVisitor, | |
| 924 _errorReporter); | |
| 925 } | |
| 926 } | |
| 927 _validateInstanceCreationArguments(node); | |
| 928 return super.visitInstanceCreationExpression(node); | |
| 929 } | |
| 930 | |
| 931 @override | |
| 932 Object visitListLiteral(ListLiteral node) { | |
| 933 super.visitListLiteral(node); | |
| 934 if (node.constKeyword != null) { | |
| 935 DartObjectImpl result; | |
| 936 for (Expression element in node.elements) { | |
| 937 result = | |
| 938 _validate(element, CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT); | |
| 939 if (result != null) { | |
| 940 _reportErrorIfFromDeferredLibrary(element, | |
| 941 CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT_FROM_DEFERRED_LIBRA
RY); | |
| 942 } | |
| 943 } | |
| 944 } | |
| 945 return null; | |
| 946 } | |
| 947 | |
| 948 @override | |
| 949 Object visitMapLiteral(MapLiteral node) { | |
| 950 super.visitMapLiteral(node); | |
| 951 bool isConst = node.constKeyword != null; | |
| 952 bool reportEqualKeys = true; | |
| 953 HashSet<DartObject> keys = new HashSet<DartObject>(); | |
| 954 List<Expression> invalidKeys = new List<Expression>(); | |
| 955 for (MapLiteralEntry entry in node.entries) { | |
| 956 Expression key = entry.key; | |
| 957 if (isConst) { | |
| 958 DartObjectImpl keyResult = | |
| 959 _validate(key, CompileTimeErrorCode.NON_CONSTANT_MAP_KEY); | |
| 960 Expression valueExpression = entry.value; | |
| 961 DartObjectImpl valueResult = _validate( | |
| 962 valueExpression, CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE); | |
| 963 if (valueResult != null) { | |
| 964 _reportErrorIfFromDeferredLibrary(valueExpression, | |
| 965 CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE_FROM_DEFERRED_LIBRARY)
; | |
| 966 } | |
| 967 if (keyResult != null) { | |
| 968 _reportErrorIfFromDeferredLibrary(key, | |
| 969 CompileTimeErrorCode.NON_CONSTANT_MAP_KEY_FROM_DEFERRED_LIBRARY); | |
| 970 if (keys.contains(keyResult)) { | |
| 971 invalidKeys.add(key); | |
| 972 } else { | |
| 973 keys.add(keyResult); | |
| 974 } | |
| 975 DartType type = keyResult.type; | |
| 976 if (_implementsEqualsWhenNotAllowed(type)) { | |
| 977 _errorReporter.reportErrorForNode( | |
| 978 CompileTimeErrorCode.CONST_MAP_KEY_EXPRESSION_TYPE_IMPLEMENTS_EQ
UALS, | |
| 979 key, [type.displayName]); | |
| 980 } | |
| 981 } | |
| 982 } else { | |
| 983 // Note: we throw the errors away because this isn't actually a const. | |
| 984 AnalysisErrorListener errorListener = | |
| 985 AnalysisErrorListener.NULL_LISTENER; | |
| 986 ErrorReporter subErrorReporter = | |
| 987 new ErrorReporter(errorListener, _errorReporter.source); | |
| 988 DartObjectImpl result = key.accept(new ConstantVisitor( | |
| 989 new ConstantEvaluationEngine(_typeProvider, declaredVariables), | |
| 990 subErrorReporter)); | |
| 991 if (result != null) { | |
| 992 if (keys.contains(result)) { | |
| 993 invalidKeys.add(key); | |
| 994 } else { | |
| 995 keys.add(result); | |
| 996 } | |
| 997 } else { | |
| 998 reportEqualKeys = false; | |
| 999 } | |
| 1000 } | |
| 1001 } | |
| 1002 if (reportEqualKeys) { | |
| 1003 for (Expression key in invalidKeys) { | |
| 1004 _errorReporter.reportErrorForNode( | |
| 1005 StaticWarningCode.EQUAL_KEYS_IN_MAP, key); | |
| 1006 } | |
| 1007 } | |
| 1008 return null; | |
| 1009 } | |
| 1010 | |
| 1011 @override | |
| 1012 Object visitMethodDeclaration(MethodDeclaration node) { | |
| 1013 super.visitMethodDeclaration(node); | |
| 1014 _validateDefaultValues(node.parameters); | |
| 1015 return null; | |
| 1016 } | |
| 1017 | |
| 1018 @override | |
| 1019 Object visitSwitchStatement(SwitchStatement node) { | |
| 1020 // TODO(paulberry): to minimize error messages, it would be nice to | |
| 1021 // compare all types with the most popular type rather than the first | |
| 1022 // type. | |
| 1023 NodeList<SwitchMember> switchMembers = node.members; | |
| 1024 bool foundError = false; | |
| 1025 DartType firstType = null; | |
| 1026 for (SwitchMember switchMember in switchMembers) { | |
| 1027 if (switchMember is SwitchCase) { | |
| 1028 SwitchCase switchCase = switchMember; | |
| 1029 Expression expression = switchCase.expression; | |
| 1030 DartObjectImpl caseResult = _validate( | |
| 1031 expression, CompileTimeErrorCode.NON_CONSTANT_CASE_EXPRESSION); | |
| 1032 if (caseResult != null) { | |
| 1033 _reportErrorIfFromDeferredLibrary(expression, | |
| 1034 CompileTimeErrorCode.NON_CONSTANT_CASE_EXPRESSION_FROM_DEFERRED_LI
BRARY); | |
| 1035 DartObject value = caseResult; | |
| 1036 if (firstType == null) { | |
| 1037 firstType = value.type; | |
| 1038 } else { | |
| 1039 DartType nType = value.type; | |
| 1040 if (firstType != nType) { | |
| 1041 _errorReporter.reportErrorForNode( | |
| 1042 CompileTimeErrorCode.INCONSISTENT_CASE_EXPRESSION_TYPES, | |
| 1043 expression, [expression.toSource(), firstType.displayName]); | |
| 1044 foundError = true; | |
| 1045 } | |
| 1046 } | |
| 1047 } | |
| 1048 } | |
| 1049 } | |
| 1050 if (!foundError) { | |
| 1051 _checkForCaseExpressionTypeImplementsEquals(node, firstType); | |
| 1052 } | |
| 1053 return super.visitSwitchStatement(node); | |
| 1054 } | |
| 1055 | |
| 1056 @override | |
| 1057 Object visitVariableDeclaration(VariableDeclaration node) { | |
| 1058 super.visitVariableDeclaration(node); | |
| 1059 Expression initializer = node.initializer; | |
| 1060 if (initializer != null && (node.isConst || node.isFinal)) { | |
| 1061 VariableElementImpl element = node.element as VariableElementImpl; | |
| 1062 EvaluationResultImpl result = element.evaluationResult; | |
| 1063 if (result == null) { | |
| 1064 // Variables marked "const" should have had their values computed by | |
| 1065 // ConstantValueComputer. Other variables will only have had their | |
| 1066 // values computed if the value was needed (e.g. final variables in a | |
| 1067 // class containing const constructors). | |
| 1068 assert(!node.isConst); | |
| 1069 return null; | |
| 1070 } | |
| 1071 _reportErrors(result.errors, | |
| 1072 CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE); | |
| 1073 _reportErrorIfFromDeferredLibrary(initializer, | |
| 1074 CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE_FROM_DE
FERRED_LIBRARY); | |
| 1075 } | |
| 1076 return null; | |
| 1077 } | |
| 1078 | |
| 1079 /** | |
| 1080 * This verifies that the passed switch statement does not have a case express
ion with the | |
| 1081 * operator '==' overridden. | |
| 1082 * | |
| 1083 * @param node the switch statement to evaluate | |
| 1084 * @param type the common type of all 'case' expressions | |
| 1085 * @return `true` if and only if an error code is generated on the passed node | |
| 1086 * See [CompileTimeErrorCode.CASE_EXPRESSION_TYPE_IMPLEMENTS_EQUALS]. | |
| 1087 */ | |
| 1088 bool _checkForCaseExpressionTypeImplementsEquals( | |
| 1089 SwitchStatement node, DartType type) { | |
| 1090 if (!_implementsEqualsWhenNotAllowed(type)) { | |
| 1091 return false; | |
| 1092 } | |
| 1093 // report error | |
| 1094 _errorReporter.reportErrorForToken( | |
| 1095 CompileTimeErrorCode.CASE_EXPRESSION_TYPE_IMPLEMENTS_EQUALS, | |
| 1096 node.switchKeyword, [type.displayName]); | |
| 1097 return true; | |
| 1098 } | |
| 1099 | |
| 1100 /** | |
| 1101 * @return `true` if given [Type] implements operator <i>==</i>, and it is not | |
| 1102 * <i>int</i> or <i>String</i>. | |
| 1103 */ | |
| 1104 bool _implementsEqualsWhenNotAllowed(DartType type) { | |
| 1105 // ignore int or String | |
| 1106 if (type == null || type == _intType || type == _typeProvider.stringType) { | |
| 1107 return false; | |
| 1108 } else if (type == _typeProvider.doubleType) { | |
| 1109 return true; | |
| 1110 } | |
| 1111 // prepare ClassElement | |
| 1112 Element element = type.element; | |
| 1113 if (element is! ClassElement) { | |
| 1114 return false; | |
| 1115 } | |
| 1116 ClassElement classElement = element as ClassElement; | |
| 1117 // lookup for == | |
| 1118 MethodElement method = | |
| 1119 classElement.lookUpConcreteMethod("==", _currentLibrary); | |
| 1120 if (method == null || method.enclosingElement.type.isObject) { | |
| 1121 return false; | |
| 1122 } | |
| 1123 // there is == that we don't like | |
| 1124 return true; | |
| 1125 } | |
| 1126 | |
| 1127 /** | |
| 1128 * Given some computed [Expression], this method generates the passed [ErrorCo
de] on | |
| 1129 * the node if its' value consists of information from a deferred library. | |
| 1130 * | |
| 1131 * @param expression the expression to be tested for a deferred library refere
nce | |
| 1132 * @param errorCode the error code to be used if the expression is or consists
of a reference to a | |
| 1133 * deferred library | |
| 1134 */ | |
| 1135 void _reportErrorIfFromDeferredLibrary( | |
| 1136 Expression expression, ErrorCode errorCode) { | |
| 1137 DeferredLibraryReferenceDetector referenceDetector = | |
| 1138 new DeferredLibraryReferenceDetector(); | |
| 1139 expression.accept(referenceDetector); | |
| 1140 if (referenceDetector.result) { | |
| 1141 _errorReporter.reportErrorForNode(errorCode, expression); | |
| 1142 } | |
| 1143 } | |
| 1144 | |
| 1145 /** | |
| 1146 * Report any errors in the given list. Except for special cases, use the give
n error code rather | |
| 1147 * than the one reported in the error. | |
| 1148 * | |
| 1149 * @param errors the errors that need to be reported | |
| 1150 * @param errorCode the error code to be used | |
| 1151 */ | |
| 1152 void _reportErrors(List<AnalysisError> errors, ErrorCode errorCode) { | |
| 1153 for (AnalysisError data in errors) { | |
| 1154 ErrorCode dataErrorCode = data.errorCode; | |
| 1155 if (identical(dataErrorCode, | |
| 1156 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION) || | |
| 1157 identical( | |
| 1158 dataErrorCode, CompileTimeErrorCode.CONST_EVAL_THROWS_IDBZE) || | |
| 1159 identical(dataErrorCode, | |
| 1160 CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING) || | |
| 1161 identical(dataErrorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL) || | |
| 1162 identical(dataErrorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_INT) || | |
| 1163 identical(dataErrorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_NUM) || | |
| 1164 identical(dataErrorCode, | |
| 1165 CompileTimeErrorCode.RECURSIVE_COMPILE_TIME_CONSTANT) || | |
| 1166 identical(dataErrorCode, | |
| 1167 CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_FIELD_TYPE_MISMA
TCH) || | |
| 1168 identical(dataErrorCode, | |
| 1169 CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMA
TCH) || | |
| 1170 identical(dataErrorCode, | |
| 1171 CheckedModeCompileTimeErrorCode.VARIABLE_TYPE_MISMATCH)) { | |
| 1172 _errorReporter.reportError(data); | |
| 1173 } else if (errorCode != null) { | |
| 1174 _errorReporter.reportError(new AnalysisError( | |
| 1175 data.source, data.offset, data.length, errorCode)); | |
| 1176 } | |
| 1177 } | |
| 1178 } | |
| 1179 | |
| 1180 /** | |
| 1181 * Validate that the given expression is a compile time constant. Return the v
alue of the compile | |
| 1182 * time constant, or `null` if the expression is not a compile time constant. | |
| 1183 * | |
| 1184 * @param expression the expression to be validated | |
| 1185 * @param errorCode the error code to be used if the expression is not a compi
le time constant | |
| 1186 * @return the value of the compile time constant | |
| 1187 */ | |
| 1188 DartObjectImpl _validate(Expression expression, ErrorCode errorCode) { | |
| 1189 RecordingErrorListener errorListener = new RecordingErrorListener(); | |
| 1190 ErrorReporter subErrorReporter = | |
| 1191 new ErrorReporter(errorListener, _errorReporter.source); | |
| 1192 DartObjectImpl result = expression.accept(new ConstantVisitor( | |
| 1193 new ConstantEvaluationEngine(_typeProvider, declaredVariables), | |
| 1194 subErrorReporter)); | |
| 1195 _reportErrors(errorListener.errors, errorCode); | |
| 1196 return result; | |
| 1197 } | |
| 1198 | |
| 1199 /** | |
| 1200 * Validate that if the passed arguments are constant expressions. | |
| 1201 * | |
| 1202 * @param argumentList the argument list to evaluate | |
| 1203 */ | |
| 1204 void _validateConstantArguments(ArgumentList argumentList) { | |
| 1205 for (Expression argument in argumentList.arguments) { | |
| 1206 if (argument is NamedExpression) { | |
| 1207 argument = (argument as NamedExpression).expression; | |
| 1208 } | |
| 1209 _validate( | |
| 1210 argument, CompileTimeErrorCode.CONST_WITH_NON_CONSTANT_ARGUMENT); | |
| 1211 } | |
| 1212 } | |
| 1213 | |
| 1214 /** | |
| 1215 * Validates that the expressions of the given initializers (of a constant con
structor) are all | |
| 1216 * compile time constants. | |
| 1217 * | |
| 1218 * @param constructor the constant constructor declaration to validate | |
| 1219 */ | |
| 1220 void _validateConstructorInitializers(ConstructorDeclaration constructor) { | |
| 1221 List<ParameterElement> parameterElements = | |
| 1222 constructor.parameters.parameterElements; | |
| 1223 NodeList<ConstructorInitializer> initializers = constructor.initializers; | |
| 1224 for (ConstructorInitializer initializer in initializers) { | |
| 1225 if (initializer is ConstructorFieldInitializer) { | |
| 1226 ConstructorFieldInitializer fieldInitializer = initializer; | |
| 1227 _validateInitializerExpression( | |
| 1228 parameterElements, fieldInitializer.expression); | |
| 1229 } | |
| 1230 if (initializer is RedirectingConstructorInvocation) { | |
| 1231 RedirectingConstructorInvocation invocation = initializer; | |
| 1232 _validateInitializerInvocationArguments( | |
| 1233 parameterElements, invocation.argumentList); | |
| 1234 } | |
| 1235 if (initializer is SuperConstructorInvocation) { | |
| 1236 SuperConstructorInvocation invocation = initializer; | |
| 1237 _validateInitializerInvocationArguments( | |
| 1238 parameterElements, invocation.argumentList); | |
| 1239 } | |
| 1240 } | |
| 1241 } | |
| 1242 | |
| 1243 /** | |
| 1244 * Validate that the default value associated with each of the parameters in t
he given list is a | |
| 1245 * compile time constant. | |
| 1246 * | |
| 1247 * @param parameters the list of parameters to be validated | |
| 1248 */ | |
| 1249 void _validateDefaultValues(FormalParameterList parameters) { | |
| 1250 if (parameters == null) { | |
| 1251 return; | |
| 1252 } | |
| 1253 for (FormalParameter parameter in parameters.parameters) { | |
| 1254 if (parameter is DefaultFormalParameter) { | |
| 1255 DefaultFormalParameter defaultParameter = parameter; | |
| 1256 Expression defaultValue = defaultParameter.defaultValue; | |
| 1257 DartObjectImpl result; | |
| 1258 if (defaultValue == null) { | |
| 1259 result = | |
| 1260 new DartObjectImpl(_typeProvider.nullType, NullState.NULL_STATE); | |
| 1261 } else { | |
| 1262 result = _validate( | |
| 1263 defaultValue, CompileTimeErrorCode.NON_CONSTANT_DEFAULT_VALUE); | |
| 1264 if (result != null) { | |
| 1265 _reportErrorIfFromDeferredLibrary(defaultValue, | |
| 1266 CompileTimeErrorCode.NON_CONSTANT_DEFAULT_VALUE_FROM_DEFERRED_LI
BRARY); | |
| 1267 } | |
| 1268 } | |
| 1269 VariableElementImpl element = parameter.element as VariableElementImpl; | |
| 1270 element.evaluationResult = new EvaluationResultImpl(result); | |
| 1271 } | |
| 1272 } | |
| 1273 } | |
| 1274 | |
| 1275 /** | |
| 1276 * Validates that the expressions of any field initializers in the class decla
ration are all | |
| 1277 * compile time constants. Since this is only required if the class has a cons
tant constructor, | |
| 1278 * the error is reported at the constructor site. | |
| 1279 * | |
| 1280 * @param classDeclaration the class which should be validated | |
| 1281 * @param errorSite the site at which errors should be reported. | |
| 1282 */ | |
| 1283 void _validateFieldInitializers( | |
| 1284 ClassDeclaration classDeclaration, ConstructorDeclaration errorSite) { | |
| 1285 NodeList<ClassMember> members = classDeclaration.members; | |
| 1286 for (ClassMember member in members) { | |
| 1287 if (member is FieldDeclaration) { | |
| 1288 FieldDeclaration fieldDeclaration = member; | |
| 1289 if (!fieldDeclaration.isStatic) { | |
| 1290 for (VariableDeclaration variableDeclaration | |
| 1291 in fieldDeclaration.fields.variables) { | |
| 1292 Expression initializer = variableDeclaration.initializer; | |
| 1293 if (initializer != null) { | |
| 1294 // Ignore any errors produced during validation--if the constant | |
| 1295 // can't be eavluated we'll just report a single error. | |
| 1296 AnalysisErrorListener errorListener = | |
| 1297 AnalysisErrorListener.NULL_LISTENER; | |
| 1298 ErrorReporter subErrorReporter = | |
| 1299 new ErrorReporter(errorListener, _errorReporter.source); | |
| 1300 DartObjectImpl result = initializer.accept(new ConstantVisitor( | |
| 1301 new ConstantEvaluationEngine( | |
| 1302 _typeProvider, declaredVariables), subErrorReporter)); | |
| 1303 if (result == null) { | |
| 1304 _errorReporter.reportErrorForNode( | |
| 1305 CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_FIELD_INITIALIZE
D_BY_NON_CONST, | |
| 1306 errorSite, [variableDeclaration.name.name]); | |
| 1307 } | |
| 1308 } | |
| 1309 } | |
| 1310 } | |
| 1311 } | |
| 1312 } | |
| 1313 } | |
| 1314 | |
| 1315 /** | |
| 1316 * Validates that the given expression is a compile time constant. | |
| 1317 * | |
| 1318 * @param parameterElements the elements of parameters of constant constructor
, they are | |
| 1319 * considered as a valid potentially constant expressions | |
| 1320 * @param expression the expression to validate | |
| 1321 */ | |
| 1322 void _validateInitializerExpression( | |
| 1323 List<ParameterElement> parameterElements, Expression expression) { | |
| 1324 RecordingErrorListener errorListener = new RecordingErrorListener(); | |
| 1325 ErrorReporter subErrorReporter = | |
| 1326 new ErrorReporter(errorListener, _errorReporter.source); | |
| 1327 DartObjectImpl result = expression.accept( | |
| 1328 new _ConstantVerifier_validateInitializerExpression(_typeProvider, | |
| 1329 subErrorReporter, this, parameterElements, declaredVariables)); | |
| 1330 _reportErrors(errorListener.errors, | |
| 1331 CompileTimeErrorCode.NON_CONSTANT_VALUE_IN_INITIALIZER); | |
| 1332 if (result != null) { | |
| 1333 _reportErrorIfFromDeferredLibrary(expression, | |
| 1334 CompileTimeErrorCode.NON_CONSTANT_VALUE_IN_INITIALIZER_FROM_DEFERRED_L
IBRARY); | |
| 1335 } | |
| 1336 } | |
| 1337 | |
| 1338 /** | |
| 1339 * Validates that all of the arguments of a constructor initializer are compil
e time constants. | |
| 1340 * | |
| 1341 * @param parameterElements the elements of parameters of constant constructor
, they are | |
| 1342 * considered as a valid potentially constant expressions | |
| 1343 * @param argumentList the argument list to validate | |
| 1344 */ | |
| 1345 void _validateInitializerInvocationArguments( | |
| 1346 List<ParameterElement> parameterElements, ArgumentList argumentList) { | |
| 1347 if (argumentList == null) { | |
| 1348 return; | |
| 1349 } | |
| 1350 for (Expression argument in argumentList.arguments) { | |
| 1351 _validateInitializerExpression(parameterElements, argument); | |
| 1352 } | |
| 1353 } | |
| 1354 | |
| 1355 /** | |
| 1356 * Validate that if the passed instance creation is 'const' then all its argum
ents are constant | |
| 1357 * expressions. | |
| 1358 * | |
| 1359 * @param node the instance creation evaluate | |
| 1360 */ | |
| 1361 void _validateInstanceCreationArguments(InstanceCreationExpression node) { | |
| 1362 if (!node.isConst) { | |
| 1363 return; | |
| 1364 } | |
| 1365 ArgumentList argumentList = node.argumentList; | |
| 1366 if (argumentList == null) { | |
| 1367 return; | |
| 1368 } | |
| 1369 _validateConstantArguments(argumentList); | |
| 1370 } | |
| 1371 } | |
| 1372 | |
| 1373 /** | |
| 1374 * Instances of the class `Dart2JSVerifier` traverse an AST structure looking fo
r hints for | |
| 1375 * code that will be compiled to JS, such as [HintCode.IS_DOUBLE]. | |
| 1376 */ | |
| 1377 class Dart2JSVerifier extends RecursiveAstVisitor<Object> { | |
| 1378 /** | |
| 1379 * The name of the `double` type. | |
| 1380 */ | |
| 1381 static String _DOUBLE_TYPE_NAME = "double"; | |
| 1382 | |
| 1383 /** | |
| 1384 * The error reporter by which errors will be reported. | |
| 1385 */ | |
| 1386 final ErrorReporter _errorReporter; | |
| 1387 | |
| 1388 /** | |
| 1389 * Create a new instance of the [Dart2JSVerifier]. | |
| 1390 * | |
| 1391 * @param errorReporter the error reporter | |
| 1392 */ | |
| 1393 Dart2JSVerifier(this._errorReporter); | |
| 1394 | |
| 1395 @override | |
| 1396 Object visitIsExpression(IsExpression node) { | |
| 1397 _checkForIsDoubleHints(node); | |
| 1398 return super.visitIsExpression(node); | |
| 1399 } | |
| 1400 | |
| 1401 /** | |
| 1402 * Check for instances of `x is double`, `x is int`, `x is! double` and | |
| 1403 * `x is! int`. | |
| 1404 * | |
| 1405 * @param node the is expression to check | |
| 1406 * @return `true` if and only if a hint code is generated on the passed node | |
| 1407 * See [HintCode.IS_DOUBLE], | |
| 1408 * [HintCode.IS_INT], | |
| 1409 * [HintCode.IS_NOT_DOUBLE], and | |
| 1410 * [HintCode.IS_NOT_INT]. | |
| 1411 */ | |
| 1412 bool _checkForIsDoubleHints(IsExpression node) { | |
| 1413 TypeName typeName = node.type; | |
| 1414 DartType type = typeName.type; | |
| 1415 if (type != null && type.element != null) { | |
| 1416 Element element = type.element; | |
| 1417 String typeNameStr = element.name; | |
| 1418 LibraryElement libraryElement = element.library; | |
| 1419 // if (typeNameStr.equals(INT_TYPE_NAME) && libraryElement != null | |
| 1420 // && libraryElement.isDartCore()) { | |
| 1421 // if (node.getNotOperator() == null) { | |
| 1422 // errorReporter.reportError(HintCode.IS_INT, node); | |
| 1423 // } else { | |
| 1424 // errorReporter.reportError(HintCode.IS_NOT_INT, node); | |
| 1425 // } | |
| 1426 // return true; | |
| 1427 // } else | |
| 1428 if (typeNameStr == _DOUBLE_TYPE_NAME && | |
| 1429 libraryElement != null && | |
| 1430 libraryElement.isDartCore) { | |
| 1431 if (node.notOperator == null) { | |
| 1432 _errorReporter.reportErrorForNode(HintCode.IS_DOUBLE, node); | |
| 1433 } else { | |
| 1434 _errorReporter.reportErrorForNode(HintCode.IS_NOT_DOUBLE, node); | |
| 1435 } | |
| 1436 return true; | |
| 1437 } | |
| 1438 } | |
| 1439 return false; | |
| 1440 } | |
| 1441 } | |
| 1442 | |
| 1443 /** | |
| 1444 * Instances of the class `DeadCodeVerifier` traverse an AST structure looking f
or cases of | |
| 1445 * [HintCode.DEAD_CODE]. | |
| 1446 */ | |
| 1447 class DeadCodeVerifier extends RecursiveAstVisitor<Object> { | |
| 1448 /** | |
| 1449 * The error reporter by which errors will be reported. | |
| 1450 */ | |
| 1451 final ErrorReporter _errorReporter; | |
| 1452 | |
| 1453 /** | |
| 1454 * Create a new instance of the [DeadCodeVerifier]. | |
| 1455 * | |
| 1456 * @param errorReporter the error reporter | |
| 1457 */ | |
| 1458 DeadCodeVerifier(this._errorReporter); | |
| 1459 | |
| 1460 @override | |
| 1461 Object visitBinaryExpression(BinaryExpression node) { | |
| 1462 sc.Token operator = node.operator; | |
| 1463 bool isAmpAmp = operator.type == sc.TokenType.AMPERSAND_AMPERSAND; | |
| 1464 bool isBarBar = operator.type == sc.TokenType.BAR_BAR; | |
| 1465 if (isAmpAmp || isBarBar) { | |
| 1466 Expression lhsCondition = node.leftOperand; | |
| 1467 if (!_isDebugConstant(lhsCondition)) { | |
| 1468 EvaluationResultImpl lhsResult = _getConstantBooleanValue(lhsCondition); | |
| 1469 if (lhsResult != null) { | |
| 1470 if (lhsResult.value.isTrue && isBarBar) { | |
| 1471 // report error on else block: true || !e! | |
| 1472 _errorReporter.reportErrorForNode( | |
| 1473 HintCode.DEAD_CODE, node.rightOperand); | |
| 1474 // only visit the LHS: | |
| 1475 _safelyVisit(lhsCondition); | |
| 1476 return null; | |
| 1477 } else if (lhsResult.value.isFalse && isAmpAmp) { | |
| 1478 // report error on if block: false && !e! | |
| 1479 _errorReporter.reportErrorForNode( | |
| 1480 HintCode.DEAD_CODE, node.rightOperand); | |
| 1481 // only visit the LHS: | |
| 1482 _safelyVisit(lhsCondition); | |
| 1483 return null; | |
| 1484 } | |
| 1485 } | |
| 1486 } | |
| 1487 // How do we want to handle the RHS? It isn't dead code, but "pointless" | |
| 1488 // or "obscure"... | |
| 1489 // Expression rhsCondition = node.getRightOperand(); | |
| 1490 // ValidResult rhsResult = getConstantBooleanValue(rhsCondition); | |
| 1491 // if (rhsResult != null) { | |
| 1492 // if (rhsResult == ValidResult.RESULT_TRUE && isBarBar) { | |
| 1493 // // report error on else block: !e! || true | |
| 1494 // errorReporter.reportError(HintCode.DEAD_CODE, node.getRightOpe
rand()); | |
| 1495 // // only visit the RHS: | |
| 1496 // safelyVisit(rhsCondition); | |
| 1497 // return null; | |
| 1498 // } else if (rhsResult == ValidResult.RESULT_FALSE && isAmpAmp) { | |
| 1499 // // report error on if block: !e! && false | |
| 1500 // errorReporter.reportError(HintCode.DEAD_CODE, node.getRightOpe
rand()); | |
| 1501 // // only visit the RHS: | |
| 1502 // safelyVisit(rhsCondition); | |
| 1503 // return null; | |
| 1504 // } | |
| 1505 // } | |
| 1506 } | |
| 1507 return super.visitBinaryExpression(node); | |
| 1508 } | |
| 1509 | |
| 1510 /** | |
| 1511 * For each [Block], this method reports and error on all statements between t
he end of the | |
| 1512 * block and the first return statement (assuming there it is not at the end o
f the block.) | |
| 1513 * | |
| 1514 * @param node the block to evaluate | |
| 1515 */ | |
| 1516 @override | |
| 1517 Object visitBlock(Block node) { | |
| 1518 NodeList<Statement> statements = node.statements; | |
| 1519 _checkForDeadStatementsInNodeList(statements); | |
| 1520 return null; | |
| 1521 } | |
| 1522 | |
| 1523 @override | |
| 1524 Object visitConditionalExpression(ConditionalExpression node) { | |
| 1525 Expression conditionExpression = node.condition; | |
| 1526 _safelyVisit(conditionExpression); | |
| 1527 if (!_isDebugConstant(conditionExpression)) { | |
| 1528 EvaluationResultImpl result = | |
| 1529 _getConstantBooleanValue(conditionExpression); | |
| 1530 if (result != null) { | |
| 1531 if (result.value.isTrue) { | |
| 1532 // report error on else block: true ? 1 : !2! | |
| 1533 _errorReporter.reportErrorForNode( | |
| 1534 HintCode.DEAD_CODE, node.elseExpression); | |
| 1535 _safelyVisit(node.thenExpression); | |
| 1536 return null; | |
| 1537 } else { | |
| 1538 // report error on if block: false ? !1! : 2 | |
| 1539 _errorReporter.reportErrorForNode( | |
| 1540 HintCode.DEAD_CODE, node.thenExpression); | |
| 1541 _safelyVisit(node.elseExpression); | |
| 1542 return null; | |
| 1543 } | |
| 1544 } | |
| 1545 } | |
| 1546 return super.visitConditionalExpression(node); | |
| 1547 } | |
| 1548 | |
| 1549 @override | |
| 1550 Object visitIfStatement(IfStatement node) { | |
| 1551 Expression conditionExpression = node.condition; | |
| 1552 _safelyVisit(conditionExpression); | |
| 1553 if (!_isDebugConstant(conditionExpression)) { | |
| 1554 EvaluationResultImpl result = | |
| 1555 _getConstantBooleanValue(conditionExpression); | |
| 1556 if (result != null) { | |
| 1557 if (result.value.isTrue) { | |
| 1558 // report error on else block: if(true) {} else {!} | |
| 1559 Statement elseStatement = node.elseStatement; | |
| 1560 if (elseStatement != null) { | |
| 1561 _errorReporter.reportErrorForNode( | |
| 1562 HintCode.DEAD_CODE, elseStatement); | |
| 1563 _safelyVisit(node.thenStatement); | |
| 1564 return null; | |
| 1565 } | |
| 1566 } else { | |
| 1567 // report error on if block: if (false) {!} else {} | |
| 1568 _errorReporter.reportErrorForNode( | |
| 1569 HintCode.DEAD_CODE, node.thenStatement); | |
| 1570 _safelyVisit(node.elseStatement); | |
| 1571 return null; | |
| 1572 } | |
| 1573 } | |
| 1574 } | |
| 1575 return super.visitIfStatement(node); | |
| 1576 } | |
| 1577 | |
| 1578 @override | |
| 1579 Object visitSwitchCase(SwitchCase node) { | |
| 1580 _checkForDeadStatementsInNodeList(node.statements); | |
| 1581 return super.visitSwitchCase(node); | |
| 1582 } | |
| 1583 | |
| 1584 @override | |
| 1585 Object visitSwitchDefault(SwitchDefault node) { | |
| 1586 _checkForDeadStatementsInNodeList(node.statements); | |
| 1587 return super.visitSwitchDefault(node); | |
| 1588 } | |
| 1589 | |
| 1590 @override | |
| 1591 Object visitTryStatement(TryStatement node) { | |
| 1592 _safelyVisit(node.body); | |
| 1593 _safelyVisit(node.finallyBlock); | |
| 1594 NodeList<CatchClause> catchClauses = node.catchClauses; | |
| 1595 int numOfCatchClauses = catchClauses.length; | |
| 1596 List<DartType> visitedTypes = new List<DartType>(); | |
| 1597 for (int i = 0; i < numOfCatchClauses; i++) { | |
| 1598 CatchClause catchClause = catchClauses[i]; | |
| 1599 if (catchClause.onKeyword != null) { | |
| 1600 // on-catch clause found, verify that the exception type is not a | |
| 1601 // subtype of a previous on-catch exception type | |
| 1602 TypeName typeName = catchClause.exceptionType; | |
| 1603 if (typeName != null && typeName.type != null) { | |
| 1604 DartType currentType = typeName.type; | |
| 1605 if (currentType.isObject) { | |
| 1606 // Found catch clause clause that has Object as an exception type, | |
| 1607 // this is equivalent to having a catch clause that doesn't have an | |
| 1608 // exception type, visit the block, but generate an error on any | |
| 1609 // following catch clauses (and don't visit them). | |
| 1610 _safelyVisit(catchClause); | |
| 1611 if (i + 1 != numOfCatchClauses) { | |
| 1612 // this catch clause is not the last in the try statement | |
| 1613 CatchClause nextCatchClause = catchClauses[i + 1]; | |
| 1614 CatchClause lastCatchClause = catchClauses[numOfCatchClauses - 1]; | |
| 1615 int offset = nextCatchClause.offset; | |
| 1616 int length = lastCatchClause.end - offset; | |
| 1617 _errorReporter.reportErrorForOffset( | |
| 1618 HintCode.DEAD_CODE_CATCH_FOLLOWING_CATCH, offset, length); | |
| 1619 return null; | |
| 1620 } | |
| 1621 } | |
| 1622 for (DartType type in visitedTypes) { | |
| 1623 if (currentType.isSubtypeOf(type)) { | |
| 1624 CatchClause lastCatchClause = catchClauses[numOfCatchClauses - 1]; | |
| 1625 int offset = catchClause.offset; | |
| 1626 int length = lastCatchClause.end - offset; | |
| 1627 _errorReporter.reportErrorForOffset( | |
| 1628 HintCode.DEAD_CODE_ON_CATCH_SUBTYPE, offset, length, [ | |
| 1629 currentType.displayName, | |
| 1630 type.displayName | |
| 1631 ]); | |
| 1632 return null; | |
| 1633 } | |
| 1634 } | |
| 1635 visitedTypes.add(currentType); | |
| 1636 } | |
| 1637 _safelyVisit(catchClause); | |
| 1638 } else { | |
| 1639 // Found catch clause clause that doesn't have an exception type, | |
| 1640 // visit the block, but generate an error on any following catch clauses | |
| 1641 // (and don't visit them). | |
| 1642 _safelyVisit(catchClause); | |
| 1643 if (i + 1 != numOfCatchClauses) { | |
| 1644 // this catch clause is not the last in the try statement | |
| 1645 CatchClause nextCatchClause = catchClauses[i + 1]; | |
| 1646 CatchClause lastCatchClause = catchClauses[numOfCatchClauses - 1]; | |
| 1647 int offset = nextCatchClause.offset; | |
| 1648 int length = lastCatchClause.end - offset; | |
| 1649 _errorReporter.reportErrorForOffset( | |
| 1650 HintCode.DEAD_CODE_CATCH_FOLLOWING_CATCH, offset, length); | |
| 1651 return null; | |
| 1652 } | |
| 1653 } | |
| 1654 } | |
| 1655 return null; | |
| 1656 } | |
| 1657 | |
| 1658 @override | |
| 1659 Object visitWhileStatement(WhileStatement node) { | |
| 1660 Expression conditionExpression = node.condition; | |
| 1661 _safelyVisit(conditionExpression); | |
| 1662 if (!_isDebugConstant(conditionExpression)) { | |
| 1663 EvaluationResultImpl result = | |
| 1664 _getConstantBooleanValue(conditionExpression); | |
| 1665 if (result != null) { | |
| 1666 if (result.value.isFalse) { | |
| 1667 // report error on if block: while (false) {!} | |
| 1668 _errorReporter.reportErrorForNode(HintCode.DEAD_CODE, node.body); | |
| 1669 return null; | |
| 1670 } | |
| 1671 } | |
| 1672 } | |
| 1673 _safelyVisit(node.body); | |
| 1674 return null; | |
| 1675 } | |
| 1676 | |
| 1677 /** | |
| 1678 * Given some [NodeList] of [Statement]s, from either a [Block] or | |
| 1679 * [SwitchMember], this loops through the list in reverse order searching for
statements | |
| 1680 * after a return, unlabeled break or unlabeled continue statement to mark the
m as dead code. | |
| 1681 * | |
| 1682 * @param statements some ordered list of statements in a [Block] or [SwitchMe
mber] | |
| 1683 */ | |
| 1684 void _checkForDeadStatementsInNodeList(NodeList<Statement> statements) { | |
| 1685 int size = statements.length; | |
| 1686 for (int i = 0; i < size; i++) { | |
| 1687 Statement currentStatement = statements[i]; | |
| 1688 _safelyVisit(currentStatement); | |
| 1689 bool returnOrBreakingStatement = currentStatement is ReturnStatement || | |
| 1690 (currentStatement is BreakStatement && | |
| 1691 currentStatement.label == null) || | |
| 1692 (currentStatement is ContinueStatement && | |
| 1693 currentStatement.label == null); | |
| 1694 if (returnOrBreakingStatement && i != size - 1) { | |
| 1695 Statement nextStatement = statements[i + 1]; | |
| 1696 Statement lastStatement = statements[size - 1]; | |
| 1697 int offset = nextStatement.offset; | |
| 1698 int length = lastStatement.end - offset; | |
| 1699 _errorReporter.reportErrorForOffset(HintCode.DEAD_CODE, offset, length); | |
| 1700 return; | |
| 1701 } | |
| 1702 } | |
| 1703 } | |
| 1704 | |
| 1705 /** | |
| 1706 * Given some [Expression], this method returns [ValidResult.RESULT_TRUE] if i
t is | |
| 1707 * `true`, [ValidResult.RESULT_FALSE] if it is `false`, or `null` if the | |
| 1708 * expression is not a constant boolean value. | |
| 1709 * | |
| 1710 * @param expression the expression to evaluate | |
| 1711 * @return [ValidResult.RESULT_TRUE] if it is `true`, [ValidResult.RESULT_FALS
E] | |
| 1712 * if it is `false`, or `null` if the expression is not a constant boo
lean | |
| 1713 * value | |
| 1714 */ | |
| 1715 EvaluationResultImpl _getConstantBooleanValue(Expression expression) { | |
| 1716 if (expression is BooleanLiteral) { | |
| 1717 if (expression.value) { | |
| 1718 return new EvaluationResultImpl( | |
| 1719 new DartObjectImpl(null, BoolState.from(true))); | |
| 1720 } else { | |
| 1721 return new EvaluationResultImpl( | |
| 1722 new DartObjectImpl(null, BoolState.from(false))); | |
| 1723 } | |
| 1724 } | |
| 1725 // Don't consider situations where we could evaluate to a constant boolean | |
| 1726 // expression with the ConstantVisitor | |
| 1727 // else { | |
| 1728 // EvaluationResultImpl result = expression.accept(new ConstantVisitor()); | |
| 1729 // if (result == ValidResult.RESULT_TRUE) { | |
| 1730 // return ValidResult.RESULT_TRUE; | |
| 1731 // } else if (result == ValidResult.RESULT_FALSE) { | |
| 1732 // return ValidResult.RESULT_FALSE; | |
| 1733 // } | |
| 1734 // return null; | |
| 1735 // } | |
| 1736 return null; | |
| 1737 } | |
| 1738 | |
| 1739 /** | |
| 1740 * Return `true` if and only if the passed expression is resolved to a constan
t variable. | |
| 1741 * | |
| 1742 * @param expression some conditional expression | |
| 1743 * @return `true` if and only if the passed expression is resolved to a consta
nt variable | |
| 1744 */ | |
| 1745 bool _isDebugConstant(Expression expression) { | |
| 1746 Element element = null; | |
| 1747 if (expression is Identifier) { | |
| 1748 Identifier identifier = expression; | |
| 1749 element = identifier.staticElement; | |
| 1750 } else if (expression is PropertyAccess) { | |
| 1751 PropertyAccess propertyAccess = expression; | |
| 1752 element = propertyAccess.propertyName.staticElement; | |
| 1753 } | |
| 1754 if (element is PropertyAccessorElement) { | |
| 1755 PropertyInducingElement variable = element.variable; | |
| 1756 return variable != null && variable.isConst; | |
| 1757 } | |
| 1758 return false; | |
| 1759 } | |
| 1760 | |
| 1761 /** | |
| 1762 * If the given node is not `null`, visit this instance of the dead code verif
ier. | |
| 1763 * | |
| 1764 * @param node the node to be visited | |
| 1765 */ | |
| 1766 void _safelyVisit(AstNode node) { | |
| 1767 if (node != null) { | |
| 1768 node.accept(this); | |
| 1769 } | |
| 1770 } | |
| 1771 } | |
| 1772 | |
| 1773 /** | |
| 1774 * Instances of the class `DeclarationResolver` are used to resolve declarations
in an AST | |
| 1775 * structure to already built elements. | |
| 1776 */ | |
| 1777 class DeclarationResolver extends RecursiveAstVisitor<Object> { | |
| 1778 /** | |
| 1779 * The compilation unit containing the AST nodes being visited. | |
| 1780 */ | |
| 1781 CompilationUnitElement _enclosingUnit; | |
| 1782 | |
| 1783 /** | |
| 1784 * The function type alias containing the AST nodes being visited, or `null` i
f we are not | |
| 1785 * in the scope of a function type alias. | |
| 1786 */ | |
| 1787 FunctionTypeAliasElement _enclosingAlias; | |
| 1788 | |
| 1789 /** | |
| 1790 * The class containing the AST nodes being visited, or `null` if we are not i
n the scope of | |
| 1791 * a class. | |
| 1792 */ | |
| 1793 ClassElement _enclosingClass; | |
| 1794 | |
| 1795 /** | |
| 1796 * The method or function containing the AST nodes being visited, or `null` if
we are not in | |
| 1797 * the scope of a method or function. | |
| 1798 */ | |
| 1799 ExecutableElement _enclosingExecutable; | |
| 1800 | |
| 1801 /** | |
| 1802 * The parameter containing the AST nodes being visited, or `null` if we are n
ot in the | |
| 1803 * scope of a parameter. | |
| 1804 */ | |
| 1805 ParameterElement _enclosingParameter; | |
| 1806 | |
| 1807 /** | |
| 1808 * Resolve the declarations within the given compilation unit to the elements
rooted at the given | |
| 1809 * element. | |
| 1810 * | |
| 1811 * @param unit the compilation unit to be resolved | |
| 1812 * @param element the root of the element model used to resolve the AST nodes | |
| 1813 */ | |
| 1814 void resolve(CompilationUnit unit, CompilationUnitElement element) { | |
| 1815 _enclosingUnit = element; | |
| 1816 unit.element = element; | |
| 1817 unit.accept(this); | |
| 1818 } | |
| 1819 | |
| 1820 @override | |
| 1821 Object visitCatchClause(CatchClause node) { | |
| 1822 SimpleIdentifier exceptionParameter = node.exceptionParameter; | |
| 1823 if (exceptionParameter != null) { | |
| 1824 List<LocalVariableElement> localVariables = | |
| 1825 _enclosingExecutable.localVariables; | |
| 1826 _findIdentifier(localVariables, exceptionParameter); | |
| 1827 SimpleIdentifier stackTraceParameter = node.stackTraceParameter; | |
| 1828 if (stackTraceParameter != null) { | |
| 1829 _findIdentifier(localVariables, stackTraceParameter); | |
| 1830 } | |
| 1831 } | |
| 1832 return super.visitCatchClause(node); | |
| 1833 } | |
| 1834 | |
| 1835 @override | |
| 1836 Object visitClassDeclaration(ClassDeclaration node) { | |
| 1837 ClassElement outerClass = _enclosingClass; | |
| 1838 try { | |
| 1839 SimpleIdentifier className = node.name; | |
| 1840 _enclosingClass = _findIdentifier(_enclosingUnit.types, className); | |
| 1841 return super.visitClassDeclaration(node); | |
| 1842 } finally { | |
| 1843 _enclosingClass = outerClass; | |
| 1844 } | |
| 1845 } | |
| 1846 | |
| 1847 @override | |
| 1848 Object visitClassTypeAlias(ClassTypeAlias node) { | |
| 1849 ClassElement outerClass = _enclosingClass; | |
| 1850 try { | |
| 1851 SimpleIdentifier className = node.name; | |
| 1852 _enclosingClass = _findIdentifier(_enclosingUnit.types, className); | |
| 1853 return super.visitClassTypeAlias(node); | |
| 1854 } finally { | |
| 1855 _enclosingClass = outerClass; | |
| 1856 } | |
| 1857 } | |
| 1858 | |
| 1859 @override | |
| 1860 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
| 1861 ExecutableElement outerExecutable = _enclosingExecutable; | |
| 1862 try { | |
| 1863 SimpleIdentifier constructorName = node.name; | |
| 1864 if (constructorName == null) { | |
| 1865 _enclosingExecutable = _enclosingClass.unnamedConstructor; | |
| 1866 } else { | |
| 1867 _enclosingExecutable = | |
| 1868 _enclosingClass.getNamedConstructor(constructorName.name); | |
| 1869 constructorName.staticElement = _enclosingExecutable; | |
| 1870 } | |
| 1871 node.element = _enclosingExecutable as ConstructorElement; | |
| 1872 return super.visitConstructorDeclaration(node); | |
| 1873 } finally { | |
| 1874 _enclosingExecutable = outerExecutable; | |
| 1875 } | |
| 1876 } | |
| 1877 | |
| 1878 @override | |
| 1879 Object visitDeclaredIdentifier(DeclaredIdentifier node) { | |
| 1880 SimpleIdentifier variableName = node.identifier; | |
| 1881 _findIdentifier(_enclosingExecutable.localVariables, variableName); | |
| 1882 return super.visitDeclaredIdentifier(node); | |
| 1883 } | |
| 1884 | |
| 1885 @override | |
| 1886 Object visitDefaultFormalParameter(DefaultFormalParameter node) { | |
| 1887 SimpleIdentifier parameterName = node.parameter.identifier; | |
| 1888 ParameterElement element = _getElementForParameter(node, parameterName); | |
| 1889 Expression defaultValue = node.defaultValue; | |
| 1890 if (defaultValue != null) { | |
| 1891 ExecutableElement outerExecutable = _enclosingExecutable; | |
| 1892 try { | |
| 1893 if (element == null) { | |
| 1894 // TODO(brianwilkerson) Report this internal error. | |
| 1895 } else { | |
| 1896 _enclosingExecutable = element.initializer; | |
| 1897 } | |
| 1898 defaultValue.accept(this); | |
| 1899 } finally { | |
| 1900 _enclosingExecutable = outerExecutable; | |
| 1901 } | |
| 1902 } | |
| 1903 ParameterElement outerParameter = _enclosingParameter; | |
| 1904 try { | |
| 1905 _enclosingParameter = element; | |
| 1906 return super.visitDefaultFormalParameter(node); | |
| 1907 } finally { | |
| 1908 _enclosingParameter = outerParameter; | |
| 1909 } | |
| 1910 } | |
| 1911 | |
| 1912 @override | |
| 1913 Object visitEnumDeclaration(EnumDeclaration node) { | |
| 1914 ClassElement enclosingEnum = | |
| 1915 _findIdentifier(_enclosingUnit.enums, node.name); | |
| 1916 List<FieldElement> constants = enclosingEnum.fields; | |
| 1917 for (EnumConstantDeclaration constant in node.constants) { | |
| 1918 _findIdentifier(constants, constant.name); | |
| 1919 } | |
| 1920 return super.visitEnumDeclaration(node); | |
| 1921 } | |
| 1922 | |
| 1923 @override | |
| 1924 Object visitExportDirective(ExportDirective node) { | |
| 1925 String uri = _getStringValue(node.uri); | |
| 1926 if (uri != null) { | |
| 1927 LibraryElement library = _enclosingUnit.library; | |
| 1928 ExportElement exportElement = _findExport(library.exports, | |
| 1929 _enclosingUnit.context.sourceFactory.resolveUri( | |
| 1930 _enclosingUnit.source, uri)); | |
| 1931 node.element = exportElement; | |
| 1932 } | |
| 1933 return super.visitExportDirective(node); | |
| 1934 } | |
| 1935 | |
| 1936 @override | |
| 1937 Object visitFieldFormalParameter(FieldFormalParameter node) { | |
| 1938 if (node.parent is! DefaultFormalParameter) { | |
| 1939 SimpleIdentifier parameterName = node.identifier; | |
| 1940 ParameterElement element = _getElementForParameter(node, parameterName); | |
| 1941 ParameterElement outerParameter = _enclosingParameter; | |
| 1942 try { | |
| 1943 _enclosingParameter = element; | |
| 1944 return super.visitFieldFormalParameter(node); | |
| 1945 } finally { | |
| 1946 _enclosingParameter = outerParameter; | |
| 1947 } | |
| 1948 } else { | |
| 1949 return super.visitFieldFormalParameter(node); | |
| 1950 } | |
| 1951 } | |
| 1952 | |
| 1953 @override | |
| 1954 Object visitFunctionDeclaration(FunctionDeclaration node) { | |
| 1955 ExecutableElement outerExecutable = _enclosingExecutable; | |
| 1956 try { | |
| 1957 SimpleIdentifier functionName = node.name; | |
| 1958 sc.Token property = node.propertyKeyword; | |
| 1959 if (property == null) { | |
| 1960 if (_enclosingExecutable != null) { | |
| 1961 _enclosingExecutable = | |
| 1962 _findIdentifier(_enclosingExecutable.functions, functionName); | |
| 1963 } else { | |
| 1964 _enclosingExecutable = | |
| 1965 _findIdentifier(_enclosingUnit.functions, functionName); | |
| 1966 } | |
| 1967 } else { | |
| 1968 PropertyAccessorElement accessor = | |
| 1969 _findIdentifier(_enclosingUnit.accessors, functionName); | |
| 1970 if ((property as sc.KeywordToken).keyword == sc.Keyword.SET) { | |
| 1971 accessor = accessor.variable.setter; | |
| 1972 functionName.staticElement = accessor; | |
| 1973 } | |
| 1974 _enclosingExecutable = accessor; | |
| 1975 } | |
| 1976 node.functionExpression.element = _enclosingExecutable; | |
| 1977 return super.visitFunctionDeclaration(node); | |
| 1978 } finally { | |
| 1979 _enclosingExecutable = outerExecutable; | |
| 1980 } | |
| 1981 } | |
| 1982 | |
| 1983 @override | |
| 1984 Object visitFunctionExpression(FunctionExpression node) { | |
| 1985 if (node.parent is! FunctionDeclaration) { | |
| 1986 FunctionElement element = | |
| 1987 _findAtOffset(_enclosingExecutable.functions, node.beginToken.offset); | |
| 1988 node.element = element; | |
| 1989 } | |
| 1990 ExecutableElement outerExecutable = _enclosingExecutable; | |
| 1991 try { | |
| 1992 _enclosingExecutable = node.element; | |
| 1993 return super.visitFunctionExpression(node); | |
| 1994 } finally { | |
| 1995 _enclosingExecutable = outerExecutable; | |
| 1996 } | |
| 1997 } | |
| 1998 | |
| 1999 @override | |
| 2000 Object visitFunctionTypeAlias(FunctionTypeAlias node) { | |
| 2001 FunctionTypeAliasElement outerAlias = _enclosingAlias; | |
| 2002 try { | |
| 2003 SimpleIdentifier aliasName = node.name; | |
| 2004 _enclosingAlias = | |
| 2005 _findIdentifier(_enclosingUnit.functionTypeAliases, aliasName); | |
| 2006 return super.visitFunctionTypeAlias(node); | |
| 2007 } finally { | |
| 2008 _enclosingAlias = outerAlias; | |
| 2009 } | |
| 2010 } | |
| 2011 | |
| 2012 @override | |
| 2013 Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { | |
| 2014 if (node.parent is! DefaultFormalParameter) { | |
| 2015 SimpleIdentifier parameterName = node.identifier; | |
| 2016 ParameterElement element = _getElementForParameter(node, parameterName); | |
| 2017 ParameterElement outerParameter = _enclosingParameter; | |
| 2018 try { | |
| 2019 _enclosingParameter = element; | |
| 2020 return super.visitFunctionTypedFormalParameter(node); | |
| 2021 } finally { | |
| 2022 _enclosingParameter = outerParameter; | |
| 2023 } | |
| 2024 } else { | |
| 2025 return super.visitFunctionTypedFormalParameter(node); | |
| 2026 } | |
| 2027 } | |
| 2028 | |
| 2029 @override | |
| 2030 Object visitImportDirective(ImportDirective node) { | |
| 2031 String uri = _getStringValue(node.uri); | |
| 2032 if (uri != null) { | |
| 2033 LibraryElement library = _enclosingUnit.library; | |
| 2034 ImportElement importElement = _findImport(library.imports, | |
| 2035 _enclosingUnit.context.sourceFactory.resolveUri( | |
| 2036 _enclosingUnit.source, uri), node.prefix); | |
| 2037 node.element = importElement; | |
| 2038 } | |
| 2039 return super.visitImportDirective(node); | |
| 2040 } | |
| 2041 | |
| 2042 @override | |
| 2043 Object visitLabeledStatement(LabeledStatement node) { | |
| 2044 for (Label label in node.labels) { | |
| 2045 SimpleIdentifier labelName = label.label; | |
| 2046 _findIdentifier(_enclosingExecutable.labels, labelName); | |
| 2047 } | |
| 2048 return super.visitLabeledStatement(node); | |
| 2049 } | |
| 2050 | |
| 2051 @override | |
| 2052 Object visitLibraryDirective(LibraryDirective node) { | |
| 2053 node.element = _enclosingUnit.library; | |
| 2054 return super.visitLibraryDirective(node); | |
| 2055 } | |
| 2056 | |
| 2057 @override | |
| 2058 Object visitMethodDeclaration(MethodDeclaration node) { | |
| 2059 ExecutableElement outerExecutable = _enclosingExecutable; | |
| 2060 try { | |
| 2061 sc.Token property = node.propertyKeyword; | |
| 2062 SimpleIdentifier methodName = node.name; | |
| 2063 String nameOfMethod = methodName.name; | |
| 2064 if (property == null) { | |
| 2065 _enclosingExecutable = _findWithNameAndOffset( | |
| 2066 _enclosingClass.methods, nameOfMethod, methodName.offset); | |
| 2067 methodName.staticElement = _enclosingExecutable; | |
| 2068 } else { | |
| 2069 PropertyAccessorElement accessor = | |
| 2070 _findIdentifier(_enclosingClass.accessors, methodName); | |
| 2071 if ((property as sc.KeywordToken).keyword == sc.Keyword.SET) { | |
| 2072 accessor = accessor.variable.setter; | |
| 2073 methodName.staticElement = accessor; | |
| 2074 } | |
| 2075 _enclosingExecutable = accessor; | |
| 2076 } | |
| 2077 return super.visitMethodDeclaration(node); | |
| 2078 } finally { | |
| 2079 _enclosingExecutable = outerExecutable; | |
| 2080 } | |
| 2081 } | |
| 2082 | |
| 2083 @override | |
| 2084 Object visitPartDirective(PartDirective node) { | |
| 2085 String uri = _getStringValue(node.uri); | |
| 2086 if (uri != null) { | |
| 2087 Source partSource = _enclosingUnit.context.sourceFactory.resolveUri( | |
| 2088 _enclosingUnit.source, uri); | |
| 2089 node.element = _findPart(_enclosingUnit.library.parts, partSource); | |
| 2090 } | |
| 2091 return super.visitPartDirective(node); | |
| 2092 } | |
| 2093 | |
| 2094 @override | |
| 2095 Object visitPartOfDirective(PartOfDirective node) { | |
| 2096 node.element = _enclosingUnit.library; | |
| 2097 return super.visitPartOfDirective(node); | |
| 2098 } | |
| 2099 | |
| 2100 @override | |
| 2101 Object visitSimpleFormalParameter(SimpleFormalParameter node) { | |
| 2102 if (node.parent is! DefaultFormalParameter) { | |
| 2103 SimpleIdentifier parameterName = node.identifier; | |
| 2104 ParameterElement element = _getElementForParameter(node, parameterName); | |
| 2105 ParameterElement outerParameter = _enclosingParameter; | |
| 2106 try { | |
| 2107 _enclosingParameter = element; | |
| 2108 return super.visitSimpleFormalParameter(node); | |
| 2109 } finally { | |
| 2110 _enclosingParameter = outerParameter; | |
| 2111 } | |
| 2112 } else {} | |
| 2113 return super.visitSimpleFormalParameter(node); | |
| 2114 } | |
| 2115 | |
| 2116 @override | |
| 2117 Object visitSwitchCase(SwitchCase node) { | |
| 2118 for (Label label in node.labels) { | |
| 2119 SimpleIdentifier labelName = label.label; | |
| 2120 _findIdentifier(_enclosingExecutable.labels, labelName); | |
| 2121 } | |
| 2122 return super.visitSwitchCase(node); | |
| 2123 } | |
| 2124 | |
| 2125 @override | |
| 2126 Object visitSwitchDefault(SwitchDefault node) { | |
| 2127 for (Label label in node.labels) { | |
| 2128 SimpleIdentifier labelName = label.label; | |
| 2129 _findIdentifier(_enclosingExecutable.labels, labelName); | |
| 2130 } | |
| 2131 return super.visitSwitchDefault(node); | |
| 2132 } | |
| 2133 | |
| 2134 @override | |
| 2135 Object visitTypeParameter(TypeParameter node) { | |
| 2136 SimpleIdentifier parameterName = node.name; | |
| 2137 if (_enclosingClass != null) { | |
| 2138 _findIdentifier(_enclosingClass.typeParameters, parameterName); | |
| 2139 } else if (_enclosingAlias != null) { | |
| 2140 _findIdentifier(_enclosingAlias.typeParameters, parameterName); | |
| 2141 } | |
| 2142 return super.visitTypeParameter(node); | |
| 2143 } | |
| 2144 | |
| 2145 @override | |
| 2146 Object visitVariableDeclaration(VariableDeclaration node) { | |
| 2147 VariableElement element = null; | |
| 2148 SimpleIdentifier variableName = node.name; | |
| 2149 if (_enclosingExecutable != null) { | |
| 2150 element = | |
| 2151 _findIdentifier(_enclosingExecutable.localVariables, variableName); | |
| 2152 } | |
| 2153 if (element == null && _enclosingClass != null) { | |
| 2154 element = _findIdentifier(_enclosingClass.fields, variableName); | |
| 2155 } | |
| 2156 if (element == null && _enclosingUnit != null) { | |
| 2157 element = _findIdentifier(_enclosingUnit.topLevelVariables, variableName); | |
| 2158 } | |
| 2159 Expression initializer = node.initializer; | |
| 2160 if (initializer != null) { | |
| 2161 ExecutableElement outerExecutable = _enclosingExecutable; | |
| 2162 try { | |
| 2163 if (element == null) { | |
| 2164 // TODO(brianwilkerson) Report this internal error. | |
| 2165 } else { | |
| 2166 _enclosingExecutable = element.initializer; | |
| 2167 } | |
| 2168 return super.visitVariableDeclaration(node); | |
| 2169 } finally { | |
| 2170 _enclosingExecutable = outerExecutable; | |
| 2171 } | |
| 2172 } | |
| 2173 return super.visitVariableDeclaration(node); | |
| 2174 } | |
| 2175 | |
| 2176 /** | |
| 2177 * Return the element in the given array of elements that was created for the
declaration at the | |
| 2178 * given offset. This method should only be used when there is no name | |
| 2179 * | |
| 2180 * @param elements the elements of the appropriate kind that exist in the curr
ent context | |
| 2181 * @param offset the offset of the name of the element to be returned | |
| 2182 * @return the element at the given offset | |
| 2183 */ | |
| 2184 Element _findAtOffset(List<Element> elements, int offset) => | |
| 2185 _findWithNameAndOffset(elements, "", offset); | |
| 2186 | |
| 2187 /** | |
| 2188 * Return the export element from the given array whose library has the given
source, or | |
| 2189 * `null` if there is no such export. | |
| 2190 * | |
| 2191 * @param exports the export elements being searched | |
| 2192 * @param source the source of the library associated with the export element
to being searched | |
| 2193 * for | |
| 2194 * @return the export element whose library has the given source | |
| 2195 */ | |
| 2196 ExportElement _findExport(List<ExportElement> exports, Source source) { | |
| 2197 for (ExportElement export in exports) { | |
| 2198 if (export.exportedLibrary.source == source) { | |
| 2199 return export; | |
| 2200 } | |
| 2201 } | |
| 2202 return null; | |
| 2203 } | |
| 2204 | |
| 2205 /** | |
| 2206 * Return the element in the given array of elements that was created for the
declaration with the | |
| 2207 * given name. | |
| 2208 * | |
| 2209 * @param elements the elements of the appropriate kind that exist in the curr
ent context | |
| 2210 * @param identifier the name node in the declaration of the element to be ret
urned | |
| 2211 * @return the element created for the declaration with the given name | |
| 2212 */ | |
| 2213 Element _findIdentifier(List<Element> elements, SimpleIdentifier identifier) { | |
| 2214 Element element = | |
| 2215 _findWithNameAndOffset(elements, identifier.name, identifier.offset); | |
| 2216 identifier.staticElement = element; | |
| 2217 return element; | |
| 2218 } | |
| 2219 | |
| 2220 /** | |
| 2221 * Return the import element from the given array whose library has the given
source and that has | |
| 2222 * the given prefix, or `null` if there is no such import. | |
| 2223 * | |
| 2224 * @param imports the import elements being searched | |
| 2225 * @param source the source of the library associated with the import element
to being searched | |
| 2226 * for | |
| 2227 * @param prefix the prefix with which the library was imported | |
| 2228 * @return the import element whose library has the given source and prefix | |
| 2229 */ | |
| 2230 ImportElement _findImport( | |
| 2231 List<ImportElement> imports, Source source, SimpleIdentifier prefix) { | |
| 2232 for (ImportElement element in imports) { | |
| 2233 if (element.importedLibrary.source == source) { | |
| 2234 PrefixElement prefixElement = element.prefix; | |
| 2235 if (prefix == null) { | |
| 2236 if (prefixElement == null) { | |
| 2237 return element; | |
| 2238 } | |
| 2239 } else { | |
| 2240 if (prefixElement != null && | |
| 2241 prefix.name == prefixElement.displayName) { | |
| 2242 return element; | |
| 2243 } | |
| 2244 } | |
| 2245 } | |
| 2246 } | |
| 2247 return null; | |
| 2248 } | |
| 2249 | |
| 2250 /** | |
| 2251 * Return the element for the part with the given source, or `null` if there i
s no element | |
| 2252 * for the given source. | |
| 2253 * | |
| 2254 * @param parts the elements for the parts | |
| 2255 * @param partSource the source for the part whose element is to be returned | |
| 2256 * @return the element for the part with the given source | |
| 2257 */ | |
| 2258 CompilationUnitElement _findPart( | |
| 2259 List<CompilationUnitElement> parts, Source partSource) { | |
| 2260 for (CompilationUnitElement part in parts) { | |
| 2261 if (part.source == partSource) { | |
| 2262 return part; | |
| 2263 } | |
| 2264 } | |
| 2265 return null; | |
| 2266 } | |
| 2267 | |
| 2268 /** | |
| 2269 * Return the element in the given array of elements that was created for the
declaration with the | |
| 2270 * given name at the given offset. | |
| 2271 * | |
| 2272 * @param elements the elements of the appropriate kind that exist in the curr
ent context | |
| 2273 * @param name the name of the element to be returned | |
| 2274 * @param offset the offset of the name of the element to be returned | |
| 2275 * @return the element with the given name and offset | |
| 2276 */ | |
| 2277 Element _findWithNameAndOffset( | |
| 2278 List<Element> elements, String name, int offset) { | |
| 2279 for (Element element in elements) { | |
| 2280 if (element.nameOffset == offset && element.displayName == name) { | |
| 2281 return element; | |
| 2282 } | |
| 2283 } | |
| 2284 return null; | |
| 2285 } | |
| 2286 | |
| 2287 /** | |
| 2288 * Search the most closely enclosing list of parameters for a parameter with t
he given name. | |
| 2289 * | |
| 2290 * @param node the node defining the parameter with the given name | |
| 2291 * @param parameterName the name of the parameter being searched for | |
| 2292 * @return the element representing the parameter with that name | |
| 2293 */ | |
| 2294 ParameterElement _getElementForParameter( | |
| 2295 FormalParameter node, SimpleIdentifier parameterName) { | |
| 2296 List<ParameterElement> parameters = null; | |
| 2297 if (_enclosingParameter != null) { | |
| 2298 parameters = _enclosingParameter.parameters; | |
| 2299 } | |
| 2300 if (parameters == null && _enclosingExecutable != null) { | |
| 2301 parameters = _enclosingExecutable.parameters; | |
| 2302 } | |
| 2303 if (parameters == null && _enclosingAlias != null) { | |
| 2304 parameters = _enclosingAlias.parameters; | |
| 2305 } | |
| 2306 ParameterElement element = | |
| 2307 parameters == null ? null : _findIdentifier(parameters, parameterName); | |
| 2308 if (element == null) { | |
| 2309 StringBuffer buffer = new StringBuffer(); | |
| 2310 buffer.writeln("Invalid state found in the Analysis Engine:"); | |
| 2311 buffer.writeln( | |
| 2312 "DeclarationResolver.getElementForParameter() is visiting a parameter
that does not appear to be in a method or function."); | |
| 2313 buffer.writeln("Ancestors:"); | |
| 2314 AstNode parent = node.parent; | |
| 2315 while (parent != null) { | |
| 2316 buffer.writeln(parent.runtimeType.toString()); | |
| 2317 buffer.writeln("---------"); | |
| 2318 parent = parent.parent; | |
| 2319 } | |
| 2320 AnalysisEngine.instance.logger.logError(buffer.toString(), | |
| 2321 new CaughtException(new AnalysisException(), null)); | |
| 2322 } | |
| 2323 return element; | |
| 2324 } | |
| 2325 | |
| 2326 /** | |
| 2327 * Return the value of the given string literal, or `null` if the string is no
t a constant | |
| 2328 * string without any string interpolation. | |
| 2329 * | |
| 2330 * @param literal the string literal whose value is to be returned | |
| 2331 * @return the value of the given string literal | |
| 2332 */ | |
| 2333 String _getStringValue(StringLiteral literal) { | |
| 2334 if (literal is StringInterpolation) { | |
| 2335 return null; | |
| 2336 } | |
| 2337 return literal.stringValue; | |
| 2338 } | |
| 2339 } | |
| 2340 | |
| 2341 /** | |
| 2342 * Instances of the class `ElementBuilder` traverse an AST structure and build t
he element | |
| 2343 * model representing the AST structure. | |
| 2344 */ | |
| 2345 class ElementBuilder extends RecursiveAstVisitor<Object> { | |
| 2346 /** | |
| 2347 * The element holder associated with the element that is currently being buil
t. | |
| 2348 */ | |
| 2349 ElementHolder _currentHolder; | |
| 2350 | |
| 2351 /** | |
| 2352 * A flag indicating whether a variable declaration is in the context of a fie
ld declaration. | |
| 2353 */ | |
| 2354 bool _inFieldContext = false; | |
| 2355 | |
| 2356 /** | |
| 2357 * A flag indicating whether a variable declaration is within the body of a me
thod or function. | |
| 2358 */ | |
| 2359 bool _inFunction = false; | |
| 2360 | |
| 2361 /** | |
| 2362 * A flag indicating whether the class currently being visited can be used as
a mixin. | |
| 2363 */ | |
| 2364 bool _isValidMixin = false; | |
| 2365 | |
| 2366 /** | |
| 2367 * A collection holding the function types defined in a class that need to hav
e their type | |
| 2368 * arguments set to the types of the type parameters for the class, or `null`
if we are not | |
| 2369 * currently processing nodes within a class. | |
| 2370 */ | |
| 2371 List<FunctionTypeImpl> _functionTypesToFix = null; | |
| 2372 | |
| 2373 /** | |
| 2374 * A table mapping field names to field elements for the fields defined in the
current class, or | |
| 2375 * `null` if we are not in the scope of a class. | |
| 2376 */ | |
| 2377 HashMap<String, FieldElement> _fieldMap; | |
| 2378 | |
| 2379 /** | |
| 2380 * Initialize a newly created element builder to build the elements for a comp
ilation unit. | |
| 2381 * | |
| 2382 * @param initialHolder the element holder associated with the compilation uni
t being built | |
| 2383 */ | |
| 2384 ElementBuilder(ElementHolder initialHolder) { | |
| 2385 _currentHolder = initialHolder; | |
| 2386 } | |
| 2387 | |
| 2388 @override | |
| 2389 Object visitBlock(Block node) { | |
| 2390 bool wasInField = _inFieldContext; | |
| 2391 _inFieldContext = false; | |
| 2392 try { | |
| 2393 node.visitChildren(this); | |
| 2394 } finally { | |
| 2395 _inFieldContext = wasInField; | |
| 2396 } | |
| 2397 return null; | |
| 2398 } | |
| 2399 | |
| 2400 @override | |
| 2401 Object visitCatchClause(CatchClause node) { | |
| 2402 SimpleIdentifier exceptionParameter = node.exceptionParameter; | |
| 2403 if (exceptionParameter != null) { | |
| 2404 // exception | |
| 2405 LocalVariableElementImpl exception = | |
| 2406 new LocalVariableElementImpl.forNode(exceptionParameter); | |
| 2407 _currentHolder.addLocalVariable(exception); | |
| 2408 exceptionParameter.staticElement = exception; | |
| 2409 // stack trace | |
| 2410 SimpleIdentifier stackTraceParameter = node.stackTraceParameter; | |
| 2411 if (stackTraceParameter != null) { | |
| 2412 LocalVariableElementImpl stackTrace = | |
| 2413 new LocalVariableElementImpl.forNode(stackTraceParameter); | |
| 2414 _currentHolder.addLocalVariable(stackTrace); | |
| 2415 stackTraceParameter.staticElement = stackTrace; | |
| 2416 } | |
| 2417 } | |
| 2418 return super.visitCatchClause(node); | |
| 2419 } | |
| 2420 | |
| 2421 @override | |
| 2422 Object visitClassDeclaration(ClassDeclaration node) { | |
| 2423 ElementHolder holder = new ElementHolder(); | |
| 2424 _isValidMixin = true; | |
| 2425 _functionTypesToFix = new List<FunctionTypeImpl>(); | |
| 2426 // | |
| 2427 // Process field declarations before constructors and methods so that field | |
| 2428 // formal parameters can be correctly resolved to their fields. | |
| 2429 // | |
| 2430 ElementHolder previousHolder = _currentHolder; | |
| 2431 _currentHolder = holder; | |
| 2432 try { | |
| 2433 List<ClassMember> nonFields = new List<ClassMember>(); | |
| 2434 node.visitChildren( | |
| 2435 new _ElementBuilder_visitClassDeclaration(this, nonFields)); | |
| 2436 _buildFieldMap(holder.fieldsWithoutFlushing); | |
| 2437 int count = nonFields.length; | |
| 2438 for (int i = 0; i < count; i++) { | |
| 2439 nonFields[i].accept(this); | |
| 2440 } | |
| 2441 } finally { | |
| 2442 _currentHolder = previousHolder; | |
| 2443 } | |
| 2444 SimpleIdentifier className = node.name; | |
| 2445 ClassElementImpl element = new ClassElementImpl.forNode(className); | |
| 2446 List<TypeParameterElement> typeParameters = holder.typeParameters; | |
| 2447 List<DartType> typeArguments = _createTypeParameterTypes(typeParameters); | |
| 2448 InterfaceTypeImpl interfaceType = new InterfaceTypeImpl(element); | |
| 2449 interfaceType.typeArguments = typeArguments; | |
| 2450 element.type = interfaceType; | |
| 2451 List<ConstructorElement> constructors = holder.constructors; | |
| 2452 if (constructors.length == 0) { | |
| 2453 // | |
| 2454 // Create the default constructor. | |
| 2455 // | |
| 2456 constructors = _createDefaultConstructors(interfaceType); | |
| 2457 } | |
| 2458 element.abstract = node.isAbstract; | |
| 2459 element.accessors = holder.accessors; | |
| 2460 element.constructors = constructors; | |
| 2461 element.fields = holder.fields; | |
| 2462 element.methods = holder.methods; | |
| 2463 element.typeParameters = typeParameters; | |
| 2464 element.validMixin = _isValidMixin; | |
| 2465 int functionTypeCount = _functionTypesToFix.length; | |
| 2466 for (int i = 0; i < functionTypeCount; i++) { | |
| 2467 _functionTypesToFix[i].typeArguments = typeArguments; | |
| 2468 } | |
| 2469 _functionTypesToFix = null; | |
| 2470 _currentHolder.addType(element); | |
| 2471 className.staticElement = element; | |
| 2472 _fieldMap = null; | |
| 2473 holder.validate(); | |
| 2474 return null; | |
| 2475 } | |
| 2476 | |
| 2477 /** | |
| 2478 * Implementation of this method should be synchronized with | |
| 2479 * [visitClassDeclaration]. | |
| 2480 */ | |
| 2481 void visitClassDeclarationIncrementally(ClassDeclaration node) { | |
| 2482 // | |
| 2483 // Process field declarations before constructors and methods so that field | |
| 2484 // formal parameters can be correctly resolved to their fields. | |
| 2485 // | |
| 2486 ClassElement classElement = node.element; | |
| 2487 _buildFieldMap(classElement.fields); | |
| 2488 } | |
| 2489 | |
| 2490 @override | |
| 2491 Object visitClassTypeAlias(ClassTypeAlias node) { | |
| 2492 ElementHolder holder = new ElementHolder(); | |
| 2493 _functionTypesToFix = new List<FunctionTypeImpl>(); | |
| 2494 _visitChildren(holder, node); | |
| 2495 SimpleIdentifier className = node.name; | |
| 2496 ClassElementImpl element = new ClassElementImpl.forNode(className); | |
| 2497 element.abstract = node.abstractKeyword != null; | |
| 2498 element.mixinApplication = true; | |
| 2499 List<TypeParameterElement> typeParameters = holder.typeParameters; | |
| 2500 element.typeParameters = typeParameters; | |
| 2501 List<DartType> typeArguments = _createTypeParameterTypes(typeParameters); | |
| 2502 InterfaceTypeImpl interfaceType = new InterfaceTypeImpl(element); | |
| 2503 interfaceType.typeArguments = typeArguments; | |
| 2504 element.type = interfaceType; | |
| 2505 // set default constructor | |
| 2506 for (FunctionTypeImpl functionType in _functionTypesToFix) { | |
| 2507 functionType.typeArguments = typeArguments; | |
| 2508 } | |
| 2509 _functionTypesToFix = null; | |
| 2510 _currentHolder.addType(element); | |
| 2511 className.staticElement = element; | |
| 2512 holder.validate(); | |
| 2513 return null; | |
| 2514 } | |
| 2515 | |
| 2516 @override | |
| 2517 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
| 2518 _isValidMixin = false; | |
| 2519 ElementHolder holder = new ElementHolder(); | |
| 2520 bool wasInFunction = _inFunction; | |
| 2521 _inFunction = true; | |
| 2522 try { | |
| 2523 _visitChildren(holder, node); | |
| 2524 } finally { | |
| 2525 _inFunction = wasInFunction; | |
| 2526 } | |
| 2527 FunctionBody body = node.body; | |
| 2528 SimpleIdentifier constructorName = node.name; | |
| 2529 ConstructorElementImpl element = | |
| 2530 new ConstructorElementImpl.forNode(constructorName); | |
| 2531 if (node.externalKeyword != null) { | |
| 2532 element.external = true; | |
| 2533 } | |
| 2534 if (node.factoryKeyword != null) { | |
| 2535 element.factory = true; | |
| 2536 } | |
| 2537 element.functions = holder.functions; | |
| 2538 element.labels = holder.labels; | |
| 2539 element.localVariables = holder.localVariables; | |
| 2540 element.parameters = holder.parameters; | |
| 2541 element.const2 = node.constKeyword != null; | |
| 2542 if (body.isAsynchronous) { | |
| 2543 element.asynchronous = true; | |
| 2544 } | |
| 2545 if (body.isGenerator) { | |
| 2546 element.generator = true; | |
| 2547 } | |
| 2548 _currentHolder.addConstructor(element); | |
| 2549 node.element = element; | |
| 2550 if (constructorName == null) { | |
| 2551 Identifier returnType = node.returnType; | |
| 2552 if (returnType != null) { | |
| 2553 element.nameOffset = returnType.offset; | |
| 2554 element.nameEnd = returnType.end; | |
| 2555 } | |
| 2556 } else { | |
| 2557 constructorName.staticElement = element; | |
| 2558 element.periodOffset = node.period.offset; | |
| 2559 element.nameEnd = constructorName.end; | |
| 2560 } | |
| 2561 holder.validate(); | |
| 2562 return null; | |
| 2563 } | |
| 2564 | |
| 2565 @override | |
| 2566 Object visitDeclaredIdentifier(DeclaredIdentifier node) { | |
| 2567 SimpleIdentifier variableName = node.identifier; | |
| 2568 LocalVariableElementImpl element = | |
| 2569 new LocalVariableElementImpl.forNode(variableName); | |
| 2570 ForEachStatement statement = node.parent as ForEachStatement; | |
| 2571 int declarationEnd = node.offset + node.length; | |
| 2572 int statementEnd = statement.offset + statement.length; | |
| 2573 element.setVisibleRange(declarationEnd, statementEnd - declarationEnd - 1); | |
| 2574 element.const3 = node.isConst; | |
| 2575 element.final2 = node.isFinal; | |
| 2576 _currentHolder.addLocalVariable(element); | |
| 2577 variableName.staticElement = element; | |
| 2578 return super.visitDeclaredIdentifier(node); | |
| 2579 } | |
| 2580 | |
| 2581 @override | |
| 2582 Object visitDefaultFormalParameter(DefaultFormalParameter node) { | |
| 2583 ElementHolder holder = new ElementHolder(); | |
| 2584 NormalFormalParameter normalParameter = node.parameter; | |
| 2585 SimpleIdentifier parameterName = normalParameter.identifier; | |
| 2586 ParameterElementImpl parameter; | |
| 2587 if (normalParameter is FieldFormalParameter) { | |
| 2588 parameter = new DefaultFieldFormalParameterElementImpl(parameterName); | |
| 2589 FieldElement field = | |
| 2590 _fieldMap == null ? null : _fieldMap[parameterName.name]; | |
| 2591 if (field != null) { | |
| 2592 (parameter as DefaultFieldFormalParameterElementImpl).field = field; | |
| 2593 } | |
| 2594 } else { | |
| 2595 parameter = new DefaultParameterElementImpl(parameterName); | |
| 2596 } | |
| 2597 parameter.const3 = node.isConst; | |
| 2598 parameter.final2 = node.isFinal; | |
| 2599 parameter.parameterKind = node.kind; | |
| 2600 // set initializer, default value range | |
| 2601 Expression defaultValue = node.defaultValue; | |
| 2602 if (defaultValue != null) { | |
| 2603 _visit(holder, defaultValue); | |
| 2604 FunctionElementImpl initializer = | |
| 2605 new FunctionElementImpl.forOffset(defaultValue.beginToken.offset); | |
| 2606 initializer.functions = holder.functions; | |
| 2607 initializer.labels = holder.labels; | |
| 2608 initializer.localVariables = holder.localVariables; | |
| 2609 initializer.parameters = holder.parameters; | |
| 2610 initializer.synthetic = true; | |
| 2611 parameter.initializer = initializer; | |
| 2612 parameter.defaultValueCode = defaultValue.toSource(); | |
| 2613 } | |
| 2614 // visible range | |
| 2615 _setParameterVisibleRange(node, parameter); | |
| 2616 _currentHolder.addParameter(parameter); | |
| 2617 parameterName.staticElement = parameter; | |
| 2618 normalParameter.accept(this); | |
| 2619 holder.validate(); | |
| 2620 return null; | |
| 2621 } | |
| 2622 | |
| 2623 @override | |
| 2624 Object visitEnumDeclaration(EnumDeclaration node) { | |
| 2625 SimpleIdentifier enumName = node.name; | |
| 2626 ClassElementImpl enumElement = new ClassElementImpl.forNode(enumName); | |
| 2627 enumElement.enum2 = true; | |
| 2628 InterfaceTypeImpl enumType = new InterfaceTypeImpl(enumElement); | |
| 2629 enumElement.type = enumType; | |
| 2630 // The equivalent code for enums in the spec shows a single constructor, | |
| 2631 // but that constructor is not callable (since it is a compile-time error | |
| 2632 // to subclass, mix-in, implement, or explicitly instantiate an enum). So | |
| 2633 // we represent this as having no constructors. | |
| 2634 enumElement.constructors = ConstructorElement.EMPTY_LIST; | |
| 2635 _currentHolder.addEnum(enumElement); | |
| 2636 enumName.staticElement = enumElement; | |
| 2637 return super.visitEnumDeclaration(node); | |
| 2638 } | |
| 2639 | |
| 2640 @override | |
| 2641 Object visitFieldDeclaration(FieldDeclaration node) { | |
| 2642 bool wasInField = _inFieldContext; | |
| 2643 _inFieldContext = true; | |
| 2644 try { | |
| 2645 node.visitChildren(this); | |
| 2646 } finally { | |
| 2647 _inFieldContext = wasInField; | |
| 2648 } | |
| 2649 return null; | |
| 2650 } | |
| 2651 | |
| 2652 @override | |
| 2653 Object visitFieldFormalParameter(FieldFormalParameter node) { | |
| 2654 if (node.parent is! DefaultFormalParameter) { | |
| 2655 SimpleIdentifier parameterName = node.identifier; | |
| 2656 FieldElement field = | |
| 2657 _fieldMap == null ? null : _fieldMap[parameterName.name]; | |
| 2658 FieldFormalParameterElementImpl parameter = | |
| 2659 new FieldFormalParameterElementImpl(parameterName); | |
| 2660 parameter.const3 = node.isConst; | |
| 2661 parameter.final2 = node.isFinal; | |
| 2662 parameter.parameterKind = node.kind; | |
| 2663 if (field != null) { | |
| 2664 parameter.field = field; | |
| 2665 } | |
| 2666 _currentHolder.addParameter(parameter); | |
| 2667 parameterName.staticElement = parameter; | |
| 2668 } | |
| 2669 // | |
| 2670 // The children of this parameter include any parameters defined on the type | |
| 2671 // of this parameter. | |
| 2672 // | |
| 2673 ElementHolder holder = new ElementHolder(); | |
| 2674 _visitChildren(holder, node); | |
| 2675 ParameterElementImpl element = node.element; | |
| 2676 element.parameters = holder.parameters; | |
| 2677 element.typeParameters = holder.typeParameters; | |
| 2678 holder.validate(); | |
| 2679 return null; | |
| 2680 } | |
| 2681 | |
| 2682 @override | |
| 2683 Object visitFunctionDeclaration(FunctionDeclaration node) { | |
| 2684 FunctionExpression expression = node.functionExpression; | |
| 2685 if (expression != null) { | |
| 2686 ElementHolder holder = new ElementHolder(); | |
| 2687 bool wasInFunction = _inFunction; | |
| 2688 _inFunction = true; | |
| 2689 try { | |
| 2690 _visitChildren(holder, node); | |
| 2691 } finally { | |
| 2692 _inFunction = wasInFunction; | |
| 2693 } | |
| 2694 FunctionBody body = expression.body; | |
| 2695 sc.Token property = node.propertyKeyword; | |
| 2696 if (property == null || _inFunction) { | |
| 2697 SimpleIdentifier functionName = node.name; | |
| 2698 FunctionElementImpl element = | |
| 2699 new FunctionElementImpl.forNode(functionName); | |
| 2700 if (node.externalKeyword != null) { | |
| 2701 element.external = true; | |
| 2702 } | |
| 2703 element.functions = holder.functions; | |
| 2704 element.labels = holder.labels; | |
| 2705 element.localVariables = holder.localVariables; | |
| 2706 element.parameters = holder.parameters; | |
| 2707 element.typeParameters = holder.typeParameters; | |
| 2708 if (body.isAsynchronous) { | |
| 2709 element.asynchronous = true; | |
| 2710 } | |
| 2711 if (body.isGenerator) { | |
| 2712 element.generator = true; | |
| 2713 } | |
| 2714 if (_inFunction) { | |
| 2715 Block enclosingBlock = node.getAncestor((node) => node is Block); | |
| 2716 if (enclosingBlock != null) { | |
| 2717 int functionEnd = node.offset + node.length; | |
| 2718 int blockEnd = enclosingBlock.offset + enclosingBlock.length; | |
| 2719 element.setVisibleRange(functionEnd, blockEnd - functionEnd - 1); | |
| 2720 } | |
| 2721 } | |
| 2722 _currentHolder.addFunction(element); | |
| 2723 expression.element = element; | |
| 2724 functionName.staticElement = element; | |
| 2725 } else { | |
| 2726 SimpleIdentifier propertyNameNode = node.name; | |
| 2727 if (propertyNameNode == null) { | |
| 2728 // TODO(brianwilkerson) Report this internal error. | |
| 2729 return null; | |
| 2730 } | |
| 2731 String propertyName = propertyNameNode.name; | |
| 2732 TopLevelVariableElementImpl variable = _currentHolder | |
| 2733 .getTopLevelVariable(propertyName) as TopLevelVariableElementImpl; | |
| 2734 if (variable == null) { | |
| 2735 variable = new TopLevelVariableElementImpl(node.name.name, -1); | |
| 2736 variable.final2 = true; | |
| 2737 variable.synthetic = true; | |
| 2738 _currentHolder.addTopLevelVariable(variable); | |
| 2739 } | |
| 2740 if (node.isGetter) { | |
| 2741 PropertyAccessorElementImpl getter = | |
| 2742 new PropertyAccessorElementImpl.forNode(propertyNameNode); | |
| 2743 if (node.externalKeyword != null) { | |
| 2744 getter.external = true; | |
| 2745 } | |
| 2746 getter.functions = holder.functions; | |
| 2747 getter.labels = holder.labels; | |
| 2748 getter.localVariables = holder.localVariables; | |
| 2749 if (body.isAsynchronous) { | |
| 2750 getter.asynchronous = true; | |
| 2751 } | |
| 2752 if (body.isGenerator) { | |
| 2753 getter.generator = true; | |
| 2754 } | |
| 2755 getter.variable = variable; | |
| 2756 getter.getter = true; | |
| 2757 getter.static = true; | |
| 2758 variable.getter = getter; | |
| 2759 _currentHolder.addAccessor(getter); | |
| 2760 expression.element = getter; | |
| 2761 propertyNameNode.staticElement = getter; | |
| 2762 } else { | |
| 2763 PropertyAccessorElementImpl setter = | |
| 2764 new PropertyAccessorElementImpl.forNode(propertyNameNode); | |
| 2765 if (node.externalKeyword != null) { | |
| 2766 setter.external = true; | |
| 2767 } | |
| 2768 setter.functions = holder.functions; | |
| 2769 setter.labels = holder.labels; | |
| 2770 setter.localVariables = holder.localVariables; | |
| 2771 setter.parameters = holder.parameters; | |
| 2772 if (body.isAsynchronous) { | |
| 2773 setter.asynchronous = true; | |
| 2774 } | |
| 2775 if (body.isGenerator) { | |
| 2776 setter.generator = true; | |
| 2777 } | |
| 2778 setter.variable = variable; | |
| 2779 setter.setter = true; | |
| 2780 setter.static = true; | |
| 2781 variable.setter = setter; | |
| 2782 variable.final2 = false; | |
| 2783 _currentHolder.addAccessor(setter); | |
| 2784 expression.element = setter; | |
| 2785 propertyNameNode.staticElement = setter; | |
| 2786 } | |
| 2787 } | |
| 2788 holder.validate(); | |
| 2789 } | |
| 2790 return null; | |
| 2791 } | |
| 2792 | |
| 2793 @override | |
| 2794 Object visitFunctionExpression(FunctionExpression node) { | |
| 2795 if (node.parent is FunctionDeclaration) { | |
| 2796 // visitFunctionDeclaration has already created the element for the | |
| 2797 // declaration. We just need to visit children. | |
| 2798 return super.visitFunctionExpression(node); | |
| 2799 } | |
| 2800 ElementHolder holder = new ElementHolder(); | |
| 2801 bool wasInFunction = _inFunction; | |
| 2802 _inFunction = true; | |
| 2803 try { | |
| 2804 _visitChildren(holder, node); | |
| 2805 } finally { | |
| 2806 _inFunction = wasInFunction; | |
| 2807 } | |
| 2808 FunctionBody body = node.body; | |
| 2809 FunctionElementImpl element = | |
| 2810 new FunctionElementImpl.forOffset(node.beginToken.offset); | |
| 2811 element.functions = holder.functions; | |
| 2812 element.labels = holder.labels; | |
| 2813 element.localVariables = holder.localVariables; | |
| 2814 element.parameters = holder.parameters; | |
| 2815 element.typeParameters = holder.typeParameters; | |
| 2816 if (body.isAsynchronous) { | |
| 2817 element.asynchronous = true; | |
| 2818 } | |
| 2819 if (body.isGenerator) { | |
| 2820 element.generator = true; | |
| 2821 } | |
| 2822 if (_inFunction) { | |
| 2823 Block enclosingBlock = node.getAncestor((node) => node is Block); | |
| 2824 if (enclosingBlock != null) { | |
| 2825 int functionEnd = node.offset + node.length; | |
| 2826 int blockEnd = enclosingBlock.offset + enclosingBlock.length; | |
| 2827 element.setVisibleRange(functionEnd, blockEnd - functionEnd - 1); | |
| 2828 } | |
| 2829 } | |
| 2830 FunctionTypeImpl type = new FunctionTypeImpl(element); | |
| 2831 if (_functionTypesToFix != null) { | |
| 2832 _functionTypesToFix.add(type); | |
| 2833 } | |
| 2834 element.type = type; | |
| 2835 _currentHolder.addFunction(element); | |
| 2836 node.element = element; | |
| 2837 holder.validate(); | |
| 2838 return null; | |
| 2839 } | |
| 2840 | |
| 2841 @override | |
| 2842 Object visitFunctionTypeAlias(FunctionTypeAlias node) { | |
| 2843 ElementHolder holder = new ElementHolder(); | |
| 2844 _visitChildren(holder, node); | |
| 2845 SimpleIdentifier aliasName = node.name; | |
| 2846 List<ParameterElement> parameters = holder.parameters; | |
| 2847 List<TypeParameterElement> typeParameters = holder.typeParameters; | |
| 2848 FunctionTypeAliasElementImpl element = | |
| 2849 new FunctionTypeAliasElementImpl.forNode(aliasName); | |
| 2850 element.parameters = parameters; | |
| 2851 element.typeParameters = typeParameters; | |
| 2852 FunctionTypeImpl type = new FunctionTypeImpl.forTypedef(element); | |
| 2853 type.typeArguments = _createTypeParameterTypes(typeParameters); | |
| 2854 element.type = type; | |
| 2855 _currentHolder.addTypeAlias(element); | |
| 2856 aliasName.staticElement = element; | |
| 2857 holder.validate(); | |
| 2858 return null; | |
| 2859 } | |
| 2860 | |
| 2861 @override | |
| 2862 Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { | |
| 2863 if (node.parent is! DefaultFormalParameter) { | |
| 2864 SimpleIdentifier parameterName = node.identifier; | |
| 2865 ParameterElementImpl parameter = | |
| 2866 new ParameterElementImpl.forNode(parameterName); | |
| 2867 parameter.parameterKind = node.kind; | |
| 2868 _setParameterVisibleRange(node, parameter); | |
| 2869 _currentHolder.addParameter(parameter); | |
| 2870 parameterName.staticElement = parameter; | |
| 2871 } | |
| 2872 // | |
| 2873 // The children of this parameter include any parameters defined on the type | |
| 2874 //of this parameter. | |
| 2875 // | |
| 2876 ElementHolder holder = new ElementHolder(); | |
| 2877 _visitChildren(holder, node); | |
| 2878 ParameterElementImpl element = node.element; | |
| 2879 element.parameters = holder.parameters; | |
| 2880 element.typeParameters = holder.typeParameters; | |
| 2881 holder.validate(); | |
| 2882 return null; | |
| 2883 } | |
| 2884 | |
| 2885 @override | |
| 2886 Object visitLabeledStatement(LabeledStatement node) { | |
| 2887 bool onSwitchStatement = node.statement is SwitchStatement; | |
| 2888 for (Label label in node.labels) { | |
| 2889 SimpleIdentifier labelName = label.label; | |
| 2890 LabelElementImpl element = | |
| 2891 new LabelElementImpl(labelName, onSwitchStatement, false); | |
| 2892 _currentHolder.addLabel(element); | |
| 2893 labelName.staticElement = element; | |
| 2894 } | |
| 2895 return super.visitLabeledStatement(node); | |
| 2896 } | |
| 2897 | |
| 2898 @override | |
| 2899 Object visitMethodDeclaration(MethodDeclaration node) { | |
| 2900 try { | |
| 2901 ElementHolder holder = new ElementHolder(); | |
| 2902 bool wasInFunction = _inFunction; | |
| 2903 _inFunction = true; | |
| 2904 try { | |
| 2905 _visitChildren(holder, node); | |
| 2906 } finally { | |
| 2907 _inFunction = wasInFunction; | |
| 2908 } | |
| 2909 bool isStatic = node.isStatic; | |
| 2910 sc.Token property = node.propertyKeyword; | |
| 2911 FunctionBody body = node.body; | |
| 2912 if (property == null) { | |
| 2913 SimpleIdentifier methodName = node.name; | |
| 2914 String nameOfMethod = methodName.name; | |
| 2915 if (nameOfMethod == sc.TokenType.MINUS.lexeme && | |
| 2916 node.parameters.parameters.length == 0) { | |
| 2917 nameOfMethod = "unary-"; | |
| 2918 } | |
| 2919 MethodElementImpl element = | |
| 2920 new MethodElementImpl(nameOfMethod, methodName.offset); | |
| 2921 element.abstract = node.isAbstract; | |
| 2922 if (node.externalKeyword != null) { | |
| 2923 element.external = true; | |
| 2924 } | |
| 2925 element.functions = holder.functions; | |
| 2926 element.labels = holder.labels; | |
| 2927 element.localVariables = holder.localVariables; | |
| 2928 element.parameters = holder.parameters; | |
| 2929 element.static = isStatic; | |
| 2930 element.typeParameters = holder.typeParameters; | |
| 2931 if (body.isAsynchronous) { | |
| 2932 element.asynchronous = true; | |
| 2933 } | |
| 2934 if (body.isGenerator) { | |
| 2935 element.generator = true; | |
| 2936 } | |
| 2937 _currentHolder.addMethod(element); | |
| 2938 methodName.staticElement = element; | |
| 2939 } else { | |
| 2940 SimpleIdentifier propertyNameNode = node.name; | |
| 2941 String propertyName = propertyNameNode.name; | |
| 2942 FieldElementImpl field = | |
| 2943 _currentHolder.getField(propertyName) as FieldElementImpl; | |
| 2944 if (field == null) { | |
| 2945 field = new FieldElementImpl(node.name.name, -1); | |
| 2946 field.final2 = true; | |
| 2947 field.static = isStatic; | |
| 2948 field.synthetic = true; | |
| 2949 _currentHolder.addField(field); | |
| 2950 } | |
| 2951 if (node.isGetter) { | |
| 2952 PropertyAccessorElementImpl getter = | |
| 2953 new PropertyAccessorElementImpl.forNode(propertyNameNode); | |
| 2954 if (node.externalKeyword != null) { | |
| 2955 getter.external = true; | |
| 2956 } | |
| 2957 getter.functions = holder.functions; | |
| 2958 getter.labels = holder.labels; | |
| 2959 getter.localVariables = holder.localVariables; | |
| 2960 if (body.isAsynchronous) { | |
| 2961 getter.asynchronous = true; | |
| 2962 } | |
| 2963 if (body.isGenerator) { | |
| 2964 getter.generator = true; | |
| 2965 } | |
| 2966 getter.variable = field; | |
| 2967 getter.abstract = node.isAbstract; | |
| 2968 getter.getter = true; | |
| 2969 getter.static = isStatic; | |
| 2970 field.getter = getter; | |
| 2971 _currentHolder.addAccessor(getter); | |
| 2972 propertyNameNode.staticElement = getter; | |
| 2973 } else { | |
| 2974 PropertyAccessorElementImpl setter = | |
| 2975 new PropertyAccessorElementImpl.forNode(propertyNameNode); | |
| 2976 if (node.externalKeyword != null) { | |
| 2977 setter.external = true; | |
| 2978 } | |
| 2979 setter.functions = holder.functions; | |
| 2980 setter.labels = holder.labels; | |
| 2981 setter.localVariables = holder.localVariables; | |
| 2982 setter.parameters = holder.parameters; | |
| 2983 if (body.isAsynchronous) { | |
| 2984 setter.asynchronous = true; | |
| 2985 } | |
| 2986 if (body.isGenerator) { | |
| 2987 setter.generator = true; | |
| 2988 } | |
| 2989 setter.variable = field; | |
| 2990 setter.abstract = node.isAbstract; | |
| 2991 setter.setter = true; | |
| 2992 setter.static = isStatic; | |
| 2993 field.setter = setter; | |
| 2994 field.final2 = false; | |
| 2995 _currentHolder.addAccessor(setter); | |
| 2996 propertyNameNode.staticElement = setter; | |
| 2997 } | |
| 2998 } | |
| 2999 holder.validate(); | |
| 3000 } catch (exception, stackTrace) { | |
| 3001 if (node.name.staticElement == null) { | |
| 3002 ClassDeclaration classNode = | |
| 3003 node.getAncestor((node) => node is ClassDeclaration); | |
| 3004 StringBuffer buffer = new StringBuffer(); | |
| 3005 buffer.write("The element for the method "); | |
| 3006 buffer.write(node.name); | |
| 3007 buffer.write(" in "); | |
| 3008 buffer.write(classNode.name); | |
| 3009 buffer.write(" was not set while trying to build the element model."); | |
| 3010 AnalysisEngine.instance.logger.logError( | |
| 3011 buffer.toString(), new CaughtException(exception, stackTrace)); | |
| 3012 } else { | |
| 3013 String message = | |
| 3014 "Exception caught in ElementBuilder.visitMethodDeclaration()"; | |
| 3015 AnalysisEngine.instance.logger.logError( | |
| 3016 message, new CaughtException(exception, stackTrace)); | |
| 3017 } | |
| 3018 } finally { | |
| 3019 if (node.name.staticElement == null) { | |
| 3020 ClassDeclaration classNode = | |
| 3021 node.getAncestor((node) => node is ClassDeclaration); | |
| 3022 StringBuffer buffer = new StringBuffer(); | |
| 3023 buffer.write("The element for the method "); | |
| 3024 buffer.write(node.name); | |
| 3025 buffer.write(" in "); | |
| 3026 buffer.write(classNode.name); | |
| 3027 buffer.write(" was not set while trying to resolve types."); | |
| 3028 AnalysisEngine.instance.logger.logError(buffer.toString(), | |
| 3029 new CaughtException( | |
| 3030 new AnalysisException(buffer.toString()), null)); | |
| 3031 } | |
| 3032 } | |
| 3033 return null; | |
| 3034 } | |
| 3035 | |
| 3036 @override | |
| 3037 Object visitSimpleFormalParameter(SimpleFormalParameter node) { | |
| 3038 if (node.parent is! DefaultFormalParameter) { | |
| 3039 SimpleIdentifier parameterName = node.identifier; | |
| 3040 ParameterElementImpl parameter = | |
| 3041 new ParameterElementImpl.forNode(parameterName); | |
| 3042 parameter.const3 = node.isConst; | |
| 3043 parameter.final2 = node.isFinal; | |
| 3044 parameter.parameterKind = node.kind; | |
| 3045 _setParameterVisibleRange(node, parameter); | |
| 3046 _currentHolder.addParameter(parameter); | |
| 3047 parameterName.staticElement = parameter; | |
| 3048 } | |
| 3049 return super.visitSimpleFormalParameter(node); | |
| 3050 } | |
| 3051 | |
| 3052 @override | |
| 3053 Object visitSuperExpression(SuperExpression node) { | |
| 3054 _isValidMixin = false; | |
| 3055 return super.visitSuperExpression(node); | |
| 3056 } | |
| 3057 | |
| 3058 @override | |
| 3059 Object visitSwitchCase(SwitchCase node) { | |
| 3060 for (Label label in node.labels) { | |
| 3061 SimpleIdentifier labelName = label.label; | |
| 3062 LabelElementImpl element = new LabelElementImpl(labelName, false, true); | |
| 3063 _currentHolder.addLabel(element); | |
| 3064 labelName.staticElement = element; | |
| 3065 } | |
| 3066 return super.visitSwitchCase(node); | |
| 3067 } | |
| 3068 | |
| 3069 @override | |
| 3070 Object visitSwitchDefault(SwitchDefault node) { | |
| 3071 for (Label label in node.labels) { | |
| 3072 SimpleIdentifier labelName = label.label; | |
| 3073 LabelElementImpl element = new LabelElementImpl(labelName, false, true); | |
| 3074 _currentHolder.addLabel(element); | |
| 3075 labelName.staticElement = element; | |
| 3076 } | |
| 3077 return super.visitSwitchDefault(node); | |
| 3078 } | |
| 3079 | |
| 3080 @override | |
| 3081 Object visitTypeParameter(TypeParameter node) { | |
| 3082 SimpleIdentifier parameterName = node.name; | |
| 3083 TypeParameterElementImpl typeParameter = | |
| 3084 new TypeParameterElementImpl.forNode(parameterName); | |
| 3085 TypeParameterTypeImpl typeParameterType = | |
| 3086 new TypeParameterTypeImpl(typeParameter); | |
| 3087 typeParameter.type = typeParameterType; | |
| 3088 _currentHolder.addTypeParameter(typeParameter); | |
| 3089 parameterName.staticElement = typeParameter; | |
| 3090 return super.visitTypeParameter(node); | |
| 3091 } | |
| 3092 | |
| 3093 @override | |
| 3094 Object visitVariableDeclaration(VariableDeclaration node) { | |
| 3095 bool isConst = node.isConst; | |
| 3096 bool isFinal = node.isFinal; | |
| 3097 bool hasInitializer = node.initializer != null; | |
| 3098 VariableElementImpl element; | |
| 3099 if (_inFieldContext) { | |
| 3100 SimpleIdentifier fieldName = node.name; | |
| 3101 FieldElementImpl field; | |
| 3102 if ((isConst || isFinal) && hasInitializer) { | |
| 3103 field = new ConstFieldElementImpl.forNode(fieldName); | |
| 3104 } else { | |
| 3105 field = new FieldElementImpl.forNode(fieldName); | |
| 3106 } | |
| 3107 element = field; | |
| 3108 _currentHolder.addField(field); | |
| 3109 fieldName.staticElement = field; | |
| 3110 } else if (_inFunction) { | |
| 3111 SimpleIdentifier variableName = node.name; | |
| 3112 LocalVariableElementImpl variable; | |
| 3113 if (isConst && hasInitializer) { | |
| 3114 variable = new ConstLocalVariableElementImpl.forNode(variableName); | |
| 3115 } else { | |
| 3116 variable = new LocalVariableElementImpl.forNode(variableName); | |
| 3117 } | |
| 3118 element = variable; | |
| 3119 Block enclosingBlock = node.getAncestor((node) => node is Block); | |
| 3120 // TODO(brianwilkerson) This isn't right for variables declared in a for | |
| 3121 // loop. | |
| 3122 variable.setVisibleRange(enclosingBlock.offset, enclosingBlock.length); | |
| 3123 _currentHolder.addLocalVariable(variable); | |
| 3124 variableName.staticElement = element; | |
| 3125 } else { | |
| 3126 SimpleIdentifier variableName = node.name; | |
| 3127 TopLevelVariableElementImpl variable; | |
| 3128 if (isConst && hasInitializer) { | |
| 3129 variable = new ConstTopLevelVariableElementImpl(variableName); | |
| 3130 } else { | |
| 3131 variable = new TopLevelVariableElementImpl.forNode(variableName); | |
| 3132 } | |
| 3133 element = variable; | |
| 3134 _currentHolder.addTopLevelVariable(variable); | |
| 3135 variableName.staticElement = element; | |
| 3136 } | |
| 3137 element.const3 = isConst; | |
| 3138 element.final2 = isFinal; | |
| 3139 if (hasInitializer) { | |
| 3140 ElementHolder holder = new ElementHolder(); | |
| 3141 bool wasInFieldContext = _inFieldContext; | |
| 3142 _inFieldContext = false; | |
| 3143 try { | |
| 3144 _visit(holder, node.initializer); | |
| 3145 } finally { | |
| 3146 _inFieldContext = wasInFieldContext; | |
| 3147 } | |
| 3148 FunctionElementImpl initializer = | |
| 3149 new FunctionElementImpl.forOffset(node.initializer.beginToken.offset); | |
| 3150 initializer.functions = holder.functions; | |
| 3151 initializer.labels = holder.labels; | |
| 3152 initializer.localVariables = holder.localVariables; | |
| 3153 initializer.synthetic = true; | |
| 3154 element.initializer = initializer; | |
| 3155 holder.validate(); | |
| 3156 } | |
| 3157 if (element is PropertyInducingElementImpl) { | |
| 3158 if (_inFieldContext) { | |
| 3159 (element as FieldElementImpl).static = | |
| 3160 (node.parent.parent as FieldDeclaration).isStatic; | |
| 3161 } | |
| 3162 PropertyAccessorElementImpl getter = | |
| 3163 new PropertyAccessorElementImpl.forVariable(element); | |
| 3164 getter.getter = true; | |
| 3165 _currentHolder.addAccessor(getter); | |
| 3166 element.getter = getter; | |
| 3167 if (!isConst && !isFinal) { | |
| 3168 PropertyAccessorElementImpl setter = | |
| 3169 new PropertyAccessorElementImpl.forVariable(element); | |
| 3170 setter.setter = true; | |
| 3171 ParameterElementImpl parameter = | |
| 3172 new ParameterElementImpl("_${element.name}", element.nameOffset); | |
| 3173 parameter.synthetic = true; | |
| 3174 parameter.parameterKind = ParameterKind.REQUIRED; | |
| 3175 setter.parameters = <ParameterElement>[parameter]; | |
| 3176 _currentHolder.addAccessor(setter); | |
| 3177 element.setter = setter; | |
| 3178 } | |
| 3179 } | |
| 3180 return null; | |
| 3181 } | |
| 3182 | |
| 3183 /** | |
| 3184 * Build the table mapping field names to field elements for the fields define
d in the current | |
| 3185 * class. | |
| 3186 * | |
| 3187 * @param fields the field elements defined in the current class | |
| 3188 */ | |
| 3189 void _buildFieldMap(List<FieldElement> fields) { | |
| 3190 _fieldMap = new HashMap<String, FieldElement>(); | |
| 3191 int count = fields.length; | |
| 3192 for (int i = 0; i < count; i++) { | |
| 3193 FieldElement field = fields[i]; | |
| 3194 _fieldMap[field.name] = field; | |
| 3195 } | |
| 3196 } | |
| 3197 | |
| 3198 /** | |
| 3199 * Creates the [ConstructorElement]s array with the single default constructor
element. | |
| 3200 * | |
| 3201 * @param interfaceType the interface type for which to create a default const
ructor | |
| 3202 * @return the [ConstructorElement]s array with the single default constructor
element | |
| 3203 */ | |
| 3204 List<ConstructorElement> _createDefaultConstructors( | |
| 3205 InterfaceTypeImpl interfaceType) { | |
| 3206 ConstructorElementImpl constructor = | |
| 3207 new ConstructorElementImpl.forNode(null); | |
| 3208 constructor.synthetic = true; | |
| 3209 constructor.returnType = interfaceType; | |
| 3210 FunctionTypeImpl type = new FunctionTypeImpl(constructor); | |
| 3211 _functionTypesToFix.add(type); | |
| 3212 constructor.type = type; | |
| 3213 return <ConstructorElement>[constructor]; | |
| 3214 } | |
| 3215 | |
| 3216 /** | |
| 3217 * Create the types associated with the given type parameters, setting the typ
e of each type | |
| 3218 * parameter, and return an array of types corresponding to the given paramete
rs. | |
| 3219 * | |
| 3220 * @param typeParameters the type parameters for which types are to be created | |
| 3221 * @return an array of types corresponding to the given parameters | |
| 3222 */ | |
| 3223 List<DartType> _createTypeParameterTypes( | |
| 3224 List<TypeParameterElement> typeParameters) { | |
| 3225 int typeParameterCount = typeParameters.length; | |
| 3226 List<DartType> typeArguments = new List<DartType>(typeParameterCount); | |
| 3227 for (int i = 0; i < typeParameterCount; i++) { | |
| 3228 TypeParameterElementImpl typeParameter = | |
| 3229 typeParameters[i] as TypeParameterElementImpl; | |
| 3230 TypeParameterTypeImpl typeParameterType = | |
| 3231 new TypeParameterTypeImpl(typeParameter); | |
| 3232 typeParameter.type = typeParameterType; | |
| 3233 typeArguments[i] = typeParameterType; | |
| 3234 } | |
| 3235 return typeArguments; | |
| 3236 } | |
| 3237 | |
| 3238 /** | |
| 3239 * Return the body of the function that contains the given parameter, or `null
` if no | |
| 3240 * function body could be found. | |
| 3241 * | |
| 3242 * @param node the parameter contained in the function whose body is to be ret
urned | |
| 3243 * @return the body of the function that contains the given parameter | |
| 3244 */ | |
| 3245 FunctionBody _getFunctionBody(FormalParameter node) { | |
| 3246 AstNode parent = node.parent; | |
| 3247 while (parent != null) { | |
| 3248 if (parent is ConstructorDeclaration) { | |
| 3249 return parent.body; | |
| 3250 } else if (parent is FunctionExpression) { | |
| 3251 return parent.body; | |
| 3252 } else if (parent is MethodDeclaration) { | |
| 3253 return parent.body; | |
| 3254 } | |
| 3255 parent = parent.parent; | |
| 3256 } | |
| 3257 return null; | |
| 3258 } | |
| 3259 | |
| 3260 /** | |
| 3261 * Sets the visible source range for formal parameter. | |
| 3262 */ | |
| 3263 void _setParameterVisibleRange( | |
| 3264 FormalParameter node, ParameterElementImpl element) { | |
| 3265 FunctionBody body = _getFunctionBody(node); | |
| 3266 if (body != null) { | |
| 3267 element.setVisibleRange(body.offset, body.length); | |
| 3268 } | |
| 3269 } | |
| 3270 | |
| 3271 /** | |
| 3272 * Make the given holder be the current holder while visiting the given node. | |
| 3273 * | |
| 3274 * @param holder the holder that will gather elements that are built while vis
iting the children | |
| 3275 * @param node the node to be visited | |
| 3276 */ | |
| 3277 void _visit(ElementHolder holder, AstNode node) { | |
| 3278 if (node != null) { | |
| 3279 ElementHolder previousHolder = _currentHolder; | |
| 3280 _currentHolder = holder; | |
| 3281 try { | |
| 3282 node.accept(this); | |
| 3283 } finally { | |
| 3284 _currentHolder = previousHolder; | |
| 3285 } | |
| 3286 } | |
| 3287 } | |
| 3288 | |
| 3289 /** | |
| 3290 * Make the given holder be the current holder while visiting the children of
the given node. | |
| 3291 * | |
| 3292 * @param holder the holder that will gather elements that are built while vis
iting the children | |
| 3293 * @param node the node whose children are to be visited | |
| 3294 */ | |
| 3295 void _visitChildren(ElementHolder holder, AstNode node) { | |
| 3296 if (node != null) { | |
| 3297 ElementHolder previousHolder = _currentHolder; | |
| 3298 _currentHolder = holder; | |
| 3299 try { | |
| 3300 node.visitChildren(this); | |
| 3301 } finally { | |
| 3302 _currentHolder = previousHolder; | |
| 3303 } | |
| 3304 } | |
| 3305 } | |
| 3306 } | |
| 3307 | |
| 3308 /** | |
| 3309 * Instances of the class `ElementHolder` hold on to elements created while trav
ersing an AST | |
| 3310 * structure so that they can be accessed when creating their enclosing element. | |
| 3311 */ | |
| 3312 class ElementHolder { | |
| 3313 List<PropertyAccessorElement> _accessors; | |
| 3314 | |
| 3315 List<ConstructorElement> _constructors; | |
| 3316 | |
| 3317 List<ClassElement> _enums; | |
| 3318 | |
| 3319 List<FieldElement> _fields; | |
| 3320 | |
| 3321 List<FunctionElement> _functions; | |
| 3322 | |
| 3323 List<LabelElement> _labels; | |
| 3324 | |
| 3325 List<LocalVariableElement> _localVariables; | |
| 3326 | |
| 3327 List<MethodElement> _methods; | |
| 3328 | |
| 3329 List<ParameterElement> _parameters; | |
| 3330 | |
| 3331 List<TopLevelVariableElement> _topLevelVariables; | |
| 3332 | |
| 3333 List<ClassElement> _types; | |
| 3334 | |
| 3335 List<FunctionTypeAliasElement> _typeAliases; | |
| 3336 | |
| 3337 List<TypeParameterElement> _typeParameters; | |
| 3338 | |
| 3339 List<PropertyAccessorElement> get accessors { | |
| 3340 if (_accessors == null) { | |
| 3341 return PropertyAccessorElement.EMPTY_LIST; | |
| 3342 } | |
| 3343 List<PropertyAccessorElement> result = _accessors; | |
| 3344 _accessors = null; | |
| 3345 return result; | |
| 3346 } | |
| 3347 | |
| 3348 List<ConstructorElement> get constructors { | |
| 3349 if (_constructors == null) { | |
| 3350 return ConstructorElement.EMPTY_LIST; | |
| 3351 } | |
| 3352 List<ConstructorElement> result = _constructors; | |
| 3353 _constructors = null; | |
| 3354 return result; | |
| 3355 } | |
| 3356 | |
| 3357 List<ClassElement> get enums { | |
| 3358 if (_enums == null) { | |
| 3359 return ClassElement.EMPTY_LIST; | |
| 3360 } | |
| 3361 List<ClassElement> result = _enums; | |
| 3362 _enums = null; | |
| 3363 return result; | |
| 3364 } | |
| 3365 | |
| 3366 List<FieldElement> get fields { | |
| 3367 if (_fields == null) { | |
| 3368 return FieldElement.EMPTY_LIST; | |
| 3369 } | |
| 3370 List<FieldElement> result = _fields; | |
| 3371 _fields = null; | |
| 3372 return result; | |
| 3373 } | |
| 3374 | |
| 3375 List<FieldElement> get fieldsWithoutFlushing { | |
| 3376 if (_fields == null) { | |
| 3377 return FieldElement.EMPTY_LIST; | |
| 3378 } | |
| 3379 List<FieldElement> result = _fields; | |
| 3380 return result; | |
| 3381 } | |
| 3382 | |
| 3383 List<FunctionElement> get functions { | |
| 3384 if (_functions == null) { | |
| 3385 return FunctionElement.EMPTY_LIST; | |
| 3386 } | |
| 3387 List<FunctionElement> result = _functions; | |
| 3388 _functions = null; | |
| 3389 return result; | |
| 3390 } | |
| 3391 | |
| 3392 List<LabelElement> get labels { | |
| 3393 if (_labels == null) { | |
| 3394 return LabelElement.EMPTY_LIST; | |
| 3395 } | |
| 3396 List<LabelElement> result = _labels; | |
| 3397 _labels = null; | |
| 3398 return result; | |
| 3399 } | |
| 3400 | |
| 3401 List<LocalVariableElement> get localVariables { | |
| 3402 if (_localVariables == null) { | |
| 3403 return LocalVariableElement.EMPTY_LIST; | |
| 3404 } | |
| 3405 List<LocalVariableElement> result = _localVariables; | |
| 3406 _localVariables = null; | |
| 3407 return result; | |
| 3408 } | |
| 3409 | |
| 3410 List<MethodElement> get methods { | |
| 3411 if (_methods == null) { | |
| 3412 return MethodElement.EMPTY_LIST; | |
| 3413 } | |
| 3414 List<MethodElement> result = _methods; | |
| 3415 _methods = null; | |
| 3416 return result; | |
| 3417 } | |
| 3418 | |
| 3419 List<ParameterElement> get parameters { | |
| 3420 if (_parameters == null) { | |
| 3421 return ParameterElement.EMPTY_LIST; | |
| 3422 } | |
| 3423 List<ParameterElement> result = _parameters; | |
| 3424 _parameters = null; | |
| 3425 return result; | |
| 3426 } | |
| 3427 | |
| 3428 List<TopLevelVariableElement> get topLevelVariables { | |
| 3429 if (_topLevelVariables == null) { | |
| 3430 return TopLevelVariableElement.EMPTY_LIST; | |
| 3431 } | |
| 3432 List<TopLevelVariableElement> result = _topLevelVariables; | |
| 3433 _topLevelVariables = null; | |
| 3434 return result; | |
| 3435 } | |
| 3436 | |
| 3437 List<FunctionTypeAliasElement> get typeAliases { | |
| 3438 if (_typeAliases == null) { | |
| 3439 return FunctionTypeAliasElement.EMPTY_LIST; | |
| 3440 } | |
| 3441 List<FunctionTypeAliasElement> result = _typeAliases; | |
| 3442 _typeAliases = null; | |
| 3443 return result; | |
| 3444 } | |
| 3445 | |
| 3446 List<TypeParameterElement> get typeParameters { | |
| 3447 if (_typeParameters == null) { | |
| 3448 return TypeParameterElement.EMPTY_LIST; | |
| 3449 } | |
| 3450 List<TypeParameterElement> result = _typeParameters; | |
| 3451 _typeParameters = null; | |
| 3452 return result; | |
| 3453 } | |
| 3454 | |
| 3455 List<ClassElement> get types { | |
| 3456 if (_types == null) { | |
| 3457 return ClassElement.EMPTY_LIST; | |
| 3458 } | |
| 3459 List<ClassElement> result = _types; | |
| 3460 _types = null; | |
| 3461 return result; | |
| 3462 } | |
| 3463 | |
| 3464 void addAccessor(PropertyAccessorElement element) { | |
| 3465 if (_accessors == null) { | |
| 3466 _accessors = new List<PropertyAccessorElement>(); | |
| 3467 } | |
| 3468 _accessors.add(element); | |
| 3469 } | |
| 3470 | |
| 3471 void addConstructor(ConstructorElement element) { | |
| 3472 if (_constructors == null) { | |
| 3473 _constructors = new List<ConstructorElement>(); | |
| 3474 } | |
| 3475 _constructors.add(element); | |
| 3476 } | |
| 3477 | |
| 3478 void addEnum(ClassElement element) { | |
| 3479 if (_enums == null) { | |
| 3480 _enums = new List<ClassElement>(); | |
| 3481 } | |
| 3482 _enums.add(element); | |
| 3483 } | |
| 3484 | |
| 3485 void addField(FieldElement element) { | |
| 3486 if (_fields == null) { | |
| 3487 _fields = new List<FieldElement>(); | |
| 3488 } | |
| 3489 _fields.add(element); | |
| 3490 } | |
| 3491 | |
| 3492 void addFunction(FunctionElement element) { | |
| 3493 if (_functions == null) { | |
| 3494 _functions = new List<FunctionElement>(); | |
| 3495 } | |
| 3496 _functions.add(element); | |
| 3497 } | |
| 3498 | |
| 3499 void addLabel(LabelElement element) { | |
| 3500 if (_labels == null) { | |
| 3501 _labels = new List<LabelElement>(); | |
| 3502 } | |
| 3503 _labels.add(element); | |
| 3504 } | |
| 3505 | |
| 3506 void addLocalVariable(LocalVariableElement element) { | |
| 3507 if (_localVariables == null) { | |
| 3508 _localVariables = new List<LocalVariableElement>(); | |
| 3509 } | |
| 3510 _localVariables.add(element); | |
| 3511 } | |
| 3512 | |
| 3513 void addMethod(MethodElement element) { | |
| 3514 if (_methods == null) { | |
| 3515 _methods = new List<MethodElement>(); | |
| 3516 } | |
| 3517 _methods.add(element); | |
| 3518 } | |
| 3519 | |
| 3520 void addParameter(ParameterElement element) { | |
| 3521 if (_parameters == null) { | |
| 3522 _parameters = new List<ParameterElement>(); | |
| 3523 } | |
| 3524 _parameters.add(element); | |
| 3525 } | |
| 3526 | |
| 3527 void addTopLevelVariable(TopLevelVariableElement element) { | |
| 3528 if (_topLevelVariables == null) { | |
| 3529 _topLevelVariables = new List<TopLevelVariableElement>(); | |
| 3530 } | |
| 3531 _topLevelVariables.add(element); | |
| 3532 } | |
| 3533 | |
| 3534 void addType(ClassElement element) { | |
| 3535 if (_types == null) { | |
| 3536 _types = new List<ClassElement>(); | |
| 3537 } | |
| 3538 _types.add(element); | |
| 3539 } | |
| 3540 | |
| 3541 void addTypeAlias(FunctionTypeAliasElement element) { | |
| 3542 if (_typeAliases == null) { | |
| 3543 _typeAliases = new List<FunctionTypeAliasElement>(); | |
| 3544 } | |
| 3545 _typeAliases.add(element); | |
| 3546 } | |
| 3547 | |
| 3548 void addTypeParameter(TypeParameterElement element) { | |
| 3549 if (_typeParameters == null) { | |
| 3550 _typeParameters = new List<TypeParameterElement>(); | |
| 3551 } | |
| 3552 _typeParameters.add(element); | |
| 3553 } | |
| 3554 | |
| 3555 FieldElement getField(String fieldName) { | |
| 3556 if (_fields == null) { | |
| 3557 return null; | |
| 3558 } | |
| 3559 for (FieldElement field in _fields) { | |
| 3560 if (field.name == fieldName) { | |
| 3561 return field; | |
| 3562 } | |
| 3563 } | |
| 3564 return null; | |
| 3565 } | |
| 3566 | |
| 3567 TopLevelVariableElement getTopLevelVariable(String variableName) { | |
| 3568 if (_topLevelVariables == null) { | |
| 3569 return null; | |
| 3570 } | |
| 3571 for (TopLevelVariableElement variable in _topLevelVariables) { | |
| 3572 if (variable.name == variableName) { | |
| 3573 return variable; | |
| 3574 } | |
| 3575 } | |
| 3576 return null; | |
| 3577 } | |
| 3578 | |
| 3579 void validate() { | |
| 3580 StringBuffer buffer = new StringBuffer(); | |
| 3581 if (_accessors != null) { | |
| 3582 buffer.write(_accessors.length); | |
| 3583 buffer.write(" accessors"); | |
| 3584 } | |
| 3585 if (_constructors != null) { | |
| 3586 if (buffer.length > 0) { | |
| 3587 buffer.write("; "); | |
| 3588 } | |
| 3589 buffer.write(_constructors.length); | |
| 3590 buffer.write(" constructors"); | |
| 3591 } | |
| 3592 if (_fields != null) { | |
| 3593 if (buffer.length > 0) { | |
| 3594 buffer.write("; "); | |
| 3595 } | |
| 3596 buffer.write(_fields.length); | |
| 3597 buffer.write(" fields"); | |
| 3598 } | |
| 3599 if (_functions != null) { | |
| 3600 if (buffer.length > 0) { | |
| 3601 buffer.write("; "); | |
| 3602 } | |
| 3603 buffer.write(_functions.length); | |
| 3604 buffer.write(" functions"); | |
| 3605 } | |
| 3606 if (_labels != null) { | |
| 3607 if (buffer.length > 0) { | |
| 3608 buffer.write("; "); | |
| 3609 } | |
| 3610 buffer.write(_labels.length); | |
| 3611 buffer.write(" labels"); | |
| 3612 } | |
| 3613 if (_localVariables != null) { | |
| 3614 if (buffer.length > 0) { | |
| 3615 buffer.write("; "); | |
| 3616 } | |
| 3617 buffer.write(_localVariables.length); | |
| 3618 buffer.write(" local variables"); | |
| 3619 } | |
| 3620 if (_methods != null) { | |
| 3621 if (buffer.length > 0) { | |
| 3622 buffer.write("; "); | |
| 3623 } | |
| 3624 buffer.write(_methods.length); | |
| 3625 buffer.write(" methods"); | |
| 3626 } | |
| 3627 if (_parameters != null) { | |
| 3628 if (buffer.length > 0) { | |
| 3629 buffer.write("; "); | |
| 3630 } | |
| 3631 buffer.write(_parameters.length); | |
| 3632 buffer.write(" parameters"); | |
| 3633 } | |
| 3634 if (_topLevelVariables != null) { | |
| 3635 if (buffer.length > 0) { | |
| 3636 buffer.write("; "); | |
| 3637 } | |
| 3638 buffer.write(_topLevelVariables.length); | |
| 3639 buffer.write(" top-level variables"); | |
| 3640 } | |
| 3641 if (_types != null) { | |
| 3642 if (buffer.length > 0) { | |
| 3643 buffer.write("; "); | |
| 3644 } | |
| 3645 buffer.write(_types.length); | |
| 3646 buffer.write(" types"); | |
| 3647 } | |
| 3648 if (_typeAliases != null) { | |
| 3649 if (buffer.length > 0) { | |
| 3650 buffer.write("; "); | |
| 3651 } | |
| 3652 buffer.write(_typeAliases.length); | |
| 3653 buffer.write(" type aliases"); | |
| 3654 } | |
| 3655 if (_typeParameters != null) { | |
| 3656 if (buffer.length > 0) { | |
| 3657 buffer.write("; "); | |
| 3658 } | |
| 3659 buffer.write(_typeParameters.length); | |
| 3660 buffer.write(" type parameters"); | |
| 3661 } | |
| 3662 if (buffer.length > 0) { | |
| 3663 AnalysisEngine.instance.logger | |
| 3664 .logError("Failed to capture elements: $buffer"); | |
| 3665 } | |
| 3666 } | |
| 3667 } | |
| 3668 | |
| 3669 /** | |
| 3670 * Instances of the class `EnclosedScope` implement a scope that is lexically en
closed in | |
| 3671 * another scope. | |
| 3672 */ | |
| 3673 class EnclosedScope extends Scope { | |
| 3674 /** | |
| 3675 * The scope in which this scope is lexically enclosed. | |
| 3676 */ | |
| 3677 final Scope enclosingScope; | |
| 3678 | |
| 3679 /** | |
| 3680 * A table mapping names that will be defined in this scope, but right now are
not initialized. | |
| 3681 * According to the scoping rules these names are hidden, even if they were de
fined in an outer | |
| 3682 * scope. | |
| 3683 */ | |
| 3684 HashMap<String, Element> _hiddenElements = new HashMap<String, Element>(); | |
| 3685 | |
| 3686 /** | |
| 3687 * A flag indicating whether there are any names defined in this scope. | |
| 3688 */ | |
| 3689 bool _hasHiddenName = false; | |
| 3690 | |
| 3691 /** | |
| 3692 * Initialize a newly created scope enclosed within another scope. | |
| 3693 * | |
| 3694 * @param enclosingScope the scope in which this scope is lexically enclosed | |
| 3695 */ | |
| 3696 EnclosedScope(this.enclosingScope); | |
| 3697 | |
| 3698 @override | |
| 3699 AnalysisErrorListener get errorListener => enclosingScope.errorListener; | |
| 3700 | |
| 3701 /** | |
| 3702 * Record that given element is declared in this scope, but hasn't been initia
lized yet, so it is | |
| 3703 * error to use. If there is already an element with the given name defined in
an outer scope, | |
| 3704 * then it will become unavailable. | |
| 3705 * | |
| 3706 * @param element the element declared, but not initialized in this scope | |
| 3707 */ | |
| 3708 void hide(Element element) { | |
| 3709 if (element != null) { | |
| 3710 String name = element.name; | |
| 3711 if (name != null && !name.isEmpty) { | |
| 3712 _hiddenElements[name] = element; | |
| 3713 _hasHiddenName = true; | |
| 3714 } | |
| 3715 } | |
| 3716 } | |
| 3717 | |
| 3718 @override | |
| 3719 Element internalLookup( | |
| 3720 Identifier identifier, String name, LibraryElement referencingLibrary) { | |
| 3721 Element element = localLookup(name, referencingLibrary); | |
| 3722 if (element != null) { | |
| 3723 return element; | |
| 3724 } | |
| 3725 // May be there is a hidden Element. | |
| 3726 if (_hasHiddenName) { | |
| 3727 Element hiddenElement = _hiddenElements[name]; | |
| 3728 if (hiddenElement != null) { | |
| 3729 errorListener.onError(new AnalysisError(getSource(identifier), | |
| 3730 identifier.offset, identifier.length, | |
| 3731 CompileTimeErrorCode.REFERENCED_BEFORE_DECLARATION, [])); | |
| 3732 return hiddenElement; | |
| 3733 } | |
| 3734 } | |
| 3735 // Check enclosing scope. | |
| 3736 return enclosingScope.internalLookup(identifier, name, referencingLibrary); | |
| 3737 } | |
| 3738 } | |
| 3739 | |
| 3740 /** | |
| 3741 * Instances of the class `EnumMemberBuilder` build the members in enum declarat
ions. | |
| 3742 */ | |
| 3743 class EnumMemberBuilder extends RecursiveAstVisitor<Object> { | |
| 3744 /** | |
| 3745 * The type provider used to access the types needed to build an element model
for enum | |
| 3746 * declarations. | |
| 3747 */ | |
| 3748 final TypeProvider _typeProvider; | |
| 3749 | |
| 3750 /** | |
| 3751 * Initialize a newly created enum member builder. | |
| 3752 * | |
| 3753 * @param typeProvider the type provider used to access the types needed to bu
ild an element model | |
| 3754 * for enum declarations | |
| 3755 */ | |
| 3756 EnumMemberBuilder(this._typeProvider); | |
| 3757 | |
| 3758 @override | |
| 3759 Object visitEnumDeclaration(EnumDeclaration node) { | |
| 3760 // | |
| 3761 // Finish building the enum. | |
| 3762 // | |
| 3763 ClassElementImpl enumElement = node.name.staticElement as ClassElementImpl; | |
| 3764 InterfaceType enumType = enumElement.type; | |
| 3765 enumElement.supertype = _typeProvider.objectType; | |
| 3766 // | |
| 3767 // Populate the fields. | |
| 3768 // | |
| 3769 List<FieldElement> fields = new List<FieldElement>(); | |
| 3770 List<PropertyAccessorElement> getters = new List<PropertyAccessorElement>(); | |
| 3771 InterfaceType intType = _typeProvider.intType; | |
| 3772 String indexFieldName = "index"; | |
| 3773 FieldElementImpl indexField = new FieldElementImpl(indexFieldName, -1); | |
| 3774 indexField.final2 = true; | |
| 3775 indexField.synthetic = true; | |
| 3776 indexField.type = intType; | |
| 3777 fields.add(indexField); | |
| 3778 getters.add(_createGetter(indexField)); | |
| 3779 ConstFieldElementImpl valuesField = new ConstFieldElementImpl("values", -1); | |
| 3780 valuesField.static = true; | |
| 3781 valuesField.const3 = true; | |
| 3782 valuesField.synthetic = true; | |
| 3783 valuesField.type = _typeProvider.listType.substitute4(<DartType>[enumType]); | |
| 3784 fields.add(valuesField); | |
| 3785 getters.add(_createGetter(valuesField)); | |
| 3786 // | |
| 3787 // Build the enum constants. | |
| 3788 // | |
| 3789 NodeList<EnumConstantDeclaration> constants = node.constants; | |
| 3790 List<DartObjectImpl> constantValues = new List<DartObjectImpl>(); | |
| 3791 int constantCount = constants.length; | |
| 3792 for (int i = 0; i < constantCount; i++) { | |
| 3793 SimpleIdentifier constantName = constants[i].name; | |
| 3794 FieldElementImpl constantField = | |
| 3795 new ConstFieldElementImpl.forNode(constantName); | |
| 3796 constantField.static = true; | |
| 3797 constantField.const3 = true; | |
| 3798 constantField.type = enumType; | |
| 3799 // | |
| 3800 // Create a value for the constant. | |
| 3801 // | |
| 3802 HashMap<String, DartObjectImpl> fieldMap = | |
| 3803 new HashMap<String, DartObjectImpl>(); | |
| 3804 fieldMap[indexFieldName] = new DartObjectImpl(intType, new IntState(i)); | |
| 3805 DartObjectImpl value = | |
| 3806 new DartObjectImpl(enumType, new GenericState(fieldMap)); | |
| 3807 constantValues.add(value); | |
| 3808 constantField.evaluationResult = new EvaluationResultImpl(value); | |
| 3809 fields.add(constantField); | |
| 3810 getters.add(_createGetter(constantField)); | |
| 3811 constantName.staticElement = constantField; | |
| 3812 } | |
| 3813 // | |
| 3814 // Build the value of the 'values' field. | |
| 3815 // | |
| 3816 valuesField.evaluationResult = new EvaluationResultImpl( | |
| 3817 new DartObjectImpl(valuesField.type, new ListState(constantValues))); | |
| 3818 // | |
| 3819 // Finish building the enum. | |
| 3820 // | |
| 3821 enumElement.fields = fields; | |
| 3822 enumElement.accessors = getters; | |
| 3823 // Client code isn't allowed to invoke the constructor, so we do not model | |
| 3824 // it. | |
| 3825 return super.visitEnumDeclaration(node); | |
| 3826 } | |
| 3827 | |
| 3828 /** | |
| 3829 * Create a getter that corresponds to the given field. | |
| 3830 * | |
| 3831 * @param field the field for which a getter is to be created | |
| 3832 * @return the getter that was created | |
| 3833 */ | |
| 3834 PropertyAccessorElement _createGetter(FieldElementImpl field) { | |
| 3835 PropertyAccessorElementImpl getter = | |
| 3836 new PropertyAccessorElementImpl.forVariable(field); | |
| 3837 getter.getter = true; | |
| 3838 getter.returnType = field.type; | |
| 3839 getter.type = new FunctionTypeImpl(getter); | |
| 3840 field.getter = getter; | |
| 3841 return getter; | |
| 3842 } | |
| 3843 } | |
| 3844 | |
| 3845 /** | |
| 3846 * Instances of the class `ExitDetector` determine whether the visited AST node
is guaranteed | |
| 3847 * to terminate by executing a `return` statement, `throw` expression, `rethrow` | |
| 3848 * expression, or simple infinite loop such as `while(true)`. | |
| 3849 */ | |
| 3850 class ExitDetector extends GeneralizingAstVisitor<bool> { | |
| 3851 /** | |
| 3852 * Set to `true` when a `break` is encountered, and reset to `false` when a | |
| 3853 * `do`, `while`, `for` or `switch` block is entered. | |
| 3854 */ | |
| 3855 bool _enclosingBlockContainsBreak = false; | |
| 3856 | |
| 3857 @override | |
| 3858 bool visitArgumentList(ArgumentList node) => | |
| 3859 _visitExpressions(node.arguments); | |
| 3860 | |
| 3861 @override | |
| 3862 bool visitAsExpression(AsExpression node) => _nodeExits(node.expression); | |
| 3863 | |
| 3864 @override | |
| 3865 bool visitAssertStatement(AssertStatement node) => _nodeExits(node.condition); | |
| 3866 | |
| 3867 @override | |
| 3868 bool visitAssignmentExpression(AssignmentExpression node) => | |
| 3869 _nodeExits(node.leftHandSide) || _nodeExits(node.rightHandSide); | |
| 3870 | |
| 3871 @override | |
| 3872 bool visitAwaitExpression(AwaitExpression node) => | |
| 3873 _nodeExits(node.expression); | |
| 3874 | |
| 3875 @override | |
| 3876 bool visitBinaryExpression(BinaryExpression node) { | |
| 3877 Expression lhsExpression = node.leftOperand; | |
| 3878 sc.TokenType operatorType = node.operator.type; | |
| 3879 // If the operator is || and the left hand side is false literal, don't | |
| 3880 // consider the RHS of the binary expression. | |
| 3881 // TODO(jwren) Do we want to take constant expressions into account, | |
| 3882 // evaluate if(false) {} differently than if(<condition>), when <condition> | |
| 3883 // evaluates to a constant false value? | |
| 3884 if (operatorType == sc.TokenType.BAR_BAR) { | |
| 3885 if (lhsExpression is BooleanLiteral) { | |
| 3886 BooleanLiteral booleanLiteral = lhsExpression; | |
| 3887 if (!booleanLiteral.value) { | |
| 3888 return false; | |
| 3889 } | |
| 3890 } | |
| 3891 } | |
| 3892 // If the operator is && and the left hand side is true literal, don't | |
| 3893 // consider the RHS of the binary expression. | |
| 3894 if (operatorType == sc.TokenType.AMPERSAND_AMPERSAND) { | |
| 3895 if (lhsExpression is BooleanLiteral) { | |
| 3896 BooleanLiteral booleanLiteral = lhsExpression; | |
| 3897 if (booleanLiteral.value) { | |
| 3898 return false; | |
| 3899 } | |
| 3900 } | |
| 3901 } | |
| 3902 Expression rhsExpression = node.rightOperand; | |
| 3903 return _nodeExits(lhsExpression) || _nodeExits(rhsExpression); | |
| 3904 } | |
| 3905 | |
| 3906 @override | |
| 3907 bool visitBlock(Block node) => _visitStatements(node.statements); | |
| 3908 | |
| 3909 @override | |
| 3910 bool visitBlockFunctionBody(BlockFunctionBody node) => _nodeExits(node.block); | |
| 3911 | |
| 3912 @override | |
| 3913 bool visitBreakStatement(BreakStatement node) { | |
| 3914 _enclosingBlockContainsBreak = true; | |
| 3915 return false; | |
| 3916 } | |
| 3917 | |
| 3918 @override | |
| 3919 bool visitCascadeExpression(CascadeExpression node) => | |
| 3920 _nodeExits(node.target) || _visitExpressions(node.cascadeSections); | |
| 3921 | |
| 3922 @override | |
| 3923 bool visitConditionalExpression(ConditionalExpression node) { | |
| 3924 Expression conditionExpression = node.condition; | |
| 3925 Expression thenStatement = node.thenExpression; | |
| 3926 Expression elseStatement = node.elseExpression; | |
| 3927 // TODO(jwren) Do we want to take constant expressions into account, | |
| 3928 // evaluate if(false) {} differently than if(<condition>), when <condition> | |
| 3929 // evaluates to a constant false value? | |
| 3930 if (_nodeExits(conditionExpression)) { | |
| 3931 return true; | |
| 3932 } | |
| 3933 if (thenStatement == null || elseStatement == null) { | |
| 3934 return false; | |
| 3935 } | |
| 3936 return thenStatement.accept(this) && elseStatement.accept(this); | |
| 3937 } | |
| 3938 | |
| 3939 @override | |
| 3940 bool visitContinueStatement(ContinueStatement node) => false; | |
| 3941 | |
| 3942 @override | |
| 3943 bool visitDoStatement(DoStatement node) { | |
| 3944 bool outerBreakValue = _enclosingBlockContainsBreak; | |
| 3945 _enclosingBlockContainsBreak = false; | |
| 3946 try { | |
| 3947 Expression conditionExpression = node.condition; | |
| 3948 if (_nodeExits(conditionExpression)) { | |
| 3949 return true; | |
| 3950 } | |
| 3951 // TODO(jwren) Do we want to take all constant expressions into account? | |
| 3952 if (conditionExpression is BooleanLiteral) { | |
| 3953 BooleanLiteral booleanLiteral = conditionExpression; | |
| 3954 // If do {} while (true), and the body doesn't return or the body | |
| 3955 // doesn't have a break, then return true. | |
| 3956 bool blockReturns = _nodeExits(node.body); | |
| 3957 if (booleanLiteral.value && | |
| 3958 (blockReturns || !_enclosingBlockContainsBreak)) { | |
| 3959 return true; | |
| 3960 } | |
| 3961 } | |
| 3962 return false; | |
| 3963 } finally { | |
| 3964 _enclosingBlockContainsBreak = outerBreakValue; | |
| 3965 } | |
| 3966 } | |
| 3967 | |
| 3968 @override | |
| 3969 bool visitEmptyStatement(EmptyStatement node) => false; | |
| 3970 | |
| 3971 @override | |
| 3972 bool visitExpressionStatement(ExpressionStatement node) => | |
| 3973 _nodeExits(node.expression); | |
| 3974 | |
| 3975 @override | |
| 3976 bool visitForEachStatement(ForEachStatement node) { | |
| 3977 bool outerBreakValue = _enclosingBlockContainsBreak; | |
| 3978 _enclosingBlockContainsBreak = false; | |
| 3979 try { | |
| 3980 return _nodeExits(node.iterable); | |
| 3981 } finally { | |
| 3982 _enclosingBlockContainsBreak = outerBreakValue; | |
| 3983 } | |
| 3984 } | |
| 3985 | |
| 3986 @override | |
| 3987 bool visitForStatement(ForStatement node) { | |
| 3988 bool outerBreakValue = _enclosingBlockContainsBreak; | |
| 3989 _enclosingBlockContainsBreak = false; | |
| 3990 try { | |
| 3991 if (node.variables != null && | |
| 3992 _visitVariableDeclarations(node.variables.variables)) { | |
| 3993 return true; | |
| 3994 } | |
| 3995 if (node.initialization != null && _nodeExits(node.initialization)) { | |
| 3996 return true; | |
| 3997 } | |
| 3998 Expression conditionExpression = node.condition; | |
| 3999 if (conditionExpression != null && _nodeExits(conditionExpression)) { | |
| 4000 return true; | |
| 4001 } | |
| 4002 if (_visitExpressions(node.updaters)) { | |
| 4003 return true; | |
| 4004 } | |
| 4005 // TODO(jwren) Do we want to take all constant expressions into account? | |
| 4006 // If for(; true; ) (or for(;;)), and the body doesn't return or the body | |
| 4007 // doesn't have a break, then return true. | |
| 4008 bool implicitOrExplictTrue = conditionExpression == null || | |
| 4009 (conditionExpression is BooleanLiteral && conditionExpression.value); | |
| 4010 if (implicitOrExplictTrue) { | |
| 4011 bool blockReturns = _nodeExits(node.body); | |
| 4012 if (blockReturns || !_enclosingBlockContainsBreak) { | |
| 4013 return true; | |
| 4014 } | |
| 4015 } | |
| 4016 return false; | |
| 4017 } finally { | |
| 4018 _enclosingBlockContainsBreak = outerBreakValue; | |
| 4019 } | |
| 4020 } | |
| 4021 | |
| 4022 @override | |
| 4023 bool visitFunctionDeclarationStatement(FunctionDeclarationStatement node) => | |
| 4024 false; | |
| 4025 | |
| 4026 @override | |
| 4027 bool visitFunctionExpression(FunctionExpression node) => false; | |
| 4028 | |
| 4029 @override | |
| 4030 bool visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { | |
| 4031 if (_nodeExits(node.function)) { | |
| 4032 return true; | |
| 4033 } | |
| 4034 return node.argumentList.accept(this); | |
| 4035 } | |
| 4036 | |
| 4037 @override | |
| 4038 bool visitIdentifier(Identifier node) => false; | |
| 4039 | |
| 4040 @override | |
| 4041 bool visitIfStatement(IfStatement node) { | |
| 4042 Expression conditionExpression = node.condition; | |
| 4043 Statement thenStatement = node.thenStatement; | |
| 4044 Statement elseStatement = node.elseStatement; | |
| 4045 if (_nodeExits(conditionExpression)) { | |
| 4046 return true; | |
| 4047 } | |
| 4048 // TODO(jwren) Do we want to take all constant expressions into account? | |
| 4049 if (conditionExpression is BooleanLiteral) { | |
| 4050 BooleanLiteral booleanLiteral = conditionExpression; | |
| 4051 if (booleanLiteral.value) { | |
| 4052 // if(true) ... | |
| 4053 return _nodeExits(thenStatement); | |
| 4054 } else if (elseStatement != null) { | |
| 4055 // if (false) ... | |
| 4056 return _nodeExits(elseStatement); | |
| 4057 } | |
| 4058 } | |
| 4059 if (thenStatement == null || elseStatement == null) { | |
| 4060 return false; | |
| 4061 } | |
| 4062 return _nodeExits(thenStatement) && _nodeExits(elseStatement); | |
| 4063 } | |
| 4064 | |
| 4065 @override | |
| 4066 bool visitIndexExpression(IndexExpression node) { | |
| 4067 Expression target = node.realTarget; | |
| 4068 if (_nodeExits(target)) { | |
| 4069 return true; | |
| 4070 } | |
| 4071 if (_nodeExits(node.index)) { | |
| 4072 return true; | |
| 4073 } | |
| 4074 return false; | |
| 4075 } | |
| 4076 | |
| 4077 @override | |
| 4078 bool visitInstanceCreationExpression(InstanceCreationExpression node) => | |
| 4079 _nodeExits(node.argumentList); | |
| 4080 | |
| 4081 @override | |
| 4082 bool visitIsExpression(IsExpression node) => node.expression.accept(this); | |
| 4083 | |
| 4084 @override | |
| 4085 bool visitLabel(Label node) => false; | |
| 4086 | |
| 4087 @override | |
| 4088 bool visitLabeledStatement(LabeledStatement node) => | |
| 4089 node.statement.accept(this); | |
| 4090 | |
| 4091 @override | |
| 4092 bool visitLiteral(Literal node) => false; | |
| 4093 | |
| 4094 @override | |
| 4095 bool visitMethodInvocation(MethodInvocation node) { | |
| 4096 Expression target = node.realTarget; | |
| 4097 if (target != null && target.accept(this)) { | |
| 4098 return true; | |
| 4099 } | |
| 4100 return _nodeExits(node.argumentList); | |
| 4101 } | |
| 4102 | |
| 4103 @override | |
| 4104 bool visitNamedExpression(NamedExpression node) => | |
| 4105 node.expression.accept(this); | |
| 4106 | |
| 4107 @override | |
| 4108 bool visitParenthesizedExpression(ParenthesizedExpression node) => | |
| 4109 node.expression.accept(this); | |
| 4110 | |
| 4111 @override | |
| 4112 bool visitPostfixExpression(PostfixExpression node) => false; | |
| 4113 | |
| 4114 @override | |
| 4115 bool visitPrefixExpression(PrefixExpression node) => false; | |
| 4116 | |
| 4117 @override | |
| 4118 bool visitPropertyAccess(PropertyAccess node) { | |
| 4119 Expression target = node.realTarget; | |
| 4120 if (target != null && target.accept(this)) { | |
| 4121 return true; | |
| 4122 } | |
| 4123 return false; | |
| 4124 } | |
| 4125 | |
| 4126 @override | |
| 4127 bool visitRethrowExpression(RethrowExpression node) => true; | |
| 4128 | |
| 4129 @override | |
| 4130 bool visitReturnStatement(ReturnStatement node) => true; | |
| 4131 | |
| 4132 @override | |
| 4133 bool visitSuperExpression(SuperExpression node) => false; | |
| 4134 | |
| 4135 @override | |
| 4136 bool visitSwitchCase(SwitchCase node) => _visitStatements(node.statements); | |
| 4137 | |
| 4138 @override | |
| 4139 bool visitSwitchDefault(SwitchDefault node) => | |
| 4140 _visitStatements(node.statements); | |
| 4141 | |
| 4142 @override | |
| 4143 bool visitSwitchStatement(SwitchStatement node) { | |
| 4144 bool outerBreakValue = _enclosingBlockContainsBreak; | |
| 4145 _enclosingBlockContainsBreak = false; | |
| 4146 try { | |
| 4147 bool hasDefault = false; | |
| 4148 List<SwitchMember> members = node.members; | |
| 4149 for (int i = 0; i < members.length; i++) { | |
| 4150 SwitchMember switchMember = members[i]; | |
| 4151 if (switchMember is SwitchDefault) { | |
| 4152 hasDefault = true; | |
| 4153 // If this is the last member and there are no statements, return | |
| 4154 // false | |
| 4155 if (switchMember.statements.isEmpty && i + 1 == members.length) { | |
| 4156 return false; | |
| 4157 } | |
| 4158 } | |
| 4159 // For switch members with no statements, don't visit the children, | |
| 4160 // otherwise, return false if no return is found in the children | |
| 4161 // statements. | |
| 4162 if (!switchMember.statements.isEmpty && !switchMember.accept(this)) { | |
| 4163 return false; | |
| 4164 } | |
| 4165 } | |
| 4166 // All of the members exit, determine whether there are possible cases | |
| 4167 // that are not caught by the members. | |
| 4168 DartType type = node.expression == null ? null : node.expression.bestType; | |
| 4169 if (type is InterfaceType) { | |
| 4170 ClassElement element = type.element; | |
| 4171 if (element != null && element.isEnum) { | |
| 4172 // If some of the enum values are not covered, then a warning will | |
| 4173 // have already been generated, so there's no point in generating a | |
| 4174 // hint. | |
| 4175 return true; | |
| 4176 } | |
| 4177 } | |
| 4178 return hasDefault; | |
| 4179 } finally { | |
| 4180 _enclosingBlockContainsBreak = outerBreakValue; | |
| 4181 } | |
| 4182 } | |
| 4183 | |
| 4184 @override | |
| 4185 bool visitThisExpression(ThisExpression node) => false; | |
| 4186 | |
| 4187 @override | |
| 4188 bool visitThrowExpression(ThrowExpression node) => true; | |
| 4189 | |
| 4190 @override | |
| 4191 bool visitTryStatement(TryStatement node) { | |
| 4192 if (_nodeExits(node.body)) { | |
| 4193 return true; | |
| 4194 } | |
| 4195 Block finallyBlock = node.finallyBlock; | |
| 4196 if (_nodeExits(finallyBlock)) { | |
| 4197 return true; | |
| 4198 } | |
| 4199 return false; | |
| 4200 } | |
| 4201 | |
| 4202 @override | |
| 4203 bool visitTypeName(TypeName node) => false; | |
| 4204 | |
| 4205 @override | |
| 4206 bool visitVariableDeclaration(VariableDeclaration node) { | |
| 4207 Expression initializer = node.initializer; | |
| 4208 if (initializer != null) { | |
| 4209 return initializer.accept(this); | |
| 4210 } | |
| 4211 return false; | |
| 4212 } | |
| 4213 | |
| 4214 @override | |
| 4215 bool visitVariableDeclarationList(VariableDeclarationList node) => | |
| 4216 _visitVariableDeclarations(node.variables); | |
| 4217 | |
| 4218 @override | |
| 4219 bool visitVariableDeclarationStatement(VariableDeclarationStatement node) { | |
| 4220 NodeList<VariableDeclaration> variables = node.variables.variables; | |
| 4221 for (int i = 0; i < variables.length; i++) { | |
| 4222 if (variables[i].accept(this)) { | |
| 4223 return true; | |
| 4224 } | |
| 4225 } | |
| 4226 return false; | |
| 4227 } | |
| 4228 | |
| 4229 @override | |
| 4230 bool visitWhileStatement(WhileStatement node) { | |
| 4231 bool outerBreakValue = _enclosingBlockContainsBreak; | |
| 4232 _enclosingBlockContainsBreak = false; | |
| 4233 try { | |
| 4234 Expression conditionExpression = node.condition; | |
| 4235 if (conditionExpression.accept(this)) { | |
| 4236 return true; | |
| 4237 } | |
| 4238 // TODO(jwren) Do we want to take all constant expressions into account? | |
| 4239 if (conditionExpression is BooleanLiteral) { | |
| 4240 BooleanLiteral booleanLiteral = conditionExpression; | |
| 4241 // If while(true), and the body doesn't return or the body doesn't have | |
| 4242 // a break, then return true. | |
| 4243 bool blockReturns = node.body.accept(this); | |
| 4244 if (booleanLiteral.value && | |
| 4245 (blockReturns || !_enclosingBlockContainsBreak)) { | |
| 4246 return true; | |
| 4247 } | |
| 4248 } | |
| 4249 return false; | |
| 4250 } finally { | |
| 4251 _enclosingBlockContainsBreak = outerBreakValue; | |
| 4252 } | |
| 4253 } | |
| 4254 | |
| 4255 /** | |
| 4256 * Return `true` if the given node exits. | |
| 4257 * | |
| 4258 * @param node the node being tested | |
| 4259 * @return `true` if the given node exits | |
| 4260 */ | |
| 4261 bool _nodeExits(AstNode node) { | |
| 4262 if (node == null) { | |
| 4263 return false; | |
| 4264 } | |
| 4265 return node.accept(this); | |
| 4266 } | |
| 4267 | |
| 4268 bool _visitExpressions(NodeList<Expression> expressions) { | |
| 4269 for (int i = expressions.length - 1; i >= 0; i--) { | |
| 4270 if (expressions[i].accept(this)) { | |
| 4271 return true; | |
| 4272 } | |
| 4273 } | |
| 4274 return false; | |
| 4275 } | |
| 4276 | |
| 4277 bool _visitStatements(NodeList<Statement> statements) { | |
| 4278 for (int i = statements.length - 1; i >= 0; i--) { | |
| 4279 if (statements[i].accept(this)) { | |
| 4280 return true; | |
| 4281 } | |
| 4282 } | |
| 4283 return false; | |
| 4284 } | |
| 4285 | |
| 4286 bool _visitVariableDeclarations( | |
| 4287 NodeList<VariableDeclaration> variableDeclarations) { | |
| 4288 for (int i = variableDeclarations.length - 1; i >= 0; i--) { | |
| 4289 if (variableDeclarations[i].accept(this)) { | |
| 4290 return true; | |
| 4291 } | |
| 4292 } | |
| 4293 return false; | |
| 4294 } | |
| 4295 | |
| 4296 /** | |
| 4297 * Return `true` if the given [node] exits. | |
| 4298 */ | |
| 4299 static bool exits(AstNode node) { | |
| 4300 return new ExitDetector()._nodeExits(node); | |
| 4301 } | |
| 4302 } | |
| 4303 | |
| 4304 /** | |
| 4305 * The scope defined by a function. | |
| 4306 */ | |
| 4307 class FunctionScope extends EnclosedScope { | |
| 4308 /** | |
| 4309 * The element representing the function that defines this scope. | |
| 4310 */ | |
| 4311 final ExecutableElement _functionElement; | |
| 4312 | |
| 4313 /** | |
| 4314 * A flag indicating whether the parameters have already been defined, used to | |
| 4315 * prevent the parameters from being defined multiple times. | |
| 4316 */ | |
| 4317 bool _parametersDefined = false; | |
| 4318 | |
| 4319 /** | |
| 4320 * Initialize a newly created scope enclosed within the [enclosingScope] that | |
| 4321 * represents the given [_functionElement]. | |
| 4322 */ | |
| 4323 FunctionScope(Scope enclosingScope, this._functionElement) | |
| 4324 : super(new EnclosedScope(new EnclosedScope(enclosingScope))) { | |
| 4325 if (_functionElement == null) { | |
| 4326 throw new IllegalArgumentException("function element cannot be null"); | |
| 4327 } | |
| 4328 _defineTypeParameters(); | |
| 4329 } | |
| 4330 | |
| 4331 /** | |
| 4332 * Define the parameters for the given function in the scope that encloses | |
| 4333 * this function. | |
| 4334 */ | |
| 4335 void defineParameters() { | |
| 4336 if (_parametersDefined) { | |
| 4337 return; | |
| 4338 } | |
| 4339 _parametersDefined = true; | |
| 4340 Scope parameterScope = enclosingScope; | |
| 4341 for (ParameterElement parameter in _functionElement.parameters) { | |
| 4342 if (!parameter.isInitializingFormal) { | |
| 4343 parameterScope.define(parameter); | |
| 4344 } | |
| 4345 } | |
| 4346 } | |
| 4347 | |
| 4348 /** | |
| 4349 * Define the type parameters for the function. | |
| 4350 */ | |
| 4351 void _defineTypeParameters() { | |
| 4352 Scope typeParameterScope = enclosingScope.enclosingScope; | |
| 4353 for (TypeParameterElement typeParameter | |
| 4354 in _functionElement.typeParameters) { | |
| 4355 typeParameterScope.define(typeParameter); | |
| 4356 } | |
| 4357 } | |
| 4358 } | |
| 4359 | |
| 4360 /** | |
| 4361 * The scope defined by a function type alias. | |
| 4362 */ | |
| 4363 class FunctionTypeScope extends EnclosedScope { | |
| 4364 final FunctionTypeAliasElement _typeElement; | |
| 4365 | |
| 4366 bool _parametersDefined = false; | |
| 4367 | |
| 4368 /** | |
| 4369 * Initialize a newly created scope enclosed within the [enclosingScope] that | |
| 4370 * represents the given [_typeElement]. | |
| 4371 */ | |
| 4372 FunctionTypeScope(Scope enclosingScope, this._typeElement) | |
| 4373 : super(new EnclosedScope(enclosingScope)) { | |
| 4374 _defineTypeParameters(); | |
| 4375 } | |
| 4376 | |
| 4377 /** | |
| 4378 * Define the parameters for the function type alias. | |
| 4379 */ | |
| 4380 void defineParameters() { | |
| 4381 if (_parametersDefined) { | |
| 4382 return; | |
| 4383 } | |
| 4384 _parametersDefined = true; | |
| 4385 for (ParameterElement parameter in _typeElement.parameters) { | |
| 4386 define(parameter); | |
| 4387 } | |
| 4388 } | |
| 4389 | |
| 4390 /** | |
| 4391 * Define the type parameters for the function type alias. | |
| 4392 */ | |
| 4393 void _defineTypeParameters() { | |
| 4394 Scope typeParameterScope = enclosingScope; | |
| 4395 for (TypeParameterElement typeParameter in _typeElement.typeParameters) { | |
| 4396 typeParameterScope.define(typeParameter); | |
| 4397 } | |
| 4398 } | |
| 4399 } | |
| 4400 | |
| 4401 /** | |
| 4402 * A visitor that visits ASTs and fills [UsedImportedElements]. | |
| 4403 */ | |
| 4404 class GatherUsedImportedElementsVisitor extends RecursiveAstVisitor { | |
| 4405 final LibraryElement library; | |
| 4406 final UsedImportedElements usedElements = new UsedImportedElements(); | |
| 4407 | |
| 4408 GatherUsedImportedElementsVisitor(this.library); | |
| 4409 | |
| 4410 @override | |
| 4411 void visitExportDirective(ExportDirective node) { | |
| 4412 _visitMetadata(node.metadata); | |
| 4413 } | |
| 4414 | |
| 4415 @override | |
| 4416 void visitImportDirective(ImportDirective node) { | |
| 4417 _visitMetadata(node.metadata); | |
| 4418 } | |
| 4419 | |
| 4420 @override | |
| 4421 void visitLibraryDirective(LibraryDirective node) { | |
| 4422 _visitMetadata(node.metadata); | |
| 4423 } | |
| 4424 | |
| 4425 @override | |
| 4426 void visitPrefixedIdentifier(PrefixedIdentifier node) { | |
| 4427 // If the prefixed identifier references some A.B, where A is a library | |
| 4428 // prefix, then we can lookup the associated ImportDirective in | |
| 4429 // prefixElementMap and remove it from the unusedImports list. | |
| 4430 SimpleIdentifier prefixIdentifier = node.prefix; | |
| 4431 Element element = prefixIdentifier.staticElement; | |
| 4432 if (element is PrefixElement) { | |
| 4433 usedElements.prefixes.add(element); | |
| 4434 return; | |
| 4435 } | |
| 4436 // Otherwise, pass the prefixed identifier element and name onto | |
| 4437 // visitIdentifier. | |
| 4438 _visitIdentifier(element, prefixIdentifier.name); | |
| 4439 } | |
| 4440 | |
| 4441 @override | |
| 4442 void visitSimpleIdentifier(SimpleIdentifier node) { | |
| 4443 _visitIdentifier(node.staticElement, node.name); | |
| 4444 } | |
| 4445 | |
| 4446 void _visitIdentifier(Element element, String name) { | |
| 4447 if (element == null) { | |
| 4448 return; | |
| 4449 } | |
| 4450 // If the element is multiply defined then call this method recursively for | |
| 4451 // each of the conflicting elements. | |
| 4452 if (element is MultiplyDefinedElement) { | |
| 4453 MultiplyDefinedElement multiplyDefinedElement = element; | |
| 4454 for (Element elt in multiplyDefinedElement.conflictingElements) { | |
| 4455 _visitIdentifier(elt, name); | |
| 4456 } | |
| 4457 return; | |
| 4458 } else if (element is PrefixElement) { | |
| 4459 usedElements.prefixes.add(element); | |
| 4460 return; | |
| 4461 } else if (element.enclosingElement is! CompilationUnitElement) { | |
| 4462 // Identifiers that aren't a prefix element and whose enclosing element | |
| 4463 // isn't a CompilationUnit are ignored- this covers the case the | |
| 4464 // identifier is a relative-reference, a reference to an identifier not | |
| 4465 // imported by this library. | |
| 4466 return; | |
| 4467 } | |
| 4468 // Ignore if an unknown library. | |
| 4469 LibraryElement containingLibrary = element.library; | |
| 4470 if (containingLibrary == null) { | |
| 4471 return; | |
| 4472 } | |
| 4473 // Ignore if a local element. | |
| 4474 if (library == containingLibrary) { | |
| 4475 return; | |
| 4476 } | |
| 4477 // Remember the element. | |
| 4478 usedElements.elements.add(element); | |
| 4479 } | |
| 4480 | |
| 4481 /** | |
| 4482 * Given some [NodeList] of [Annotation]s, ensure that the identifiers are vis
ited by | |
| 4483 * this visitor. Specifically, this covers the cases where AST nodes don't hav
e their identifiers | |
| 4484 * visited by this visitor, but still need their annotations visited. | |
| 4485 * | |
| 4486 * @param annotations the list of annotations to visit | |
| 4487 */ | |
| 4488 void _visitMetadata(NodeList<Annotation> annotations) { | |
| 4489 int count = annotations.length; | |
| 4490 for (int i = 0; i < count; i++) { | |
| 4491 annotations[i].accept(this); | |
| 4492 } | |
| 4493 } | |
| 4494 } | |
| 4495 | |
| 4496 /** | |
| 4497 * An [AstVisitor] that fills [UsedLocalElements]. | |
| 4498 */ | |
| 4499 class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor { | |
| 4500 final UsedLocalElements usedElements = new UsedLocalElements(); | |
| 4501 | |
| 4502 final LibraryElement _enclosingLibrary; | |
| 4503 ClassElement _enclosingClass; | |
| 4504 ExecutableElement _enclosingExec; | |
| 4505 | |
| 4506 GatherUsedLocalElementsVisitor(this._enclosingLibrary); | |
| 4507 | |
| 4508 @override | |
| 4509 visitCatchClause(CatchClause node) { | |
| 4510 SimpleIdentifier exceptionParameter = node.exceptionParameter; | |
| 4511 SimpleIdentifier stackTraceParameter = node.stackTraceParameter; | |
| 4512 if (exceptionParameter != null) { | |
| 4513 Element element = exceptionParameter.staticElement; | |
| 4514 usedElements.addCatchException(element); | |
| 4515 if (stackTraceParameter != null || node.onKeyword == null) { | |
| 4516 usedElements.addElement(element); | |
| 4517 } | |
| 4518 } | |
| 4519 if (stackTraceParameter != null) { | |
| 4520 Element element = stackTraceParameter.staticElement; | |
| 4521 usedElements.addCatchStackTrace(element); | |
| 4522 } | |
| 4523 super.visitCatchClause(node); | |
| 4524 } | |
| 4525 | |
| 4526 @override | |
| 4527 visitClassDeclaration(ClassDeclaration node) { | |
| 4528 ClassElement enclosingClassOld = _enclosingClass; | |
| 4529 try { | |
| 4530 _enclosingClass = node.element; | |
| 4531 super.visitClassDeclaration(node); | |
| 4532 } finally { | |
| 4533 _enclosingClass = enclosingClassOld; | |
| 4534 } | |
| 4535 } | |
| 4536 | |
| 4537 @override | |
| 4538 visitFunctionDeclaration(FunctionDeclaration node) { | |
| 4539 ExecutableElement enclosingExecOld = _enclosingExec; | |
| 4540 try { | |
| 4541 _enclosingExec = node.element; | |
| 4542 super.visitFunctionDeclaration(node); | |
| 4543 } finally { | |
| 4544 _enclosingExec = enclosingExecOld; | |
| 4545 } | |
| 4546 } | |
| 4547 | |
| 4548 @override | |
| 4549 visitFunctionExpression(FunctionExpression node) { | |
| 4550 if (node.parent is! FunctionDeclaration) { | |
| 4551 usedElements.addElement(node.element); | |
| 4552 } | |
| 4553 super.visitFunctionExpression(node); | |
| 4554 } | |
| 4555 | |
| 4556 @override | |
| 4557 visitMethodDeclaration(MethodDeclaration node) { | |
| 4558 ExecutableElement enclosingExecOld = _enclosingExec; | |
| 4559 try { | |
| 4560 _enclosingExec = node.element; | |
| 4561 super.visitMethodDeclaration(node); | |
| 4562 } finally { | |
| 4563 _enclosingExec = enclosingExecOld; | |
| 4564 } | |
| 4565 } | |
| 4566 | |
| 4567 @override | |
| 4568 visitSimpleIdentifier(SimpleIdentifier node) { | |
| 4569 if (node.inDeclarationContext()) { | |
| 4570 return; | |
| 4571 } | |
| 4572 Element element = node.staticElement; | |
| 4573 bool isIdentifierRead = _isReadIdentifier(node); | |
| 4574 if (element is LocalVariableElement) { | |
| 4575 if (isIdentifierRead) { | |
| 4576 usedElements.addElement(element); | |
| 4577 } | |
| 4578 } else { | |
| 4579 _useIdentifierElement(node); | |
| 4580 if (element == null || | |
| 4581 element.enclosingElement is ClassElement && | |
| 4582 !identical(element, _enclosingExec)) { | |
| 4583 usedElements.members.add(node.name); | |
| 4584 if (isIdentifierRead) { | |
| 4585 usedElements.readMembers.add(node.name); | |
| 4586 } | |
| 4587 } | |
| 4588 } | |
| 4589 } | |
| 4590 | |
| 4591 /** | |
| 4592 * Marks an [Element] of [node] as used in the library. | |
| 4593 */ | |
| 4594 void _useIdentifierElement(Identifier node) { | |
| 4595 Element element = node.staticElement; | |
| 4596 if (element == null) { | |
| 4597 return; | |
| 4598 } | |
| 4599 // check if a local element | |
| 4600 if (!identical(element.library, _enclosingLibrary)) { | |
| 4601 return; | |
| 4602 } | |
| 4603 // ignore references to an element from itself | |
| 4604 if (identical(element, _enclosingClass)) { | |
| 4605 return; | |
| 4606 } | |
| 4607 if (identical(element, _enclosingExec)) { | |
| 4608 return; | |
| 4609 } | |
| 4610 // ignore places where the element is not actually used | |
| 4611 if (node.parent is TypeName) { | |
| 4612 if (element is ClassElement) { | |
| 4613 AstNode parent2 = node.parent.parent; | |
| 4614 if (parent2 is IsExpression) { | |
| 4615 return; | |
| 4616 } | |
| 4617 if (parent2 is VariableDeclarationList) { | |
| 4618 return; | |
| 4619 } | |
| 4620 } | |
| 4621 } | |
| 4622 // OK | |
| 4623 usedElements.addElement(element); | |
| 4624 } | |
| 4625 | |
| 4626 static bool _isReadIdentifier(SimpleIdentifier node) { | |
| 4627 // not reading at all | |
| 4628 if (!node.inGetterContext()) { | |
| 4629 return false; | |
| 4630 } | |
| 4631 // check if useless reading | |
| 4632 AstNode parent = node.parent; | |
| 4633 if (parent.parent is ExpressionStatement && | |
| 4634 (parent is PrefixExpression || | |
| 4635 parent is PostfixExpression || | |
| 4636 parent is AssignmentExpression && parent.leftHandSide == node)) { | |
| 4637 // v++; | |
| 4638 // ++v; | |
| 4639 // v += 2; | |
| 4640 return false; | |
| 4641 } | |
| 4642 // OK | |
| 4643 return true; | |
| 4644 } | |
| 4645 } | |
| 4646 | |
| 4647 /** | |
| 4648 * Instances of the class `HintGenerator` traverse a library's worth of dart cod
e at a time to | |
| 4649 * generate hints over the set of sources. | |
| 4650 * | |
| 4651 * See [HintCode]. | |
| 4652 */ | |
| 4653 class HintGenerator { | |
| 4654 final List<CompilationUnit> _compilationUnits; | |
| 4655 | |
| 4656 final InternalAnalysisContext _context; | |
| 4657 | |
| 4658 final AnalysisErrorListener _errorListener; | |
| 4659 | |
| 4660 LibraryElement _library; | |
| 4661 | |
| 4662 GatherUsedImportedElementsVisitor _usedImportedElementsVisitor; | |
| 4663 | |
| 4664 bool _enableDart2JSHints = false; | |
| 4665 | |
| 4666 /** | |
| 4667 * The inheritance manager used to find overridden methods. | |
| 4668 */ | |
| 4669 InheritanceManager _manager; | |
| 4670 | |
| 4671 GatherUsedLocalElementsVisitor _usedLocalElementsVisitor; | |
| 4672 | |
| 4673 HintGenerator(this._compilationUnits, this._context, this._errorListener) { | |
| 4674 _library = _compilationUnits[0].element.library; | |
| 4675 _usedImportedElementsVisitor = | |
| 4676 new GatherUsedImportedElementsVisitor(_library); | |
| 4677 _enableDart2JSHints = _context.analysisOptions.dart2jsHint; | |
| 4678 _manager = new InheritanceManager(_compilationUnits[0].element.library); | |
| 4679 _usedLocalElementsVisitor = new GatherUsedLocalElementsVisitor(_library); | |
| 4680 } | |
| 4681 | |
| 4682 void generateForLibrary() { | |
| 4683 PerformanceStatistics.hints.makeCurrentWhile(() { | |
| 4684 for (CompilationUnit unit in _compilationUnits) { | |
| 4685 CompilationUnitElement element = unit.element; | |
| 4686 if (element != null) { | |
| 4687 _generateForCompilationUnit(unit, element.source); | |
| 4688 } | |
| 4689 } | |
| 4690 CompilationUnit definingUnit = _compilationUnits[0]; | |
| 4691 ErrorReporter definingUnitErrorReporter = | |
| 4692 new ErrorReporter(_errorListener, definingUnit.element.source); | |
| 4693 { | |
| 4694 ImportsVerifier importsVerifier = new ImportsVerifier(); | |
| 4695 importsVerifier.addImports(definingUnit); | |
| 4696 importsVerifier | |
| 4697 .removeUsedElements(_usedImportedElementsVisitor.usedElements); | |
| 4698 importsVerifier.generateDuplicateImportHints(definingUnitErrorReporter); | |
| 4699 importsVerifier.generateUnusedImportHints(definingUnitErrorReporter); | |
| 4700 } | |
| 4701 _library.accept(new UnusedLocalElementsVerifier( | |
| 4702 _errorListener, _usedLocalElementsVisitor.usedElements)); | |
| 4703 }); | |
| 4704 } | |
| 4705 | |
| 4706 void _generateForCompilationUnit(CompilationUnit unit, Source source) { | |
| 4707 ErrorReporter errorReporter = new ErrorReporter(_errorListener, source); | |
| 4708 unit.accept(_usedImportedElementsVisitor); | |
| 4709 // dead code analysis | |
| 4710 unit.accept(new DeadCodeVerifier(errorReporter)); | |
| 4711 unit.accept(_usedLocalElementsVisitor); | |
| 4712 // dart2js analysis | |
| 4713 if (_enableDart2JSHints) { | |
| 4714 unit.accept(new Dart2JSVerifier(errorReporter)); | |
| 4715 } | |
| 4716 // Dart best practices | |
| 4717 unit.accept( | |
| 4718 new BestPracticesVerifier(errorReporter, _context.typeProvider)); | |
| 4719 unit.accept(new OverrideVerifier(errorReporter, _manager)); | |
| 4720 // Find to-do comments | |
| 4721 new ToDoFinder(errorReporter).findIn(unit); | |
| 4722 // pub analysis | |
| 4723 // TODO(danrubel/jwren) Commented out until bugs in the pub verifier are | |
| 4724 // fixed | |
| 4725 // unit.accept(new PubVerifier(context, errorReporter)); | |
| 4726 } | |
| 4727 } | |
| 4728 | |
| 4729 /** | |
| 4730 * Instances of the class {@code HtmlTagInfo} record information about the tags
used in an HTML | |
| 4731 * file. | |
| 4732 */ | |
| 4733 class HtmlTagInfo { | |
| 4734 /** | |
| 4735 * An array containing all of the tags used in the HTML file. | |
| 4736 */ | |
| 4737 List<String> allTags; | |
| 4738 | |
| 4739 /** | |
| 4740 * A table mapping the id's defined in the HTML file to an array containing th
e names of tags with | |
| 4741 * that identifier. | |
| 4742 */ | |
| 4743 HashMap<String, String> idToTagMap; | |
| 4744 | |
| 4745 /** | |
| 4746 * A table mapping the classes defined in the HTML file to an array containing
the names of tags | |
| 4747 * with that class. | |
| 4748 */ | |
| 4749 HashMap<String, List<String>> classToTagsMap; | |
| 4750 | |
| 4751 /** | |
| 4752 * Initialize a newly created information holder to hold the given information
about the tags in | |
| 4753 * an HTML file. | |
| 4754 * | |
| 4755 * @param allTags an array containing all of the tags used in the HTML file | |
| 4756 * @param idToTagMap a table mapping the id's defined in the HTML file to an a
rray containing the | |
| 4757 * names of tags with that identifier | |
| 4758 * @param classToTagsMap a table mapping the classes defined in the HTML file
to an array | |
| 4759 * containing the names of tags with that class | |
| 4760 */ | |
| 4761 HtmlTagInfo(List<String> allTags, HashMap<String, String> idToTagMap, | |
| 4762 HashMap<String, List<String>> classToTagsMap) { | |
| 4763 this.allTags = allTags; | |
| 4764 this.idToTagMap = idToTagMap; | |
| 4765 this.classToTagsMap = classToTagsMap; | |
| 4766 } | |
| 4767 | |
| 4768 /** | |
| 4769 * Return an array containing the tags that have the given class, or {@code nu
ll} if there are no | |
| 4770 * such tags. | |
| 4771 * | |
| 4772 * @return an array containing the tags that have the given class | |
| 4773 */ | |
| 4774 List<String> getTagsWithClass(String identifier) { | |
| 4775 return classToTagsMap[identifier]; | |
| 4776 } | |
| 4777 | |
| 4778 /** | |
| 4779 * Return the tag that has the given identifier, or {@code null} if there is n
o such tag (the | |
| 4780 * identifier is not defined). | |
| 4781 * | |
| 4782 * @return the tag that has the given identifier | |
| 4783 */ | |
| 4784 String getTagWithId(String identifier) { | |
| 4785 return idToTagMap[identifier]; | |
| 4786 } | |
| 4787 } | |
| 4788 | |
| 4789 /** | |
| 4790 * Instances of the class {@code HtmlTagInfoBuilder} gather information about th
e tags used in one | |
| 4791 * or more HTML structures. | |
| 4792 */ | |
| 4793 class HtmlTagInfoBuilder implements ht.XmlVisitor { | |
| 4794 /** | |
| 4795 * The name of the 'id' attribute. | |
| 4796 */ | |
| 4797 static final String ID_ATTRIBUTE = "id"; | |
| 4798 | |
| 4799 /** | |
| 4800 * The name of the 'class' attribute. | |
| 4801 */ | |
| 4802 static final String ID_CLASS = "class"; | |
| 4803 | |
| 4804 /** | |
| 4805 * A set containing all of the tag names used in the HTML. | |
| 4806 */ | |
| 4807 HashSet<String> tagSet = new HashSet<String>(); | |
| 4808 | |
| 4809 /** | |
| 4810 * A table mapping the id's that are defined to the tag name with that id. | |
| 4811 */ | |
| 4812 HashMap<String, String> idMap = new HashMap<String, String>(); | |
| 4813 | |
| 4814 /** | |
| 4815 * A table mapping the classes that are defined to a set of the tag names with
that class. | |
| 4816 */ | |
| 4817 HashMap<String, HashSet<String>> classMap = | |
| 4818 new HashMap<String, HashSet<String>>(); | |
| 4819 | |
| 4820 /** | |
| 4821 * Initialize a newly created HTML tag info builder. | |
| 4822 */ | |
| 4823 HtmlTagInfoBuilder(); | |
| 4824 | |
| 4825 /** | |
| 4826 * Create a tag information holder holding all of the information gathered abo
ut the tags in the | |
| 4827 * HTML structures that were visited. | |
| 4828 * | |
| 4829 * @return the information gathered about the tags in the visited HTML structu
res | |
| 4830 */ | |
| 4831 HtmlTagInfo getTagInfo() { | |
| 4832 List<String> allTags = tagSet.toList(); | |
| 4833 HashMap<String, List<String>> classToTagsMap = | |
| 4834 new HashMap<String, List<String>>(); | |
| 4835 classMap.forEach((String key, Set<String> tags) { | |
| 4836 classToTagsMap[key] = tags.toList(); | |
| 4837 }); | |
| 4838 return new HtmlTagInfo(allTags, idMap, classToTagsMap); | |
| 4839 } | |
| 4840 | |
| 4841 @override | |
| 4842 visitHtmlScriptTagNode(ht.HtmlScriptTagNode node) { | |
| 4843 visitXmlTagNode(node); | |
| 4844 } | |
| 4845 | |
| 4846 @override | |
| 4847 visitHtmlUnit(ht.HtmlUnit node) { | |
| 4848 node.visitChildren(this); | |
| 4849 } | |
| 4850 | |
| 4851 @override | |
| 4852 visitXmlAttributeNode(ht.XmlAttributeNode node) {} | |
| 4853 | |
| 4854 @override | |
| 4855 visitXmlTagNode(ht.XmlTagNode node) { | |
| 4856 node.visitChildren(this); | |
| 4857 String tagName = node.tag; | |
| 4858 tagSet.add(tagName); | |
| 4859 for (ht.XmlAttributeNode attribute in node.attributes) { | |
| 4860 String attributeName = attribute.name; | |
| 4861 if (attributeName == ID_ATTRIBUTE) { | |
| 4862 String attributeValue = attribute.text; | |
| 4863 if (attributeValue != null) { | |
| 4864 String tag = idMap[attributeValue]; | |
| 4865 if (tag == null) { | |
| 4866 idMap[attributeValue] = tagName; | |
| 4867 } else { | |
| 4868 // reportError(HtmlWarningCode.MULTIPLY_DEFINED_ID, valueToken); | |
| 4869 } | |
| 4870 } | |
| 4871 } else if (attributeName == ID_CLASS) { | |
| 4872 String attributeValue = attribute.text; | |
| 4873 if (attributeValue != null) { | |
| 4874 HashSet<String> tagList = classMap[attributeValue]; | |
| 4875 if (tagList == null) { | |
| 4876 tagList = new HashSet<String>(); | |
| 4877 classMap[attributeValue] = tagList; | |
| 4878 } else { | |
| 4879 // reportError(HtmlWarningCode.MULTIPLY_DEFINED_ID, valueToken); | |
| 4880 } | |
| 4881 tagList.add(tagName); | |
| 4882 } | |
| 4883 } | |
| 4884 } | |
| 4885 } | |
| 4886 | |
| 4887 // /** | |
| 4888 // * Report an error with the given error code at the given location. Use the
given arguments to | |
| 4889 // * compose the error message. | |
| 4890 // * | |
| 4891 // * @param errorCode the error code of the error to be reported | |
| 4892 // * @param offset the offset of the first character to be highlighted | |
| 4893 // * @param length the number of characters to be highlighted | |
| 4894 // * @param arguments the arguments used to compose the error message | |
| 4895 // */ | |
| 4896 // private void reportError(ErrorCode errorCode, Token token, Object... argumen
ts) { | |
| 4897 // errorListener.onError(new AnalysisError( | |
| 4898 // htmlElement.getSource(), | |
| 4899 // token.getOffset(), | |
| 4900 // token.getLength(), | |
| 4901 // errorCode, | |
| 4902 // arguments)); | |
| 4903 // } | |
| 4904 // | |
| 4905 // /** | |
| 4906 // * Report an error with the given error code at the given location. Use the
given arguments to | |
| 4907 // * compose the error message. | |
| 4908 // * | |
| 4909 // * @param errorCode the error code of the error to be reported | |
| 4910 // * @param offset the offset of the first character to be highlighted | |
| 4911 // * @param length the number of characters to be highlighted | |
| 4912 // * @param arguments the arguments used to compose the error message | |
| 4913 // */ | |
| 4914 // private void reportError(ErrorCode errorCode, int offset, int length, Object
... arguments) { | |
| 4915 // errorListener.onError(new AnalysisError( | |
| 4916 // htmlElement.getSource(), | |
| 4917 // offset, | |
| 4918 // length, | |
| 4919 // errorCode, | |
| 4920 // arguments)); | |
| 4921 // } | |
| 4922 } | |
| 4923 | |
| 4924 /** | |
| 4925 * Instances of the class `HtmlUnitBuilder` build an element model for a single
HTML unit. | |
| 4926 */ | |
| 4927 class HtmlUnitBuilder implements ht.XmlVisitor<Object> { | |
| 4928 static String _SRC = "src"; | |
| 4929 | |
| 4930 /** | |
| 4931 * The analysis context in which the element model will be built. | |
| 4932 */ | |
| 4933 final InternalAnalysisContext _context; | |
| 4934 | |
| 4935 /** | |
| 4936 * The error listener to which errors will be reported. | |
| 4937 */ | |
| 4938 RecordingErrorListener _errorListener; | |
| 4939 | |
| 4940 /** | |
| 4941 * The HTML element being built. | |
| 4942 */ | |
| 4943 HtmlElementImpl _htmlElement; | |
| 4944 | |
| 4945 /** | |
| 4946 * The elements in the path from the HTML unit to the current tag node. | |
| 4947 */ | |
| 4948 List<ht.XmlTagNode> _parentNodes; | |
| 4949 | |
| 4950 /** | |
| 4951 * The script elements being built. | |
| 4952 */ | |
| 4953 List<HtmlScriptElement> _scripts; | |
| 4954 | |
| 4955 /** | |
| 4956 * A set of the libraries that were resolved while resolving the HTML unit. | |
| 4957 */ | |
| 4958 Set<Library> _resolvedLibraries = new HashSet<Library>(); | |
| 4959 | |
| 4960 /** | |
| 4961 * Initialize a newly created HTML unit builder. | |
| 4962 * | |
| 4963 * @param context the analysis context in which the element model will be buil
t | |
| 4964 */ | |
| 4965 HtmlUnitBuilder(this._context) { | |
| 4966 this._errorListener = new RecordingErrorListener(); | |
| 4967 } | |
| 4968 | |
| 4969 /** | |
| 4970 * Return the listener to which analysis errors will be reported. | |
| 4971 * | |
| 4972 * @return the listener to which analysis errors will be reported | |
| 4973 */ | |
| 4974 RecordingErrorListener get errorListener => _errorListener; | |
| 4975 | |
| 4976 /** | |
| 4977 * Return an array containing information about all of the libraries that were
resolved. | |
| 4978 * | |
| 4979 * @return an array containing the libraries that were resolved | |
| 4980 */ | |
| 4981 Set<Library> get resolvedLibraries => _resolvedLibraries; | |
| 4982 | |
| 4983 /** | |
| 4984 * Build the HTML element for the given source. | |
| 4985 * | |
| 4986 * @param source the source describing the compilation unit | |
| 4987 * @param unit the AST structure representing the HTML | |
| 4988 * @throws AnalysisException if the analysis could not be performed | |
| 4989 */ | |
| 4990 HtmlElementImpl buildHtmlElement(Source source, ht.HtmlUnit unit) { | |
| 4991 HtmlElementImpl result = new HtmlElementImpl(_context, source.shortName); | |
| 4992 result.source = source; | |
| 4993 _htmlElement = result; | |
| 4994 unit.accept(this); | |
| 4995 _htmlElement = null; | |
| 4996 unit.element = result; | |
| 4997 return result; | |
| 4998 } | |
| 4999 | |
| 5000 @override | |
| 5001 Object visitHtmlScriptTagNode(ht.HtmlScriptTagNode node) { | |
| 5002 if (_parentNodes.contains(node)) { | |
| 5003 return _reportCircularity(node); | |
| 5004 } | |
| 5005 _parentNodes.add(node); | |
| 5006 try { | |
| 5007 Source htmlSource = _htmlElement.source; | |
| 5008 ht.XmlAttributeNode scriptAttribute = _getScriptSourcePath(node); | |
| 5009 String scriptSourcePath = | |
| 5010 scriptAttribute == null ? null : scriptAttribute.text; | |
| 5011 if (node.attributeEnd.type == ht.TokenType.GT && | |
| 5012 scriptSourcePath == null) { | |
| 5013 EmbeddedHtmlScriptElementImpl script = | |
| 5014 new EmbeddedHtmlScriptElementImpl(node); | |
| 5015 try { | |
| 5016 LibraryResolver resolver = new LibraryResolver(_context); | |
| 5017 LibraryElementImpl library = | |
| 5018 resolver.resolveEmbeddedLibrary(htmlSource, node.script, true); | |
| 5019 script.scriptLibrary = library; | |
| 5020 _resolvedLibraries.addAll(resolver.resolvedLibraries); | |
| 5021 _errorListener.addAll(resolver.errorListener); | |
| 5022 } on AnalysisException catch (exception, stackTrace) { | |
| 5023 //TODO (danrubel): Handle or forward the exception | |
| 5024 AnalysisEngine.instance.logger.logError( | |
| 5025 "Could not resolve script tag", | |
| 5026 new CaughtException(exception, stackTrace)); | |
| 5027 } | |
| 5028 node.scriptElement = script; | |
| 5029 _scripts.add(script); | |
| 5030 } else { | |
| 5031 ExternalHtmlScriptElementImpl script = | |
| 5032 new ExternalHtmlScriptElementImpl(node); | |
| 5033 if (scriptSourcePath != null) { | |
| 5034 try { | |
| 5035 scriptSourcePath = Uri.encodeFull(scriptSourcePath); | |
| 5036 // Force an exception to be thrown if the URI is invalid so that we | |
| 5037 // can report the problem. | |
| 5038 parseUriWithException(scriptSourcePath); | |
| 5039 Source scriptSource = | |
| 5040 _context.sourceFactory.resolveUri(htmlSource, scriptSourcePath); | |
| 5041 script.scriptSource = scriptSource; | |
| 5042 if (!_context.exists(scriptSource)) { | |
| 5043 _reportValueError(HtmlWarningCode.URI_DOES_NOT_EXIST, | |
| 5044 scriptAttribute, [scriptSourcePath]); | |
| 5045 } | |
| 5046 } on URISyntaxException { | |
| 5047 _reportValueError(HtmlWarningCode.INVALID_URI, scriptAttribute, | |
| 5048 [scriptSourcePath]); | |
| 5049 } | |
| 5050 } | |
| 5051 node.scriptElement = script; | |
| 5052 _scripts.add(script); | |
| 5053 } | |
| 5054 } finally { | |
| 5055 _parentNodes.remove(node); | |
| 5056 } | |
| 5057 return null; | |
| 5058 } | |
| 5059 | |
| 5060 @override | |
| 5061 Object visitHtmlUnit(ht.HtmlUnit node) { | |
| 5062 _parentNodes = new List<ht.XmlTagNode>(); | |
| 5063 _scripts = new List<HtmlScriptElement>(); | |
| 5064 try { | |
| 5065 node.visitChildren(this); | |
| 5066 _htmlElement.scripts = new List.from(_scripts); | |
| 5067 } finally { | |
| 5068 _scripts = null; | |
| 5069 _parentNodes = null; | |
| 5070 } | |
| 5071 return null; | |
| 5072 } | |
| 5073 | |
| 5074 @override | |
| 5075 Object visitXmlAttributeNode(ht.XmlAttributeNode node) => null; | |
| 5076 | |
| 5077 @override | |
| 5078 Object visitXmlTagNode(ht.XmlTagNode node) { | |
| 5079 if (_parentNodes.contains(node)) { | |
| 5080 return _reportCircularity(node); | |
| 5081 } | |
| 5082 _parentNodes.add(node); | |
| 5083 try { | |
| 5084 node.visitChildren(this); | |
| 5085 } finally { | |
| 5086 _parentNodes.remove(node); | |
| 5087 } | |
| 5088 return null; | |
| 5089 } | |
| 5090 | |
| 5091 /** | |
| 5092 * Return the first source attribute for the given tag node, or `null` if it d
oes not exist. | |
| 5093 * | |
| 5094 * @param node the node containing attributes | |
| 5095 * @return the source attribute contained in the given tag | |
| 5096 */ | |
| 5097 ht.XmlAttributeNode _getScriptSourcePath(ht.XmlTagNode node) { | |
| 5098 for (ht.XmlAttributeNode attribute in node.attributes) { | |
| 5099 if (attribute.name == _SRC) { | |
| 5100 return attribute; | |
| 5101 } | |
| 5102 } | |
| 5103 return null; | |
| 5104 } | |
| 5105 | |
| 5106 Object _reportCircularity(ht.XmlTagNode node) { | |
| 5107 // | |
| 5108 // This should not be possible, but we have an error report that suggests | |
| 5109 // that it happened at least once. This code will guard against infinite | |
| 5110 // recursion and might help us identify the cause of the issue. | |
| 5111 // | |
| 5112 StringBuffer buffer = new StringBuffer(); | |
| 5113 buffer.write("Found circularity in XML nodes: "); | |
| 5114 bool first = true; | |
| 5115 for (ht.XmlTagNode pathNode in _parentNodes) { | |
| 5116 if (first) { | |
| 5117 first = false; | |
| 5118 } else { | |
| 5119 buffer.write(", "); | |
| 5120 } | |
| 5121 String tagName = pathNode.tag; | |
| 5122 if (identical(pathNode, node)) { | |
| 5123 buffer.write("*"); | |
| 5124 buffer.write(tagName); | |
| 5125 buffer.write("*"); | |
| 5126 } else { | |
| 5127 buffer.write(tagName); | |
| 5128 } | |
| 5129 } | |
| 5130 AnalysisEngine.instance.logger.logError(buffer.toString()); | |
| 5131 return null; | |
| 5132 } | |
| 5133 | |
| 5134 /** | |
| 5135 * Report an error with the given error code at the given location. Use the gi
ven arguments to | |
| 5136 * compose the error message. | |
| 5137 * | |
| 5138 * @param errorCode the error code of the error to be reported | |
| 5139 * @param offset the offset of the first character to be highlighted | |
| 5140 * @param length the number of characters to be highlighted | |
| 5141 * @param arguments the arguments used to compose the error message | |
| 5142 */ | |
| 5143 void _reportErrorForOffset( | |
| 5144 ErrorCode errorCode, int offset, int length, List<Object> arguments) { | |
| 5145 _errorListener.onError(new AnalysisError( | |
| 5146 _htmlElement.source, offset, length, errorCode, arguments)); | |
| 5147 } | |
| 5148 | |
| 5149 /** | |
| 5150 * Report an error with the given error code at the location of the value of t
he given attribute. | |
| 5151 * Use the given arguments to compose the error message. | |
| 5152 * | |
| 5153 * @param errorCode the error code of the error to be reported | |
| 5154 * @param offset the offset of the first character to be highlighted | |
| 5155 * @param length the number of characters to be highlighted | |
| 5156 * @param arguments the arguments used to compose the error message | |
| 5157 */ | |
| 5158 void _reportValueError(ErrorCode errorCode, ht.XmlAttributeNode attribute, | |
| 5159 List<Object> arguments) { | |
| 5160 int offset = attribute.valueToken.offset + 1; | |
| 5161 int length = attribute.valueToken.length - 2; | |
| 5162 _reportErrorForOffset(errorCode, offset, length, arguments); | |
| 5163 } | |
| 5164 } | |
| 5165 | |
| 5166 /** | |
| 5167 * Instances of the class `ImplicitLabelScope` represent the scope statements | |
| 5168 * that can be the target of unlabeled break and continue statements. | |
| 5169 */ | |
| 5170 class ImplicitLabelScope { | |
| 5171 /** | |
| 5172 * The implicit label scope associated with the top level of a function. | |
| 5173 */ | |
| 5174 static const ImplicitLabelScope ROOT = const ImplicitLabelScope._(null, null); | |
| 5175 | |
| 5176 /** | |
| 5177 * The implicit label scope enclosing this implicit label scope. | |
| 5178 */ | |
| 5179 final ImplicitLabelScope outerScope; | |
| 5180 | |
| 5181 /** | |
| 5182 * The statement that acts as a target for break and/or continue statements | |
| 5183 * at this scoping level. | |
| 5184 */ | |
| 5185 final Statement statement; | |
| 5186 | |
| 5187 /** | |
| 5188 * Private constructor. | |
| 5189 */ | |
| 5190 const ImplicitLabelScope._(this.outerScope, this.statement); | |
| 5191 | |
| 5192 /** | |
| 5193 * Get the statement which should be the target of an unlabeled `break` or | |
| 5194 * `continue` statement, or `null` if there is no appropriate target. | |
| 5195 */ | |
| 5196 Statement getTarget(bool isContinue) { | |
| 5197 if (outerScope == null) { | |
| 5198 // This scope represents the toplevel of a function body, so it doesn't | |
| 5199 // match either break or continue. | |
| 5200 return null; | |
| 5201 } | |
| 5202 if (isContinue && statement is SwitchStatement) { | |
| 5203 return outerScope.getTarget(isContinue); | |
| 5204 } | |
| 5205 return statement; | |
| 5206 } | |
| 5207 | |
| 5208 /** | |
| 5209 * Initialize a newly created scope to represent a switch statement or loop | |
| 5210 * nested within the current scope. [statement] is the statement associated | |
| 5211 * with the newly created scope. | |
| 5212 */ | |
| 5213 ImplicitLabelScope nest(Statement statement) => | |
| 5214 new ImplicitLabelScope._(this, statement); | |
| 5215 } | |
| 5216 | |
| 5217 /** | |
| 5218 * Instances of the class `ImportsVerifier` visit all of the referenced librarie
s in the | |
| 5219 * source code verifying that all of the imports are used, otherwise a | |
| 5220 * [HintCode.UNUSED_IMPORT] is generated with | |
| 5221 * [generateUnusedImportHints]. | |
| 5222 * | |
| 5223 * While this class does not yet have support for an "Organize Imports" action,
this logic built up | |
| 5224 * in this class could be used for such an action in the future. | |
| 5225 */ | |
| 5226 class ImportsVerifier /*extends RecursiveAstVisitor<Object>*/ { | |
| 5227 /** | |
| 5228 * A list of [ImportDirective]s that the current library imports, as identifie
rs are visited | |
| 5229 * by this visitor and an import has been identified as being used by the libr
ary, the | |
| 5230 * [ImportDirective] is removed from this list. After all the sources in the l
ibrary have | |
| 5231 * been evaluated, this list represents the set of unused imports. | |
| 5232 * | |
| 5233 * See [ImportsVerifier.generateUnusedImportErrors]. | |
| 5234 */ | |
| 5235 final List<ImportDirective> _unusedImports = <ImportDirective>[]; | |
| 5236 | |
| 5237 /** | |
| 5238 * After the list of [unusedImports] has been computed, this list is a proper
subset of the | |
| 5239 * unused imports that are listed more than once. | |
| 5240 */ | |
| 5241 final List<ImportDirective> _duplicateImports = <ImportDirective>[]; | |
| 5242 | |
| 5243 /** | |
| 5244 * This is a map between the set of [LibraryElement]s that the current library
imports, and | |
| 5245 * a list of [ImportDirective]s that imports the library. In cases where the c
urrent library | |
| 5246 * imports a library with a single directive (such as `import lib1.dart;`), th
e library | |
| 5247 * element will map to a list of one [ImportDirective], which will then be rem
oved from the | |
| 5248 * [unusedImports] list. In cases where the current library imports a library
with multiple | |
| 5249 * directives (such as `import lib1.dart; import lib1.dart show C;`), the | |
| 5250 * [LibraryElement] will be mapped to a list of the import directives, and the
namespace | |
| 5251 * will need to be used to compute the correct [ImportDirective] being used, s
ee | |
| 5252 * [namespaceMap]. | |
| 5253 */ | |
| 5254 final HashMap<LibraryElement, List<ImportDirective>> _libraryMap = | |
| 5255 new HashMap<LibraryElement, List<ImportDirective>>(); | |
| 5256 | |
| 5257 /** | |
| 5258 * In cases where there is more than one import directive per library element,
this mapping is | |
| 5259 * used to determine which of the multiple import directives are used by gener
ating a | |
| 5260 * [Namespace] for each of the imports to do lookups in the same way that they
are done from | |
| 5261 * the [ElementResolver]. | |
| 5262 */ | |
| 5263 final HashMap<ImportDirective, Namespace> _namespaceMap = | |
| 5264 new HashMap<ImportDirective, Namespace>(); | |
| 5265 | |
| 5266 /** | |
| 5267 * This is a map between prefix elements and the import directives from which
they are derived. In | |
| 5268 * cases where a type is referenced via a prefix element, the import directive
can be marked as | |
| 5269 * used (removed from the unusedImports) by looking at the resolved `lib` in `
lib.X`, | |
| 5270 * instead of looking at which library the `lib.X` resolves. | |
| 5271 * | |
| 5272 * TODO (jwren) Since multiple [ImportDirective]s can share the same [PrefixEl
ement], | |
| 5273 * it is possible to have an unreported unused import in situations where two
imports use the same | |
| 5274 * prefix and at least one import directive is used. | |
| 5275 */ | |
| 5276 final HashMap<PrefixElement, List<ImportDirective>> _prefixElementMap = | |
| 5277 new HashMap<PrefixElement, List<ImportDirective>>(); | |
| 5278 | |
| 5279 void addImports(CompilationUnit node) { | |
| 5280 for (Directive directive in node.directives) { | |
| 5281 if (directive is ImportDirective) { | |
| 5282 ImportDirective importDirective = directive; | |
| 5283 LibraryElement libraryElement = importDirective.uriElement; | |
| 5284 if (libraryElement != null) { | |
| 5285 _unusedImports.add(importDirective); | |
| 5286 // | |
| 5287 // Initialize prefixElementMap | |
| 5288 // | |
| 5289 if (importDirective.asKeyword != null) { | |
| 5290 SimpleIdentifier prefixIdentifier = importDirective.prefix; | |
| 5291 if (prefixIdentifier != null) { | |
| 5292 Element element = prefixIdentifier.staticElement; | |
| 5293 if (element is PrefixElement) { | |
| 5294 PrefixElement prefixElementKey = element; | |
| 5295 List<ImportDirective> list = | |
| 5296 _prefixElementMap[prefixElementKey]; | |
| 5297 if (list == null) { | |
| 5298 list = new List<ImportDirective>(); | |
| 5299 _prefixElementMap[prefixElementKey] = list; | |
| 5300 } | |
| 5301 list.add(importDirective); | |
| 5302 } | |
| 5303 // TODO (jwren) Can the element ever not be a PrefixElement? | |
| 5304 } | |
| 5305 } | |
| 5306 // | |
| 5307 // Initialize libraryMap: libraryElement -> importDirective | |
| 5308 // | |
| 5309 _putIntoLibraryMap(libraryElement, importDirective); | |
| 5310 // | |
| 5311 // For this new addition to the libraryMap, also recursively add any | |
| 5312 // exports from the libraryElement. | |
| 5313 // | |
| 5314 _addAdditionalLibrariesForExports( | |
| 5315 libraryElement, importDirective, new List<LibraryElement>()); | |
| 5316 } | |
| 5317 } | |
| 5318 } | |
| 5319 if (_unusedImports.length > 1) { | |
| 5320 // order the list of unusedImports to find duplicates in faster than | |
| 5321 // O(n^2) time | |
| 5322 List<ImportDirective> importDirectiveArray = | |
| 5323 new List<ImportDirective>.from(_unusedImports); | |
| 5324 importDirectiveArray.sort(ImportDirective.COMPARATOR); | |
| 5325 ImportDirective currentDirective = importDirectiveArray[0]; | |
| 5326 for (int i = 1; i < importDirectiveArray.length; i++) { | |
| 5327 ImportDirective nextDirective = importDirectiveArray[i]; | |
| 5328 if (ImportDirective.COMPARATOR(currentDirective, nextDirective) == 0) { | |
| 5329 // Add either the currentDirective or nextDirective depending on which | |
| 5330 // comes second, this guarantees that the first of the duplicates | |
| 5331 // won't be highlighted. | |
| 5332 if (currentDirective.offset < nextDirective.offset) { | |
| 5333 _duplicateImports.add(nextDirective); | |
| 5334 } else { | |
| 5335 _duplicateImports.add(currentDirective); | |
| 5336 } | |
| 5337 } | |
| 5338 currentDirective = nextDirective; | |
| 5339 } | |
| 5340 } | |
| 5341 } | |
| 5342 | |
| 5343 /** | |
| 5344 * Any time after the defining compilation unit has been visited by this visit
or, this method can | |
| 5345 * be called to report an [HintCode.DUPLICATE_IMPORT] hint for each of the imp
ort directives | |
| 5346 * in the [duplicateImports] list. | |
| 5347 * | |
| 5348 * @param errorReporter the error reporter to report the set of [HintCode.DUPL
ICATE_IMPORT] | |
| 5349 * hints to | |
| 5350 */ | |
| 5351 void generateDuplicateImportHints(ErrorReporter errorReporter) { | |
| 5352 for (ImportDirective duplicateImport in _duplicateImports) { | |
| 5353 errorReporter.reportErrorForNode( | |
| 5354 HintCode.DUPLICATE_IMPORT, duplicateImport.uri); | |
| 5355 } | |
| 5356 } | |
| 5357 | |
| 5358 /** | |
| 5359 * After all of the compilation units have been visited by this visitor, this
method can be called | |
| 5360 * to report an [HintCode.UNUSED_IMPORT] hint for each of the import directive
s in the | |
| 5361 * [unusedImports] list. | |
| 5362 * | |
| 5363 * @param errorReporter the error reporter to report the set of [HintCode.UNUS
ED_IMPORT] | |
| 5364 * hints to | |
| 5365 */ | |
| 5366 void generateUnusedImportHints(ErrorReporter errorReporter) { | |
| 5367 for (ImportDirective unusedImport in _unusedImports) { | |
| 5368 // Check that the import isn't dart:core | |
| 5369 ImportElement importElement = unusedImport.element; | |
| 5370 if (importElement != null) { | |
| 5371 LibraryElement libraryElement = importElement.importedLibrary; | |
| 5372 if (libraryElement != null && libraryElement.isDartCore) { | |
| 5373 continue; | |
| 5374 } | |
| 5375 } | |
| 5376 errorReporter.reportErrorForNode( | |
| 5377 HintCode.UNUSED_IMPORT, unusedImport.uri); | |
| 5378 } | |
| 5379 } | |
| 5380 | |
| 5381 /** | |
| 5382 * Remove elements from [_unusedImports] using the given [usedElements]. | |
| 5383 */ | |
| 5384 void removeUsedElements(UsedImportedElements usedElements) { | |
| 5385 // Stop if all the imports are known to be used. | |
| 5386 if (_unusedImports.isEmpty) { | |
| 5387 return; | |
| 5388 } | |
| 5389 // Process import prefixes. | |
| 5390 for (PrefixElement prefix in usedElements.prefixes) { | |
| 5391 List<ImportDirective> importDirectives = _prefixElementMap[prefix]; | |
| 5392 if (importDirectives != null) { | |
| 5393 for (ImportDirective importDirective in importDirectives) { | |
| 5394 _unusedImports.remove(importDirective); | |
| 5395 } | |
| 5396 } | |
| 5397 } | |
| 5398 // Process top-level elements. | |
| 5399 for (Element element in usedElements.elements) { | |
| 5400 // Stop if all the imports are known to be used. | |
| 5401 if (_unusedImports.isEmpty) { | |
| 5402 return; | |
| 5403 } | |
| 5404 // Prepare import directives for this library. | |
| 5405 LibraryElement library = element.library; | |
| 5406 List<ImportDirective> importsLibrary = _libraryMap[library]; | |
| 5407 if (importsLibrary == null) { | |
| 5408 continue; | |
| 5409 } | |
| 5410 // If there is only one import directive for this library, then it must be | |
| 5411 // the directive that this element is imported with, remove it from the | |
| 5412 // unusedImports list. | |
| 5413 if (importsLibrary.length == 1) { | |
| 5414 ImportDirective usedImportDirective = importsLibrary[0]; | |
| 5415 _unusedImports.remove(usedImportDirective); | |
| 5416 continue; | |
| 5417 } | |
| 5418 // Otherwise, find import directives using namespaces. | |
| 5419 String name = element.displayName; | |
| 5420 for (ImportDirective importDirective in importsLibrary) { | |
| 5421 Namespace namespace = _computeNamespace(importDirective); | |
| 5422 if (namespace != null && namespace.get(name) != null) { | |
| 5423 _unusedImports.remove(importDirective); | |
| 5424 } | |
| 5425 } | |
| 5426 } | |
| 5427 } | |
| 5428 | |
| 5429 /** | |
| 5430 * Recursively add any exported library elements into the [libraryMap]. | |
| 5431 */ | |
| 5432 void _addAdditionalLibrariesForExports(LibraryElement library, | |
| 5433 ImportDirective importDirective, List<LibraryElement> exportPath) { | |
| 5434 if (exportPath.contains(library)) { | |
| 5435 return; | |
| 5436 } | |
| 5437 exportPath.add(library); | |
| 5438 for (LibraryElement exportedLibraryElt in library.exportedLibraries) { | |
| 5439 _putIntoLibraryMap(exportedLibraryElt, importDirective); | |
| 5440 _addAdditionalLibrariesForExports( | |
| 5441 exportedLibraryElt, importDirective, exportPath); | |
| 5442 } | |
| 5443 } | |
| 5444 | |
| 5445 /** | |
| 5446 * Lookup and return the [Namespace] from the [namespaceMap], if the map does
not | |
| 5447 * have the computed namespace, compute it and cache it in the map. If the imp
ort directive is not | |
| 5448 * resolved or is not resolvable, `null` is returned. | |
| 5449 * | |
| 5450 * @param importDirective the import directive used to compute the returned na
mespace | |
| 5451 * @return the computed or looked up [Namespace] | |
| 5452 */ | |
| 5453 Namespace _computeNamespace(ImportDirective importDirective) { | |
| 5454 Namespace namespace = _namespaceMap[importDirective]; | |
| 5455 if (namespace == null) { | |
| 5456 // If the namespace isn't in the namespaceMap, then compute and put it in | |
| 5457 // the map. | |
| 5458 ImportElement importElement = importDirective.element; | |
| 5459 if (importElement != null) { | |
| 5460 NamespaceBuilder builder = new NamespaceBuilder(); | |
| 5461 namespace = builder.createImportNamespaceForDirective(importElement); | |
| 5462 _namespaceMap[importDirective] = namespace; | |
| 5463 } | |
| 5464 } | |
| 5465 return namespace; | |
| 5466 } | |
| 5467 | |
| 5468 /** | |
| 5469 * The [libraryMap] is a mapping between a library elements and a list of impo
rt | |
| 5470 * directives, but when adding these mappings into the [libraryMap], this meth
od can be | |
| 5471 * used to simply add the mapping between the library element an an import dir
ective without | |
| 5472 * needing to check to see if a list needs to be created. | |
| 5473 */ | |
| 5474 void _putIntoLibraryMap( | |
| 5475 LibraryElement libraryElement, ImportDirective importDirective) { | |
| 5476 List<ImportDirective> importList = _libraryMap[libraryElement]; | |
| 5477 if (importList == null) { | |
| 5478 importList = new List<ImportDirective>(); | |
| 5479 _libraryMap[libraryElement] = importList; | |
| 5480 } | |
| 5481 importList.add(importDirective); | |
| 5482 } | |
| 5483 } | |
| 5484 | |
| 5485 /** | |
| 5486 * Instances of the class `InheritanceManager` manage the knowledge of where cla
ss members | |
| 5487 * (methods, getters & setters) are inherited from. | |
| 5488 */ | |
| 5489 class InheritanceManager { | |
| 5490 /** | |
| 5491 * The [LibraryElement] that is managed by this manager. | |
| 5492 */ | |
| 5493 LibraryElement _library; | |
| 5494 | |
| 5495 /** | |
| 5496 * This is a mapping between each [ClassElement] and a map between the [String
] member | |
| 5497 * names and the associated [ExecutableElement] in the mixin and superclass ch
ain. | |
| 5498 */ | |
| 5499 HashMap<ClassElement, MemberMap> _classLookup; | |
| 5500 | |
| 5501 /** | |
| 5502 * This is a mapping between each [ClassElement] and a map between the [String
] member | |
| 5503 * names and the associated [ExecutableElement] in the interface set. | |
| 5504 */ | |
| 5505 HashMap<ClassElement, MemberMap> _interfaceLookup; | |
| 5506 | |
| 5507 /** | |
| 5508 * A map between each visited [ClassElement] and the set of [AnalysisError]s f
ound on | |
| 5509 * the class element. | |
| 5510 */ | |
| 5511 HashMap<ClassElement, HashSet<AnalysisError>> _errorsInClassElement = | |
| 5512 new HashMap<ClassElement, HashSet<AnalysisError>>(); | |
| 5513 | |
| 5514 /** | |
| 5515 * Initialize a newly created inheritance manager. | |
| 5516 * | |
| 5517 * @param library the library element context that the inheritance mappings ar
e being generated | |
| 5518 */ | |
| 5519 InheritanceManager(LibraryElement library) { | |
| 5520 this._library = library; | |
| 5521 _classLookup = new HashMap<ClassElement, MemberMap>(); | |
| 5522 _interfaceLookup = new HashMap<ClassElement, MemberMap>(); | |
| 5523 } | |
| 5524 | |
| 5525 /** | |
| 5526 * Set the new library element context. | |
| 5527 * | |
| 5528 * @param library the new library element | |
| 5529 */ | |
| 5530 void set libraryElement(LibraryElement library) { | |
| 5531 this._library = library; | |
| 5532 } | |
| 5533 | |
| 5534 /** | |
| 5535 * Return the set of [AnalysisError]s found on the passed [ClassElement], or | |
| 5536 * `null` if there are none. | |
| 5537 * | |
| 5538 * @param classElt the class element to query | |
| 5539 * @return the set of [AnalysisError]s found on the passed [ClassElement], or | |
| 5540 * `null` if there are none | |
| 5541 */ | |
| 5542 HashSet<AnalysisError> getErrors(ClassElement classElt) => | |
| 5543 _errorsInClassElement[classElt]; | |
| 5544 | |
| 5545 /** | |
| 5546 * Get and return a mapping between the set of all string names of the members
inherited from the | |
| 5547 * passed [ClassElement] superclass hierarchy, and the associated [ExecutableE
lement]. | |
| 5548 * | |
| 5549 * @param classElt the class element to query | |
| 5550 * @return a mapping between the set of all members inherited from the passed
[ClassElement] | |
| 5551 * superclass hierarchy, and the associated [ExecutableElement] | |
| 5552 */ | |
| 5553 MemberMap getMapOfMembersInheritedFromClasses(ClassElement classElt) => | |
| 5554 _computeClassChainLookupMap(classElt, new HashSet<ClassElement>()); | |
| 5555 | |
| 5556 /** | |
| 5557 * Get and return a mapping between the set of all string names of the members
inherited from the | |
| 5558 * passed [ClassElement] interface hierarchy, and the associated [ExecutableEl
ement]. | |
| 5559 * | |
| 5560 * @param classElt the class element to query | |
| 5561 * @return a mapping between the set of all string names of the members inheri
ted from the passed | |
| 5562 * [ClassElement] interface hierarchy, and the associated [ExecutableE
lement]. | |
| 5563 */ | |
| 5564 MemberMap getMapOfMembersInheritedFromInterfaces(ClassElement classElt) => | |
| 5565 _computeInterfaceLookupMap(classElt, new HashSet<ClassElement>()); | |
| 5566 | |
| 5567 /** | |
| 5568 * Given some [ClassElement] and some member name, this returns the | |
| 5569 * [ExecutableElement] that the class inherits from the mixins, | |
| 5570 * superclasses or interfaces, that has the member name, if no member is inher
ited `null` is | |
| 5571 * returned. | |
| 5572 * | |
| 5573 * @param classElt the class element to query | |
| 5574 * @param memberName the name of the executable element to find and return | |
| 5575 * @return the inherited executable element with the member name, or `null` if
no such | |
| 5576 * member exists | |
| 5577 */ | |
| 5578 ExecutableElement lookupInheritance( | |
| 5579 ClassElement classElt, String memberName) { | |
| 5580 if (memberName == null || memberName.isEmpty) { | |
| 5581 return null; | |
| 5582 } | |
| 5583 ExecutableElement executable = _computeClassChainLookupMap( | |
| 5584 classElt, new HashSet<ClassElement>()).get(memberName); | |
| 5585 if (executable == null) { | |
| 5586 return _computeInterfaceLookupMap(classElt, new HashSet<ClassElement>()) | |
| 5587 .get(memberName); | |
| 5588 } | |
| 5589 return executable; | |
| 5590 } | |
| 5591 | |
| 5592 /** | |
| 5593 * Given some [ClassElement] and some member name, this returns the | |
| 5594 * [ExecutableElement] that the class either declares itself, or | |
| 5595 * inherits, that has the member name, if no member is inherited `null` is ret
urned. | |
| 5596 * | |
| 5597 * @param classElt the class element to query | |
| 5598 * @param memberName the name of the executable element to find and return | |
| 5599 * @return the inherited executable element with the member name, or `null` if
no such | |
| 5600 * member exists | |
| 5601 */ | |
| 5602 ExecutableElement lookupMember(ClassElement classElt, String memberName) { | |
| 5603 ExecutableElement element = _lookupMemberInClass(classElt, memberName); | |
| 5604 if (element != null) { | |
| 5605 return element; | |
| 5606 } | |
| 5607 return lookupInheritance(classElt, memberName); | |
| 5608 } | |
| 5609 | |
| 5610 /** | |
| 5611 * Given some [InterfaceType] and some member name, this returns the | |
| 5612 * [FunctionType] of the [ExecutableElement] that the | |
| 5613 * class either declares itself, or inherits, that has the member name, if no
member is inherited | |
| 5614 * `null` is returned. The returned [FunctionType] has all type | |
| 5615 * parameters substituted with corresponding type arguments from the given [In
terfaceType]. | |
| 5616 * | |
| 5617 * @param interfaceType the interface type to query | |
| 5618 * @param memberName the name of the executable element to find and return | |
| 5619 * @return the member's function type, or `null` if no such member exists | |
| 5620 */ | |
| 5621 FunctionType lookupMemberType( | |
| 5622 InterfaceType interfaceType, String memberName) { | |
| 5623 ExecutableElement iteratorMember = | |
| 5624 lookupMember(interfaceType.element, memberName); | |
| 5625 if (iteratorMember == null) { | |
| 5626 return null; | |
| 5627 } | |
| 5628 return substituteTypeArgumentsInMemberFromInheritance( | |
| 5629 iteratorMember.type, memberName, interfaceType); | |
| 5630 } | |
| 5631 | |
| 5632 /** | |
| 5633 * Determine the set of methods which is overridden by the given class member.
If no member is | |
| 5634 * inherited, an empty list is returned. If one of the inherited members is a | |
| 5635 * [MultiplyInheritedExecutableElement], then it is expanded into its constitu
ent inherited | |
| 5636 * elements. | |
| 5637 * | |
| 5638 * @param classElt the class to query | |
| 5639 * @param memberName the name of the class member to query | |
| 5640 * @return a list of overridden methods | |
| 5641 */ | |
| 5642 List<ExecutableElement> lookupOverrides( | |
| 5643 ClassElement classElt, String memberName) { | |
| 5644 List<ExecutableElement> result = new List<ExecutableElement>(); | |
| 5645 if (memberName == null || memberName.isEmpty) { | |
| 5646 return result; | |
| 5647 } | |
| 5648 List<MemberMap> interfaceMaps = | |
| 5649 _gatherInterfaceLookupMaps(classElt, new HashSet<ClassElement>()); | |
| 5650 if (interfaceMaps != null) { | |
| 5651 for (MemberMap interfaceMap in interfaceMaps) { | |
| 5652 ExecutableElement overriddenElement = interfaceMap.get(memberName); | |
| 5653 if (overriddenElement != null) { | |
| 5654 if (overriddenElement is MultiplyInheritedExecutableElement) { | |
| 5655 MultiplyInheritedExecutableElement multiplyInheritedElement = | |
| 5656 overriddenElement; | |
| 5657 for (ExecutableElement element | |
| 5658 in multiplyInheritedElement.inheritedElements) { | |
| 5659 result.add(element); | |
| 5660 } | |
| 5661 } else { | |
| 5662 result.add(overriddenElement); | |
| 5663 } | |
| 5664 } | |
| 5665 } | |
| 5666 } | |
| 5667 return result; | |
| 5668 } | |
| 5669 | |
| 5670 /** | |
| 5671 * This method takes some inherited [FunctionType], and resolves all the param
eterized types | |
| 5672 * in the function type, dependent on the class in which it is being overridde
n. | |
| 5673 * | |
| 5674 * @param baseFunctionType the function type that is being overridden | |
| 5675 * @param memberName the name of the member, this is used to lookup the inheri
tance path of the | |
| 5676 * override | |
| 5677 * @param definingType the type that is overriding the member | |
| 5678 * @return the passed function type with any parameterized types substituted | |
| 5679 */ | |
| 5680 FunctionType substituteTypeArgumentsInMemberFromInheritance( | |
| 5681 FunctionType baseFunctionType, String memberName, | |
| 5682 InterfaceType definingType) { | |
| 5683 // if the baseFunctionType is null, or does not have any parameters, | |
| 5684 // return it. | |
| 5685 if (baseFunctionType == null || | |
| 5686 baseFunctionType.typeArguments.length == 0) { | |
| 5687 return baseFunctionType; | |
| 5688 } | |
| 5689 // First, generate the path from the defining type to the overridden member | |
| 5690 Queue<InterfaceType> inheritancePath = new Queue<InterfaceType>(); | |
| 5691 _computeInheritancePath(inheritancePath, definingType, memberName); | |
| 5692 if (inheritancePath == null || inheritancePath.isEmpty) { | |
| 5693 // TODO(jwren) log analysis engine error | |
| 5694 return baseFunctionType; | |
| 5695 } | |
| 5696 FunctionType functionTypeToReturn = baseFunctionType; | |
| 5697 // loop backward through the list substituting as we go: | |
| 5698 while (!inheritancePath.isEmpty) { | |
| 5699 InterfaceType lastType = inheritancePath.removeLast(); | |
| 5700 List<DartType> parameterTypes = lastType.element.type.typeArguments; | |
| 5701 List<DartType> argumentTypes = lastType.typeArguments; | |
| 5702 functionTypeToReturn = | |
| 5703 functionTypeToReturn.substitute2(argumentTypes, parameterTypes); | |
| 5704 } | |
| 5705 return functionTypeToReturn; | |
| 5706 } | |
| 5707 | |
| 5708 /** | |
| 5709 * Compute and return a mapping between the set of all string names of the mem
bers inherited from | |
| 5710 * the passed [ClassElement] superclass hierarchy, and the associated | |
| 5711 * [ExecutableElement]. | |
| 5712 * | |
| 5713 * @param classElt the class element to query | |
| 5714 * @param visitedClasses a set of visited classes passed back into this method
when it calls | |
| 5715 * itself recursively | |
| 5716 * @return a mapping between the set of all string names of the members inheri
ted from the passed | |
| 5717 * [ClassElement] superclass hierarchy, and the associated [Executable
Element] | |
| 5718 */ | |
| 5719 MemberMap _computeClassChainLookupMap( | |
| 5720 ClassElement classElt, HashSet<ClassElement> visitedClasses) { | |
| 5721 MemberMap resultMap = _classLookup[classElt]; | |
| 5722 if (resultMap != null) { | |
| 5723 return resultMap; | |
| 5724 } else { | |
| 5725 resultMap = new MemberMap(); | |
| 5726 } | |
| 5727 ClassElement superclassElt = null; | |
| 5728 InterfaceType supertype = classElt.supertype; | |
| 5729 if (supertype != null) { | |
| 5730 superclassElt = supertype.element; | |
| 5731 } else { | |
| 5732 // classElt is Object | |
| 5733 _classLookup[classElt] = resultMap; | |
| 5734 return resultMap; | |
| 5735 } | |
| 5736 if (superclassElt != null) { | |
| 5737 if (!visitedClasses.contains(superclassElt)) { | |
| 5738 visitedClasses.add(superclassElt); | |
| 5739 try { | |
| 5740 resultMap = new MemberMap.from( | |
| 5741 _computeClassChainLookupMap(superclassElt, visitedClasses)); | |
| 5742 // | |
| 5743 // Substitute the super types down the hierarchy. | |
| 5744 // | |
| 5745 _substituteTypeParametersDownHierarchy(supertype, resultMap); | |
| 5746 // | |
| 5747 // Include the members from the superclass in the resultMap. | |
| 5748 // | |
| 5749 _recordMapWithClassMembers(resultMap, supertype, false); | |
| 5750 } finally { | |
| 5751 visitedClasses.remove(superclassElt); | |
| 5752 } | |
| 5753 } else { | |
| 5754 // This case happens only when the superclass was previously visited and | |
| 5755 // not in the lookup, meaning this is meant to shorten the compute for | |
| 5756 // recursive cases. | |
| 5757 _classLookup[superclassElt] = resultMap; | |
| 5758 return resultMap; | |
| 5759 } | |
| 5760 } | |
| 5761 // | |
| 5762 // Include the members from the mixins in the resultMap. If there are | |
| 5763 // multiple mixins, visit them in the order listed so that methods in later | |
| 5764 // mixins will overwrite identically-named methods in earlier mixins. | |
| 5765 // | |
| 5766 List<InterfaceType> mixins = classElt.mixins; | |
| 5767 for (InterfaceType mixin in mixins) { | |
| 5768 ClassElement mixinElement = mixin.element; | |
| 5769 if (mixinElement != null) { | |
| 5770 if (!visitedClasses.contains(mixinElement)) { | |
| 5771 visitedClasses.add(mixinElement); | |
| 5772 try { | |
| 5773 MemberMap map = new MemberMap.from( | |
| 5774 _computeClassChainLookupMap(mixinElement, visitedClasses)); | |
| 5775 // | |
| 5776 // Substitute the super types down the hierarchy. | |
| 5777 // | |
| 5778 _substituteTypeParametersDownHierarchy(mixin, map); | |
| 5779 // | |
| 5780 // Include the members from the superclass in the resultMap. | |
| 5781 // | |
| 5782 _recordMapWithClassMembers(map, mixin, false); | |
| 5783 // | |
| 5784 // Add the members from map into result map. | |
| 5785 // | |
| 5786 for (int j = 0; j < map.size; j++) { | |
| 5787 String key = map.getKey(j); | |
| 5788 ExecutableElement value = map.getValue(j); | |
| 5789 if (key != null) { | |
| 5790 ClassElement definingClass = value | |
| 5791 .getAncestor((Element element) => element is ClassElement); | |
| 5792 if (!definingClass.type.isObject) { | |
| 5793 ExecutableElement existingValue = resultMap.get(key); | |
| 5794 if (existingValue == null || | |
| 5795 (existingValue != null && !_isAbstract(value))) { | |
| 5796 resultMap.put(key, value); | |
| 5797 } | |
| 5798 } | |
| 5799 } | |
| 5800 } | |
| 5801 } finally { | |
| 5802 visitedClasses.remove(mixinElement); | |
| 5803 } | |
| 5804 } else { | |
| 5805 // This case happens only when the superclass was previously visited | |
| 5806 // and not in the lookup, meaning this is meant to shorten the compute | |
| 5807 // for recursive cases. | |
| 5808 _classLookup[mixinElement] = resultMap; | |
| 5809 return resultMap; | |
| 5810 } | |
| 5811 } | |
| 5812 } | |
| 5813 _classLookup[classElt] = resultMap; | |
| 5814 return resultMap; | |
| 5815 } | |
| 5816 | |
| 5817 /** | |
| 5818 * Compute and return the inheritance path given the context of a type and a m
ember that is | |
| 5819 * overridden in the inheritance path (for which the type is in the path). | |
| 5820 * | |
| 5821 * @param chain the inheritance path that is built up as this method calls its
elf recursively, | |
| 5822 * when this method is called an empty [LinkedList] should be provide
d | |
| 5823 * @param currentType the current type in the inheritance path | |
| 5824 * @param memberName the name of the member that is being looked up the inheri
tance path | |
| 5825 */ | |
| 5826 void _computeInheritancePath(Queue<InterfaceType> chain, | |
| 5827 InterfaceType currentType, String memberName) { | |
| 5828 // TODO (jwren) create a public version of this method which doesn't require | |
| 5829 // the initial chain to be provided, then provided tests for this | |
| 5830 // functionality in InheritanceManagerTest | |
| 5831 chain.add(currentType); | |
| 5832 ClassElement classElt = currentType.element; | |
| 5833 InterfaceType supertype = classElt.supertype; | |
| 5834 // Base case- reached Object | |
| 5835 if (supertype == null) { | |
| 5836 // Looked up the chain all the way to Object, return null. | |
| 5837 // This should never happen. | |
| 5838 return; | |
| 5839 } | |
| 5840 // If we are done, return the chain | |
| 5841 // We are not done if this is the first recursive call on this method. | |
| 5842 if (chain.length != 1) { | |
| 5843 // We are done however if the member is in this classElt | |
| 5844 if (_lookupMemberInClass(classElt, memberName) != null) { | |
| 5845 return; | |
| 5846 } | |
| 5847 } | |
| 5848 // Mixins- note that mixins call lookupMemberInClass, not lookupMember | |
| 5849 List<InterfaceType> mixins = classElt.mixins; | |
| 5850 for (int i = mixins.length - 1; i >= 0; i--) { | |
| 5851 ClassElement mixinElement = mixins[i].element; | |
| 5852 if (mixinElement != null) { | |
| 5853 ExecutableElement elt = _lookupMemberInClass(mixinElement, memberName); | |
| 5854 if (elt != null) { | |
| 5855 // this is equivalent (but faster than) calling this method | |
| 5856 // recursively | |
| 5857 // (return computeInheritancePath(chain, mixins[i], memberName);) | |
| 5858 chain.add(mixins[i]); | |
| 5859 return; | |
| 5860 } | |
| 5861 } | |
| 5862 } | |
| 5863 // Superclass | |
| 5864 ClassElement superclassElt = supertype.element; | |
| 5865 if (lookupMember(superclassElt, memberName) != null) { | |
| 5866 _computeInheritancePath(chain, supertype, memberName); | |
| 5867 return; | |
| 5868 } | |
| 5869 // Interfaces | |
| 5870 List<InterfaceType> interfaces = classElt.interfaces; | |
| 5871 for (InterfaceType interfaceType in interfaces) { | |
| 5872 ClassElement interfaceElement = interfaceType.element; | |
| 5873 if (interfaceElement != null && | |
| 5874 lookupMember(interfaceElement, memberName) != null) { | |
| 5875 _computeInheritancePath(chain, interfaceType, memberName); | |
| 5876 return; | |
| 5877 } | |
| 5878 } | |
| 5879 } | |
| 5880 | |
| 5881 /** | |
| 5882 * Compute and return a mapping between the set of all string names of the mem
bers inherited from | |
| 5883 * the passed [ClassElement] interface hierarchy, and the associated | |
| 5884 * [ExecutableElement]. | |
| 5885 * | |
| 5886 * @param classElt the class element to query | |
| 5887 * @param visitedInterfaces a set of visited classes passed back into this met
hod when it calls | |
| 5888 * itself recursively | |
| 5889 * @return a mapping between the set of all string names of the members inheri
ted from the passed | |
| 5890 * [ClassElement] interface hierarchy, and the associated [ExecutableE
lement] | |
| 5891 */ | |
| 5892 MemberMap _computeInterfaceLookupMap( | |
| 5893 ClassElement classElt, HashSet<ClassElement> visitedInterfaces) { | |
| 5894 MemberMap resultMap = _interfaceLookup[classElt]; | |
| 5895 if (resultMap != null) { | |
| 5896 return resultMap; | |
| 5897 } | |
| 5898 List<MemberMap> lookupMaps = | |
| 5899 _gatherInterfaceLookupMaps(classElt, visitedInterfaces); | |
| 5900 if (lookupMaps == null) { | |
| 5901 resultMap = new MemberMap(); | |
| 5902 } else { | |
| 5903 HashMap<String, List<ExecutableElement>> unionMap = | |
| 5904 _unionInterfaceLookupMaps(lookupMaps); | |
| 5905 resultMap = _resolveInheritanceLookup(classElt, unionMap); | |
| 5906 } | |
| 5907 _interfaceLookup[classElt] = resultMap; | |
| 5908 return resultMap; | |
| 5909 } | |
| 5910 | |
| 5911 /** | |
| 5912 * Collect a list of interface lookup maps whose elements correspond to all of
the classes | |
| 5913 * directly above [classElt] in the class hierarchy (the direct superclass if
any, all | |
| 5914 * mixins, and all direct superinterfaces). Each item in the list is the inter
face lookup map | |
| 5915 * returned by [computeInterfaceLookupMap] for the corresponding super, except
with type | |
| 5916 * parameters appropriately substituted. | |
| 5917 * | |
| 5918 * @param classElt the class element to query | |
| 5919 * @param visitedInterfaces a set of visited classes passed back into this met
hod when it calls | |
| 5920 * itself recursively | |
| 5921 * @return `null` if there was a problem (such as a loop in the class hierarch
y) or if there | |
| 5922 * are no classes above this one in the class hierarchy. Otherwise, a
list of interface | |
| 5923 * lookup maps. | |
| 5924 */ | |
| 5925 List<MemberMap> _gatherInterfaceLookupMaps( | |
| 5926 ClassElement classElt, HashSet<ClassElement> visitedInterfaces) { | |
| 5927 InterfaceType supertype = classElt.supertype; | |
| 5928 ClassElement superclassElement = | |
| 5929 supertype != null ? supertype.element : null; | |
| 5930 List<InterfaceType> mixins = classElt.mixins; | |
| 5931 List<InterfaceType> interfaces = classElt.interfaces; | |
| 5932 // Recursively collect the list of mappings from all of the interface types | |
| 5933 List<MemberMap> lookupMaps = new List<MemberMap>(); | |
| 5934 // | |
| 5935 // Superclass element | |
| 5936 // | |
| 5937 if (superclassElement != null) { | |
| 5938 if (!visitedInterfaces.contains(superclassElement)) { | |
| 5939 try { | |
| 5940 visitedInterfaces.add(superclassElement); | |
| 5941 // | |
| 5942 // Recursively compute the map for the super type. | |
| 5943 // | |
| 5944 MemberMap map = | |
| 5945 _computeInterfaceLookupMap(superclassElement, visitedInterfaces); | |
| 5946 map = new MemberMap.from(map); | |
| 5947 // | |
| 5948 // Substitute the super type down the hierarchy. | |
| 5949 // | |
| 5950 _substituteTypeParametersDownHierarchy(supertype, map); | |
| 5951 // | |
| 5952 // Add any members from the super type into the map as well. | |
| 5953 // | |
| 5954 _recordMapWithClassMembers(map, supertype, true); | |
| 5955 lookupMaps.add(map); | |
| 5956 } finally { | |
| 5957 visitedInterfaces.remove(superclassElement); | |
| 5958 } | |
| 5959 } else { | |
| 5960 return null; | |
| 5961 } | |
| 5962 } | |
| 5963 // | |
| 5964 // Mixin elements | |
| 5965 // | |
| 5966 for (int i = mixins.length - 1; i >= 0; i--) { | |
| 5967 InterfaceType mixinType = mixins[i]; | |
| 5968 ClassElement mixinElement = mixinType.element; | |
| 5969 if (mixinElement != null) { | |
| 5970 if (!visitedInterfaces.contains(mixinElement)) { | |
| 5971 try { | |
| 5972 visitedInterfaces.add(mixinElement); | |
| 5973 // | |
| 5974 // Recursively compute the map for the mixin. | |
| 5975 // | |
| 5976 MemberMap map = | |
| 5977 _computeInterfaceLookupMap(mixinElement, visitedInterfaces); | |
| 5978 map = new MemberMap.from(map); | |
| 5979 // | |
| 5980 // Substitute the mixin type down the hierarchy. | |
| 5981 // | |
| 5982 _substituteTypeParametersDownHierarchy(mixinType, map); | |
| 5983 // | |
| 5984 // Add any members from the mixin type into the map as well. | |
| 5985 // | |
| 5986 _recordMapWithClassMembers(map, mixinType, true); | |
| 5987 lookupMaps.add(map); | |
| 5988 } finally { | |
| 5989 visitedInterfaces.remove(mixinElement); | |
| 5990 } | |
| 5991 } else { | |
| 5992 return null; | |
| 5993 } | |
| 5994 } | |
| 5995 } | |
| 5996 // | |
| 5997 // Interface elements | |
| 5998 // | |
| 5999 for (InterfaceType interfaceType in interfaces) { | |
| 6000 ClassElement interfaceElement = interfaceType.element; | |
| 6001 if (interfaceElement != null) { | |
| 6002 if (!visitedInterfaces.contains(interfaceElement)) { | |
| 6003 try { | |
| 6004 visitedInterfaces.add(interfaceElement); | |
| 6005 // | |
| 6006 // Recursively compute the map for the interfaces. | |
| 6007 // | |
| 6008 MemberMap map = | |
| 6009 _computeInterfaceLookupMap(interfaceElement, visitedInterfaces); | |
| 6010 map = new MemberMap.from(map); | |
| 6011 // | |
| 6012 // Substitute the supertypes down the hierarchy | |
| 6013 // | |
| 6014 _substituteTypeParametersDownHierarchy(interfaceType, map); | |
| 6015 // | |
| 6016 // And add any members from the interface into the map as well. | |
| 6017 // | |
| 6018 _recordMapWithClassMembers(map, interfaceType, true); | |
| 6019 lookupMaps.add(map); | |
| 6020 } finally { | |
| 6021 visitedInterfaces.remove(interfaceElement); | |
| 6022 } | |
| 6023 } else { | |
| 6024 return null; | |
| 6025 } | |
| 6026 } | |
| 6027 } | |
| 6028 if (lookupMaps.length == 0) { | |
| 6029 return null; | |
| 6030 } | |
| 6031 return lookupMaps; | |
| 6032 } | |
| 6033 | |
| 6034 /** | |
| 6035 * Given some [ClassElement], this method finds and returns the [ExecutableEle
ment] of | |
| 6036 * the passed name in the class element. Static members, members in super type
s and members not | |
| 6037 * accessible from the current library are not considered. | |
| 6038 * | |
| 6039 * @param classElt the class element to query | |
| 6040 * @param memberName the name of the member to lookup in the class | |
| 6041 * @return the found [ExecutableElement], or `null` if no such member was foun
d | |
| 6042 */ | |
| 6043 ExecutableElement _lookupMemberInClass( | |
| 6044 ClassElement classElt, String memberName) { | |
| 6045 List<MethodElement> methods = classElt.methods; | |
| 6046 for (MethodElement method in methods) { | |
| 6047 if (memberName == method.name && | |
| 6048 method.isAccessibleIn(_library) && | |
| 6049 !method.isStatic) { | |
| 6050 return method; | |
| 6051 } | |
| 6052 } | |
| 6053 List<PropertyAccessorElement> accessors = classElt.accessors; | |
| 6054 for (PropertyAccessorElement accessor in accessors) { | |
| 6055 if (memberName == accessor.name && | |
| 6056 accessor.isAccessibleIn(_library) && | |
| 6057 !accessor.isStatic) { | |
| 6058 return accessor; | |
| 6059 } | |
| 6060 } | |
| 6061 return null; | |
| 6062 } | |
| 6063 | |
| 6064 /** | |
| 6065 * Record the passed map with the set of all members (methods, getters and set
ters) in the type | |
| 6066 * into the passed map. | |
| 6067 * | |
| 6068 * @param map some non-`null` map to put the methods and accessors from the pa
ssed | |
| 6069 * [ClassElement] into | |
| 6070 * @param type the type that will be recorded into the passed map | |
| 6071 * @param doIncludeAbstract `true` if abstract members will be put into the ma
p | |
| 6072 */ | |
| 6073 void _recordMapWithClassMembers( | |
| 6074 MemberMap map, InterfaceType type, bool doIncludeAbstract) { | |
| 6075 List<MethodElement> methods = type.methods; | |
| 6076 for (MethodElement method in methods) { | |
| 6077 if (method.isAccessibleIn(_library) && | |
| 6078 !method.isStatic && | |
| 6079 (doIncludeAbstract || !method.isAbstract)) { | |
| 6080 map.put(method.name, method); | |
| 6081 } | |
| 6082 } | |
| 6083 List<PropertyAccessorElement> accessors = type.accessors; | |
| 6084 for (PropertyAccessorElement accessor in accessors) { | |
| 6085 if (accessor.isAccessibleIn(_library) && | |
| 6086 !accessor.isStatic && | |
| 6087 (doIncludeAbstract || !accessor.isAbstract)) { | |
| 6088 map.put(accessor.name, accessor); | |
| 6089 } | |
| 6090 } | |
| 6091 } | |
| 6092 | |
| 6093 /** | |
| 6094 * This method is used to report errors on when they are found computing inher
itance information. | |
| 6095 * See [ErrorVerifier.checkForInconsistentMethodInheritance] to see where thes
e generated | |
| 6096 * error codes are reported back into the analysis engine. | |
| 6097 * | |
| 6098 * @param classElt the location of the source for which the exception occurred | |
| 6099 * @param offset the offset of the location of the error | |
| 6100 * @param length the length of the location of the error | |
| 6101 * @param errorCode the error code to be associated with this error | |
| 6102 * @param arguments the arguments used to build the error message | |
| 6103 */ | |
| 6104 void _reportError(ClassElement classElt, int offset, int length, | |
| 6105 ErrorCode errorCode, List<Object> arguments) { | |
| 6106 HashSet<AnalysisError> errorSet = _errorsInClassElement[classElt]; | |
| 6107 if (errorSet == null) { | |
| 6108 errorSet = new HashSet<AnalysisError>(); | |
| 6109 _errorsInClassElement[classElt] = errorSet; | |
| 6110 } | |
| 6111 errorSet.add(new AnalysisError( | |
| 6112 classElt.source, offset, length, errorCode, arguments)); | |
| 6113 } | |
| 6114 | |
| 6115 /** | |
| 6116 * Given the set of methods defined by classes above [classElt] in the class h
ierarchy, | |
| 6117 * apply the appropriate inheritance rules to determine those methods inherite
d by or overridden | |
| 6118 * by [classElt]. Also report static warnings | |
| 6119 * [StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE] and | |
| 6120 * [StaticWarningCode.INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD] if ap
propriate. | |
| 6121 * | |
| 6122 * @param classElt the class element to query. | |
| 6123 * @param unionMap a mapping from method name to the set of unique (in terms o
f signature) methods | |
| 6124 * defined in superclasses of [classElt]. | |
| 6125 * @return the inheritance lookup map for [classElt]. | |
| 6126 */ | |
| 6127 MemberMap _resolveInheritanceLookup(ClassElement classElt, | |
| 6128 HashMap<String, List<ExecutableElement>> unionMap) { | |
| 6129 MemberMap resultMap = new MemberMap(); | |
| 6130 unionMap.forEach((String key, List<ExecutableElement> list) { | |
| 6131 int numOfEltsWithMatchingNames = list.length; | |
| 6132 if (numOfEltsWithMatchingNames == 1) { | |
| 6133 // | |
| 6134 // Example: class A inherits only 1 method named 'm'. | |
| 6135 // Since it is the only such method, it is inherited. | |
| 6136 // Another example: class A inherits 2 methods named 'm' from 2 | |
| 6137 // different interfaces, but they both have the same signature, so it is | |
| 6138 // the method inherited. | |
| 6139 // | |
| 6140 resultMap.put(key, list[0]); | |
| 6141 } else { | |
| 6142 // | |
| 6143 // Then numOfEltsWithMatchingNames > 1, check for the warning cases. | |
| 6144 // | |
| 6145 bool allMethods = true; | |
| 6146 bool allSetters = true; | |
| 6147 bool allGetters = true; | |
| 6148 for (ExecutableElement executableElement in list) { | |
| 6149 if (executableElement is PropertyAccessorElement) { | |
| 6150 allMethods = false; | |
| 6151 if (executableElement.isSetter) { | |
| 6152 allGetters = false; | |
| 6153 } else { | |
| 6154 allSetters = false; | |
| 6155 } | |
| 6156 } else { | |
| 6157 allGetters = false; | |
| 6158 allSetters = false; | |
| 6159 } | |
| 6160 } | |
| 6161 // | |
| 6162 // If there isn't a mixture of methods with getters, then continue, | |
| 6163 // otherwise create a warning. | |
| 6164 // | |
| 6165 if (allMethods || allGetters || allSetters) { | |
| 6166 // | |
| 6167 // Compute the element whose type is the subtype of all of the other | |
| 6168 // types. | |
| 6169 // | |
| 6170 List<ExecutableElement> elements = new List.from(list); | |
| 6171 List<FunctionType> executableElementTypes = | |
| 6172 new List<FunctionType>(numOfEltsWithMatchingNames); | |
| 6173 for (int i = 0; i < numOfEltsWithMatchingNames; i++) { | |
| 6174 executableElementTypes[i] = elements[i].type; | |
| 6175 } | |
| 6176 List<int> subtypesOfAllOtherTypesIndexes = new List<int>(); | |
| 6177 for (int i = 0; i < numOfEltsWithMatchingNames; i++) { | |
| 6178 FunctionType subtype = executableElementTypes[i]; | |
| 6179 if (subtype == null) { | |
| 6180 continue; | |
| 6181 } | |
| 6182 bool subtypeOfAllTypes = true; | |
| 6183 for (int j = 0; | |
| 6184 j < numOfEltsWithMatchingNames && subtypeOfAllTypes; | |
| 6185 j++) { | |
| 6186 if (i != j) { | |
| 6187 if (!subtype.isSubtypeOf(executableElementTypes[j])) { | |
| 6188 subtypeOfAllTypes = false; | |
| 6189 break; | |
| 6190 } | |
| 6191 } | |
| 6192 } | |
| 6193 if (subtypeOfAllTypes) { | |
| 6194 subtypesOfAllOtherTypesIndexes.add(i); | |
| 6195 } | |
| 6196 } | |
| 6197 // | |
| 6198 // The following is split into three cases determined by the number of | |
| 6199 // elements in subtypesOfAllOtherTypes | |
| 6200 // | |
| 6201 if (subtypesOfAllOtherTypesIndexes.length == 1) { | |
| 6202 // | |
| 6203 // Example: class A inherited only 2 method named 'm'. | |
| 6204 // One has the function type '() -> dynamic' and one has the | |
| 6205 // function type '([int]) -> dynamic'. Since the second method is a | |
| 6206 // subtype of all the others, it is the inherited method. | |
| 6207 // Tests: InheritanceManagerTest. | |
| 6208 // test_getMapOfMembersInheritedFromInterfaces_union_oneSubtype_* | |
| 6209 // | |
| 6210 resultMap.put(key, elements[subtypesOfAllOtherTypesIndexes[0]]); | |
| 6211 } else { | |
| 6212 if (subtypesOfAllOtherTypesIndexes.isEmpty) { | |
| 6213 // | |
| 6214 // Determine if the current class has a method or accessor with | |
| 6215 // the member name, if it does then then this class does not | |
| 6216 // "inherit" from any of the supertypes. See issue 16134. | |
| 6217 // | |
| 6218 bool classHasMember = false; | |
| 6219 if (allMethods) { | |
| 6220 classHasMember = classElt.getMethod(key) != null; | |
| 6221 } else { | |
| 6222 List<PropertyAccessorElement> accessors = classElt.accessors; | |
| 6223 for (int i = 0; i < accessors.length; i++) { | |
| 6224 if (accessors[i].name == key) { | |
| 6225 classHasMember = true; | |
| 6226 } | |
| 6227 } | |
| 6228 } | |
| 6229 // | |
| 6230 // Example: class A inherited only 2 method named 'm'. | |
| 6231 // One has the function type '() -> int' and one has the function | |
| 6232 // type '() -> String'. Since neither is a subtype of the other, | |
| 6233 // we create a warning, and have this class inherit nothing. | |
| 6234 // | |
| 6235 if (!classHasMember) { | |
| 6236 String firstTwoFuntionTypesStr = | |
| 6237 "${executableElementTypes[0]}, ${executableElementTypes[1]}"
; | |
| 6238 _reportError(classElt, classElt.nameOffset, | |
| 6239 classElt.displayName.length, | |
| 6240 StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE, [ | |
| 6241 key, | |
| 6242 firstTwoFuntionTypesStr | |
| 6243 ]); | |
| 6244 } | |
| 6245 } else { | |
| 6246 // | |
| 6247 // Example: class A inherits 2 methods named 'm'. | |
| 6248 // One has the function type '(int) -> dynamic' and one has the | |
| 6249 // function type '(num) -> dynamic'. Since they are both a subtype | |
| 6250 // of the other, a synthetic function '(dynamic) -> dynamic' is | |
| 6251 // inherited. | |
| 6252 // Tests: test_getMapOfMembersInheritedFromInterfaces_ | |
| 6253 // union_multipleSubtypes_* | |
| 6254 // | |
| 6255 List<ExecutableElement> elementArrayToMerge = | |
| 6256 new List<ExecutableElement>( | |
| 6257 subtypesOfAllOtherTypesIndexes.length); | |
| 6258 for (int i = 0; i < elementArrayToMerge.length; i++) { | |
| 6259 elementArrayToMerge[i] = | |
| 6260 elements[subtypesOfAllOtherTypesIndexes[i]]; | |
| 6261 } | |
| 6262 ExecutableElement mergedExecutableElement = | |
| 6263 _computeMergedExecutableElement(elementArrayToMerge); | |
| 6264 resultMap.put(key, mergedExecutableElement); | |
| 6265 } | |
| 6266 } | |
| 6267 } else { | |
| 6268 _reportError(classElt, classElt.nameOffset, | |
| 6269 classElt.displayName.length, | |
| 6270 StaticWarningCode.INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHO
D, | |
| 6271 [key]); | |
| 6272 } | |
| 6273 } | |
| 6274 }); | |
| 6275 return resultMap; | |
| 6276 } | |
| 6277 | |
| 6278 /** | |
| 6279 * Loop through all of the members in some [MemberMap], performing type parame
ter | |
| 6280 * substitutions using a passed supertype. | |
| 6281 * | |
| 6282 * @param superType the supertype to substitute into the members of the [Membe
rMap] | |
| 6283 * @param map the MemberMap to perform the substitutions on | |
| 6284 */ | |
| 6285 void _substituteTypeParametersDownHierarchy( | |
| 6286 InterfaceType superType, MemberMap map) { | |
| 6287 for (int i = 0; i < map.size; i++) { | |
| 6288 ExecutableElement executableElement = map.getValue(i); | |
| 6289 if (executableElement is MethodMember) { | |
| 6290 executableElement = | |
| 6291 MethodMember.from(executableElement as MethodMember, superType); | |
| 6292 map.setValue(i, executableElement); | |
| 6293 } else if (executableElement is PropertyAccessorMember) { | |
| 6294 executableElement = PropertyAccessorMember.from( | |
| 6295 executableElement as PropertyAccessorMember, superType); | |
| 6296 map.setValue(i, executableElement); | |
| 6297 } | |
| 6298 } | |
| 6299 } | |
| 6300 | |
| 6301 /** | |
| 6302 * Union all of the [lookupMaps] together into a single map, grouping the Exec
utableElements | |
| 6303 * into a list where none of the elements are equal where equality is determin
ed by having equal | |
| 6304 * function types. (We also take note too of the kind of the element: ()->int
and () -> int may | |
| 6305 * not be equal if one is a getter and the other is a method.) | |
| 6306 * | |
| 6307 * @param lookupMaps the maps to be unioned together. | |
| 6308 * @return the resulting union map. | |
| 6309 */ | |
| 6310 HashMap<String, List<ExecutableElement>> _unionInterfaceLookupMaps( | |
| 6311 List<MemberMap> lookupMaps) { | |
| 6312 HashMap<String, List<ExecutableElement>> unionMap = | |
| 6313 new HashMap<String, List<ExecutableElement>>(); | |
| 6314 for (MemberMap lookupMap in lookupMaps) { | |
| 6315 int lookupMapSize = lookupMap.size; | |
| 6316 for (int i = 0; i < lookupMapSize; i++) { | |
| 6317 // Get the string key, if null, break. | |
| 6318 String key = lookupMap.getKey(i); | |
| 6319 if (key == null) { | |
| 6320 break; | |
| 6321 } | |
| 6322 // Get the list value out of the unionMap | |
| 6323 List<ExecutableElement> list = unionMap[key]; | |
| 6324 // If we haven't created such a map for this key yet, do create it and | |
| 6325 // put the list entry into the unionMap. | |
| 6326 if (list == null) { | |
| 6327 list = new List<ExecutableElement>(); | |
| 6328 unionMap[key] = list; | |
| 6329 } | |
| 6330 // Fetch the entry out of this lookupMap | |
| 6331 ExecutableElement newExecutableElementEntry = lookupMap.getValue(i); | |
| 6332 if (list.isEmpty) { | |
| 6333 // If the list is empty, just the new value | |
| 6334 list.add(newExecutableElementEntry); | |
| 6335 } else { | |
| 6336 // Otherwise, only add the newExecutableElementEntry if it isn't | |
| 6337 // already in the list, this covers situation where a class inherits | |
| 6338 // two methods (or two getters) that are identical. | |
| 6339 bool alreadyInList = false; | |
| 6340 bool isMethod1 = newExecutableElementEntry is MethodElement; | |
| 6341 for (ExecutableElement executableElementInList in list) { | |
| 6342 bool isMethod2 = executableElementInList is MethodElement; | |
| 6343 if (isMethod1 == isMethod2 && | |
| 6344 executableElementInList.type == | |
| 6345 newExecutableElementEntry.type) { | |
| 6346 alreadyInList = true; | |
| 6347 break; | |
| 6348 } | |
| 6349 } | |
| 6350 if (!alreadyInList) { | |
| 6351 list.add(newExecutableElementEntry); | |
| 6352 } | |
| 6353 } | |
| 6354 } | |
| 6355 } | |
| 6356 return unionMap; | |
| 6357 } | |
| 6358 | |
| 6359 /** | |
| 6360 * Given some array of [ExecutableElement]s, this method creates a synthetic e
lement as | |
| 6361 * described in 8.1.1: | |
| 6362 * | |
| 6363 * Let <i>numberOfPositionals</i>(<i>f</i>) denote the number of positional pa
rameters of a | |
| 6364 * function <i>f</i>, and let <i>numberOfRequiredParams</i>(<i>f</i>) denote t
he number of | |
| 6365 * required parameters of a function <i>f</i>. Furthermore, let <i>s</i> denot
e the set of all | |
| 6366 * named parameters of the <i>m<sub>1</sub>, …, m<sub>k</sub></i>. Then
let | |
| 6367 * * <i>h = max(numberOfPositionals(m<sub>i</sub>)),</i> | |
| 6368 * * <i>r = min(numberOfRequiredParams(m<sub>i</sub>)), for all <i>i</i>, 1 <=
i <= k.</i> | |
| 6369 * Then <i>I</i> has a method named <i>n</i>, with <i>r</i> required parameter
s of type | |
| 6370 * <b>dynamic</b>, <i>h</i> positional parameters of type <b>dynamic</b>, name
d parameters | |
| 6371 * <i>s</i> of type <b>dynamic</b> and return type <b>dynamic</b>. | |
| 6372 * | |
| 6373 */ | |
| 6374 static ExecutableElement _computeMergedExecutableElement( | |
| 6375 List<ExecutableElement> elementArrayToMerge) { | |
| 6376 int h = _getNumOfPositionalParameters(elementArrayToMerge[0]); | |
| 6377 int r = _getNumOfRequiredParameters(elementArrayToMerge[0]); | |
| 6378 Set<String> namedParametersList = new HashSet<String>(); | |
| 6379 for (int i = 1; i < elementArrayToMerge.length; i++) { | |
| 6380 ExecutableElement element = elementArrayToMerge[i]; | |
| 6381 int numOfPositionalParams = _getNumOfPositionalParameters(element); | |
| 6382 if (h < numOfPositionalParams) { | |
| 6383 h = numOfPositionalParams; | |
| 6384 } | |
| 6385 int numOfRequiredParams = _getNumOfRequiredParameters(element); | |
| 6386 if (r > numOfRequiredParams) { | |
| 6387 r = numOfRequiredParams; | |
| 6388 } | |
| 6389 namedParametersList.addAll(_getNamedParameterNames(element)); | |
| 6390 } | |
| 6391 return _createSyntheticExecutableElement(elementArrayToMerge, | |
| 6392 elementArrayToMerge[0].displayName, r, h - r, | |
| 6393 new List.from(namedParametersList)); | |
| 6394 } | |
| 6395 | |
| 6396 /** | |
| 6397 * Used by [computeMergedExecutableElement] to actually create the | |
| 6398 * synthetic element. | |
| 6399 * | |
| 6400 * @param elementArrayToMerge the array used to create the synthetic element | |
| 6401 * @param name the name of the method, getter or setter | |
| 6402 * @param numOfRequiredParameters the number of required parameters | |
| 6403 * @param numOfPositionalParameters the number of positional parameters | |
| 6404 * @param namedParameters the list of [String]s that are the named parameters | |
| 6405 * @return the created synthetic element | |
| 6406 */ | |
| 6407 static ExecutableElement _createSyntheticExecutableElement( | |
| 6408 List<ExecutableElement> elementArrayToMerge, String name, | |
| 6409 int numOfRequiredParameters, int numOfPositionalParameters, | |
| 6410 List<String> namedParameters) { | |
| 6411 DynamicTypeImpl dynamicType = DynamicTypeImpl.instance; | |
| 6412 SimpleIdentifier nameIdentifier = new SimpleIdentifier( | |
| 6413 new sc.StringToken(sc.TokenType.IDENTIFIER, name, 0)); | |
| 6414 ExecutableElementImpl executable; | |
| 6415 if (elementArrayToMerge[0] is MethodElement) { | |
| 6416 MultiplyInheritedMethodElementImpl unionedMethod = | |
| 6417 new MultiplyInheritedMethodElementImpl(nameIdentifier); | |
| 6418 unionedMethod.inheritedElements = elementArrayToMerge; | |
| 6419 executable = unionedMethod; | |
| 6420 } else { | |
| 6421 MultiplyInheritedPropertyAccessorElementImpl unionedPropertyAccessor = | |
| 6422 new MultiplyInheritedPropertyAccessorElementImpl(nameIdentifier); | |
| 6423 unionedPropertyAccessor.getter = | |
| 6424 (elementArrayToMerge[0] as PropertyAccessorElement).isGetter; | |
| 6425 unionedPropertyAccessor.setter = | |
| 6426 (elementArrayToMerge[0] as PropertyAccessorElement).isSetter; | |
| 6427 unionedPropertyAccessor.inheritedElements = elementArrayToMerge; | |
| 6428 executable = unionedPropertyAccessor; | |
| 6429 } | |
| 6430 int numOfParameters = numOfRequiredParameters + | |
| 6431 numOfPositionalParameters + | |
| 6432 namedParameters.length; | |
| 6433 List<ParameterElement> parameters = | |
| 6434 new List<ParameterElement>(numOfParameters); | |
| 6435 int i = 0; | |
| 6436 for (int j = 0; j < numOfRequiredParameters; j++, i++) { | |
| 6437 ParameterElementImpl parameter = new ParameterElementImpl("", 0); | |
| 6438 parameter.type = dynamicType; | |
| 6439 parameter.parameterKind = ParameterKind.REQUIRED; | |
| 6440 parameters[i] = parameter; | |
| 6441 } | |
| 6442 for (int k = 0; k < numOfPositionalParameters; k++, i++) { | |
| 6443 ParameterElementImpl parameter = new ParameterElementImpl("", 0); | |
| 6444 parameter.type = dynamicType; | |
| 6445 parameter.parameterKind = ParameterKind.POSITIONAL; | |
| 6446 parameters[i] = parameter; | |
| 6447 } | |
| 6448 for (int m = 0; m < namedParameters.length; m++, i++) { | |
| 6449 ParameterElementImpl parameter = | |
| 6450 new ParameterElementImpl(namedParameters[m], 0); | |
| 6451 parameter.type = dynamicType; | |
| 6452 parameter.parameterKind = ParameterKind.NAMED; | |
| 6453 parameters[i] = parameter; | |
| 6454 } | |
| 6455 executable.returnType = dynamicType; | |
| 6456 executable.parameters = parameters; | |
| 6457 FunctionTypeImpl methodType = new FunctionTypeImpl(executable); | |
| 6458 executable.type = methodType; | |
| 6459 return executable; | |
| 6460 } | |
| 6461 | |
| 6462 /** | |
| 6463 * Given some [ExecutableElement], return the list of named parameters. | |
| 6464 */ | |
| 6465 static List<String> _getNamedParameterNames( | |
| 6466 ExecutableElement executableElement) { | |
| 6467 List<String> namedParameterNames = new List<String>(); | |
| 6468 List<ParameterElement> parameters = executableElement.parameters; | |
| 6469 for (int i = 0; i < parameters.length; i++) { | |
| 6470 ParameterElement parameterElement = parameters[i]; | |
| 6471 if (parameterElement.parameterKind == ParameterKind.NAMED) { | |
| 6472 namedParameterNames.add(parameterElement.name); | |
| 6473 } | |
| 6474 } | |
| 6475 return namedParameterNames; | |
| 6476 } | |
| 6477 | |
| 6478 /** | |
| 6479 * Given some [ExecutableElement] return the number of parameters of the speci
fied kind. | |
| 6480 */ | |
| 6481 static int _getNumOfParameters( | |
| 6482 ExecutableElement executableElement, ParameterKind parameterKind) { | |
| 6483 int parameterCount = 0; | |
| 6484 List<ParameterElement> parameters = executableElement.parameters; | |
| 6485 for (int i = 0; i < parameters.length; i++) { | |
| 6486 ParameterElement parameterElement = parameters[i]; | |
| 6487 if (parameterElement.parameterKind == parameterKind) { | |
| 6488 parameterCount++; | |
| 6489 } | |
| 6490 } | |
| 6491 return parameterCount; | |
| 6492 } | |
| 6493 | |
| 6494 /** | |
| 6495 * Given some [ExecutableElement] return the number of positional parameters. | |
| 6496 * | |
| 6497 * Note: by positional we mean [ParameterKind.REQUIRED] or [ParameterKind.POSI
TIONAL]. | |
| 6498 */ | |
| 6499 static int _getNumOfPositionalParameters( | |
| 6500 ExecutableElement executableElement) => | |
| 6501 _getNumOfParameters(executableElement, ParameterKind.REQUIRED) + | |
| 6502 _getNumOfParameters(executableElement, ParameterKind.POSITIONAL); | |
| 6503 | |
| 6504 /** | |
| 6505 * Given some [ExecutableElement] return the number of required parameters. | |
| 6506 */ | |
| 6507 static int _getNumOfRequiredParameters(ExecutableElement executableElement) => | |
| 6508 _getNumOfParameters(executableElement, ParameterKind.REQUIRED); | |
| 6509 | |
| 6510 /** | |
| 6511 * Given some [ExecutableElement] returns `true` if it is an abstract member o
f a | |
| 6512 * class. | |
| 6513 * | |
| 6514 * @param executableElement some [ExecutableElement] to evaluate | |
| 6515 * @return `true` if the given element is an abstract member of a class | |
| 6516 */ | |
| 6517 static bool _isAbstract(ExecutableElement executableElement) { | |
| 6518 if (executableElement is MethodElement) { | |
| 6519 return executableElement.isAbstract; | |
| 6520 } else if (executableElement is PropertyAccessorElement) { | |
| 6521 return executableElement.isAbstract; | |
| 6522 } | |
| 6523 return false; | |
| 6524 } | |
| 6525 } | |
| 6526 | |
| 6527 /** | |
| 6528 * This enum holds one of four states of a field initialization state through a
constructor | |
| 6529 * signature, not initialized, initialized in the field declaration, initialized
in the field | |
| 6530 * formal, and finally, initialized in the initializers list. | |
| 6531 */ | |
| 6532 class INIT_STATE extends Enum<INIT_STATE> { | |
| 6533 static const INIT_STATE NOT_INIT = const INIT_STATE('NOT_INIT', 0); | |
| 6534 | |
| 6535 static const INIT_STATE INIT_IN_DECLARATION = | |
| 6536 const INIT_STATE('INIT_IN_DECLARATION', 1); | |
| 6537 | |
| 6538 static const INIT_STATE INIT_IN_FIELD_FORMAL = | |
| 6539 const INIT_STATE('INIT_IN_FIELD_FORMAL', 2); | |
| 6540 | |
| 6541 static const INIT_STATE INIT_IN_INITIALIZERS = | |
| 6542 const INIT_STATE('INIT_IN_INITIALIZERS', 3); | |
| 6543 | |
| 6544 static const List<INIT_STATE> values = const [ | |
| 6545 NOT_INIT, | |
| 6546 INIT_IN_DECLARATION, | |
| 6547 INIT_IN_FIELD_FORMAL, | |
| 6548 INIT_IN_INITIALIZERS | |
| 6549 ]; | |
| 6550 | |
| 6551 const INIT_STATE(String name, int ordinal) : super(name, ordinal); | |
| 6552 } | |
| 6553 | |
| 6554 /** | |
| 6555 * Instances of the class `LabelScope` represent a scope in which a single label
is defined. | |
| 6556 */ | |
| 6557 class LabelScope { | |
| 6558 /** | |
| 6559 * The label scope enclosing this label scope. | |
| 6560 */ | |
| 6561 final LabelScope _outerScope; | |
| 6562 | |
| 6563 /** | |
| 6564 * The label defined in this scope. | |
| 6565 */ | |
| 6566 final String _label; | |
| 6567 | |
| 6568 /** | |
| 6569 * The element to which the label resolves. | |
| 6570 */ | |
| 6571 final LabelElement element; | |
| 6572 | |
| 6573 /** | |
| 6574 * The AST node to which the label resolves. | |
| 6575 */ | |
| 6576 final AstNode node; | |
| 6577 | |
| 6578 /** | |
| 6579 * Initialize a newly created scope to represent the label [_label]. | |
| 6580 * [_outerScope] is the scope enclosing the new label scope. [node] is the | |
| 6581 * AST node the label resolves to. [element] is the element the label | |
| 6582 * resolves to. | |
| 6583 */ | |
| 6584 LabelScope(this._outerScope, this._label, this.node, this.element); | |
| 6585 | |
| 6586 /** | |
| 6587 * Return the LabelScope which defines [targetLabel], or `null` if it is not | |
| 6588 * defined in this scope. | |
| 6589 */ | |
| 6590 LabelScope lookup(String targetLabel) { | |
| 6591 if (_label == targetLabel) { | |
| 6592 return this; | |
| 6593 } else if (_outerScope != null) { | |
| 6594 return _outerScope.lookup(targetLabel); | |
| 6595 } else { | |
| 6596 return null; | |
| 6597 } | |
| 6598 } | |
| 6599 } | |
| 6600 | |
| 6601 /** | |
| 6602 * Instances of the class `Library` represent the data about a single library du
ring the | |
| 6603 * resolution of some (possibly different) library. They are not intended to be
used except during | |
| 6604 * the resolution process. | |
| 6605 */ | |
| 6606 class Library { | |
| 6607 /** | |
| 6608 * An empty list that can be used to initialize lists of libraries. | |
| 6609 */ | |
| 6610 static const List<Library> _EMPTY_ARRAY = const <Library>[]; | |
| 6611 | |
| 6612 /** | |
| 6613 * The prefix of a URI using the dart-ext scheme to reference a native code li
brary. | |
| 6614 */ | |
| 6615 static String _DART_EXT_SCHEME = "dart-ext:"; | |
| 6616 | |
| 6617 /** | |
| 6618 * The analysis context in which this library is being analyzed. | |
| 6619 */ | |
| 6620 final InternalAnalysisContext _analysisContext; | |
| 6621 | |
| 6622 /** | |
| 6623 * The inheritance manager which is used for this member lookups in this libra
ry. | |
| 6624 */ | |
| 6625 InheritanceManager _inheritanceManager; | |
| 6626 | |
| 6627 /** | |
| 6628 * The listener to which analysis errors will be reported. | |
| 6629 */ | |
| 6630 final AnalysisErrorListener errorListener; | |
| 6631 | |
| 6632 /** | |
| 6633 * The source specifying the defining compilation unit of this library. | |
| 6634 */ | |
| 6635 final Source librarySource; | |
| 6636 | |
| 6637 /** | |
| 6638 * The library element representing this library. | |
| 6639 */ | |
| 6640 LibraryElementImpl _libraryElement; | |
| 6641 | |
| 6642 /** | |
| 6643 * A list containing all of the libraries that are imported into this library. | |
| 6644 */ | |
| 6645 List<Library> _importedLibraries = _EMPTY_ARRAY; | |
| 6646 | |
| 6647 /** | |
| 6648 * A table mapping URI-based directive to the actual URI value. | |
| 6649 */ | |
| 6650 HashMap<UriBasedDirective, String> _directiveUris = | |
| 6651 new HashMap<UriBasedDirective, String>(); | |
| 6652 | |
| 6653 /** | |
| 6654 * A flag indicating whether this library explicitly imports core. | |
| 6655 */ | |
| 6656 bool explicitlyImportsCore = false; | |
| 6657 | |
| 6658 /** | |
| 6659 * A list containing all of the libraries that are exported from this library. | |
| 6660 */ | |
| 6661 List<Library> _exportedLibraries = _EMPTY_ARRAY; | |
| 6662 | |
| 6663 /** | |
| 6664 * A table mapping the sources for the compilation units in this library to th
eir corresponding | |
| 6665 * AST structures. | |
| 6666 */ | |
| 6667 HashMap<Source, CompilationUnit> _astMap = | |
| 6668 new HashMap<Source, CompilationUnit>(); | |
| 6669 | |
| 6670 /** | |
| 6671 * The library scope used when resolving elements within this library's compil
ation units. | |
| 6672 */ | |
| 6673 LibraryScope _libraryScope; | |
| 6674 | |
| 6675 /** | |
| 6676 * Initialize a newly created data holder that can maintain the data associate
d with a library. | |
| 6677 * | |
| 6678 * @param analysisContext the analysis context in which this library is being
analyzed | |
| 6679 * @param errorListener the listener to which analysis errors will be reported | |
| 6680 * @param librarySource the source specifying the defining compilation unit of
this library | |
| 6681 */ | |
| 6682 Library(this._analysisContext, this.errorListener, this.librarySource) { | |
| 6683 this._libraryElement = | |
| 6684 _analysisContext.getLibraryElement(librarySource) as LibraryElementImpl; | |
| 6685 } | |
| 6686 | |
| 6687 /** | |
| 6688 * Return an array of the [CompilationUnit]s that make up the library. The fir
st unit is | |
| 6689 * always the defining unit. | |
| 6690 * | |
| 6691 * @return an array of the [CompilationUnit]s that make up the library. The fi
rst unit is | |
| 6692 * always the defining unit | |
| 6693 */ | |
| 6694 List<CompilationUnit> get compilationUnits { | |
| 6695 List<CompilationUnit> unitArrayList = new List<CompilationUnit>(); | |
| 6696 unitArrayList.add(definingCompilationUnit); | |
| 6697 for (Source source in _astMap.keys.toSet()) { | |
| 6698 if (librarySource != source) { | |
| 6699 unitArrayList.add(getAST(source)); | |
| 6700 } | |
| 6701 } | |
| 6702 return unitArrayList; | |
| 6703 } | |
| 6704 | |
| 6705 /** | |
| 6706 * Return a collection containing the sources for the compilation units in thi
s library, including | |
| 6707 * the defining compilation unit. | |
| 6708 * | |
| 6709 * @return the sources for the compilation units in this library | |
| 6710 */ | |
| 6711 Set<Source> get compilationUnitSources => _astMap.keys.toSet(); | |
| 6712 | |
| 6713 /** | |
| 6714 * Return the AST structure associated with the defining compilation unit for
this library. | |
| 6715 * | |
| 6716 * @return the AST structure associated with the defining compilation unit for
this library | |
| 6717 * @throws AnalysisException if an AST structure could not be created for the
defining compilation | |
| 6718 * unit | |
| 6719 */ | |
| 6720 CompilationUnit get definingCompilationUnit => getAST(librarySource); | |
| 6721 | |
| 6722 /** | |
| 6723 * Set the libraries that are exported by this library to be those in the give
n array. | |
| 6724 * | |
| 6725 * @param exportedLibraries the libraries that are exported by this library | |
| 6726 */ | |
| 6727 void set exportedLibraries(List<Library> exportedLibraries) { | |
| 6728 this._exportedLibraries = exportedLibraries; | |
| 6729 } | |
| 6730 | |
| 6731 /** | |
| 6732 * Return an array containing the libraries that are exported from this librar
y. | |
| 6733 * | |
| 6734 * @return an array containing the libraries that are exported from this libra
ry | |
| 6735 */ | |
| 6736 List<Library> get exports => _exportedLibraries; | |
| 6737 | |
| 6738 /** | |
| 6739 * Set the libraries that are imported into this library to be those in the gi
ven array. | |
| 6740 * | |
| 6741 * @param importedLibraries the libraries that are imported into this library | |
| 6742 */ | |
| 6743 void set importedLibraries(List<Library> importedLibraries) { | |
| 6744 this._importedLibraries = importedLibraries; | |
| 6745 } | |
| 6746 | |
| 6747 /** | |
| 6748 * Return an array containing the libraries that are imported into this librar
y. | |
| 6749 * | |
| 6750 * @return an array containing the libraries that are imported into this libra
ry | |
| 6751 */ | |
| 6752 List<Library> get imports => _importedLibraries; | |
| 6753 | |
| 6754 /** | |
| 6755 * Return an array containing the libraries that are either imported or export
ed from this | |
| 6756 * library. | |
| 6757 * | |
| 6758 * @return the libraries that are either imported or exported from this librar
y | |
| 6759 */ | |
| 6760 List<Library> get importsAndExports { | |
| 6761 HashSet<Library> libraries = new HashSet<Library>(); | |
| 6762 for (Library library in _importedLibraries) { | |
| 6763 libraries.add(library); | |
| 6764 } | |
| 6765 for (Library library in _exportedLibraries) { | |
| 6766 libraries.add(library); | |
| 6767 } | |
| 6768 return new List.from(libraries); | |
| 6769 } | |
| 6770 | |
| 6771 /** | |
| 6772 * Return the inheritance manager for this library. | |
| 6773 * | |
| 6774 * @return the inheritance manager for this library | |
| 6775 */ | |
| 6776 InheritanceManager get inheritanceManager { | |
| 6777 if (_inheritanceManager == null) { | |
| 6778 return _inheritanceManager = new InheritanceManager(_libraryElement); | |
| 6779 } | |
| 6780 return _inheritanceManager; | |
| 6781 } | |
| 6782 | |
| 6783 /** | |
| 6784 * Return the library element representing this library, creating it if necess
ary. | |
| 6785 * | |
| 6786 * @return the library element representing this library | |
| 6787 */ | |
| 6788 LibraryElementImpl get libraryElement { | |
| 6789 if (_libraryElement == null) { | |
| 6790 try { | |
| 6791 _libraryElement = _analysisContext | |
| 6792 .computeLibraryElement(librarySource) as LibraryElementImpl; | |
| 6793 } on AnalysisException catch (exception, stackTrace) { | |
| 6794 AnalysisEngine.instance.logger.logError( | |
| 6795 "Could not compute library element for ${librarySource.fullName}", | |
| 6796 new CaughtException(exception, stackTrace)); | |
| 6797 } | |
| 6798 } | |
| 6799 return _libraryElement; | |
| 6800 } | |
| 6801 | |
| 6802 /** | |
| 6803 * Set the library element representing this library to the given library elem
ent. | |
| 6804 * | |
| 6805 * @param libraryElement the library element representing this library | |
| 6806 */ | |
| 6807 void set libraryElement(LibraryElementImpl libraryElement) { | |
| 6808 this._libraryElement = libraryElement; | |
| 6809 if (_inheritanceManager != null) { | |
| 6810 _inheritanceManager.libraryElement = libraryElement; | |
| 6811 } | |
| 6812 } | |
| 6813 | |
| 6814 /** | |
| 6815 * Return the library scope used when resolving elements within this library's
compilation units. | |
| 6816 * | |
| 6817 * @return the library scope used when resolving elements within this library'
s compilation units | |
| 6818 */ | |
| 6819 LibraryScope get libraryScope { | |
| 6820 if (_libraryScope == null) { | |
| 6821 _libraryScope = new LibraryScope(_libraryElement, errorListener); | |
| 6822 } | |
| 6823 return _libraryScope; | |
| 6824 } | |
| 6825 | |
| 6826 /** | |
| 6827 * Return the AST structure associated with the given source. | |
| 6828 * | |
| 6829 * @param source the source representing the compilation unit whose AST is to
be returned | |
| 6830 * @return the AST structure associated with the given source | |
| 6831 * @throws AnalysisException if an AST structure could not be created for the
compilation unit | |
| 6832 */ | |
| 6833 CompilationUnit getAST(Source source) { | |
| 6834 CompilationUnit unit = _astMap[source]; | |
| 6835 if (unit == null) { | |
| 6836 unit = _analysisContext.computeResolvableCompilationUnit(source); | |
| 6837 _astMap[source] = unit; | |
| 6838 } | |
| 6839 return unit; | |
| 6840 } | |
| 6841 | |
| 6842 /** | |
| 6843 * Return the result of resolving the URI of the given URI-based directive aga
inst the URI of the | |
| 6844 * library, or `null` if the URI is not valid. If the URI is not valid, report
the error. | |
| 6845 * | |
| 6846 * @param directive the directive which URI should be resolved | |
| 6847 * @return the result of resolving the URI against the URI of the library | |
| 6848 */ | |
| 6849 Source getSource(UriBasedDirective directive) { | |
| 6850 StringLiteral uriLiteral = directive.uri; | |
| 6851 if (uriLiteral is StringInterpolation) { | |
| 6852 errorListener.onError(new AnalysisError(librarySource, uriLiteral.offset, | |
| 6853 uriLiteral.length, CompileTimeErrorCode.URI_WITH_INTERPOLATION)); | |
| 6854 return null; | |
| 6855 } | |
| 6856 String uriContent = uriLiteral.stringValue.trim(); | |
| 6857 _directiveUris[directive] = uriContent; | |
| 6858 uriContent = Uri.encodeFull(uriContent); | |
| 6859 if (directive is ImportDirective && | |
| 6860 uriContent.startsWith(_DART_EXT_SCHEME)) { | |
| 6861 _libraryElement.hasExtUri = true; | |
| 6862 return null; | |
| 6863 } | |
| 6864 try { | |
| 6865 parseUriWithException(uriContent); | |
| 6866 Source source = | |
| 6867 _analysisContext.sourceFactory.resolveUri(librarySource, uriContent); | |
| 6868 if (!_analysisContext.exists(source)) { | |
| 6869 errorListener.onError(new AnalysisError(librarySource, | |
| 6870 uriLiteral.offset, uriLiteral.length, | |
| 6871 CompileTimeErrorCode.URI_DOES_NOT_EXIST, [uriContent])); | |
| 6872 } | |
| 6873 return source; | |
| 6874 } on URISyntaxException { | |
| 6875 errorListener.onError(new AnalysisError(librarySource, uriLiteral.offset, | |
| 6876 uriLiteral.length, CompileTimeErrorCode.INVALID_URI, [uriContent])); | |
| 6877 } | |
| 6878 return null; | |
| 6879 } | |
| 6880 | |
| 6881 /** | |
| 6882 * Returns the URI value of the given directive. | |
| 6883 */ | |
| 6884 String getUri(UriBasedDirective directive) => _directiveUris[directive]; | |
| 6885 | |
| 6886 /** | |
| 6887 * Set the AST structure associated with the defining compilation unit for thi
s library to the | |
| 6888 * given AST structure. | |
| 6889 * | |
| 6890 * @param unit the AST structure associated with the defining compilation unit
for this library | |
| 6891 */ | |
| 6892 void setDefiningCompilationUnit(CompilationUnit unit) { | |
| 6893 _astMap[librarySource] = unit; | |
| 6894 } | |
| 6895 | |
| 6896 @override | |
| 6897 String toString() => librarySource.shortName; | |
| 6898 } | |
| 6899 | |
| 6900 /** | |
| 6901 * Instances of the class `LibraryElementBuilder` build an element model for a s
ingle library. | |
| 6902 */ | |
| 6903 class LibraryElementBuilder { | |
| 6904 /** | |
| 6905 * The analysis context in which the element model will be built. | |
| 6906 */ | |
| 6907 final InternalAnalysisContext _analysisContext; | |
| 6908 | |
| 6909 /** | |
| 6910 * The listener to which errors will be reported. | |
| 6911 */ | |
| 6912 final AnalysisErrorListener _errorListener; | |
| 6913 | |
| 6914 /** | |
| 6915 * Initialize a newly created library element builder. | |
| 6916 * | |
| 6917 * @param analysisContext the analysis context in which the element model will
be built | |
| 6918 * @param errorListener the listener to which errors will be reported | |
| 6919 */ | |
| 6920 LibraryElementBuilder(this._analysisContext, this._errorListener); | |
| 6921 | |
| 6922 /** | |
| 6923 * Build the library element for the given library. | |
| 6924 * | |
| 6925 * @param library the library for which an element model is to be built | |
| 6926 * @return the library element that was built | |
| 6927 * @throws AnalysisException if the analysis could not be performed | |
| 6928 */ | |
| 6929 LibraryElementImpl buildLibrary(Library library) { | |
| 6930 CompilationUnitBuilder builder = new CompilationUnitBuilder(); | |
| 6931 Source librarySource = library.librarySource; | |
| 6932 CompilationUnit definingCompilationUnit = library.definingCompilationUnit; | |
| 6933 CompilationUnitElementImpl definingCompilationUnitElement = builder | |
| 6934 .buildCompilationUnit( | |
| 6935 librarySource, definingCompilationUnit, librarySource); | |
| 6936 NodeList<Directive> directives = definingCompilationUnit.directives; | |
| 6937 LibraryIdentifier libraryNameNode = null; | |
| 6938 bool hasPartDirective = false; | |
| 6939 FunctionElement entryPoint = | |
| 6940 _findEntryPoint(definingCompilationUnitElement); | |
| 6941 List<Directive> directivesToResolve = new List<Directive>(); | |
| 6942 List<CompilationUnitElementImpl> sourcedCompilationUnits = | |
| 6943 new List<CompilationUnitElementImpl>(); | |
| 6944 for (Directive directive in directives) { | |
| 6945 // | |
| 6946 // We do not build the elements representing the import and export | |
| 6947 // directives at this point. That is not done until we get to | |
| 6948 // LibraryResolver.buildDirectiveModels() because we need the | |
| 6949 // LibraryElements for the referenced libraries, which might not exist at | |
| 6950 // this point (due to the possibility of circular references). | |
| 6951 // | |
| 6952 if (directive is LibraryDirective) { | |
| 6953 if (libraryNameNode == null) { | |
| 6954 libraryNameNode = directive.name; | |
| 6955 directivesToResolve.add(directive); | |
| 6956 } | |
| 6957 } else if (directive is PartDirective) { | |
| 6958 PartDirective partDirective = directive; | |
| 6959 StringLiteral partUri = partDirective.uri; | |
| 6960 Source partSource = partDirective.source; | |
| 6961 if (_analysisContext.exists(partSource)) { | |
| 6962 hasPartDirective = true; | |
| 6963 CompilationUnit partUnit = library.getAST(partSource); | |
| 6964 CompilationUnitElementImpl part = | |
| 6965 builder.buildCompilationUnit(partSource, partUnit, librarySource); | |
| 6966 part.uriOffset = partUri.offset; | |
| 6967 part.uriEnd = partUri.end; | |
| 6968 part.uri = partDirective.uriContent; | |
| 6969 // | |
| 6970 // Validate that the part contains a part-of directive with the same | |
| 6971 // name as the library. | |
| 6972 // | |
| 6973 String partLibraryName = | |
| 6974 _getPartLibraryName(partSource, partUnit, directivesToResolve); | |
| 6975 if (partLibraryName == null) { | |
| 6976 _errorListener.onError(new AnalysisError(librarySource, | |
| 6977 partUri.offset, partUri.length, | |
| 6978 CompileTimeErrorCode.PART_OF_NON_PART, [partUri.toSource()])); | |
| 6979 } else if (libraryNameNode == null) { | |
| 6980 // TODO(brianwilkerson) Collect the names declared by the part. | |
| 6981 // If they are all the same then we can use that name as the | |
| 6982 // inferred name of the library and present it in a quick-fix. | |
| 6983 // partLibraryNames.add(partLibraryName); | |
| 6984 } else if (libraryNameNode.name != partLibraryName) { | |
| 6985 _errorListener.onError(new AnalysisError(librarySource, | |
| 6986 partUri.offset, partUri.length, | |
| 6987 StaticWarningCode.PART_OF_DIFFERENT_LIBRARY, [ | |
| 6988 libraryNameNode.name, | |
| 6989 partLibraryName | |
| 6990 ])); | |
| 6991 } | |
| 6992 if (entryPoint == null) { | |
| 6993 entryPoint = _findEntryPoint(part); | |
| 6994 } | |
| 6995 directive.element = part; | |
| 6996 sourcedCompilationUnits.add(part); | |
| 6997 } | |
| 6998 } | |
| 6999 } | |
| 7000 if (hasPartDirective && libraryNameNode == null) { | |
| 7001 _errorListener.onError(new AnalysisError(librarySource, 0, 0, | |
| 7002 ResolverErrorCode.MISSING_LIBRARY_DIRECTIVE_WITH_PART)); | |
| 7003 } | |
| 7004 // | |
| 7005 // Create and populate the library element. | |
| 7006 // | |
| 7007 LibraryElementImpl libraryElement = new LibraryElementImpl.forNode( | |
| 7008 _analysisContext.getContextFor(librarySource), libraryNameNode); | |
| 7009 libraryElement.definingCompilationUnit = definingCompilationUnitElement; | |
| 7010 if (entryPoint != null) { | |
| 7011 libraryElement.entryPoint = entryPoint; | |
| 7012 } | |
| 7013 int sourcedUnitCount = sourcedCompilationUnits.length; | |
| 7014 libraryElement.parts = sourcedCompilationUnits; | |
| 7015 for (Directive directive in directivesToResolve) { | |
| 7016 directive.element = libraryElement; | |
| 7017 } | |
| 7018 library.libraryElement = libraryElement; | |
| 7019 if (sourcedUnitCount > 0) { | |
| 7020 _patchTopLevelAccessors(libraryElement); | |
| 7021 } | |
| 7022 return libraryElement; | |
| 7023 } | |
| 7024 | |
| 7025 /** | |
| 7026 * Build the library element for the given library. The resulting element is | |
| 7027 * stored in the [ResolvableLibrary] structure. | |
| 7028 * | |
| 7029 * @param library the library for which an element model is to be built | |
| 7030 * @throws AnalysisException if the analysis could not be performed | |
| 7031 */ | |
| 7032 void buildLibrary2(ResolvableLibrary library) { | |
| 7033 CompilationUnitBuilder builder = new CompilationUnitBuilder(); | |
| 7034 Source librarySource = library.librarySource; | |
| 7035 CompilationUnit definingCompilationUnit = library.definingCompilationUnit; | |
| 7036 CompilationUnitElementImpl definingCompilationUnitElement = builder | |
| 7037 .buildCompilationUnit( | |
| 7038 librarySource, definingCompilationUnit, librarySource); | |
| 7039 NodeList<Directive> directives = definingCompilationUnit.directives; | |
| 7040 LibraryIdentifier libraryNameNode = null; | |
| 7041 bool hasPartDirective = false; | |
| 7042 FunctionElement entryPoint = | |
| 7043 _findEntryPoint(definingCompilationUnitElement); | |
| 7044 List<Directive> directivesToResolve = new List<Directive>(); | |
| 7045 List<CompilationUnitElementImpl> sourcedCompilationUnits = | |
| 7046 new List<CompilationUnitElementImpl>(); | |
| 7047 for (Directive directive in directives) { | |
| 7048 // | |
| 7049 // We do not build the elements representing the import and export | |
| 7050 // directives at this point. That is not done until we get to | |
| 7051 // LibraryResolver.buildDirectiveModels() because we need the | |
| 7052 // LibraryElements for the referenced libraries, which might not exist at | |
| 7053 // this point (due to the possibility of circular references). | |
| 7054 // | |
| 7055 if (directive is LibraryDirective) { | |
| 7056 if (libraryNameNode == null) { | |
| 7057 libraryNameNode = directive.name; | |
| 7058 directivesToResolve.add(directive); | |
| 7059 } | |
| 7060 } else if (directive is PartDirective) { | |
| 7061 PartDirective partDirective = directive; | |
| 7062 StringLiteral partUri = partDirective.uri; | |
| 7063 Source partSource = partDirective.source; | |
| 7064 if (_analysisContext.exists(partSource)) { | |
| 7065 hasPartDirective = true; | |
| 7066 CompilationUnit partUnit = library.getAST(partSource); | |
| 7067 if (partUnit != null) { | |
| 7068 CompilationUnitElementImpl part = builder.buildCompilationUnit( | |
| 7069 partSource, partUnit, librarySource); | |
| 7070 part.uriOffset = partUri.offset; | |
| 7071 part.uriEnd = partUri.end; | |
| 7072 part.uri = partDirective.uriContent; | |
| 7073 // | |
| 7074 // Validate that the part contains a part-of directive with the same | |
| 7075 // name as the library. | |
| 7076 // | |
| 7077 String partLibraryName = | |
| 7078 _getPartLibraryName(partSource, partUnit, directivesToResolve); | |
| 7079 if (partLibraryName == null) { | |
| 7080 _errorListener.onError(new AnalysisError(librarySource, | |
| 7081 partUri.offset, partUri.length, | |
| 7082 CompileTimeErrorCode.PART_OF_NON_PART, [partUri.toSource()])); | |
| 7083 } else if (libraryNameNode == null) { | |
| 7084 // TODO(brianwilkerson) Collect the names declared by the part. | |
| 7085 // If they are all the same then we can use that name as the | |
| 7086 // inferred name of the library and present it in a quick-fix. | |
| 7087 // partLibraryNames.add(partLibraryName); | |
| 7088 } else if (libraryNameNode.name != partLibraryName) { | |
| 7089 _errorListener.onError(new AnalysisError(librarySource, | |
| 7090 partUri.offset, partUri.length, | |
| 7091 StaticWarningCode.PART_OF_DIFFERENT_LIBRARY, [ | |
| 7092 libraryNameNode.name, | |
| 7093 partLibraryName | |
| 7094 ])); | |
| 7095 } | |
| 7096 if (entryPoint == null) { | |
| 7097 entryPoint = _findEntryPoint(part); | |
| 7098 } | |
| 7099 directive.element = part; | |
| 7100 sourcedCompilationUnits.add(part); | |
| 7101 } | |
| 7102 } | |
| 7103 } | |
| 7104 } | |
| 7105 if (hasPartDirective && libraryNameNode == null) { | |
| 7106 _errorListener.onError(new AnalysisError(librarySource, 0, 0, | |
| 7107 ResolverErrorCode.MISSING_LIBRARY_DIRECTIVE_WITH_PART)); | |
| 7108 } | |
| 7109 // | |
| 7110 // Create and populate the library element. | |
| 7111 // | |
| 7112 LibraryElementImpl libraryElement = new LibraryElementImpl.forNode( | |
| 7113 _analysisContext.getContextFor(librarySource), libraryNameNode); | |
| 7114 libraryElement.definingCompilationUnit = definingCompilationUnitElement; | |
| 7115 if (entryPoint != null) { | |
| 7116 libraryElement.entryPoint = entryPoint; | |
| 7117 } | |
| 7118 int sourcedUnitCount = sourcedCompilationUnits.length; | |
| 7119 libraryElement.parts = sourcedCompilationUnits; | |
| 7120 for (Directive directive in directivesToResolve) { | |
| 7121 directive.element = libraryElement; | |
| 7122 } | |
| 7123 library.libraryElement = libraryElement; | |
| 7124 if (sourcedUnitCount > 0) { | |
| 7125 _patchTopLevelAccessors(libraryElement); | |
| 7126 } | |
| 7127 } | |
| 7128 | |
| 7129 /** | |
| 7130 * Add all of the non-synthetic getters and setters defined in the given compi
lation unit that | |
| 7131 * have no corresponding accessor to one of the given collections. | |
| 7132 * | |
| 7133 * @param getters the map to which getters are to be added | |
| 7134 * @param setters the list to which setters are to be added | |
| 7135 * @param unit the compilation unit defining the accessors that are potentiall
y being added | |
| 7136 */ | |
| 7137 void _collectAccessors(HashMap<String, PropertyAccessorElement> getters, | |
| 7138 List<PropertyAccessorElement> setters, CompilationUnitElement unit) { | |
| 7139 for (PropertyAccessorElement accessor in unit.accessors) { | |
| 7140 if (accessor.isGetter) { | |
| 7141 if (!accessor.isSynthetic && accessor.correspondingSetter == null) { | |
| 7142 getters[accessor.displayName] = accessor; | |
| 7143 } | |
| 7144 } else { | |
| 7145 if (!accessor.isSynthetic && accessor.correspondingGetter == null) { | |
| 7146 setters.add(accessor); | |
| 7147 } | |
| 7148 } | |
| 7149 } | |
| 7150 } | |
| 7151 | |
| 7152 /** | |
| 7153 * Search the top-level functions defined in the given compilation unit for th
e entry point. | |
| 7154 * | |
| 7155 * @param element the compilation unit to be searched | |
| 7156 * @return the entry point that was found, or `null` if the compilation unit d
oes not define | |
| 7157 * an entry point | |
| 7158 */ | |
| 7159 FunctionElement _findEntryPoint(CompilationUnitElementImpl element) { | |
| 7160 for (FunctionElement function in element.functions) { | |
| 7161 if (function.isEntryPoint) { | |
| 7162 return function; | |
| 7163 } | |
| 7164 } | |
| 7165 return null; | |
| 7166 } | |
| 7167 | |
| 7168 /** | |
| 7169 * Return the name of the library that the given part is declared to be a part
of, or `null` | |
| 7170 * if the part does not contain a part-of directive. | |
| 7171 * | |
| 7172 * @param partSource the source representing the part | |
| 7173 * @param partUnit the AST structure of the part | |
| 7174 * @param directivesToResolve a list of directives that should be resolved to
the library being | |
| 7175 * built | |
| 7176 * @return the name of the library that the given part is declared to be a par
t of | |
| 7177 */ | |
| 7178 String _getPartLibraryName(Source partSource, CompilationUnit partUnit, | |
| 7179 List<Directive> directivesToResolve) { | |
| 7180 for (Directive directive in partUnit.directives) { | |
| 7181 if (directive is PartOfDirective) { | |
| 7182 directivesToResolve.add(directive); | |
| 7183 LibraryIdentifier libraryName = directive.libraryName; | |
| 7184 if (libraryName != null) { | |
| 7185 return libraryName.name; | |
| 7186 } | |
| 7187 } | |
| 7188 } | |
| 7189 return null; | |
| 7190 } | |
| 7191 | |
| 7192 /** | |
| 7193 * Look through all of the compilation units defined for the given library, lo
oking for getters | |
| 7194 * and setters that are defined in different compilation units but that have t
he same names. If | |
| 7195 * any are found, make sure that they have the same variable element. | |
| 7196 * | |
| 7197 * @param libraryElement the library defining the compilation units to be proc
essed | |
| 7198 */ | |
| 7199 void _patchTopLevelAccessors(LibraryElementImpl libraryElement) { | |
| 7200 HashMap<String, PropertyAccessorElement> getters = | |
| 7201 new HashMap<String, PropertyAccessorElement>(); | |
| 7202 List<PropertyAccessorElement> setters = new List<PropertyAccessorElement>(); | |
| 7203 _collectAccessors(getters, setters, libraryElement.definingCompilationUnit); | |
| 7204 for (CompilationUnitElement unit in libraryElement.parts) { | |
| 7205 _collectAccessors(getters, setters, unit); | |
| 7206 } | |
| 7207 for (PropertyAccessorElement setter in setters) { | |
| 7208 PropertyAccessorElement getter = getters[setter.displayName]; | |
| 7209 if (getter != null) { | |
| 7210 PropertyInducingElementImpl variable = | |
| 7211 getter.variable as PropertyInducingElementImpl; | |
| 7212 variable.setter = setter; | |
| 7213 (setter as PropertyAccessorElementImpl).variable = variable; | |
| 7214 } | |
| 7215 } | |
| 7216 } | |
| 7217 } | |
| 7218 | |
| 7219 /** | |
| 7220 * Instances of the class `LibraryImportScope` represent the scope containing al
l of the names | |
| 7221 * available from imported libraries. | |
| 7222 */ | |
| 7223 class LibraryImportScope extends Scope { | |
| 7224 /** | |
| 7225 * The element representing the library in which this scope is enclosed. | |
| 7226 */ | |
| 7227 final LibraryElement _definingLibrary; | |
| 7228 | |
| 7229 /** | |
| 7230 * The listener that is to be informed when an error is encountered. | |
| 7231 */ | |
| 7232 final AnalysisErrorListener errorListener; | |
| 7233 | |
| 7234 /** | |
| 7235 * A list of the namespaces representing the names that are available in this
scope from imported | |
| 7236 * libraries. | |
| 7237 */ | |
| 7238 List<Namespace> _importedNamespaces; | |
| 7239 | |
| 7240 /** | |
| 7241 * Initialize a newly created scope representing the names imported into the g
iven library. | |
| 7242 * | |
| 7243 * @param definingLibrary the element representing the library that imports th
e names defined in | |
| 7244 * this scope | |
| 7245 * @param errorListener the listener that is to be informed when an error is e
ncountered | |
| 7246 */ | |
| 7247 LibraryImportScope(this._definingLibrary, this.errorListener) { | |
| 7248 _createImportedNamespaces(); | |
| 7249 } | |
| 7250 | |
| 7251 @override | |
| 7252 void define(Element element) { | |
| 7253 if (!Scope.isPrivateName(element.displayName)) { | |
| 7254 super.define(element); | |
| 7255 } | |
| 7256 } | |
| 7257 | |
| 7258 @override | |
| 7259 Source getSource(AstNode node) { | |
| 7260 Source source = super.getSource(node); | |
| 7261 if (source == null) { | |
| 7262 source = _definingLibrary.definingCompilationUnit.source; | |
| 7263 } | |
| 7264 return source; | |
| 7265 } | |
| 7266 | |
| 7267 @override | |
| 7268 Element internalLookup( | |
| 7269 Identifier identifier, String name, LibraryElement referencingLibrary) { | |
| 7270 Element foundElement = localLookup(name, referencingLibrary); | |
| 7271 if (foundElement != null) { | |
| 7272 return foundElement; | |
| 7273 } | |
| 7274 for (int i = 0; i < _importedNamespaces.length; i++) { | |
| 7275 Namespace nameSpace = _importedNamespaces[i]; | |
| 7276 Element element = nameSpace.get(name); | |
| 7277 if (element != null) { | |
| 7278 if (foundElement == null) { | |
| 7279 foundElement = element; | |
| 7280 } else if (!identical(foundElement, element)) { | |
| 7281 foundElement = MultiplyDefinedElementImpl.fromElements( | |
| 7282 _definingLibrary.context, foundElement, element); | |
| 7283 } | |
| 7284 } | |
| 7285 } | |
| 7286 if (foundElement is MultiplyDefinedElementImpl) { | |
| 7287 foundElement = _removeSdkElements( | |
| 7288 identifier, name, foundElement as MultiplyDefinedElementImpl); | |
| 7289 } | |
| 7290 if (foundElement is MultiplyDefinedElementImpl) { | |
| 7291 String foundEltName = foundElement.displayName; | |
| 7292 List<Element> conflictingMembers = foundElement.conflictingElements; | |
| 7293 int count = conflictingMembers.length; | |
| 7294 List<String> libraryNames = new List<String>(count); | |
| 7295 for (int i = 0; i < count; i++) { | |
| 7296 libraryNames[i] = _getLibraryName(conflictingMembers[i]); | |
| 7297 } | |
| 7298 libraryNames.sort(); | |
| 7299 errorListener.onError(new AnalysisError(getSource(identifier), | |
| 7300 identifier.offset, identifier.length, | |
| 7301 StaticWarningCode.AMBIGUOUS_IMPORT, [ | |
| 7302 foundEltName, | |
| 7303 StringUtilities.printListOfQuotedNames(libraryNames) | |
| 7304 ])); | |
| 7305 return foundElement; | |
| 7306 } | |
| 7307 if (foundElement != null) { | |
| 7308 defineNameWithoutChecking(name, foundElement); | |
| 7309 } | |
| 7310 return foundElement; | |
| 7311 } | |
| 7312 | |
| 7313 /** | |
| 7314 * Create all of the namespaces associated with the libraries imported into th
is library. The | |
| 7315 * names are not added to this scope, but are stored for later reference. | |
| 7316 * | |
| 7317 * @param definingLibrary the element representing the library that imports th
e libraries for | |
| 7318 * which namespaces will be created | |
| 7319 */ | |
| 7320 void _createImportedNamespaces() { | |
| 7321 NamespaceBuilder builder = new NamespaceBuilder(); | |
| 7322 List<ImportElement> imports = _definingLibrary.imports; | |
| 7323 int count = imports.length; | |
| 7324 _importedNamespaces = new List<Namespace>(count); | |
| 7325 for (int i = 0; i < count; i++) { | |
| 7326 _importedNamespaces[i] = | |
| 7327 builder.createImportNamespaceForDirective(imports[i]); | |
| 7328 } | |
| 7329 } | |
| 7330 | |
| 7331 /** | |
| 7332 * Returns the name of the library that defines given element. | |
| 7333 * | |
| 7334 * @param element the element to get library name | |
| 7335 * @return the name of the library that defines given element | |
| 7336 */ | |
| 7337 String _getLibraryName(Element element) { | |
| 7338 if (element == null) { | |
| 7339 return StringUtilities.EMPTY; | |
| 7340 } | |
| 7341 LibraryElement library = element.library; | |
| 7342 if (library == null) { | |
| 7343 return StringUtilities.EMPTY; | |
| 7344 } | |
| 7345 List<ImportElement> imports = _definingLibrary.imports; | |
| 7346 int count = imports.length; | |
| 7347 for (int i = 0; i < count; i++) { | |
| 7348 if (identical(imports[i].importedLibrary, library)) { | |
| 7349 return library.definingCompilationUnit.displayName; | |
| 7350 } | |
| 7351 } | |
| 7352 List<String> indirectSources = new List<String>(); | |
| 7353 for (int i = 0; i < count; i++) { | |
| 7354 LibraryElement importedLibrary = imports[i].importedLibrary; | |
| 7355 if (importedLibrary != null) { | |
| 7356 for (LibraryElement exportedLibrary | |
| 7357 in importedLibrary.exportedLibraries) { | |
| 7358 if (identical(exportedLibrary, library)) { | |
| 7359 indirectSources | |
| 7360 .add(importedLibrary.definingCompilationUnit.displayName); | |
| 7361 } | |
| 7362 } | |
| 7363 } | |
| 7364 } | |
| 7365 int indirectCount = indirectSources.length; | |
| 7366 StringBuffer buffer = new StringBuffer(); | |
| 7367 buffer.write(library.definingCompilationUnit.displayName); | |
| 7368 if (indirectCount > 0) { | |
| 7369 buffer.write(" (via "); | |
| 7370 if (indirectCount > 1) { | |
| 7371 indirectSources.sort(); | |
| 7372 buffer.write(StringUtilities.printListOfQuotedNames(indirectSources)); | |
| 7373 } else { | |
| 7374 buffer.write(indirectSources[0]); | |
| 7375 } | |
| 7376 buffer.write(")"); | |
| 7377 } | |
| 7378 return buffer.toString(); | |
| 7379 } | |
| 7380 | |
| 7381 /** | |
| 7382 * Given a collection of elements (captured by the [foundElement]) that the | |
| 7383 * [identifier] (with the given [name]) resolved to, remove from the list all | |
| 7384 * of the names defined in the SDK and return the element(s) that remain. | |
| 7385 */ | |
| 7386 Element _removeSdkElements(Identifier identifier, String name, | |
| 7387 MultiplyDefinedElementImpl foundElement) { | |
| 7388 List<Element> conflictingElements = foundElement.conflictingElements; | |
| 7389 List<Element> nonSdkElements = new List<Element>(); | |
| 7390 Element sdkElement = null; | |
| 7391 for (Element member in conflictingElements) { | |
| 7392 if (member.library.isInSdk) { | |
| 7393 sdkElement = member; | |
| 7394 } else { | |
| 7395 nonSdkElements.add(member); | |
| 7396 } | |
| 7397 } | |
| 7398 if (sdkElement != null && nonSdkElements.length > 0) { | |
| 7399 String sdkLibName = _getLibraryName(sdkElement); | |
| 7400 String otherLibName = _getLibraryName(nonSdkElements[0]); | |
| 7401 errorListener.onError(new AnalysisError(getSource(identifier), | |
| 7402 identifier.offset, identifier.length, | |
| 7403 StaticWarningCode.CONFLICTING_DART_IMPORT, [ | |
| 7404 name, | |
| 7405 sdkLibName, | |
| 7406 otherLibName | |
| 7407 ])); | |
| 7408 } | |
| 7409 if (nonSdkElements.length == conflictingElements.length) { | |
| 7410 // None of the members were removed | |
| 7411 return foundElement; | |
| 7412 } else if (nonSdkElements.length == 1) { | |
| 7413 // All but one member was removed | |
| 7414 return nonSdkElements[0]; | |
| 7415 } else if (nonSdkElements.length == 0) { | |
| 7416 // All members were removed | |
| 7417 AnalysisEngine.instance.logger | |
| 7418 .logInformation("Multiply defined SDK element: $foundElement"); | |
| 7419 return foundElement; | |
| 7420 } | |
| 7421 return new MultiplyDefinedElementImpl( | |
| 7422 _definingLibrary.context, nonSdkElements); | |
| 7423 } | |
| 7424 } | |
| 7425 | |
| 7426 /** | |
| 7427 * Instances of the class `LibraryResolver` are used to resolve one or more mutu
ally dependent | |
| 7428 * libraries within a single context. | |
| 7429 */ | |
| 7430 class LibraryResolver { | |
| 7431 /** | |
| 7432 * The analysis context in which the libraries are being analyzed. | |
| 7433 */ | |
| 7434 final InternalAnalysisContext analysisContext; | |
| 7435 | |
| 7436 /** | |
| 7437 * The listener to which analysis errors will be reported, this error listener
is either | |
| 7438 * references [recordingErrorListener], or it unions the passed | |
| 7439 * [AnalysisErrorListener] with the [recordingErrorListener]. | |
| 7440 */ | |
| 7441 RecordingErrorListener _errorListener; | |
| 7442 | |
| 7443 /** | |
| 7444 * A source object representing the core library (dart:core). | |
| 7445 */ | |
| 7446 Source _coreLibrarySource; | |
| 7447 | |
| 7448 /** | |
| 7449 * A Source object representing the async library (dart:async). | |
| 7450 */ | |
| 7451 Source _asyncLibrarySource; | |
| 7452 | |
| 7453 /** | |
| 7454 * The object representing the core library. | |
| 7455 */ | |
| 7456 Library _coreLibrary; | |
| 7457 | |
| 7458 /** | |
| 7459 * The object representing the async library. | |
| 7460 */ | |
| 7461 Library _asyncLibrary; | |
| 7462 | |
| 7463 /** | |
| 7464 * The object used to access the types from the core library. | |
| 7465 */ | |
| 7466 TypeProvider _typeProvider; | |
| 7467 | |
| 7468 /** | |
| 7469 * A table mapping library sources to the information being maintained for tho
se libraries. | |
| 7470 */ | |
| 7471 HashMap<Source, Library> _libraryMap = new HashMap<Source, Library>(); | |
| 7472 | |
| 7473 /** | |
| 7474 * A collection containing the libraries that are being resolved together. | |
| 7475 */ | |
| 7476 Set<Library> _librariesInCycles; | |
| 7477 | |
| 7478 /** | |
| 7479 * Initialize a newly created library resolver to resolve libraries within the
given context. | |
| 7480 * | |
| 7481 * @param analysisContext the analysis context in which the library is being a
nalyzed | |
| 7482 */ | |
| 7483 LibraryResolver(this.analysisContext) { | |
| 7484 this._errorListener = new RecordingErrorListener(); | |
| 7485 _coreLibrarySource = | |
| 7486 analysisContext.sourceFactory.forUri(DartSdk.DART_CORE); | |
| 7487 _asyncLibrarySource = | |
| 7488 analysisContext.sourceFactory.forUri(DartSdk.DART_ASYNC); | |
| 7489 } | |
| 7490 | |
| 7491 /** | |
| 7492 * Return the listener to which analysis errors will be reported. | |
| 7493 * | |
| 7494 * @return the listener to which analysis errors will be reported | |
| 7495 */ | |
| 7496 RecordingErrorListener get errorListener => _errorListener; | |
| 7497 | |
| 7498 /** | |
| 7499 * Return an array containing information about all of the libraries that were
resolved. | |
| 7500 * | |
| 7501 * @return an array containing the libraries that were resolved | |
| 7502 */ | |
| 7503 Set<Library> get resolvedLibraries => _librariesInCycles; | |
| 7504 | |
| 7505 /** | |
| 7506 * The object used to access the types from the core library. | |
| 7507 */ | |
| 7508 TypeProvider get typeProvider => _typeProvider; | |
| 7509 | |
| 7510 /** | |
| 7511 * Create an object to represent the information about the library defined by
the compilation unit | |
| 7512 * with the given source. | |
| 7513 * | |
| 7514 * @param librarySource the source of the library's defining compilation unit | |
| 7515 * @return the library object that was created | |
| 7516 * @throws AnalysisException if the library source is not valid | |
| 7517 */ | |
| 7518 Library createLibrary(Source librarySource) { | |
| 7519 Library library = | |
| 7520 new Library(analysisContext, _errorListener, librarySource); | |
| 7521 _libraryMap[librarySource] = library; | |
| 7522 return library; | |
| 7523 } | |
| 7524 | |
| 7525 /** | |
| 7526 * Resolve the library specified by the given source in the given context. The
library is assumed | |
| 7527 * to be embedded in the given source. | |
| 7528 * | |
| 7529 * @param librarySource the source specifying the defining compilation unit of
the library to be | |
| 7530 * resolved | |
| 7531 * @param unit the compilation unit representing the embedded library | |
| 7532 * @param fullAnalysis `true` if a full analysis should be performed | |
| 7533 * @return the element representing the resolved library | |
| 7534 * @throws AnalysisException if the library could not be resolved for some rea
son | |
| 7535 */ | |
| 7536 LibraryElement resolveEmbeddedLibrary( | |
| 7537 Source librarySource, CompilationUnit unit, bool fullAnalysis) { | |
| 7538 // | |
| 7539 // Create the objects representing the library being resolved and the core | |
| 7540 // library. | |
| 7541 // | |
| 7542 Library targetLibrary = _createLibraryWithUnit(librarySource, unit); | |
| 7543 _coreLibrary = _libraryMap[_coreLibrarySource]; | |
| 7544 if (_coreLibrary == null) { | |
| 7545 // This will only happen if the library being analyzed is the core | |
| 7546 // library. | |
| 7547 _coreLibrary = createLibrary(_coreLibrarySource); | |
| 7548 if (_coreLibrary == null) { | |
| 7549 LibraryResolver2.missingCoreLibrary( | |
| 7550 analysisContext, _coreLibrarySource); | |
| 7551 } | |
| 7552 } | |
| 7553 _asyncLibrary = _libraryMap[_asyncLibrarySource]; | |
| 7554 if (_asyncLibrary == null) { | |
| 7555 // This will only happen if the library being analyzed is the async | |
| 7556 // library. | |
| 7557 _asyncLibrary = createLibrary(_asyncLibrarySource); | |
| 7558 if (_asyncLibrary == null) { | |
| 7559 LibraryResolver2.missingAsyncLibrary( | |
| 7560 analysisContext, _asyncLibrarySource); | |
| 7561 } | |
| 7562 } | |
| 7563 // | |
| 7564 // Compute the set of libraries that need to be resolved together. | |
| 7565 // | |
| 7566 _computeEmbeddedLibraryDependencies(targetLibrary, unit); | |
| 7567 _librariesInCycles = _computeLibrariesInCycles(targetLibrary); | |
| 7568 // | |
| 7569 // Build the element models representing the libraries being resolved. | |
| 7570 // This is done in three steps: | |
| 7571 // | |
| 7572 // 1. Build the basic element models without making any connections | |
| 7573 // between elements other than the basic parent/child relationships. | |
| 7574 // This includes building the elements representing the libraries. | |
| 7575 // 2. Build the elements for the import and export directives. This | |
| 7576 // requires that we have the elements built for the referenced | |
| 7577 // libraries, but because of the possibility of circular references | |
| 7578 // needs to happen after all of the library elements have been created. | |
| 7579 // 3. Build the rest of the type model by connecting superclasses, mixins, | |
| 7580 // and interfaces. This requires that we be able to compute the names | |
| 7581 // visible in the libraries being resolved, which in turn requires that | |
| 7582 // we have resolved the import directives. | |
| 7583 // | |
| 7584 _buildElementModels(); | |
| 7585 LibraryElement coreElement = _coreLibrary.libraryElement; | |
| 7586 if (coreElement == null) { | |
| 7587 throw new AnalysisException("Could not resolve dart:core"); | |
| 7588 } | |
| 7589 LibraryElement asyncElement = _asyncLibrary.libraryElement; | |
| 7590 if (asyncElement == null) { | |
| 7591 throw new AnalysisException("Could not resolve dart:async"); | |
| 7592 } | |
| 7593 _buildDirectiveModels(); | |
| 7594 _typeProvider = new TypeProviderImpl(coreElement, asyncElement); | |
| 7595 _buildTypeHierarchies(); | |
| 7596 // | |
| 7597 // Perform resolution and type analysis. | |
| 7598 // | |
| 7599 // TODO(brianwilkerson) Decide whether we want to resolve all of the | |
| 7600 // libraries or whether we want to only resolve the target library. | |
| 7601 // The advantage to resolving everything is that we have already done part | |
| 7602 // of the work so we'll avoid duplicated effort. The disadvantage of | |
| 7603 // resolving everything is that we might do extra work that we don't | |
| 7604 // really care about. Another possibility is to add a parameter to this | |
| 7605 // method and punt the decision to the clients. | |
| 7606 // | |
| 7607 //if (analyzeAll) { | |
| 7608 resolveReferencesAndTypes(); | |
| 7609 //} else { | |
| 7610 // resolveReferencesAndTypes(targetLibrary); | |
| 7611 //} | |
| 7612 _performConstantEvaluation(); | |
| 7613 return targetLibrary.libraryElement; | |
| 7614 } | |
| 7615 | |
| 7616 /** | |
| 7617 * Resolve the library specified by the given source in the given context. | |
| 7618 * | |
| 7619 * Note that because Dart allows circular imports between libraries, it is pos
sible that more than | |
| 7620 * one library will need to be resolved. In such cases the error listener can
receive errors from | |
| 7621 * multiple libraries. | |
| 7622 * | |
| 7623 * @param librarySource the source specifying the defining compilation unit of
the library to be | |
| 7624 * resolved | |
| 7625 * @param fullAnalysis `true` if a full analysis should be performed | |
| 7626 * @return the element representing the resolved library | |
| 7627 * @throws AnalysisException if the library could not be resolved for some rea
son | |
| 7628 */ | |
| 7629 LibraryElement resolveLibrary(Source librarySource, bool fullAnalysis) { | |
| 7630 // | |
| 7631 // Create the object representing the library being resolved and compute | |
| 7632 // the dependency relationship. Note that all libraries depend implicitly | |
| 7633 // on core, and we inject an ersatz dependency on async, so once this is | |
| 7634 // done the core and async library elements will have been created. | |
| 7635 // | |
| 7636 Library targetLibrary = createLibrary(librarySource); | |
| 7637 _computeLibraryDependencies(targetLibrary); | |
| 7638 _coreLibrary = _libraryMap[_coreLibrarySource]; | |
| 7639 _asyncLibrary = _libraryMap[_asyncLibrarySource]; | |
| 7640 // | |
| 7641 // Compute the set of libraries that need to be resolved together. | |
| 7642 // | |
| 7643 _librariesInCycles = _computeLibrariesInCycles(targetLibrary); | |
| 7644 // | |
| 7645 // Build the element models representing the libraries being resolved. | |
| 7646 // This is done in three steps: | |
| 7647 // | |
| 7648 // 1. Build the basic element models without making any connections | |
| 7649 // between elements other than the basic parent/child relationships. | |
| 7650 // This includes building the elements representing the libraries, but | |
| 7651 // excludes members defined in enums. | |
| 7652 // 2. Build the elements for the import and export directives. This | |
| 7653 // requires that we have the elements built for the referenced | |
| 7654 // libraries, but because of the possibility of circular references | |
| 7655 // needs to happen after all of the library elements have been created. | |
| 7656 // 3. Build the members in enum declarations. | |
| 7657 // 4. Build the rest of the type model by connecting superclasses, mixins, | |
| 7658 // and interfaces. This requires that we be able to compute the names | |
| 7659 // visible in the libraries being resolved, which in turn requires that | |
| 7660 // we have resolved the import directives. | |
| 7661 // | |
| 7662 _buildElementModels(); | |
| 7663 LibraryElement coreElement = _coreLibrary.libraryElement; | |
| 7664 if (coreElement == null) { | |
| 7665 throw new AnalysisException("Could not resolve dart:core"); | |
| 7666 } | |
| 7667 LibraryElement asyncElement = _asyncLibrary.libraryElement; | |
| 7668 if (asyncElement == null) { | |
| 7669 throw new AnalysisException("Could not resolve dart:async"); | |
| 7670 } | |
| 7671 _buildDirectiveModels(); | |
| 7672 _typeProvider = new TypeProviderImpl(coreElement, asyncElement); | |
| 7673 _buildEnumMembers(); | |
| 7674 _buildTypeHierarchies(); | |
| 7675 // | |
| 7676 // Perform resolution and type analysis. | |
| 7677 // | |
| 7678 // TODO(brianwilkerson) Decide whether we want to resolve all of the | |
| 7679 // libraries or whether we want to only resolve the target library. The | |
| 7680 // advantage to resolving everything is that we have already done part of | |
| 7681 // the work so we'll avoid duplicated effort. The disadvantage of | |
| 7682 // resolving everything is that we might do extra work that we don't | |
| 7683 // really care about. Another possibility is to add a parameter to this | |
| 7684 // method and punt the decision to the clients. | |
| 7685 // | |
| 7686 //if (analyzeAll) { | |
| 7687 resolveReferencesAndTypes(); | |
| 7688 //} else { | |
| 7689 // resolveReferencesAndTypes(targetLibrary); | |
| 7690 //} | |
| 7691 _performConstantEvaluation(); | |
| 7692 return targetLibrary.libraryElement; | |
| 7693 } | |
| 7694 | |
| 7695 /** | |
| 7696 * Resolve the identifiers and perform type analysis in the libraries in the c
urrent cycle. | |
| 7697 * | |
| 7698 * @throws AnalysisException if any of the identifiers could not be resolved o
r if any of the | |
| 7699 * libraries could not have their types analyzed | |
| 7700 */ | |
| 7701 void resolveReferencesAndTypes() { | |
| 7702 for (Library library in _librariesInCycles) { | |
| 7703 _resolveReferencesAndTypesInLibrary(library); | |
| 7704 } | |
| 7705 } | |
| 7706 | |
| 7707 /** | |
| 7708 * Add a dependency to the given map from the referencing library to the refer
enced library. | |
| 7709 * | |
| 7710 * @param dependencyMap the map to which the dependency is to be added | |
| 7711 * @param referencingLibrary the library that references the referenced librar
y | |
| 7712 * @param referencedLibrary the library referenced by the referencing library | |
| 7713 */ | |
| 7714 void _addDependencyToMap(HashMap<Library, List<Library>> dependencyMap, | |
| 7715 Library referencingLibrary, Library referencedLibrary) { | |
| 7716 List<Library> dependentLibraries = dependencyMap[referencedLibrary]; | |
| 7717 if (dependentLibraries == null) { | |
| 7718 dependentLibraries = new List<Library>(); | |
| 7719 dependencyMap[referencedLibrary] = dependentLibraries; | |
| 7720 } | |
| 7721 dependentLibraries.add(referencingLibrary); | |
| 7722 } | |
| 7723 | |
| 7724 /** | |
| 7725 * Given a library that is part of a cycle that includes the root library, add
to the given set of | |
| 7726 * libraries all of the libraries reachable from the root library that are als
o included in the | |
| 7727 * cycle. | |
| 7728 * | |
| 7729 * @param library the library to be added to the collection of libraries in cy
cles | |
| 7730 * @param librariesInCycle a collection of the libraries that are in the cycle | |
| 7731 * @param dependencyMap a table mapping libraries to the collection of librari
es from which those | |
| 7732 * libraries are referenced | |
| 7733 */ | |
| 7734 void _addLibrariesInCycle(Library library, Set<Library> librariesInCycle, | |
| 7735 HashMap<Library, List<Library>> dependencyMap) { | |
| 7736 if (librariesInCycle.add(library)) { | |
| 7737 List<Library> dependentLibraries = dependencyMap[library]; | |
| 7738 if (dependentLibraries != null) { | |
| 7739 for (Library dependentLibrary in dependentLibraries) { | |
| 7740 _addLibrariesInCycle( | |
| 7741 dependentLibrary, librariesInCycle, dependencyMap); | |
| 7742 } | |
| 7743 } | |
| 7744 } | |
| 7745 } | |
| 7746 | |
| 7747 /** | |
| 7748 * Add the given library, and all libraries reachable from it that have not al
ready been visited, | |
| 7749 * to the given dependency map. | |
| 7750 * | |
| 7751 * @param library the library currently being added to the dependency map | |
| 7752 * @param dependencyMap the dependency map being computed | |
| 7753 * @param visitedLibraries the libraries that have already been visited, used
to prevent infinite | |
| 7754 * recursion | |
| 7755 */ | |
| 7756 void _addToDependencyMap(Library library, | |
| 7757 HashMap<Library, List<Library>> dependencyMap, | |
| 7758 Set<Library> visitedLibraries) { | |
| 7759 if (visitedLibraries.add(library)) { | |
| 7760 bool asyncFound = false; | |
| 7761 for (Library referencedLibrary in library.importsAndExports) { | |
| 7762 _addDependencyToMap(dependencyMap, library, referencedLibrary); | |
| 7763 _addToDependencyMap(referencedLibrary, dependencyMap, visitedLibraries); | |
| 7764 if (identical(referencedLibrary, _asyncLibrary)) { | |
| 7765 asyncFound = true; | |
| 7766 } | |
| 7767 } | |
| 7768 if (!library.explicitlyImportsCore && !identical(library, _coreLibrary)) { | |
| 7769 _addDependencyToMap(dependencyMap, library, _coreLibrary); | |
| 7770 } | |
| 7771 if (!asyncFound && !identical(library, _asyncLibrary)) { | |
| 7772 _addDependencyToMap(dependencyMap, library, _asyncLibrary); | |
| 7773 _addToDependencyMap(_asyncLibrary, dependencyMap, visitedLibraries); | |
| 7774 } | |
| 7775 } | |
| 7776 } | |
| 7777 | |
| 7778 /** | |
| 7779 * Build the element model representing the combinators declared by the given
directive. | |
| 7780 * | |
| 7781 * @param directive the directive that declares the combinators | |
| 7782 * @return an array containing the import combinators that were built | |
| 7783 */ | |
| 7784 List<NamespaceCombinator> _buildCombinators(NamespaceDirective directive) { | |
| 7785 List<NamespaceCombinator> combinators = new List<NamespaceCombinator>(); | |
| 7786 for (Combinator combinator in directive.combinators) { | |
| 7787 if (combinator is HideCombinator) { | |
| 7788 HideElementCombinatorImpl hide = new HideElementCombinatorImpl(); | |
| 7789 hide.hiddenNames = _getIdentifiers(combinator.hiddenNames); | |
| 7790 combinators.add(hide); | |
| 7791 } else { | |
| 7792 ShowElementCombinatorImpl show = new ShowElementCombinatorImpl(); | |
| 7793 show.offset = combinator.offset; | |
| 7794 show.end = combinator.end; | |
| 7795 show.shownNames = | |
| 7796 _getIdentifiers((combinator as ShowCombinator).shownNames); | |
| 7797 combinators.add(show); | |
| 7798 } | |
| 7799 } | |
| 7800 return combinators; | |
| 7801 } | |
| 7802 | |
| 7803 /** | |
| 7804 * Every library now has a corresponding [LibraryElement], so it is now possib
le to resolve | |
| 7805 * the import and export directives. | |
| 7806 * | |
| 7807 * @throws AnalysisException if the defining compilation unit for any of the l
ibraries could not | |
| 7808 * be accessed | |
| 7809 */ | |
| 7810 void _buildDirectiveModels() { | |
| 7811 for (Library library in _librariesInCycles) { | |
| 7812 HashMap<String, PrefixElementImpl> nameToPrefixMap = | |
| 7813 new HashMap<String, PrefixElementImpl>(); | |
| 7814 List<ImportElement> imports = new List<ImportElement>(); | |
| 7815 List<ExportElement> exports = new List<ExportElement>(); | |
| 7816 for (Directive directive in library.definingCompilationUnit.directives) { | |
| 7817 if (directive is ImportDirective) { | |
| 7818 ImportDirective importDirective = directive; | |
| 7819 String uriContent = importDirective.uriContent; | |
| 7820 if (DartUriResolver.isDartExtUri(uriContent)) { | |
| 7821 library.libraryElement.hasExtUri = true; | |
| 7822 } | |
| 7823 Source importedSource = importDirective.source; | |
| 7824 if (importedSource != null) { | |
| 7825 // The imported source will be null if the URI in the import | |
| 7826 // directive was invalid. | |
| 7827 Library importedLibrary = _libraryMap[importedSource]; | |
| 7828 if (importedLibrary != null) { | |
| 7829 ImportElementImpl importElement = | |
| 7830 new ImportElementImpl(directive.offset); | |
| 7831 StringLiteral uriLiteral = importDirective.uri; | |
| 7832 importElement.uriOffset = uriLiteral.offset; | |
| 7833 importElement.uriEnd = uriLiteral.end; | |
| 7834 importElement.uri = uriContent; | |
| 7835 importElement.deferred = importDirective.deferredKeyword != null; | |
| 7836 importElement.combinators = _buildCombinators(importDirective); | |
| 7837 LibraryElement importedLibraryElement = | |
| 7838 importedLibrary.libraryElement; | |
| 7839 if (importedLibraryElement != null) { | |
| 7840 importElement.importedLibrary = importedLibraryElement; | |
| 7841 } | |
| 7842 SimpleIdentifier prefixNode = directive.prefix; | |
| 7843 if (prefixNode != null) { | |
| 7844 importElement.prefixOffset = prefixNode.offset; | |
| 7845 String prefixName = prefixNode.name; | |
| 7846 PrefixElementImpl prefix = nameToPrefixMap[prefixName]; | |
| 7847 if (prefix == null) { | |
| 7848 prefix = new PrefixElementImpl.forNode(prefixNode); | |
| 7849 nameToPrefixMap[prefixName] = prefix; | |
| 7850 } | |
| 7851 importElement.prefix = prefix; | |
| 7852 prefixNode.staticElement = prefix; | |
| 7853 } | |
| 7854 directive.element = importElement; | |
| 7855 imports.add(importElement); | |
| 7856 if (analysisContext.computeKindOf(importedSource) != | |
| 7857 SourceKind.LIBRARY) { | |
| 7858 ErrorCode errorCode = (importElement.isDeferred | |
| 7859 ? StaticWarningCode.IMPORT_OF_NON_LIBRARY | |
| 7860 : CompileTimeErrorCode.IMPORT_OF_NON_LIBRARY); | |
| 7861 _errorListener.onError(new AnalysisError(library.librarySource, | |
| 7862 uriLiteral.offset, uriLiteral.length, errorCode, | |
| 7863 [uriLiteral.toSource()])); | |
| 7864 } | |
| 7865 } | |
| 7866 } | |
| 7867 } else if (directive is ExportDirective) { | |
| 7868 ExportDirective exportDirective = directive; | |
| 7869 Source exportedSource = exportDirective.source; | |
| 7870 if (exportedSource != null) { | |
| 7871 // The exported source will be null if the URI in the export | |
| 7872 // directive was invalid. | |
| 7873 Library exportedLibrary = _libraryMap[exportedSource]; | |
| 7874 if (exportedLibrary != null) { | |
| 7875 ExportElementImpl exportElement = | |
| 7876 new ExportElementImpl(directive.offset); | |
| 7877 StringLiteral uriLiteral = exportDirective.uri; | |
| 7878 exportElement.uriOffset = uriLiteral.offset; | |
| 7879 exportElement.uriEnd = uriLiteral.end; | |
| 7880 exportElement.uri = exportDirective.uriContent; | |
| 7881 exportElement.combinators = _buildCombinators(exportDirective); | |
| 7882 LibraryElement exportedLibraryElement = | |
| 7883 exportedLibrary.libraryElement; | |
| 7884 if (exportedLibraryElement != null) { | |
| 7885 exportElement.exportedLibrary = exportedLibraryElement; | |
| 7886 } | |
| 7887 directive.element = exportElement; | |
| 7888 exports.add(exportElement); | |
| 7889 if (analysisContext.computeKindOf(exportedSource) != | |
| 7890 SourceKind.LIBRARY) { | |
| 7891 _errorListener.onError(new AnalysisError(library.librarySource, | |
| 7892 uriLiteral.offset, uriLiteral.length, | |
| 7893 CompileTimeErrorCode.EXPORT_OF_NON_LIBRARY, | |
| 7894 [uriLiteral.toSource()])); | |
| 7895 } | |
| 7896 } | |
| 7897 } | |
| 7898 } | |
| 7899 } | |
| 7900 Source librarySource = library.librarySource; | |
| 7901 if (!library.explicitlyImportsCore && | |
| 7902 _coreLibrarySource != librarySource) { | |
| 7903 ImportElementImpl importElement = new ImportElementImpl(-1); | |
| 7904 importElement.importedLibrary = _coreLibrary.libraryElement; | |
| 7905 importElement.synthetic = true; | |
| 7906 imports.add(importElement); | |
| 7907 } | |
| 7908 LibraryElementImpl libraryElement = library.libraryElement; | |
| 7909 libraryElement.imports = imports; | |
| 7910 libraryElement.exports = exports; | |
| 7911 if (libraryElement.entryPoint == null) { | |
| 7912 Namespace namespace = new NamespaceBuilder() | |
| 7913 .createExportNamespaceForLibrary(libraryElement); | |
| 7914 Element element = namespace.get(FunctionElement.MAIN_FUNCTION_NAME); | |
| 7915 if (element is FunctionElement) { | |
| 7916 libraryElement.entryPoint = element; | |
| 7917 } | |
| 7918 } | |
| 7919 } | |
| 7920 } | |
| 7921 | |
| 7922 /** | |
| 7923 * Build element models for all of the libraries in the current cycle. | |
| 7924 * | |
| 7925 * @throws AnalysisException if any of the element models cannot be built | |
| 7926 */ | |
| 7927 void _buildElementModels() { | |
| 7928 for (Library library in _librariesInCycles) { | |
| 7929 LibraryElementBuilder builder = | |
| 7930 new LibraryElementBuilder(analysisContext, errorListener); | |
| 7931 LibraryElementImpl libraryElement = builder.buildLibrary(library); | |
| 7932 library.libraryElement = libraryElement; | |
| 7933 } | |
| 7934 } | |
| 7935 | |
| 7936 /** | |
| 7937 * Build the members in enum declarations. This cannot be done while building
the rest of the | |
| 7938 * element model because it depends on being able to access core types, which
cannot happen until | |
| 7939 * the rest of the element model has been built (when resolving the core libra
ry). | |
| 7940 * | |
| 7941 * @throws AnalysisException if any of the enum members could not be built | |
| 7942 */ | |
| 7943 void _buildEnumMembers() { | |
| 7944 PerformanceStatistics.resolve.makeCurrentWhile(() { | |
| 7945 for (Library library in _librariesInCycles) { | |
| 7946 for (Source source in library.compilationUnitSources) { | |
| 7947 EnumMemberBuilder builder = new EnumMemberBuilder(_typeProvider); | |
| 7948 library.getAST(source).accept(builder); | |
| 7949 } | |
| 7950 } | |
| 7951 }); | |
| 7952 } | |
| 7953 | |
| 7954 /** | |
| 7955 * Resolve the type hierarchy across all of the types declared in the librarie
s in the current | |
| 7956 * cycle. | |
| 7957 * | |
| 7958 * @throws AnalysisException if any of the type hierarchies could not be resol
ved | |
| 7959 */ | |
| 7960 void _buildTypeHierarchies() { | |
| 7961 PerformanceStatistics.resolve.makeCurrentWhile(() { | |
| 7962 for (Library library in _librariesInCycles) { | |
| 7963 for (Source source in library.compilationUnitSources) { | |
| 7964 TypeResolverVisitorFactory typeResolverVisitorFactory = | |
| 7965 analysisContext.typeResolverVisitorFactory; | |
| 7966 TypeResolverVisitor visitor = (typeResolverVisitorFactory == null) | |
| 7967 ? new TypeResolverVisitor(library.libraryElement, source, | |
| 7968 _typeProvider, library.errorListener, | |
| 7969 nameScope: library.libraryScope) | |
| 7970 : typeResolverVisitorFactory(library, source, _typeProvider); | |
| 7971 library.getAST(source).accept(visitor); | |
| 7972 } | |
| 7973 } | |
| 7974 }); | |
| 7975 } | |
| 7976 | |
| 7977 /** | |
| 7978 * Compute a dependency map of libraries reachable from the given library. A d
ependency map is a | |
| 7979 * table that maps individual libraries to a list of the libraries that either
import or export | |
| 7980 * those libraries. | |
| 7981 * | |
| 7982 * This map is used to compute all of the libraries involved in a cycle that i
nclude the root | |
| 7983 * library. Given that we only add libraries that are reachable from the root
library, when we | |
| 7984 * work backward we are guaranteed to only get libraries in the cycle. | |
| 7985 * | |
| 7986 * @param library the library currently being added to the dependency map | |
| 7987 */ | |
| 7988 HashMap<Library, List<Library>> _computeDependencyMap(Library library) { | |
| 7989 HashMap<Library, List<Library>> dependencyMap = | |
| 7990 new HashMap<Library, List<Library>>(); | |
| 7991 _addToDependencyMap(library, dependencyMap, new HashSet<Library>()); | |
| 7992 return dependencyMap; | |
| 7993 } | |
| 7994 | |
| 7995 /** | |
| 7996 * Recursively traverse the libraries reachable from the given library, creati
ng instances of the | |
| 7997 * class [Library] to represent them, and record the references in the library
objects. | |
| 7998 * | |
| 7999 * @param library the library to be processed to find libraries that have not
yet been traversed | |
| 8000 * @throws AnalysisException if some portion of the library graph could not be
traversed | |
| 8001 */ | |
| 8002 void _computeEmbeddedLibraryDependencies( | |
| 8003 Library library, CompilationUnit unit) { | |
| 8004 Source librarySource = library.librarySource; | |
| 8005 HashSet<Source> exportedSources = new HashSet<Source>(); | |
| 8006 HashSet<Source> importedSources = new HashSet<Source>(); | |
| 8007 for (Directive directive in unit.directives) { | |
| 8008 if (directive is ExportDirective) { | |
| 8009 Source exportSource = _resolveSource(librarySource, directive); | |
| 8010 if (exportSource != null) { | |
| 8011 exportedSources.add(exportSource); | |
| 8012 } | |
| 8013 } else if (directive is ImportDirective) { | |
| 8014 Source importSource = _resolveSource(librarySource, directive); | |
| 8015 if (importSource != null) { | |
| 8016 importedSources.add(importSource); | |
| 8017 } | |
| 8018 } | |
| 8019 } | |
| 8020 _computeLibraryDependenciesFromDirectives(library, | |
| 8021 new List.from(importedSources), new List.from(exportedSources)); | |
| 8022 } | |
| 8023 | |
| 8024 /** | |
| 8025 * Return a collection containing all of the libraries reachable from the give
n library that are | |
| 8026 * contained in a cycle that includes the given library. | |
| 8027 * | |
| 8028 * @param library the library that must be included in any cycles whose member
s are to be returned | |
| 8029 * @return all of the libraries referenced by the given library that have a ci
rcular reference | |
| 8030 * back to the given library | |
| 8031 */ | |
| 8032 Set<Library> _computeLibrariesInCycles(Library library) { | |
| 8033 HashMap<Library, List<Library>> dependencyMap = | |
| 8034 _computeDependencyMap(library); | |
| 8035 Set<Library> librariesInCycle = new HashSet<Library>(); | |
| 8036 _addLibrariesInCycle(library, librariesInCycle, dependencyMap); | |
| 8037 return librariesInCycle; | |
| 8038 } | |
| 8039 | |
| 8040 /** | |
| 8041 * Recursively traverse the libraries reachable from the given library, creati
ng instances of the | |
| 8042 * class [Library] to represent them, and record the references in the library
objects. | |
| 8043 * | |
| 8044 * @param library the library to be processed to find libraries that have not
yet been traversed | |
| 8045 * @throws AnalysisException if some portion of the library graph could not be
traversed | |
| 8046 */ | |
| 8047 void _computeLibraryDependencies(Library library) { | |
| 8048 Source librarySource = library.librarySource; | |
| 8049 _computeLibraryDependenciesFromDirectives(library, | |
| 8050 analysisContext.computeImportedLibraries(librarySource), | |
| 8051 analysisContext.computeExportedLibraries(librarySource)); | |
| 8052 } | |
| 8053 | |
| 8054 /** | |
| 8055 * Recursively traverse the libraries reachable from the given library, creati
ng instances of the | |
| 8056 * class [Library] to represent them, and record the references in the library
objects. | |
| 8057 * | |
| 8058 * @param library the library to be processed to find libraries that have not
yet been traversed | |
| 8059 * @param importedSources an array containing the sources that are imported in
to the given library | |
| 8060 * @param exportedSources an array containing the sources that are exported fr
om the given library | |
| 8061 * @throws AnalysisException if some portion of the library graph could not be
traversed | |
| 8062 */ | |
| 8063 void _computeLibraryDependenciesFromDirectives(Library library, | |
| 8064 List<Source> importedSources, List<Source> exportedSources) { | |
| 8065 List<Library> importedLibraries = new List<Library>(); | |
| 8066 bool explicitlyImportsCore = false; | |
| 8067 bool importsAsync = false; | |
| 8068 for (Source importedSource in importedSources) { | |
| 8069 if (importedSource == _coreLibrarySource) { | |
| 8070 explicitlyImportsCore = true; | |
| 8071 } | |
| 8072 if (importedSource == _asyncLibrarySource) { | |
| 8073 importsAsync = true; | |
| 8074 } | |
| 8075 Library importedLibrary = _libraryMap[importedSource]; | |
| 8076 if (importedLibrary == null) { | |
| 8077 importedLibrary = _createLibraryOrNull(importedSource); | |
| 8078 if (importedLibrary != null) { | |
| 8079 _computeLibraryDependencies(importedLibrary); | |
| 8080 } | |
| 8081 } | |
| 8082 if (importedLibrary != null) { | |
| 8083 importedLibraries.add(importedLibrary); | |
| 8084 } | |
| 8085 } | |
| 8086 library.importedLibraries = importedLibraries; | |
| 8087 List<Library> exportedLibraries = new List<Library>(); | |
| 8088 for (Source exportedSource in exportedSources) { | |
| 8089 Library exportedLibrary = _libraryMap[exportedSource]; | |
| 8090 if (exportedLibrary == null) { | |
| 8091 exportedLibrary = _createLibraryOrNull(exportedSource); | |
| 8092 if (exportedLibrary != null) { | |
| 8093 _computeLibraryDependencies(exportedLibrary); | |
| 8094 } | |
| 8095 } | |
| 8096 if (exportedLibrary != null) { | |
| 8097 exportedLibraries.add(exportedLibrary); | |
| 8098 } | |
| 8099 } | |
| 8100 library.exportedLibraries = exportedLibraries; | |
| 8101 library.explicitlyImportsCore = explicitlyImportsCore; | |
| 8102 if (!explicitlyImportsCore && _coreLibrarySource != library.librarySource) { | |
| 8103 Library importedLibrary = _libraryMap[_coreLibrarySource]; | |
| 8104 if (importedLibrary == null) { | |
| 8105 importedLibrary = _createLibraryOrNull(_coreLibrarySource); | |
| 8106 if (importedLibrary != null) { | |
| 8107 _computeLibraryDependencies(importedLibrary); | |
| 8108 } | |
| 8109 } | |
| 8110 } | |
| 8111 if (!importsAsync && _asyncLibrarySource != library.librarySource) { | |
| 8112 Library importedLibrary = _libraryMap[_asyncLibrarySource]; | |
| 8113 if (importedLibrary == null) { | |
| 8114 importedLibrary = _createLibraryOrNull(_asyncLibrarySource); | |
| 8115 if (importedLibrary != null) { | |
| 8116 _computeLibraryDependencies(importedLibrary); | |
| 8117 } | |
| 8118 } | |
| 8119 } | |
| 8120 } | |
| 8121 | |
| 8122 /** | |
| 8123 * Create an object to represent the information about the library defined by
the compilation unit | |
| 8124 * with the given source. Return the library object that was created, or `null
` if the | |
| 8125 * source is not valid. | |
| 8126 * | |
| 8127 * @param librarySource the source of the library's defining compilation unit | |
| 8128 * @return the library object that was created | |
| 8129 */ | |
| 8130 Library _createLibraryOrNull(Source librarySource) { | |
| 8131 if (!analysisContext.exists(librarySource)) { | |
| 8132 return null; | |
| 8133 } | |
| 8134 Library library = | |
| 8135 new Library(analysisContext, _errorListener, librarySource); | |
| 8136 _libraryMap[librarySource] = library; | |
| 8137 return library; | |
| 8138 } | |
| 8139 | |
| 8140 /** | |
| 8141 * Create an object to represent the information about the library defined by
the compilation unit | |
| 8142 * with the given source. | |
| 8143 * | |
| 8144 * @param librarySource the source of the library's defining compilation unit | |
| 8145 * @param unit the compilation unit that defines the library | |
| 8146 * @return the library object that was created | |
| 8147 * @throws AnalysisException if the library source is not valid | |
| 8148 */ | |
| 8149 Library _createLibraryWithUnit(Source librarySource, CompilationUnit unit) { | |
| 8150 Library library = | |
| 8151 new Library(analysisContext, _errorListener, librarySource); | |
| 8152 library.setDefiningCompilationUnit(unit); | |
| 8153 _libraryMap[librarySource] = library; | |
| 8154 return library; | |
| 8155 } | |
| 8156 | |
| 8157 /** | |
| 8158 * Return an array containing the lexical identifiers associated with the node
s in the given list. | |
| 8159 * | |
| 8160 * @param names the AST nodes representing the identifiers | |
| 8161 * @return the lexical identifiers associated with the nodes in the list | |
| 8162 */ | |
| 8163 List<String> _getIdentifiers(NodeList<SimpleIdentifier> names) { | |
| 8164 int count = names.length; | |
| 8165 List<String> identifiers = new List<String>(count); | |
| 8166 for (int i = 0; i < count; i++) { | |
| 8167 identifiers[i] = names[i].name; | |
| 8168 } | |
| 8169 return identifiers; | |
| 8170 } | |
| 8171 | |
| 8172 /** | |
| 8173 * Compute a value for all of the constants in the libraries being analyzed. | |
| 8174 */ | |
| 8175 void _performConstantEvaluation() { | |
| 8176 PerformanceStatistics.resolve.makeCurrentWhile(() { | |
| 8177 ConstantValueComputer computer = new ConstantValueComputer( | |
| 8178 analysisContext, _typeProvider, analysisContext.declaredVariables); | |
| 8179 for (Library library in _librariesInCycles) { | |
| 8180 for (Source source in library.compilationUnitSources) { | |
| 8181 try { | |
| 8182 CompilationUnit unit = library.getAST(source); | |
| 8183 if (unit != null) { | |
| 8184 computer.add(unit, source, library.librarySource); | |
| 8185 } | |
| 8186 } on AnalysisException catch (exception, stackTrace) { | |
| 8187 AnalysisEngine.instance.logger.logError( | |
| 8188 "Internal Error: Could not access AST for ${source.fullName} dur
ing constant evaluation", | |
| 8189 new CaughtException(exception, stackTrace)); | |
| 8190 } | |
| 8191 } | |
| 8192 } | |
| 8193 computer.computeValues(); | |
| 8194 // As a temporary workaround for issue 21572, run ConstantVerifier now. | |
| 8195 // TODO(paulberry): remove this workaround once issue 21572 is fixed. | |
| 8196 for (Library library in _librariesInCycles) { | |
| 8197 for (Source source in library.compilationUnitSources) { | |
| 8198 try { | |
| 8199 CompilationUnit unit = library.getAST(source); | |
| 8200 ErrorReporter errorReporter = | |
| 8201 new ErrorReporter(_errorListener, source); | |
| 8202 ConstantVerifier constantVerifier = new ConstantVerifier( | |
| 8203 errorReporter, library.libraryElement, _typeProvider, | |
| 8204 analysisContext.declaredVariables); | |
| 8205 unit.accept(constantVerifier); | |
| 8206 } on AnalysisException catch (exception, stackTrace) { | |
| 8207 AnalysisEngine.instance.logger.logError( | |
| 8208 "Internal Error: Could not access AST for ${source.fullName} " | |
| 8209 "during constant verification", | |
| 8210 new CaughtException(exception, stackTrace)); | |
| 8211 } | |
| 8212 } | |
| 8213 } | |
| 8214 }); | |
| 8215 } | |
| 8216 | |
| 8217 /** | |
| 8218 * Resolve the identifiers and perform type analysis in the given library. | |
| 8219 * | |
| 8220 * @param library the library to be resolved | |
| 8221 * @throws AnalysisException if any of the identifiers could not be resolved o
r if the types in | |
| 8222 * the library cannot be analyzed | |
| 8223 */ | |
| 8224 void _resolveReferencesAndTypesInLibrary(Library library) { | |
| 8225 PerformanceStatistics.resolve.makeCurrentWhile(() { | |
| 8226 for (Source source in library.compilationUnitSources) { | |
| 8227 CompilationUnit ast = library.getAST(source); | |
| 8228 ast.accept(new VariableResolverVisitor(library.libraryElement, source, | |
| 8229 _typeProvider, library.errorListener, | |
| 8230 nameScope: library.libraryScope)); | |
| 8231 ResolverVisitorFactory visitorFactory = | |
| 8232 analysisContext.resolverVisitorFactory; | |
| 8233 ResolverVisitor visitor = visitorFactory != null | |
| 8234 ? visitorFactory(library, source, _typeProvider) | |
| 8235 : new ResolverVisitor(library.libraryElement, source, _typeProvider, | |
| 8236 library.errorListener, | |
| 8237 nameScope: library.libraryScope, | |
| 8238 inheritanceManager: library.inheritanceManager); | |
| 8239 ast.accept(visitor); | |
| 8240 } | |
| 8241 }); | |
| 8242 } | |
| 8243 | |
| 8244 /** | |
| 8245 * Return the result of resolving the URI of the given URI-based directive aga
inst the URI of the | |
| 8246 * given library, or `null` if the URI is not valid. | |
| 8247 * | |
| 8248 * @param librarySource the source representing the library containing the dir
ective | |
| 8249 * @param directive the directive which URI should be resolved | |
| 8250 * @return the result of resolving the URI against the URI of the library | |
| 8251 */ | |
| 8252 Source _resolveSource(Source librarySource, UriBasedDirective directive) { | |
| 8253 StringLiteral uriLiteral = directive.uri; | |
| 8254 if (uriLiteral is StringInterpolation) { | |
| 8255 return null; | |
| 8256 } | |
| 8257 String uriContent = uriLiteral.stringValue.trim(); | |
| 8258 if (uriContent == null || uriContent.isEmpty) { | |
| 8259 return null; | |
| 8260 } | |
| 8261 uriContent = Uri.encodeFull(uriContent); | |
| 8262 return analysisContext.sourceFactory.resolveUri(librarySource, uriContent); | |
| 8263 } | |
| 8264 } | |
| 8265 | |
| 8266 /** | |
| 8267 * Instances of the class `LibraryResolver` are used to resolve one or more mutu
ally dependent | |
| 8268 * libraries within a single context. | |
| 8269 */ | |
| 8270 class LibraryResolver2 { | |
| 8271 /** | |
| 8272 * The analysis context in which the libraries are being analyzed. | |
| 8273 */ | |
| 8274 final InternalAnalysisContext analysisContext; | |
| 8275 | |
| 8276 /** | |
| 8277 * The listener to which analysis errors will be reported, this error listener
is either | |
| 8278 * references [recordingErrorListener], or it unions the passed | |
| 8279 * [AnalysisErrorListener] with the [recordingErrorListener]. | |
| 8280 */ | |
| 8281 RecordingErrorListener _errorListener; | |
| 8282 | |
| 8283 /** | |
| 8284 * A source object representing the core library (dart:core). | |
| 8285 */ | |
| 8286 Source _coreLibrarySource; | |
| 8287 | |
| 8288 /** | |
| 8289 * A source object representing the async library (dart:async). | |
| 8290 */ | |
| 8291 Source _asyncLibrarySource; | |
| 8292 | |
| 8293 /** | |
| 8294 * The object representing the core library. | |
| 8295 */ | |
| 8296 ResolvableLibrary _coreLibrary; | |
| 8297 | |
| 8298 /** | |
| 8299 * The object representing the async library. | |
| 8300 */ | |
| 8301 ResolvableLibrary _asyncLibrary; | |
| 8302 | |
| 8303 /** | |
| 8304 * The object used to access the types from the core library. | |
| 8305 */ | |
| 8306 TypeProvider _typeProvider; | |
| 8307 | |
| 8308 /** | |
| 8309 * A table mapping library sources to the information being maintained for tho
se libraries. | |
| 8310 */ | |
| 8311 HashMap<Source, ResolvableLibrary> _libraryMap = | |
| 8312 new HashMap<Source, ResolvableLibrary>(); | |
| 8313 | |
| 8314 /** | |
| 8315 * A collection containing the libraries that are being resolved together. | |
| 8316 */ | |
| 8317 List<ResolvableLibrary> _librariesInCycle; | |
| 8318 | |
| 8319 /** | |
| 8320 * Initialize a newly created library resolver to resolve libraries within the
given context. | |
| 8321 * | |
| 8322 * @param analysisContext the analysis context in which the library is being a
nalyzed | |
| 8323 */ | |
| 8324 LibraryResolver2(this.analysisContext) { | |
| 8325 this._errorListener = new RecordingErrorListener(); | |
| 8326 _coreLibrarySource = | |
| 8327 analysisContext.sourceFactory.forUri(DartSdk.DART_CORE); | |
| 8328 _asyncLibrarySource = | |
| 8329 analysisContext.sourceFactory.forUri(DartSdk.DART_ASYNC); | |
| 8330 } | |
| 8331 | |
| 8332 /** | |
| 8333 * Return the listener to which analysis errors will be reported. | |
| 8334 * | |
| 8335 * @return the listener to which analysis errors will be reported | |
| 8336 */ | |
| 8337 RecordingErrorListener get errorListener => _errorListener; | |
| 8338 | |
| 8339 /** | |
| 8340 * Return an array containing information about all of the libraries that were
resolved. | |
| 8341 * | |
| 8342 * @return an array containing the libraries that were resolved | |
| 8343 */ | |
| 8344 List<ResolvableLibrary> get resolvedLibraries => _librariesInCycle; | |
| 8345 | |
| 8346 /** | |
| 8347 * Resolve the library specified by the given source in the given context. | |
| 8348 * | |
| 8349 * Note that because Dart allows circular imports between libraries, it is pos
sible that more than | |
| 8350 * one library will need to be resolved. In such cases the error listener can
receive errors from | |
| 8351 * multiple libraries. | |
| 8352 * | |
| 8353 * @param librarySource the source specifying the defining compilation unit of
the library to be | |
| 8354 * resolved | |
| 8355 * @param fullAnalysis `true` if a full analysis should be performed | |
| 8356 * @return the element representing the resolved library | |
| 8357 * @throws AnalysisException if the library could not be resolved for some rea
son | |
| 8358 */ | |
| 8359 LibraryElement resolveLibrary( | |
| 8360 Source librarySource, List<ResolvableLibrary> librariesInCycle) { | |
| 8361 // | |
| 8362 // Build the map of libraries that are known. | |
| 8363 // | |
| 8364 this._librariesInCycle = librariesInCycle; | |
| 8365 _libraryMap = _buildLibraryMap(); | |
| 8366 ResolvableLibrary targetLibrary = _libraryMap[librarySource]; | |
| 8367 _coreLibrary = _libraryMap[_coreLibrarySource]; | |
| 8368 _asyncLibrary = _libraryMap[_asyncLibrarySource]; | |
| 8369 // | |
| 8370 // Build the element models representing the libraries being resolved. | |
| 8371 // This is done in three steps: | |
| 8372 // | |
| 8373 // 1. Build the basic element models without making any connections | |
| 8374 // between elements other than the basic parent/child relationships. | |
| 8375 // This includes building the elements representing the libraries, but | |
| 8376 // excludes members defined in enums. | |
| 8377 // 2. Build the elements for the import and export directives. This | |
| 8378 // requires that we have the elements built for the referenced | |
| 8379 // libraries, but because of the possibility of circular references | |
| 8380 // needs to happen after all of the library elements have been created. | |
| 8381 // 3. Build the members in enum declarations. | |
| 8382 // 4. Build the rest of the type model by connecting superclasses, mixins, | |
| 8383 // and interfaces. This requires that we be able to compute the names | |
| 8384 // visible in the libraries being resolved, which in turn requires that | |
| 8385 // we have resolved the import directives. | |
| 8386 // | |
| 8387 _buildElementModels(); | |
| 8388 LibraryElement coreElement = _coreLibrary.libraryElement; | |
| 8389 if (coreElement == null) { | |
| 8390 missingCoreLibrary(analysisContext, _coreLibrarySource); | |
| 8391 } | |
| 8392 LibraryElement asyncElement = _asyncLibrary.libraryElement; | |
| 8393 if (asyncElement == null) { | |
| 8394 missingAsyncLibrary(analysisContext, _asyncLibrarySource); | |
| 8395 } | |
| 8396 _buildDirectiveModels(); | |
| 8397 _typeProvider = new TypeProviderImpl(coreElement, asyncElement); | |
| 8398 _buildEnumMembers(); | |
| 8399 _buildTypeHierarchies(); | |
| 8400 // | |
| 8401 // Perform resolution and type analysis. | |
| 8402 // | |
| 8403 // TODO(brianwilkerson) Decide whether we want to resolve all of the | |
| 8404 // libraries or whether we want to only resolve the target library. The | |
| 8405 // advantage to resolving everything is that we have already done part of | |
| 8406 // the work so we'll avoid duplicated effort. The disadvantage of | |
| 8407 // resolving everything is that we might do extra work that we don't | |
| 8408 // really care about. Another possibility is to add a parameter to this | |
| 8409 // method and punt the decision to the clients. | |
| 8410 // | |
| 8411 //if (analyzeAll) { | |
| 8412 _resolveReferencesAndTypes(); | |
| 8413 //} else { | |
| 8414 // resolveReferencesAndTypes(targetLibrary); | |
| 8415 //} | |
| 8416 _performConstantEvaluation(); | |
| 8417 return targetLibrary.libraryElement; | |
| 8418 } | |
| 8419 | |
| 8420 /** | |
| 8421 * Build the element model representing the combinators declared by the given
directive. | |
| 8422 * | |
| 8423 * @param directive the directive that declares the combinators | |
| 8424 * @return an array containing the import combinators that were built | |
| 8425 */ | |
| 8426 List<NamespaceCombinator> _buildCombinators(NamespaceDirective directive) { | |
| 8427 List<NamespaceCombinator> combinators = new List<NamespaceCombinator>(); | |
| 8428 for (Combinator combinator in directive.combinators) { | |
| 8429 if (combinator is HideCombinator) { | |
| 8430 HideElementCombinatorImpl hide = new HideElementCombinatorImpl(); | |
| 8431 hide.hiddenNames = _getIdentifiers(combinator.hiddenNames); | |
| 8432 combinators.add(hide); | |
| 8433 } else { | |
| 8434 ShowElementCombinatorImpl show = new ShowElementCombinatorImpl(); | |
| 8435 show.offset = combinator.offset; | |
| 8436 show.end = combinator.end; | |
| 8437 show.shownNames = | |
| 8438 _getIdentifiers((combinator as ShowCombinator).shownNames); | |
| 8439 combinators.add(show); | |
| 8440 } | |
| 8441 } | |
| 8442 return combinators; | |
| 8443 } | |
| 8444 | |
| 8445 /** | |
| 8446 * Every library now has a corresponding [LibraryElement], so it is now possib
le to resolve | |
| 8447 * the import and export directives. | |
| 8448 * | |
| 8449 * @throws AnalysisException if the defining compilation unit for any of the l
ibraries could not | |
| 8450 * be accessed | |
| 8451 */ | |
| 8452 void _buildDirectiveModels() { | |
| 8453 for (ResolvableLibrary library in _librariesInCycle) { | |
| 8454 HashMap<String, PrefixElementImpl> nameToPrefixMap = | |
| 8455 new HashMap<String, PrefixElementImpl>(); | |
| 8456 List<ImportElement> imports = new List<ImportElement>(); | |
| 8457 List<ExportElement> exports = new List<ExportElement>(); | |
| 8458 for (Directive directive in library.definingCompilationUnit.directives) { | |
| 8459 if (directive is ImportDirective) { | |
| 8460 ImportDirective importDirective = directive; | |
| 8461 String uriContent = importDirective.uriContent; | |
| 8462 if (DartUriResolver.isDartExtUri(uriContent)) { | |
| 8463 library.libraryElement.hasExtUri = true; | |
| 8464 } | |
| 8465 Source importedSource = importDirective.source; | |
| 8466 if (importedSource != null && | |
| 8467 analysisContext.exists(importedSource)) { | |
| 8468 // The imported source will be null if the URI in the import | |
| 8469 // directive was invalid. | |
| 8470 ResolvableLibrary importedLibrary = _libraryMap[importedSource]; | |
| 8471 if (importedLibrary != null) { | |
| 8472 ImportElementImpl importElement = | |
| 8473 new ImportElementImpl(directive.offset); | |
| 8474 StringLiteral uriLiteral = importDirective.uri; | |
| 8475 if (uriLiteral != null) { | |
| 8476 importElement.uriOffset = uriLiteral.offset; | |
| 8477 importElement.uriEnd = uriLiteral.end; | |
| 8478 } | |
| 8479 importElement.uri = uriContent; | |
| 8480 importElement.deferred = importDirective.deferredKeyword != null; | |
| 8481 importElement.combinators = _buildCombinators(importDirective); | |
| 8482 LibraryElement importedLibraryElement = | |
| 8483 importedLibrary.libraryElement; | |
| 8484 if (importedLibraryElement != null) { | |
| 8485 importElement.importedLibrary = importedLibraryElement; | |
| 8486 } | |
| 8487 SimpleIdentifier prefixNode = directive.prefix; | |
| 8488 if (prefixNode != null) { | |
| 8489 importElement.prefixOffset = prefixNode.offset; | |
| 8490 String prefixName = prefixNode.name; | |
| 8491 PrefixElementImpl prefix = nameToPrefixMap[prefixName]; | |
| 8492 if (prefix == null) { | |
| 8493 prefix = new PrefixElementImpl.forNode(prefixNode); | |
| 8494 nameToPrefixMap[prefixName] = prefix; | |
| 8495 } | |
| 8496 importElement.prefix = prefix; | |
| 8497 prefixNode.staticElement = prefix; | |
| 8498 } | |
| 8499 directive.element = importElement; | |
| 8500 imports.add(importElement); | |
| 8501 if (analysisContext.computeKindOf(importedSource) != | |
| 8502 SourceKind.LIBRARY) { | |
| 8503 ErrorCode errorCode = (importElement.isDeferred | |
| 8504 ? StaticWarningCode.IMPORT_OF_NON_LIBRARY | |
| 8505 : CompileTimeErrorCode.IMPORT_OF_NON_LIBRARY); | |
| 8506 _errorListener.onError(new AnalysisError(library.librarySource, | |
| 8507 uriLiteral.offset, uriLiteral.length, errorCode, | |
| 8508 [uriLiteral.toSource()])); | |
| 8509 } | |
| 8510 } | |
| 8511 } | |
| 8512 } else if (directive is ExportDirective) { | |
| 8513 ExportDirective exportDirective = directive; | |
| 8514 Source exportedSource = exportDirective.source; | |
| 8515 if (exportedSource != null && | |
| 8516 analysisContext.exists(exportedSource)) { | |
| 8517 // The exported source will be null if the URI in the export | |
| 8518 // directive was invalid. | |
| 8519 ResolvableLibrary exportedLibrary = _libraryMap[exportedSource]; | |
| 8520 if (exportedLibrary != null) { | |
| 8521 ExportElementImpl exportElement = | |
| 8522 new ExportElementImpl(directive.offset); | |
| 8523 StringLiteral uriLiteral = exportDirective.uri; | |
| 8524 if (uriLiteral != null) { | |
| 8525 exportElement.uriOffset = uriLiteral.offset; | |
| 8526 exportElement.uriEnd = uriLiteral.end; | |
| 8527 } | |
| 8528 exportElement.uri = exportDirective.uriContent; | |
| 8529 exportElement.combinators = _buildCombinators(exportDirective); | |
| 8530 LibraryElement exportedLibraryElement = | |
| 8531 exportedLibrary.libraryElement; | |
| 8532 if (exportedLibraryElement != null) { | |
| 8533 exportElement.exportedLibrary = exportedLibraryElement; | |
| 8534 } | |
| 8535 directive.element = exportElement; | |
| 8536 exports.add(exportElement); | |
| 8537 if (analysisContext.computeKindOf(exportedSource) != | |
| 8538 SourceKind.LIBRARY) { | |
| 8539 _errorListener.onError(new AnalysisError(library.librarySource, | |
| 8540 uriLiteral.offset, uriLiteral.length, | |
| 8541 CompileTimeErrorCode.EXPORT_OF_NON_LIBRARY, | |
| 8542 [uriLiteral.toSource()])); | |
| 8543 } | |
| 8544 } | |
| 8545 } | |
| 8546 } | |
| 8547 } | |
| 8548 Source librarySource = library.librarySource; | |
| 8549 if (!library.explicitlyImportsCore && | |
| 8550 _coreLibrarySource != librarySource) { | |
| 8551 ImportElementImpl importElement = new ImportElementImpl(-1); | |
| 8552 importElement.importedLibrary = _coreLibrary.libraryElement; | |
| 8553 importElement.synthetic = true; | |
| 8554 imports.add(importElement); | |
| 8555 } | |
| 8556 LibraryElementImpl libraryElement = library.libraryElement; | |
| 8557 libraryElement.imports = imports; | |
| 8558 libraryElement.exports = exports; | |
| 8559 if (libraryElement.entryPoint == null) { | |
| 8560 Namespace namespace = new NamespaceBuilder() | |
| 8561 .createExportNamespaceForLibrary(libraryElement); | |
| 8562 Element element = namespace.get(FunctionElement.MAIN_FUNCTION_NAME); | |
| 8563 if (element is FunctionElement) { | |
| 8564 libraryElement.entryPoint = element; | |
| 8565 } | |
| 8566 } | |
| 8567 } | |
| 8568 } | |
| 8569 | |
| 8570 /** | |
| 8571 * Build element models for all of the libraries in the current cycle. | |
| 8572 * | |
| 8573 * @throws AnalysisException if any of the element models cannot be built | |
| 8574 */ | |
| 8575 void _buildElementModels() { | |
| 8576 for (ResolvableLibrary library in _librariesInCycle) { | |
| 8577 LibraryElementBuilder builder = | |
| 8578 new LibraryElementBuilder(analysisContext, errorListener); | |
| 8579 builder.buildLibrary2(library); | |
| 8580 } | |
| 8581 } | |
| 8582 | |
| 8583 /** | |
| 8584 * Build the members in enum declarations. This cannot be done while building
the rest of the | |
| 8585 * element model because it depends on being able to access core types, which
cannot happen until | |
| 8586 * the rest of the element model has been built (when resolving the core libra
ry). | |
| 8587 * | |
| 8588 * @throws AnalysisException if any of the enum members could not be built | |
| 8589 */ | |
| 8590 void _buildEnumMembers() { | |
| 8591 PerformanceStatistics.resolve.makeCurrentWhile(() { | |
| 8592 for (ResolvableLibrary library in _librariesInCycle) { | |
| 8593 for (Source source in library.compilationUnitSources) { | |
| 8594 EnumMemberBuilder builder = new EnumMemberBuilder(_typeProvider); | |
| 8595 library.getAST(source).accept(builder); | |
| 8596 } | |
| 8597 } | |
| 8598 }); | |
| 8599 } | |
| 8600 | |
| 8601 HashMap<Source, ResolvableLibrary> _buildLibraryMap() { | |
| 8602 HashMap<Source, ResolvableLibrary> libraryMap = | |
| 8603 new HashMap<Source, ResolvableLibrary>(); | |
| 8604 int libraryCount = _librariesInCycle.length; | |
| 8605 for (int i = 0; i < libraryCount; i++) { | |
| 8606 ResolvableLibrary library = _librariesInCycle[i]; | |
| 8607 library.errorListener = _errorListener; | |
| 8608 libraryMap[library.librarySource] = library; | |
| 8609 List<ResolvableLibrary> dependencies = library.importsAndExports; | |
| 8610 int dependencyCount = dependencies.length; | |
| 8611 for (int j = 0; j < dependencyCount; j++) { | |
| 8612 ResolvableLibrary dependency = dependencies[j]; | |
| 8613 //dependency.setErrorListener(errorListener); | |
| 8614 libraryMap[dependency.librarySource] = dependency; | |
| 8615 } | |
| 8616 } | |
| 8617 return libraryMap; | |
| 8618 } | |
| 8619 | |
| 8620 /** | |
| 8621 * Resolve the type hierarchy across all of the types declared in the librarie
s in the current | |
| 8622 * cycle. | |
| 8623 * | |
| 8624 * @throws AnalysisException if any of the type hierarchies could not be resol
ved | |
| 8625 */ | |
| 8626 void _buildTypeHierarchies() { | |
| 8627 PerformanceStatistics.resolve.makeCurrentWhile(() { | |
| 8628 for (ResolvableLibrary library in _librariesInCycle) { | |
| 8629 for (ResolvableCompilationUnit unit | |
| 8630 in library.resolvableCompilationUnits) { | |
| 8631 Source source = unit.source; | |
| 8632 CompilationUnit ast = unit.compilationUnit; | |
| 8633 TypeResolverVisitor visitor = new TypeResolverVisitor( | |
| 8634 library.libraryElement, source, _typeProvider, | |
| 8635 library.libraryScope.errorListener, | |
| 8636 nameScope: library.libraryScope); | |
| 8637 ast.accept(visitor); | |
| 8638 } | |
| 8639 } | |
| 8640 }); | |
| 8641 } | |
| 8642 | |
| 8643 /** | |
| 8644 * Return an array containing the lexical identifiers associated with the node
s in the given list. | |
| 8645 * | |
| 8646 * @param names the AST nodes representing the identifiers | |
| 8647 * @return the lexical identifiers associated with the nodes in the list | |
| 8648 */ | |
| 8649 List<String> _getIdentifiers(NodeList<SimpleIdentifier> names) { | |
| 8650 int count = names.length; | |
| 8651 List<String> identifiers = new List<String>(count); | |
| 8652 for (int i = 0; i < count; i++) { | |
| 8653 identifiers[i] = names[i].name; | |
| 8654 } | |
| 8655 return identifiers; | |
| 8656 } | |
| 8657 | |
| 8658 /** | |
| 8659 * Compute a value for all of the constants in the libraries being analyzed. | |
| 8660 */ | |
| 8661 void _performConstantEvaluation() { | |
| 8662 PerformanceStatistics.resolve.makeCurrentWhile(() { | |
| 8663 ConstantValueComputer computer = new ConstantValueComputer( | |
| 8664 analysisContext, _typeProvider, analysisContext.declaredVariables); | |
| 8665 for (ResolvableLibrary library in _librariesInCycle) { | |
| 8666 for (ResolvableCompilationUnit unit | |
| 8667 in library.resolvableCompilationUnits) { | |
| 8668 CompilationUnit ast = unit.compilationUnit; | |
| 8669 if (ast != null) { | |
| 8670 computer.add(ast, unit.source, library.librarySource); | |
| 8671 } | |
| 8672 } | |
| 8673 } | |
| 8674 computer.computeValues(); | |
| 8675 // As a temporary workaround for issue 21572, run ConstantVerifier now. | |
| 8676 // TODO(paulberry): remove this workaround once issue 21572 is fixed. | |
| 8677 for (ResolvableLibrary library in _librariesInCycle) { | |
| 8678 for (ResolvableCompilationUnit unit | |
| 8679 in library.resolvableCompilationUnits) { | |
| 8680 CompilationUnit ast = unit.compilationUnit; | |
| 8681 ErrorReporter errorReporter = | |
| 8682 new ErrorReporter(_errorListener, unit.source); | |
| 8683 ConstantVerifier constantVerifier = new ConstantVerifier( | |
| 8684 errorReporter, library.libraryElement, _typeProvider, | |
| 8685 analysisContext.declaredVariables); | |
| 8686 ast.accept(constantVerifier); | |
| 8687 } | |
| 8688 } | |
| 8689 }); | |
| 8690 } | |
| 8691 | |
| 8692 /** | |
| 8693 * Resolve the identifiers and perform type analysis in the libraries in the c
urrent cycle. | |
| 8694 * | |
| 8695 * @throws AnalysisException if any of the identifiers could not be resolved o
r if any of the | |
| 8696 * libraries could not have their types analyzed | |
| 8697 */ | |
| 8698 void _resolveReferencesAndTypes() { | |
| 8699 for (ResolvableLibrary library in _librariesInCycle) { | |
| 8700 _resolveReferencesAndTypesInLibrary(library); | |
| 8701 } | |
| 8702 } | |
| 8703 | |
| 8704 /** | |
| 8705 * Resolve the identifiers and perform type analysis in the given library. | |
| 8706 * | |
| 8707 * @param library the library to be resolved | |
| 8708 * @throws AnalysisException if any of the identifiers could not be resolved o
r if the types in | |
| 8709 * the library cannot be analyzed | |
| 8710 */ | |
| 8711 void _resolveReferencesAndTypesInLibrary(ResolvableLibrary library) { | |
| 8712 PerformanceStatistics.resolve.makeCurrentWhile(() { | |
| 8713 for (ResolvableCompilationUnit unit | |
| 8714 in library.resolvableCompilationUnits) { | |
| 8715 Source source = unit.source; | |
| 8716 CompilationUnit ast = unit.compilationUnit; | |
| 8717 ast.accept(new VariableResolverVisitor(library.libraryElement, source, | |
| 8718 _typeProvider, library.libraryScope.errorListener, | |
| 8719 nameScope: library.libraryScope)); | |
| 8720 ResolverVisitor visitor = new ResolverVisitor(library.libraryElement, | |
| 8721 source, _typeProvider, library._libraryScope.errorListener, | |
| 8722 nameScope: library._libraryScope, | |
| 8723 inheritanceManager: library.inheritanceManager); | |
| 8724 ast.accept(visitor); | |
| 8725 } | |
| 8726 }); | |
| 8727 } | |
| 8728 | |
| 8729 /** | |
| 8730 * Report that the async library could not be resolved in the given | |
| 8731 * [analysisContext] and throw an exception. [asyncLibrarySource] is the sour
ce | |
| 8732 * representing the async library. | |
| 8733 */ | |
| 8734 static void missingAsyncLibrary( | |
| 8735 AnalysisContext analysisContext, Source asyncLibrarySource) { | |
| 8736 throw new AnalysisException("Could not resolve dart:async"); | |
| 8737 } | |
| 8738 | |
| 8739 /** | |
| 8740 * Report that the core library could not be resolved in the given analysis co
ntext and throw an | |
| 8741 * exception. | |
| 8742 * | |
| 8743 * @param analysisContext the analysis context in which the failure occurred | |
| 8744 * @param coreLibrarySource the source representing the core library | |
| 8745 * @throws AnalysisException always | |
| 8746 */ | |
| 8747 static void missingCoreLibrary( | |
| 8748 AnalysisContext analysisContext, Source coreLibrarySource) { | |
| 8749 throw new AnalysisException("Could not resolve dart:core"); | |
| 8750 } | |
| 8751 } | |
| 8752 | |
| 8753 /** | |
| 8754 * Instances of the class `TypeAliasInfo` hold information about a [TypeAlias]. | |
| 8755 */ | |
| 8756 class LibraryResolver2_TypeAliasInfo { | |
| 8757 final ResolvableLibrary _library; | |
| 8758 | |
| 8759 final Source _source; | |
| 8760 | |
| 8761 final FunctionTypeAlias _typeAlias; | |
| 8762 | |
| 8763 /** | |
| 8764 * Initialize a newly created information holder with the given information. | |
| 8765 * | |
| 8766 * @param library the library containing the type alias | |
| 8767 * @param source the source of the file containing the type alias | |
| 8768 * @param typeAlias the type alias being remembered | |
| 8769 */ | |
| 8770 LibraryResolver2_TypeAliasInfo(this._library, this._source, this._typeAlias); | |
| 8771 } | |
| 8772 | |
| 8773 /** | |
| 8774 * Instances of the class `TypeAliasInfo` hold information about a [TypeAlias]. | |
| 8775 */ | |
| 8776 class LibraryResolver_TypeAliasInfo { | |
| 8777 final Library _library; | |
| 8778 | |
| 8779 final Source _source; | |
| 8780 | |
| 8781 final FunctionTypeAlias _typeAlias; | |
| 8782 | |
| 8783 /** | |
| 8784 * Initialize a newly created information holder with the given information. | |
| 8785 * | |
| 8786 * @param library the library containing the type alias | |
| 8787 * @param source the source of the file containing the type alias | |
| 8788 * @param typeAlias the type alias being remembered | |
| 8789 */ | |
| 8790 LibraryResolver_TypeAliasInfo(this._library, this._source, this._typeAlias); | |
| 8791 } | |
| 8792 | |
| 8793 /** | |
| 8794 * Instances of the class `LibraryScope` implement a scope containing all of the
names defined | |
| 8795 * in a given library. | |
| 8796 */ | |
| 8797 class LibraryScope extends EnclosedScope { | |
| 8798 /** | |
| 8799 * Initialize a newly created scope representing the names defined in the give
n library. | |
| 8800 * | |
| 8801 * @param definingLibrary the element representing the library represented by
this scope | |
| 8802 * @param errorListener the listener that is to be informed when an error is e
ncountered | |
| 8803 */ | |
| 8804 LibraryScope( | |
| 8805 LibraryElement definingLibrary, AnalysisErrorListener errorListener) | |
| 8806 : super(new LibraryImportScope(definingLibrary, errorListener)) { | |
| 8807 _defineTopLevelNames(definingLibrary); | |
| 8808 } | |
| 8809 | |
| 8810 @override | |
| 8811 AnalysisError getErrorForDuplicate(Element existing, Element duplicate) { | |
| 8812 if (existing is PrefixElement) { | |
| 8813 // TODO(scheglov) consider providing actual 'nameOffset' from the | |
| 8814 // synthetic accessor | |
| 8815 int offset = duplicate.nameOffset; | |
| 8816 if (duplicate is PropertyAccessorElement) { | |
| 8817 PropertyAccessorElement accessor = duplicate; | |
| 8818 if (accessor.isSynthetic) { | |
| 8819 offset = accessor.variable.nameOffset; | |
| 8820 } | |
| 8821 } | |
| 8822 return new AnalysisError(duplicate.source, offset, | |
| 8823 duplicate.displayName.length, | |
| 8824 CompileTimeErrorCode.PREFIX_COLLIDES_WITH_TOP_LEVEL_MEMBER, | |
| 8825 [existing.displayName]); | |
| 8826 } | |
| 8827 return super.getErrorForDuplicate(existing, duplicate); | |
| 8828 } | |
| 8829 | |
| 8830 /** | |
| 8831 * Add to this scope all of the public top-level names that are defined in the
given compilation | |
| 8832 * unit. | |
| 8833 * | |
| 8834 * @param compilationUnit the compilation unit defining the top-level names to
be added to this | |
| 8835 * scope | |
| 8836 */ | |
| 8837 void _defineLocalNames(CompilationUnitElement compilationUnit) { | |
| 8838 for (PropertyAccessorElement element in compilationUnit.accessors) { | |
| 8839 define(element); | |
| 8840 } | |
| 8841 for (ClassElement element in compilationUnit.enums) { | |
| 8842 define(element); | |
| 8843 } | |
| 8844 for (FunctionElement element in compilationUnit.functions) { | |
| 8845 define(element); | |
| 8846 } | |
| 8847 for (FunctionTypeAliasElement element | |
| 8848 in compilationUnit.functionTypeAliases) { | |
| 8849 define(element); | |
| 8850 } | |
| 8851 for (ClassElement element in compilationUnit.types) { | |
| 8852 define(element); | |
| 8853 } | |
| 8854 } | |
| 8855 | |
| 8856 /** | |
| 8857 * Add to this scope all of the names that are explicitly defined in the given
library. | |
| 8858 * | |
| 8859 * @param definingLibrary the element representing the library that defines th
e names in this | |
| 8860 * scope | |
| 8861 */ | |
| 8862 void _defineTopLevelNames(LibraryElement definingLibrary) { | |
| 8863 for (PrefixElement prefix in definingLibrary.prefixes) { | |
| 8864 define(prefix); | |
| 8865 } | |
| 8866 _defineLocalNames(definingLibrary.definingCompilationUnit); | |
| 8867 for (CompilationUnitElement compilationUnit in definingLibrary.parts) { | |
| 8868 _defineLocalNames(compilationUnit); | |
| 8869 } | |
| 8870 } | |
| 8871 } | |
| 8872 | |
| 8873 /** | |
| 8874 * This class is used to replace uses of `HashMap<String, ExecutableElement>` | |
| 8875 * which are not as performant as this class. | |
| 8876 */ | |
| 8877 class MemberMap { | |
| 8878 /** | |
| 8879 * The current size of this map. | |
| 8880 */ | |
| 8881 int _size = 0; | |
| 8882 | |
| 8883 /** | |
| 8884 * The array of keys. | |
| 8885 */ | |
| 8886 List<String> _keys; | |
| 8887 | |
| 8888 /** | |
| 8889 * The array of ExecutableElement values. | |
| 8890 */ | |
| 8891 List<ExecutableElement> _values; | |
| 8892 | |
| 8893 /** | |
| 8894 * Initialize a newly created member map to have the given [initialCapacity]. | |
| 8895 * The map will grow if needed. | |
| 8896 */ | |
| 8897 MemberMap([int initialCapacity = 10]) { | |
| 8898 _initArrays(initialCapacity); | |
| 8899 } | |
| 8900 | |
| 8901 /** | |
| 8902 * This constructor takes an initial capacity of the map. | |
| 8903 * | |
| 8904 * @param initialCapacity the initial capacity | |
| 8905 */ | |
| 8906 @deprecated // Use new MemberMap(initialCapacity) | |
| 8907 MemberMap.con1(int initialCapacity) { | |
| 8908 _initArrays(initialCapacity); | |
| 8909 } | |
| 8910 | |
| 8911 /** | |
| 8912 * Copy constructor. | |
| 8913 */ | |
| 8914 @deprecated // Use new MemberMap.from(memberMap) | |
| 8915 MemberMap.con2(MemberMap memberMap) { | |
| 8916 _initArrays(memberMap._size + 5); | |
| 8917 for (int i = 0; i < memberMap._size; i++) { | |
| 8918 _keys[i] = memberMap._keys[i]; | |
| 8919 _values[i] = memberMap._values[i]; | |
| 8920 } | |
| 8921 _size = memberMap._size; | |
| 8922 } | |
| 8923 | |
| 8924 /** | |
| 8925 * Initialize a newly created member map to contain the same members as the | |
| 8926 * given [memberMap]. | |
| 8927 */ | |
| 8928 MemberMap.from(MemberMap memberMap) { | |
| 8929 _initArrays(memberMap._size + 5); | |
| 8930 for (int i = 0; i < memberMap._size; i++) { | |
| 8931 _keys[i] = memberMap._keys[i]; | |
| 8932 _values[i] = memberMap._values[i]; | |
| 8933 } | |
| 8934 _size = memberMap._size; | |
| 8935 } | |
| 8936 | |
| 8937 /** | |
| 8938 * The size of the map. | |
| 8939 * | |
| 8940 * @return the size of the map. | |
| 8941 */ | |
| 8942 int get size => _size; | |
| 8943 | |
| 8944 /** | |
| 8945 * Given some key, return the ExecutableElement value from the map, if the key
does not exist in | |
| 8946 * the map, `null` is returned. | |
| 8947 * | |
| 8948 * @param key some key to look up in the map | |
| 8949 * @return the associated ExecutableElement value from the map, if the key doe
s not exist in the | |
| 8950 * map, `null` is returned | |
| 8951 */ | |
| 8952 ExecutableElement get(String key) { | |
| 8953 for (int i = 0; i < _size; i++) { | |
| 8954 if (_keys[i] != null && _keys[i] == key) { | |
| 8955 return _values[i]; | |
| 8956 } | |
| 8957 } | |
| 8958 return null; | |
| 8959 } | |
| 8960 | |
| 8961 /** | |
| 8962 * Get and return the key at the specified location. If the key/value pair has
been removed from | |
| 8963 * the set, then `null` is returned. | |
| 8964 * | |
| 8965 * @param i some non-zero value less than size | |
| 8966 * @return the key at the passed index | |
| 8967 * @throw ArrayIndexOutOfBoundsException this exception is thrown if the passe
d index is less than | |
| 8968 * zero or greater than or equal to the capacity of the arrays | |
| 8969 */ | |
| 8970 String getKey(int i) => _keys[i]; | |
| 8971 | |
| 8972 /** | |
| 8973 * Get and return the ExecutableElement at the specified location. If the key/
value pair has been | |
| 8974 * removed from the set, then then `null` is returned. | |
| 8975 * | |
| 8976 * @param i some non-zero value less than size | |
| 8977 * @return the key at the passed index | |
| 8978 * @throw ArrayIndexOutOfBoundsException this exception is thrown if the passe
d index is less than | |
| 8979 * zero or greater than or equal to the capacity of the arrays | |
| 8980 */ | |
| 8981 ExecutableElement getValue(int i) => _values[i]; | |
| 8982 | |
| 8983 /** | |
| 8984 * Given some key/value pair, store the pair in the map. If the key exists alr
eady, then the new | |
| 8985 * value overrides the old value. | |
| 8986 * | |
| 8987 * @param key the key to store in the map | |
| 8988 * @param value the ExecutableElement value to store in the map | |
| 8989 */ | |
| 8990 void put(String key, ExecutableElement value) { | |
| 8991 // If we already have a value with this key, override the value | |
| 8992 for (int i = 0; i < _size; i++) { | |
| 8993 if (_keys[i] != null && _keys[i] == key) { | |
| 8994 _values[i] = value; | |
| 8995 return; | |
| 8996 } | |
| 8997 } | |
| 8998 // If needed, double the size of our arrays and copy values over in both | |
| 8999 // arrays | |
| 9000 if (_size == _keys.length) { | |
| 9001 int newArrayLength = _size * 2; | |
| 9002 List<String> keys_new_array = new List<String>(newArrayLength); | |
| 9003 List<ExecutableElement> values_new_array = | |
| 9004 new List<ExecutableElement>(newArrayLength); | |
| 9005 for (int i = 0; i < _size; i++) { | |
| 9006 keys_new_array[i] = _keys[i]; | |
| 9007 } | |
| 9008 for (int i = 0; i < _size; i++) { | |
| 9009 values_new_array[i] = _values[i]; | |
| 9010 } | |
| 9011 _keys = keys_new_array; | |
| 9012 _values = values_new_array; | |
| 9013 } | |
| 9014 // Put new value at end of array | |
| 9015 _keys[_size] = key; | |
| 9016 _values[_size] = value; | |
| 9017 _size++; | |
| 9018 } | |
| 9019 | |
| 9020 /** | |
| 9021 * Given some [String] key, this method replaces the associated key and value
pair with | |
| 9022 * `null`. The size is not decremented with this call, instead it is expected
that the users | |
| 9023 * check for `null`. | |
| 9024 * | |
| 9025 * @param key the key of the key/value pair to remove from the map | |
| 9026 */ | |
| 9027 void remove(String key) { | |
| 9028 for (int i = 0; i < _size; i++) { | |
| 9029 if (_keys[i] == key) { | |
| 9030 _keys[i] = null; | |
| 9031 _values[i] = null; | |
| 9032 return; | |
| 9033 } | |
| 9034 } | |
| 9035 } | |
| 9036 | |
| 9037 /** | |
| 9038 * Sets the ExecutableElement at the specified location. | |
| 9039 * | |
| 9040 * @param i some non-zero value less than size | |
| 9041 * @param value the ExecutableElement value to store in the map | |
| 9042 */ | |
| 9043 void setValue(int i, ExecutableElement value) { | |
| 9044 _values[i] = value; | |
| 9045 } | |
| 9046 | |
| 9047 /** | |
| 9048 * Initializes [keys] and [values]. | |
| 9049 */ | |
| 9050 void _initArrays(int initialCapacity) { | |
| 9051 _keys = new List<String>(initialCapacity); | |
| 9052 _values = new List<ExecutableElement>(initialCapacity); | |
| 9053 } | |
| 9054 } | |
| 9055 | |
| 9056 /** | |
| 9057 * Instances of the class `Namespace` implement a mapping of identifiers to the
elements | |
| 9058 * represented by those identifiers. Namespaces are the building blocks for scop
es. | |
| 9059 */ | |
| 9060 class Namespace { | |
| 9061 /** | |
| 9062 * An empty namespace. | |
| 9063 */ | |
| 9064 static Namespace EMPTY = new Namespace(new HashMap<String, Element>()); | |
| 9065 | |
| 9066 /** | |
| 9067 * A table mapping names that are defined in this namespace to the element rep
resenting the thing | |
| 9068 * declared with that name. | |
| 9069 */ | |
| 9070 final HashMap<String, Element> _definedNames; | |
| 9071 | |
| 9072 /** | |
| 9073 * Initialize a newly created namespace to have the given defined names. | |
| 9074 * | |
| 9075 * @param definedNames the mapping from names that are defined in this namespa
ce to the | |
| 9076 * corresponding elements | |
| 9077 */ | |
| 9078 Namespace(this._definedNames); | |
| 9079 | |
| 9080 /** | |
| 9081 * Return a table containing the same mappings as those defined by this namesp
ace. | |
| 9082 * | |
| 9083 * @return a table containing the same mappings as those defined by this names
pace | |
| 9084 */ | |
| 9085 Map<String, Element> get definedNames => | |
| 9086 new HashMap<String, Element>.from(_definedNames); | |
| 9087 | |
| 9088 /** | |
| 9089 * Return the element in this namespace that is available to the containing sc
ope using the given | |
| 9090 * name. | |
| 9091 * | |
| 9092 * @param name the name used to reference the | |
| 9093 * @return the element represented by the given identifier | |
| 9094 */ | |
| 9095 Element get(String name) => _definedNames[name]; | |
| 9096 } | |
| 9097 | |
| 9098 /** | |
| 9099 * Instances of the class `NamespaceBuilder` are used to build a `Namespace`. Na
mespace | |
| 9100 * builders are thread-safe and re-usable. | |
| 9101 */ | |
| 9102 class NamespaceBuilder { | |
| 9103 /** | |
| 9104 * Create a namespace representing the export namespace of the given [ExportEl
ement]. | |
| 9105 * | |
| 9106 * @param element the export element whose export namespace is to be created | |
| 9107 * @return the export namespace that was created | |
| 9108 */ | |
| 9109 Namespace createExportNamespaceForDirective(ExportElement element) { | |
| 9110 LibraryElement exportedLibrary = element.exportedLibrary; | |
| 9111 if (exportedLibrary == null) { | |
| 9112 // | |
| 9113 // The exported library will be null if the URI does not reference a valid | |
| 9114 // library. | |
| 9115 // | |
| 9116 return Namespace.EMPTY; | |
| 9117 } | |
| 9118 HashMap<String, Element> definedNames = | |
| 9119 _createExportMapping(exportedLibrary, new HashSet<LibraryElement>()); | |
| 9120 definedNames = _applyCombinators(definedNames, element.combinators); | |
| 9121 return new Namespace(definedNames); | |
| 9122 } | |
| 9123 | |
| 9124 /** | |
| 9125 * Create a namespace representing the export namespace of the given library. | |
| 9126 * | |
| 9127 * @param library the library whose export namespace is to be created | |
| 9128 * @return the export namespace that was created | |
| 9129 */ | |
| 9130 Namespace createExportNamespaceForLibrary(LibraryElement library) => | |
| 9131 new Namespace( | |
| 9132 _createExportMapping(library, new HashSet<LibraryElement>())); | |
| 9133 | |
| 9134 /** | |
| 9135 * Create a namespace representing the import namespace of the given library. | |
| 9136 * | |
| 9137 * @param library the library whose import namespace is to be created | |
| 9138 * @return the import namespace that was created | |
| 9139 */ | |
| 9140 Namespace createImportNamespaceForDirective(ImportElement element) { | |
| 9141 LibraryElement importedLibrary = element.importedLibrary; | |
| 9142 if (importedLibrary == null) { | |
| 9143 // | |
| 9144 // The imported library will be null if the URI does not reference a valid | |
| 9145 // library. | |
| 9146 // | |
| 9147 return Namespace.EMPTY; | |
| 9148 } | |
| 9149 HashMap<String, Element> definedNames = | |
| 9150 _createExportMapping(importedLibrary, new HashSet<LibraryElement>()); | |
| 9151 definedNames = _applyCombinators(definedNames, element.combinators); | |
| 9152 definedNames = _applyPrefix(definedNames, element.prefix); | |
| 9153 return new Namespace(definedNames); | |
| 9154 } | |
| 9155 | |
| 9156 /** | |
| 9157 * Create a namespace representing the public namespace of the given library. | |
| 9158 * | |
| 9159 * @param library the library whose public namespace is to be created | |
| 9160 * @return the public namespace that was created | |
| 9161 */ | |
| 9162 Namespace createPublicNamespaceForLibrary(LibraryElement library) { | |
| 9163 HashMap<String, Element> definedNames = new HashMap<String, Element>(); | |
| 9164 _addPublicNames(definedNames, library.definingCompilationUnit); | |
| 9165 for (CompilationUnitElement compilationUnit in library.parts) { | |
| 9166 _addPublicNames(definedNames, compilationUnit); | |
| 9167 } | |
| 9168 return new Namespace(definedNames); | |
| 9169 } | |
| 9170 | |
| 9171 /** | |
| 9172 * Add all of the names in the given namespace to the given mapping table. | |
| 9173 * | |
| 9174 * @param definedNames the mapping table to which the names in the given names
pace are to be added | |
| 9175 * @param namespace the namespace containing the names to be added to this nam
espace | |
| 9176 */ | |
| 9177 void _addAllFromNamespace( | |
| 9178 Map<String, Element> definedNames, Namespace namespace) { | |
| 9179 if (namespace != null) { | |
| 9180 definedNames.addAll(namespace.definedNames); | |
| 9181 } | |
| 9182 } | |
| 9183 | |
| 9184 /** | |
| 9185 * Add the given element to the given mapping table if it has a publicly visib
le name. | |
| 9186 * | |
| 9187 * @param definedNames the mapping table to which the public name is to be add
ed | |
| 9188 * @param element the element to be added | |
| 9189 */ | |
| 9190 void _addIfPublic(Map<String, Element> definedNames, Element element) { | |
| 9191 String name = element.name; | |
| 9192 if (name != null && !Scope.isPrivateName(name)) { | |
| 9193 definedNames[name] = element; | |
| 9194 } | |
| 9195 } | |
| 9196 | |
| 9197 /** | |
| 9198 * Add to the given mapping table all of the public top-level names that are d
efined in the given | |
| 9199 * compilation unit. | |
| 9200 * | |
| 9201 * @param definedNames the mapping table to which the public names are to be a
dded | |
| 9202 * @param compilationUnit the compilation unit defining the top-level names to
be added to this | |
| 9203 * namespace | |
| 9204 */ | |
| 9205 void _addPublicNames(Map<String, Element> definedNames, | |
| 9206 CompilationUnitElement compilationUnit) { | |
| 9207 for (PropertyAccessorElement element in compilationUnit.accessors) { | |
| 9208 _addIfPublic(definedNames, element); | |
| 9209 } | |
| 9210 for (ClassElement element in compilationUnit.enums) { | |
| 9211 _addIfPublic(definedNames, element); | |
| 9212 } | |
| 9213 for (FunctionElement element in compilationUnit.functions) { | |
| 9214 _addIfPublic(definedNames, element); | |
| 9215 } | |
| 9216 for (FunctionTypeAliasElement element | |
| 9217 in compilationUnit.functionTypeAliases) { | |
| 9218 _addIfPublic(definedNames, element); | |
| 9219 } | |
| 9220 for (ClassElement element in compilationUnit.types) { | |
| 9221 _addIfPublic(definedNames, element); | |
| 9222 } | |
| 9223 } | |
| 9224 | |
| 9225 /** | |
| 9226 * Apply the given combinators to all of the names in the given mapping table. | |
| 9227 * | |
| 9228 * @param definedNames the mapping table to which the namespace operations are
to be applied | |
| 9229 * @param combinators the combinators to be applied | |
| 9230 */ | |
| 9231 HashMap<String, Element> _applyCombinators( | |
| 9232 HashMap<String, Element> definedNames, | |
| 9233 List<NamespaceCombinator> combinators) { | |
| 9234 for (NamespaceCombinator combinator in combinators) { | |
| 9235 if (combinator is HideElementCombinator) { | |
| 9236 _hide(definedNames, combinator.hiddenNames); | |
| 9237 } else if (combinator is ShowElementCombinator) { | |
| 9238 definedNames = _show(definedNames, combinator.shownNames); | |
| 9239 } else { | |
| 9240 // Internal error. | |
| 9241 AnalysisEngine.instance.logger | |
| 9242 .logError("Unknown type of combinator: ${combinator.runtimeType}"); | |
| 9243 } | |
| 9244 } | |
| 9245 return definedNames; | |
| 9246 } | |
| 9247 | |
| 9248 /** | |
| 9249 * Apply the given prefix to all of the names in the table of defined names. | |
| 9250 * | |
| 9251 * @param definedNames the names that were defined before this operation | |
| 9252 * @param prefixElement the element defining the prefix to be added to the nam
es | |
| 9253 */ | |
| 9254 HashMap<String, Element> _applyPrefix( | |
| 9255 HashMap<String, Element> definedNames, PrefixElement prefixElement) { | |
| 9256 if (prefixElement != null) { | |
| 9257 String prefix = prefixElement.name; | |
| 9258 HashMap<String, Element> newNames = new HashMap<String, Element>(); | |
| 9259 definedNames.forEach((String name, Element element) { | |
| 9260 newNames["$prefix.$name"] = element; | |
| 9261 }); | |
| 9262 return newNames; | |
| 9263 } else { | |
| 9264 return definedNames; | |
| 9265 } | |
| 9266 } | |
| 9267 | |
| 9268 /** | |
| 9269 * Create a mapping table representing the export namespace of the given libra
ry. | |
| 9270 * | |
| 9271 * @param library the library whose public namespace is to be created | |
| 9272 * @param visitedElements a set of libraries that do not need to be visited wh
en processing the | |
| 9273 * export directives of the given library because all of the names de
fined by them will | |
| 9274 * be added by another library | |
| 9275 * @return the mapping table that was created | |
| 9276 */ | |
| 9277 HashMap<String, Element> _createExportMapping( | |
| 9278 LibraryElement library, HashSet<LibraryElement> visitedElements) { | |
| 9279 // Check if the export namespace has been already computed. | |
| 9280 { | |
| 9281 Namespace exportNamespace = library.exportNamespace; | |
| 9282 if (exportNamespace != null) { | |
| 9283 return exportNamespace.definedNames; | |
| 9284 } | |
| 9285 } | |
| 9286 // TODO(scheglov) Remove this after switching to the new task model. | |
| 9287 visitedElements.add(library); | |
| 9288 try { | |
| 9289 HashMap<String, Element> definedNames = new HashMap<String, Element>(); | |
| 9290 for (ExportElement element in library.exports) { | |
| 9291 LibraryElement exportedLibrary = element.exportedLibrary; | |
| 9292 if (exportedLibrary != null && | |
| 9293 !visitedElements.contains(exportedLibrary)) { | |
| 9294 // | |
| 9295 // The exported library will be null if the URI does not reference a | |
| 9296 // valid library. | |
| 9297 // | |
| 9298 HashMap<String, Element> exportedNames = | |
| 9299 _createExportMapping(exportedLibrary, visitedElements); | |
| 9300 exportedNames = _applyCombinators(exportedNames, element.combinators); | |
| 9301 definedNames.addAll(exportedNames); | |
| 9302 } | |
| 9303 } | |
| 9304 _addAllFromNamespace(definedNames, | |
| 9305 (library.context as InternalAnalysisContext) | |
| 9306 .getPublicNamespace(library)); | |
| 9307 return definedNames; | |
| 9308 } finally { | |
| 9309 visitedElements.remove(library); | |
| 9310 } | |
| 9311 } | |
| 9312 | |
| 9313 /** | |
| 9314 * Hide all of the given names by removing them from the given collection of d
efined names. | |
| 9315 * | |
| 9316 * @param definedNames the names that were defined before this operation | |
| 9317 * @param hiddenNames the names to be hidden | |
| 9318 */ | |
| 9319 void _hide(HashMap<String, Element> definedNames, List<String> hiddenNames) { | |
| 9320 for (String name in hiddenNames) { | |
| 9321 definedNames.remove(name); | |
| 9322 definedNames.remove("$name="); | |
| 9323 } | |
| 9324 } | |
| 9325 | |
| 9326 /** | |
| 9327 * Show only the given names by removing all other names from the given collec
tion of defined | |
| 9328 * names. | |
| 9329 * | |
| 9330 * @param definedNames the names that were defined before this operation | |
| 9331 * @param shownNames the names to be shown | |
| 9332 */ | |
| 9333 HashMap<String, Element> _show( | |
| 9334 HashMap<String, Element> definedNames, List<String> shownNames) { | |
| 9335 HashMap<String, Element> newNames = new HashMap<String, Element>(); | |
| 9336 for (String name in shownNames) { | |
| 9337 Element element = definedNames[name]; | |
| 9338 if (element != null) { | |
| 9339 newNames[name] = element; | |
| 9340 } | |
| 9341 String setterName = "$name="; | |
| 9342 element = definedNames[setterName]; | |
| 9343 if (element != null) { | |
| 9344 newNames[setterName] = element; | |
| 9345 } | |
| 9346 } | |
| 9347 return newNames; | |
| 9348 } | |
| 9349 } | |
| 9350 | |
| 9351 /** | |
| 9352 * Instances of the class `OverrideVerifier` visit all of the declarations in a
compilation | |
| 9353 * unit to verify that if they have an override annotation it is being used corr
ectly. | |
| 9354 */ | |
| 9355 class OverrideVerifier extends RecursiveAstVisitor<Object> { | |
| 9356 /** | |
| 9357 * The error reporter used to report errors. | |
| 9358 */ | |
| 9359 final ErrorReporter _errorReporter; | |
| 9360 | |
| 9361 /** | |
| 9362 * The inheritance manager used to find overridden methods. | |
| 9363 */ | |
| 9364 final InheritanceManager _manager; | |
| 9365 | |
| 9366 /** | |
| 9367 * Initialize a newly created verifier to look for inappropriate uses of the o
verride annotation. | |
| 9368 * | |
| 9369 * @param errorReporter the error reporter used to report errors | |
| 9370 * @param manager the inheritance manager used to find overridden methods | |
| 9371 */ | |
| 9372 OverrideVerifier(this._errorReporter, this._manager); | |
| 9373 | |
| 9374 @override | |
| 9375 Object visitMethodDeclaration(MethodDeclaration node) { | |
| 9376 ExecutableElement element = node.element; | |
| 9377 if (_isOverride(element)) { | |
| 9378 if (_getOverriddenMember(element) == null) { | |
| 9379 if (element is MethodElement) { | |
| 9380 _errorReporter.reportErrorForNode( | |
| 9381 HintCode.OVERRIDE_ON_NON_OVERRIDING_METHOD, node.name); | |
| 9382 } else if (element is PropertyAccessorElement) { | |
| 9383 if (element.isGetter) { | |
| 9384 _errorReporter.reportErrorForNode( | |
| 9385 HintCode.OVERRIDE_ON_NON_OVERRIDING_GETTER, node.name); | |
| 9386 } else { | |
| 9387 _errorReporter.reportErrorForNode( | |
| 9388 HintCode.OVERRIDE_ON_NON_OVERRIDING_SETTER, node.name); | |
| 9389 } | |
| 9390 } | |
| 9391 } | |
| 9392 } | |
| 9393 return super.visitMethodDeclaration(node); | |
| 9394 } | |
| 9395 | |
| 9396 /** | |
| 9397 * Return the member that overrides the given member. | |
| 9398 * | |
| 9399 * @param member the member that overrides the returned member | |
| 9400 * @return the member that overrides the given member | |
| 9401 */ | |
| 9402 ExecutableElement _getOverriddenMember(ExecutableElement member) { | |
| 9403 LibraryElement library = member.library; | |
| 9404 if (library == null) { | |
| 9405 return null; | |
| 9406 } | |
| 9407 ClassElement classElement = | |
| 9408 member.getAncestor((element) => element is ClassElement); | |
| 9409 if (classElement == null) { | |
| 9410 return null; | |
| 9411 } | |
| 9412 return _manager.lookupInheritance(classElement, member.name); | |
| 9413 } | |
| 9414 | |
| 9415 /** | |
| 9416 * Return `true` if the given element has an override annotation associated wi
th it. | |
| 9417 * | |
| 9418 * @param element the element being tested | |
| 9419 * @return `true` if the element has an override annotation associated with it | |
| 9420 */ | |
| 9421 bool _isOverride(Element element) => element != null && element.isOverride; | |
| 9422 } | |
| 9423 | |
| 9424 /** | |
| 9425 * Instances of the class `PubVerifier` traverse an AST structure looking for de
viations from | |
| 9426 * pub best practices. | |
| 9427 */ | |
| 9428 class PubVerifier extends RecursiveAstVisitor<Object> { | |
| 9429 // static String _PUBSPEC_YAML = "pubspec.yaml"; | |
| 9430 | |
| 9431 /** | |
| 9432 * The analysis context containing the sources to be analyzed | |
| 9433 */ | |
| 9434 final AnalysisContext _context; | |
| 9435 | |
| 9436 /** | |
| 9437 * The error reporter by which errors will be reported. | |
| 9438 */ | |
| 9439 final ErrorReporter _errorReporter; | |
| 9440 | |
| 9441 PubVerifier(this._context, this._errorReporter); | |
| 9442 | |
| 9443 @override | |
| 9444 Object visitImportDirective(ImportDirective directive) { | |
| 9445 return null; | |
| 9446 } | |
| 9447 | |
| 9448 // /** | |
| 9449 // * This verifies that the passed file import directive is not contained in a
source inside a | |
| 9450 // * package "lib" directory hierarchy referencing a source outside that packa
ge "lib" directory | |
| 9451 // * hierarchy. | |
| 9452 // * | |
| 9453 // * @param uriLiteral the import URL (not `null`) | |
| 9454 // * @param path the file path being verified (not `null`) | |
| 9455 // * @return `true` if and only if an error code is generated on the passed no
de | |
| 9456 // * See [PubSuggestionCode.FILE_IMPORT_INSIDE_LIB_REFERENCES_FILE_OUTSIDE]. | |
| 9457 // */ | |
| 9458 // bool | |
| 9459 // _checkForFileImportInsideLibReferencesFileOutside(StringLiteral uriLiter
al, | |
| 9460 // String path) { | |
| 9461 // Source source = _getSource(uriLiteral); | |
| 9462 // String fullName = _getSourceFullName(source); | |
| 9463 // if (fullName != null) { | |
| 9464 // int pathIndex = 0; | |
| 9465 // int fullNameIndex = fullName.length; | |
| 9466 // while (pathIndex < path.length && | |
| 9467 // StringUtilities.startsWith3(path, pathIndex, 0x2E, 0x2E, 0x2F)) { | |
| 9468 // fullNameIndex = JavaString.lastIndexOf(fullName, '/', fullNameIndex); | |
| 9469 // if (fullNameIndex < 4) { | |
| 9470 // return false; | |
| 9471 // } | |
| 9472 // // Check for "/lib" at a specified place in the fullName | |
| 9473 // if (StringUtilities.startsWith4( | |
| 9474 // fullName, | |
| 9475 // fullNameIndex - 4, | |
| 9476 // 0x2F, | |
| 9477 // 0x6C, | |
| 9478 // 0x69, | |
| 9479 // 0x62)) { | |
| 9480 // String relativePubspecPath = | |
| 9481 // path.substring(0, pathIndex + 3) + | |
| 9482 // _PUBSPEC_YAML; | |
| 9483 // Source pubspecSource = | |
| 9484 // _context.sourceFactory.resolveUri(source, relativePubspecPath); | |
| 9485 // if (_context.exists(pubspecSource)) { | |
| 9486 // // Files inside the lib directory hierarchy should not reference | |
| 9487 // // files outside | |
| 9488 // _errorReporter.reportErrorForNode( | |
| 9489 // HintCode.FILE_IMPORT_INSIDE_LIB_REFERENCES_FILE_OUTSIDE, | |
| 9490 // uriLiteral); | |
| 9491 // } | |
| 9492 // return true; | |
| 9493 // } | |
| 9494 // pathIndex += 3; | |
| 9495 // } | |
| 9496 // } | |
| 9497 // return false; | |
| 9498 // } | |
| 9499 | |
| 9500 // /** | |
| 9501 // * This verifies that the passed file import directive is not contained in a
source outside a | |
| 9502 // * package "lib" directory hierarchy referencing a source inside that packag
e "lib" directory | |
| 9503 // * hierarchy. | |
| 9504 // * | |
| 9505 // * @param uriLiteral the import URL (not `null`) | |
| 9506 // * @param path the file path being verified (not `null`) | |
| 9507 // * @return `true` if and only if an error code is generated on the passed no
de | |
| 9508 // * See [PubSuggestionCode.FILE_IMPORT_OUTSIDE_LIB_REFERENCES_FILE_INSIDE]. | |
| 9509 // */ | |
| 9510 // bool | |
| 9511 // _checkForFileImportOutsideLibReferencesFileInside(StringLiteral uriLiter
al, | |
| 9512 // String path) { | |
| 9513 // if (StringUtilities.startsWith4(path, 0, 0x6C, 0x69, 0x62, 0x2F)) { | |
| 9514 // if (_checkForFileImportOutsideLibReferencesFileInsideAtIndex( | |
| 9515 // uriLiteral, | |
| 9516 // path, | |
| 9517 // 0)) { | |
| 9518 // return true; | |
| 9519 // } | |
| 9520 // } | |
| 9521 // int pathIndex = | |
| 9522 // StringUtilities.indexOf5(path, 0, 0x2F, 0x6C, 0x69, 0x62, 0x2F); | |
| 9523 // while (pathIndex != -1) { | |
| 9524 // if (_checkForFileImportOutsideLibReferencesFileInsideAtIndex( | |
| 9525 // uriLiteral, | |
| 9526 // path, | |
| 9527 // pathIndex + 1)) { | |
| 9528 // return true; | |
| 9529 // } | |
| 9530 // pathIndex = | |
| 9531 // StringUtilities.indexOf5(path, pathIndex + 4, 0x2F, 0x6C, 0x69, 0x62
, 0x2F); | |
| 9532 // } | |
| 9533 // return false; | |
| 9534 // } | |
| 9535 | |
| 9536 // bool | |
| 9537 // _checkForFileImportOutsideLibReferencesFileInsideAtIndex(StringLiteral u
riLiteral, | |
| 9538 // String path, int pathIndex) { | |
| 9539 // Source source = _getSource(uriLiteral); | |
| 9540 // String relativePubspecPath = path.substring(0, pathIndex) + _PUBSPEC_YAML; | |
| 9541 // Source pubspecSource = | |
| 9542 // _context.sourceFactory.resolveUri(source, relativePubspecPath); | |
| 9543 // if (!_context.exists(pubspecSource)) { | |
| 9544 // return false; | |
| 9545 // } | |
| 9546 // String fullName = _getSourceFullName(source); | |
| 9547 // if (fullName != null) { | |
| 9548 // if (StringUtilities.indexOf5(fullName, 0, 0x2F, 0x6C, 0x69, 0x62, 0x2F)
< | |
| 9549 // 0) { | |
| 9550 // // Files outside the lib directory hierarchy should not reference file
s | |
| 9551 // // inside ... use package: url instead | |
| 9552 // _errorReporter.reportErrorForNode( | |
| 9553 // HintCode.FILE_IMPORT_OUTSIDE_LIB_REFERENCES_FILE_INSIDE, | |
| 9554 // uriLiteral); | |
| 9555 // return true; | |
| 9556 // } | |
| 9557 // } | |
| 9558 // return false; | |
| 9559 // } | |
| 9560 | |
| 9561 // /** | |
| 9562 // * This verifies that the passed package import directive does not contain "
.." | |
| 9563 // * | |
| 9564 // * @param uriLiteral the import URL (not `null`) | |
| 9565 // * @param path the path to be validated (not `null`) | |
| 9566 // * @return `true` if and only if an error code is generated on the passed no
de | |
| 9567 // * See [PubSuggestionCode.PACKAGE_IMPORT_CONTAINS_DOT_DOT]. | |
| 9568 // */ | |
| 9569 // bool _checkForPackageImportContainsDotDot(StringLiteral uriLiteral, | |
| 9570 // String path) { | |
| 9571 // if (StringUtilities.startsWith3(path, 0, 0x2E, 0x2E, 0x2F) || | |
| 9572 // StringUtilities.indexOf4(path, 0, 0x2F, 0x2E, 0x2E, 0x2F) >= 0) { | |
| 9573 // // Package import should not to contain ".." | |
| 9574 // _errorReporter.reportErrorForNode( | |
| 9575 // HintCode.PACKAGE_IMPORT_CONTAINS_DOT_DOT, | |
| 9576 // uriLiteral); | |
| 9577 // return true; | |
| 9578 // } | |
| 9579 // return false; | |
| 9580 // } | |
| 9581 | |
| 9582 // /** | |
| 9583 // * Answer the source associated with the compilation unit containing the giv
en AST node. | |
| 9584 // * | |
| 9585 // * @param node the node (not `null`) | |
| 9586 // * @return the source or `null` if it could not be determined | |
| 9587 // */ | |
| 9588 // Source _getSource(AstNode node) { | |
| 9589 // Source source = null; | |
| 9590 // CompilationUnit unit = node.getAncestor((node) => node is CompilationUnit)
; | |
| 9591 // if (unit != null) { | |
| 9592 // CompilationUnitElement element = unit.element; | |
| 9593 // if (element != null) { | |
| 9594 // source = element.source; | |
| 9595 // } | |
| 9596 // } | |
| 9597 // return source; | |
| 9598 // } | |
| 9599 | |
| 9600 // /** | |
| 9601 // * Answer the full name of the given source. The returned value will have al
l | |
| 9602 // * [File.separatorChar] replace by '/'. | |
| 9603 // * | |
| 9604 // * @param source the source | |
| 9605 // * @return the full name or `null` if it could not be determined | |
| 9606 // */ | |
| 9607 // String _getSourceFullName(Source source) { | |
| 9608 // if (source != null) { | |
| 9609 // String fullName = source.fullName; | |
| 9610 // if (fullName != null) { | |
| 9611 // return fullName.replaceAll(r'\', '/'); | |
| 9612 // } | |
| 9613 // } | |
| 9614 // return null; | |
| 9615 // } | |
| 9616 } | |
| 9617 | |
| 9618 /** | |
| 9619 * Kind of the redirecting constructor. | |
| 9620 */ | |
| 9621 class RedirectingConstructorKind extends Enum<RedirectingConstructorKind> { | |
| 9622 static const RedirectingConstructorKind CONST = | |
| 9623 const RedirectingConstructorKind('CONST', 0); | |
| 9624 | |
| 9625 static const RedirectingConstructorKind NORMAL = | |
| 9626 const RedirectingConstructorKind('NORMAL', 1); | |
| 9627 | |
| 9628 static const List<RedirectingConstructorKind> values = const [CONST, NORMAL]; | |
| 9629 | |
| 9630 const RedirectingConstructorKind(String name, int ordinal) | |
| 9631 : super(name, ordinal); | |
| 9632 } | |
| 9633 | |
| 9634 /** | |
| 9635 * A `ResolvableLibrary` represents a single library during the resolution of | |
| 9636 * some (possibly different) library. They are not intended to be used except | |
| 9637 * during the resolution process. | |
| 9638 */ | |
| 9639 class ResolvableLibrary { | |
| 9640 /** | |
| 9641 * An empty array that can be used to initialize lists of libraries. | |
| 9642 */ | |
| 9643 static List<ResolvableLibrary> _EMPTY_ARRAY = new List<ResolvableLibrary>(0); | |
| 9644 | |
| 9645 /** | |
| 9646 * The next artificial hash code. | |
| 9647 */ | |
| 9648 static int _NEXT_HASH_CODE = 0; | |
| 9649 | |
| 9650 /** | |
| 9651 * The artifitial hash code for this object. | |
| 9652 */ | |
| 9653 final int _hashCode = _nextHashCode(); | |
| 9654 | |
| 9655 /** | |
| 9656 * The source specifying the defining compilation unit of this library. | |
| 9657 */ | |
| 9658 final Source librarySource; | |
| 9659 | |
| 9660 /** | |
| 9661 * A list containing all of the libraries that are imported into this library. | |
| 9662 */ | |
| 9663 List<ResolvableLibrary> _importedLibraries = _EMPTY_ARRAY; | |
| 9664 | |
| 9665 /** | |
| 9666 * A flag indicating whether this library explicitly imports core. | |
| 9667 */ | |
| 9668 bool explicitlyImportsCore = false; | |
| 9669 | |
| 9670 /** | |
| 9671 * An array containing all of the libraries that are exported from this librar
y. | |
| 9672 */ | |
| 9673 List<ResolvableLibrary> _exportedLibraries = _EMPTY_ARRAY; | |
| 9674 | |
| 9675 /** | |
| 9676 * An array containing the compilation units that comprise this library. The | |
| 9677 * defining compilation unit is always first. | |
| 9678 */ | |
| 9679 List<ResolvableCompilationUnit> _compilationUnits; | |
| 9680 | |
| 9681 /** | |
| 9682 * The library element representing this library. | |
| 9683 */ | |
| 9684 LibraryElementImpl _libraryElement; | |
| 9685 | |
| 9686 /** | |
| 9687 * The listener to which analysis errors will be reported. | |
| 9688 */ | |
| 9689 AnalysisErrorListener _errorListener; | |
| 9690 | |
| 9691 /** | |
| 9692 * The inheritance manager which is used for member lookups in this library. | |
| 9693 */ | |
| 9694 InheritanceManager _inheritanceManager; | |
| 9695 | |
| 9696 /** | |
| 9697 * The library scope used when resolving elements within this library's compil
ation units. | |
| 9698 */ | |
| 9699 LibraryScope _libraryScope; | |
| 9700 | |
| 9701 /** | |
| 9702 * Initialize a newly created data holder that can maintain the data associate
d with a library. | |
| 9703 * | |
| 9704 * @param librarySource the source specifying the defining compilation unit of
this library | |
| 9705 * @param errorListener the listener to which analysis errors will be reported | |
| 9706 */ | |
| 9707 ResolvableLibrary(this.librarySource); | |
| 9708 | |
| 9709 /** | |
| 9710 * Return an array of the [CompilationUnit]s that make up the library. The fir
st unit is | |
| 9711 * always the defining unit. | |
| 9712 * | |
| 9713 * @return an array of the [CompilationUnit]s that make up the library. The fi
rst unit is | |
| 9714 * always the defining unit | |
| 9715 */ | |
| 9716 List<CompilationUnit> get compilationUnits { | |
| 9717 int count = _compilationUnits.length; | |
| 9718 List<CompilationUnit> units = new List<CompilationUnit>(count); | |
| 9719 for (int i = 0; i < count; i++) { | |
| 9720 units[i] = _compilationUnits[i].compilationUnit; | |
| 9721 } | |
| 9722 return units; | |
| 9723 } | |
| 9724 | |
| 9725 /** | |
| 9726 * Return an array containing the sources for the compilation units in this li
brary, including the | |
| 9727 * defining compilation unit. | |
| 9728 * | |
| 9729 * @return the sources for the compilation units in this library | |
| 9730 */ | |
| 9731 List<Source> get compilationUnitSources { | |
| 9732 int count = _compilationUnits.length; | |
| 9733 List<Source> sources = new List<Source>(count); | |
| 9734 for (int i = 0; i < count; i++) { | |
| 9735 sources[i] = _compilationUnits[i].source; | |
| 9736 } | |
| 9737 return sources; | |
| 9738 } | |
| 9739 | |
| 9740 /** | |
| 9741 * Return the AST structure associated with the defining compilation unit for
this library. | |
| 9742 * | |
| 9743 * @return the AST structure associated with the defining compilation unit for
this library | |
| 9744 * @throws AnalysisException if an AST structure could not be created for the
defining compilation | |
| 9745 * unit | |
| 9746 */ | |
| 9747 CompilationUnit get definingCompilationUnit => | |
| 9748 _compilationUnits[0].compilationUnit; | |
| 9749 | |
| 9750 /** | |
| 9751 * Set the listener to which analysis errors will be reported to be the given
listener. | |
| 9752 * | |
| 9753 * @param errorListener the listener to which analysis errors will be reported | |
| 9754 */ | |
| 9755 void set errorListener(AnalysisErrorListener errorListener) { | |
| 9756 this._errorListener = errorListener; | |
| 9757 } | |
| 9758 | |
| 9759 /** | |
| 9760 * Set the libraries that are exported by this library to be those in the give
n array. | |
| 9761 * | |
| 9762 * @param exportedLibraries the libraries that are exported by this library | |
| 9763 */ | |
| 9764 void set exportedLibraries(List<ResolvableLibrary> exportedLibraries) { | |
| 9765 this._exportedLibraries = exportedLibraries; | |
| 9766 } | |
| 9767 | |
| 9768 /** | |
| 9769 * Return an array containing the libraries that are exported from this librar
y. | |
| 9770 * | |
| 9771 * @return an array containing the libraries that are exported from this libra
ry | |
| 9772 */ | |
| 9773 List<ResolvableLibrary> get exports => _exportedLibraries; | |
| 9774 | |
| 9775 @override | |
| 9776 int get hashCode => _hashCode; | |
| 9777 | |
| 9778 /** | |
| 9779 * Set the libraries that are imported into this library to be those in the gi
ven array. | |
| 9780 * | |
| 9781 * @param importedLibraries the libraries that are imported into this library | |
| 9782 */ | |
| 9783 void set importedLibraries(List<ResolvableLibrary> importedLibraries) { | |
| 9784 this._importedLibraries = importedLibraries; | |
| 9785 } | |
| 9786 | |
| 9787 /** | |
| 9788 * Return an array containing the libraries that are imported into this librar
y. | |
| 9789 * | |
| 9790 * @return an array containing the libraries that are imported into this libra
ry | |
| 9791 */ | |
| 9792 List<ResolvableLibrary> get imports => _importedLibraries; | |
| 9793 | |
| 9794 /** | |
| 9795 * Return an array containing the libraries that are either imported or export
ed from this | |
| 9796 * library. | |
| 9797 * | |
| 9798 * @return the libraries that are either imported or exported from this librar
y | |
| 9799 */ | |
| 9800 List<ResolvableLibrary> get importsAndExports { | |
| 9801 HashSet<ResolvableLibrary> libraries = new HashSet<ResolvableLibrary>(); | |
| 9802 for (ResolvableLibrary library in _importedLibraries) { | |
| 9803 libraries.add(library); | |
| 9804 } | |
| 9805 for (ResolvableLibrary library in _exportedLibraries) { | |
| 9806 libraries.add(library); | |
| 9807 } | |
| 9808 return new List.from(libraries); | |
| 9809 } | |
| 9810 | |
| 9811 /** | |
| 9812 * Return the inheritance manager for this library. | |
| 9813 * | |
| 9814 * @return the inheritance manager for this library | |
| 9815 */ | |
| 9816 InheritanceManager get inheritanceManager { | |
| 9817 if (_inheritanceManager == null) { | |
| 9818 return _inheritanceManager = new InheritanceManager(_libraryElement); | |
| 9819 } | |
| 9820 return _inheritanceManager; | |
| 9821 } | |
| 9822 | |
| 9823 /** | |
| 9824 * Return the library element representing this library, creating it if necess
ary. | |
| 9825 * | |
| 9826 * @return the library element representing this library | |
| 9827 */ | |
| 9828 LibraryElementImpl get libraryElement => _libraryElement; | |
| 9829 | |
| 9830 /** | |
| 9831 * Set the library element representing this library to the given library elem
ent. | |
| 9832 * | |
| 9833 * @param libraryElement the library element representing this library | |
| 9834 */ | |
| 9835 void set libraryElement(LibraryElementImpl libraryElement) { | |
| 9836 this._libraryElement = libraryElement; | |
| 9837 if (_inheritanceManager != null) { | |
| 9838 _inheritanceManager.libraryElement = libraryElement; | |
| 9839 } | |
| 9840 } | |
| 9841 | |
| 9842 /** | |
| 9843 * Return the library scope used when resolving elements within this library's
compilation units. | |
| 9844 * | |
| 9845 * @return the library scope used when resolving elements within this library'
s compilation units | |
| 9846 */ | |
| 9847 LibraryScope get libraryScope { | |
| 9848 if (_libraryScope == null) { | |
| 9849 _libraryScope = new LibraryScope(_libraryElement, _errorListener); | |
| 9850 } | |
| 9851 return _libraryScope; | |
| 9852 } | |
| 9853 | |
| 9854 /** | |
| 9855 * Return an array containing the compilation units that comprise this library
. The defining | |
| 9856 * compilation unit is always first. | |
| 9857 * | |
| 9858 * @return the compilation units that comprise this library | |
| 9859 */ | |
| 9860 List<ResolvableCompilationUnit> get resolvableCompilationUnits => | |
| 9861 _compilationUnits; | |
| 9862 | |
| 9863 /** | |
| 9864 * Set the compilation unit in this library to the given compilation units. Th
e defining | |
| 9865 * compilation unit must be the first element of the array. | |
| 9866 * | |
| 9867 * @param units the compilation units in this library | |
| 9868 */ | |
| 9869 void set resolvableCompilationUnits(List<ResolvableCompilationUnit> units) { | |
| 9870 _compilationUnits = units; | |
| 9871 } | |
| 9872 | |
| 9873 /** | |
| 9874 * Return the AST structure associated with the given source, or `null` if the
source does | |
| 9875 * not represent a compilation unit that is included in this library. | |
| 9876 * | |
| 9877 * @param source the source representing the compilation unit whose AST is to
be returned | |
| 9878 * @return the AST structure associated with the given source | |
| 9879 * @throws AnalysisException if an AST structure could not be created for the
compilation unit | |
| 9880 */ | |
| 9881 CompilationUnit getAST(Source source) { | |
| 9882 int count = _compilationUnits.length; | |
| 9883 for (int i = 0; i < count; i++) { | |
| 9884 if (_compilationUnits[i].source == source) { | |
| 9885 return _compilationUnits[i].compilationUnit; | |
| 9886 } | |
| 9887 } | |
| 9888 return null; | |
| 9889 } | |
| 9890 | |
| 9891 @override | |
| 9892 String toString() => librarySource.shortName; | |
| 9893 | |
| 9894 static int _nextHashCode() { | |
| 9895 int next = (_NEXT_HASH_CODE + 1) & 0xFFFFFF; | |
| 9896 _NEXT_HASH_CODE = next; | |
| 9897 return next; | |
| 9898 } | |
| 9899 } | |
| 9900 | |
| 9901 /** | |
| 9902 * The enumeration `ResolverErrorCode` defines the error codes used for errors | |
| 9903 * detected by the resolver. The convention for this class is for the name of | |
| 9904 * the error code to indicate the problem that caused the error to be generated | |
| 9905 * and for the error message to explain what is wrong and, when appropriate, how | |
| 9906 * the problem can be corrected. | |
| 9907 */ | |
| 9908 class ResolverErrorCode extends ErrorCode { | |
| 9909 static const ResolverErrorCode BREAK_LABEL_ON_SWITCH_MEMBER = | |
| 9910 const ResolverErrorCode('BREAK_LABEL_ON_SWITCH_MEMBER', | |
| 9911 "Break label resolves to case or default statement"); | |
| 9912 | |
| 9913 static const ResolverErrorCode CONTINUE_LABEL_ON_SWITCH = | |
| 9914 const ResolverErrorCode('CONTINUE_LABEL_ON_SWITCH', | |
| 9915 "A continue label resolves to switch, must be loop or switch member"); | |
| 9916 | |
| 9917 static const ResolverErrorCode MISSING_LIBRARY_DIRECTIVE_WITH_PART = | |
| 9918 const ResolverErrorCode('MISSING_LIBRARY_DIRECTIVE_WITH_PART', | |
| 9919 "Libraries that have parts must have a library directive"); | |
| 9920 | |
| 9921 /** | |
| 9922 * Initialize a newly created error code to have the given [name]. The message | |
| 9923 * associated with the error will be created from the given [message] | |
| 9924 * template. The correction associated with the error will be created from the | |
| 9925 * given [correction] template. | |
| 9926 */ | |
| 9927 const ResolverErrorCode(String name, String message, [String correction]) | |
| 9928 : super(name, message, correction); | |
| 9929 | |
| 9930 @override | |
| 9931 ErrorSeverity get errorSeverity => type.severity; | |
| 9932 | |
| 9933 @override | |
| 9934 ErrorType get type => ErrorType.COMPILE_TIME_ERROR; | |
| 9935 } | |
| 9936 | |
| 9937 /** | |
| 9938 * Instances of the class `ResolverVisitor` are used to resolve the nodes within
a single | |
| 9939 * compilation unit. | |
| 9940 */ | |
| 9941 class ResolverVisitor extends ScopedVisitor { | |
| 9942 /** | |
| 9943 * The manager for the inheritance mappings. | |
| 9944 */ | |
| 9945 InheritanceManager _inheritanceManager; | |
| 9946 | |
| 9947 /** | |
| 9948 * The object used to resolve the element associated with the current node. | |
| 9949 */ | |
| 9950 ElementResolver elementResolver; | |
| 9951 | |
| 9952 /** | |
| 9953 * The object used to compute the type associated with the current node. | |
| 9954 */ | |
| 9955 StaticTypeAnalyzer typeAnalyzer; | |
| 9956 | |
| 9957 /** | |
| 9958 * The class element representing the class containing the current node, | |
| 9959 * or `null` if the current node is not contained in a class. | |
| 9960 */ | |
| 9961 ClassElement enclosingClass = null; | |
| 9962 | |
| 9963 /** | |
| 9964 * The class declaration representing the class containing the current node, o
r `null` if | |
| 9965 * the current node is not contained in a class. | |
| 9966 */ | |
| 9967 ClassDeclaration _enclosingClassDeclaration = null; | |
| 9968 | |
| 9969 /** | |
| 9970 * The function type alias representing the function type containing the curre
nt node, or | |
| 9971 * `null` if the current node is not contained in a function type alias. | |
| 9972 */ | |
| 9973 FunctionTypeAlias _enclosingFunctionTypeAlias = null; | |
| 9974 | |
| 9975 /** | |
| 9976 * The element representing the function containing the current node, or `null
` if the | |
| 9977 * current node is not contained in a function. | |
| 9978 */ | |
| 9979 ExecutableElement _enclosingFunction = null; | |
| 9980 | |
| 9981 /** | |
| 9982 * The [Comment] before a [FunctionDeclaration] or a [MethodDeclaration] that | |
| 9983 * cannot be resolved where we visited it, because it should be resolved in th
e scope of the body. | |
| 9984 */ | |
| 9985 Comment _commentBeforeFunction = null; | |
| 9986 | |
| 9987 /** | |
| 9988 * The object keeping track of which elements have had their types overridden. | |
| 9989 */ | |
| 9990 TypeOverrideManager _overrideManager = new TypeOverrideManager(); | |
| 9991 | |
| 9992 /** | |
| 9993 * The object keeping track of which elements have had their types promoted. | |
| 9994 */ | |
| 9995 TypePromotionManager _promoteManager = new TypePromotionManager(); | |
| 9996 | |
| 9997 /** | |
| 9998 * A comment before a function should be resolved in the context of the | |
| 9999 * function. But when we incrementally resolve a comment, we don't want to | |
| 10000 * resolve the whole function. | |
| 10001 * | |
| 10002 * So, this flag is set to `true`, when just context of the function should | |
| 10003 * be built and the comment resolved. | |
| 10004 */ | |
| 10005 bool resolveOnlyCommentInFunctionBody = false; | |
| 10006 | |
| 10007 /** | |
| 10008 * Initialize a newly created visitor to resolve the nodes in an AST node. | |
| 10009 * | |
| 10010 * [definingLibrary] is the element for the library containing the node being | |
| 10011 * visited. | |
| 10012 * [source] is the source representing the compilation unit containing the | |
| 10013 * node being visited. | |
| 10014 * [typeProvider] the object used to access the types from the core library. | |
| 10015 * [errorListener] the error listener that will be informed of any errors | |
| 10016 * that are found during resolution. | |
| 10017 * [nameScope] is the scope used to resolve identifiers in the node that will | |
| 10018 * first be visited. If `null` or unspecified, a new [LibraryScope] will be | |
| 10019 * created based on [definingLibrary] and [typeProvider]. | |
| 10020 * [inheritanceManager] is used to perform inheritance lookups. If `null` or | |
| 10021 * unspecified, a new [InheritanceManager] will be created based on | |
| 10022 * [definingLibrary]. | |
| 10023 * [typeAnalyzerFactory] is used to create the type analyzer. If `null` or | |
| 10024 * unspecified, a type analyzer of type [StaticTypeAnalyzer] will be created. | |
| 10025 */ | |
| 10026 ResolverVisitor(LibraryElement definingLibrary, Source source, | |
| 10027 TypeProvider typeProvider, AnalysisErrorListener errorListener, | |
| 10028 {Scope nameScope, InheritanceManager inheritanceManager, | |
| 10029 StaticTypeAnalyzerFactory typeAnalyzerFactory}) | |
| 10030 : super(definingLibrary, source, typeProvider, errorListener, | |
| 10031 nameScope: nameScope) { | |
| 10032 if (inheritanceManager == null) { | |
| 10033 this._inheritanceManager = new InheritanceManager(definingLibrary); | |
| 10034 } else { | |
| 10035 this._inheritanceManager = inheritanceManager; | |
| 10036 } | |
| 10037 this.elementResolver = new ElementResolver(this); | |
| 10038 if (typeAnalyzerFactory == null) { | |
| 10039 this.typeAnalyzer = new StaticTypeAnalyzer(this); | |
| 10040 } else { | |
| 10041 this.typeAnalyzer = typeAnalyzerFactory(this); | |
| 10042 } | |
| 10043 } | |
| 10044 | |
| 10045 /** | |
| 10046 * Initialize a newly created visitor to resolve the nodes in a compilation un
it. | |
| 10047 * | |
| 10048 * @param library the library containing the compilation unit being resolved | |
| 10049 * @param source the source representing the compilation unit being visited | |
| 10050 * @param typeProvider the object used to access the types from the core libra
ry | |
| 10051 * | |
| 10052 * Deprecated. Please use unnamed constructor instead. | |
| 10053 */ | |
| 10054 @deprecated | |
| 10055 ResolverVisitor.con1( | |
| 10056 Library library, Source source, TypeProvider typeProvider, | |
| 10057 {StaticTypeAnalyzerFactory typeAnalyzerFactory}) | |
| 10058 : this( | |
| 10059 library.libraryElement, source, typeProvider, library.errorListener, | |
| 10060 nameScope: library.libraryScope, | |
| 10061 inheritanceManager: library.inheritanceManager, | |
| 10062 typeAnalyzerFactory: typeAnalyzerFactory); | |
| 10063 | |
| 10064 /** | |
| 10065 * Return the element representing the function containing the current node, o
r `null` if | |
| 10066 * the current node is not contained in a function. | |
| 10067 * | |
| 10068 * @return the element representing the function containing the current node | |
| 10069 */ | |
| 10070 ExecutableElement get enclosingFunction => _enclosingFunction; | |
| 10071 | |
| 10072 /** | |
| 10073 * Return the object keeping track of which elements have had their types over
ridden. | |
| 10074 * | |
| 10075 * @return the object keeping track of which elements have had their types ove
rridden | |
| 10076 */ | |
| 10077 TypeOverrideManager get overrideManager => _overrideManager; | |
| 10078 | |
| 10079 /** | |
| 10080 * Return the object keeping track of which elements have had their types prom
oted. | |
| 10081 * | |
| 10082 * @return the object keeping track of which elements have had their types pro
moted | |
| 10083 */ | |
| 10084 TypePromotionManager get promoteManager => _promoteManager; | |
| 10085 | |
| 10086 /** | |
| 10087 * Return the propagated element associated with the given expression whose ty
pe can be | |
| 10088 * overridden, or `null` if there is no element whose type can be overridden. | |
| 10089 * | |
| 10090 * @param expression the expression with which the element is associated | |
| 10091 * @return the element associated with the given expression | |
| 10092 */ | |
| 10093 VariableElement getOverridablePropagatedElement(Expression expression) { | |
| 10094 Element element = null; | |
| 10095 if (expression is SimpleIdentifier) { | |
| 10096 element = expression.propagatedElement; | |
| 10097 } else if (expression is PrefixedIdentifier) { | |
| 10098 element = expression.propagatedElement; | |
| 10099 } else if (expression is PropertyAccess) { | |
| 10100 element = expression.propertyName.propagatedElement; | |
| 10101 } | |
| 10102 if (element is VariableElement) { | |
| 10103 return element; | |
| 10104 } | |
| 10105 return null; | |
| 10106 } | |
| 10107 | |
| 10108 /** | |
| 10109 * Return the static element associated with the given expression whose type c
an be overridden, or | |
| 10110 * `null` if there is no element whose type can be overridden. | |
| 10111 * | |
| 10112 * @param expression the expression with which the element is associated | |
| 10113 * @return the element associated with the given expression | |
| 10114 */ | |
| 10115 VariableElement getOverridableStaticElement(Expression expression) { | |
| 10116 Element element = null; | |
| 10117 if (expression is SimpleIdentifier) { | |
| 10118 element = expression.staticElement; | |
| 10119 } else if (expression is PrefixedIdentifier) { | |
| 10120 element = expression.staticElement; | |
| 10121 } else if (expression is PropertyAccess) { | |
| 10122 element = expression.propertyName.staticElement; | |
| 10123 } | |
| 10124 if (element is VariableElement) { | |
| 10125 return element; | |
| 10126 } | |
| 10127 return null; | |
| 10128 } | |
| 10129 | |
| 10130 /** | |
| 10131 * Return the static element associated with the given expression whose type c
an be promoted, or | |
| 10132 * `null` if there is no element whose type can be promoted. | |
| 10133 * | |
| 10134 * @param expression the expression with which the element is associated | |
| 10135 * @return the element associated with the given expression | |
| 10136 */ | |
| 10137 VariableElement getPromotionStaticElement(Expression expression) { | |
| 10138 while (expression is ParenthesizedExpression) { | |
| 10139 expression = (expression as ParenthesizedExpression).expression; | |
| 10140 } | |
| 10141 if (expression is! SimpleIdentifier) { | |
| 10142 return null; | |
| 10143 } | |
| 10144 SimpleIdentifier identifier = expression as SimpleIdentifier; | |
| 10145 Element element = identifier.staticElement; | |
| 10146 if (element is! VariableElement) { | |
| 10147 return null; | |
| 10148 } | |
| 10149 ElementKind kind = element.kind; | |
| 10150 if (kind == ElementKind.LOCAL_VARIABLE) { | |
| 10151 return element as VariableElement; | |
| 10152 } | |
| 10153 if (kind == ElementKind.PARAMETER) { | |
| 10154 return element as VariableElement; | |
| 10155 } | |
| 10156 return null; | |
| 10157 } | |
| 10158 | |
| 10159 /** | |
| 10160 * Prepares this [ResolverVisitor] to using it for incremental resolution. | |
| 10161 */ | |
| 10162 void initForIncrementalResolution() { | |
| 10163 _overrideManager.enterScope(); | |
| 10164 } | |
| 10165 | |
| 10166 /** | |
| 10167 * If it is appropriate to do so, override the current type of the static and
propagated elements | |
| 10168 * associated with the given expression with the given type. Generally speakin
g, it is appropriate | |
| 10169 * if the given type is more specific than the current type. | |
| 10170 * | |
| 10171 * @param expression the expression used to access the static and propagated e
lements whose types | |
| 10172 * might be overridden | |
| 10173 * @param potentialType the potential type of the elements | |
| 10174 * @param allowPrecisionLoss see @{code overrideVariable} docs | |
| 10175 */ | |
| 10176 void overrideExpression(Expression expression, DartType potentialType, | |
| 10177 bool allowPrecisionLoss, bool setExpressionType) { | |
| 10178 VariableElement element = getOverridableStaticElement(expression); | |
| 10179 if (element != null) { | |
| 10180 DartType newBestType = | |
| 10181 overrideVariable(element, potentialType, allowPrecisionLoss); | |
| 10182 if (setExpressionType) { | |
| 10183 recordPropagatedTypeIfBetter(expression, newBestType); | |
| 10184 } | |
| 10185 } | |
| 10186 element = getOverridablePropagatedElement(expression); | |
| 10187 if (element != null) { | |
| 10188 overrideVariable(element, potentialType, allowPrecisionLoss); | |
| 10189 } | |
| 10190 } | |
| 10191 | |
| 10192 /** | |
| 10193 * If it is appropriate to do so, override the current type of the given eleme
nt with the given | |
| 10194 * type. | |
| 10195 * | |
| 10196 * @param element the element whose type might be overridden | |
| 10197 * @param potentialType the potential type of the element | |
| 10198 * @param allowPrecisionLoss true if `potentialType` is allowed to be less pre
cise than the | |
| 10199 * current best type | |
| 10200 * | |
| 10201 * Return a new better [DartType], or `null` if [potentialType] is not better | |
| 10202 * than the current [element] type. | |
| 10203 */ | |
| 10204 DartType overrideVariable(VariableElement element, DartType potentialType, | |
| 10205 bool allowPrecisionLoss) { | |
| 10206 if (potentialType == null || potentialType.isBottom) { | |
| 10207 return null; | |
| 10208 } | |
| 10209 DartType currentType = _overrideManager.getBestType(element); | |
| 10210 | |
| 10211 if (potentialType == currentType) { | |
| 10212 return null; | |
| 10213 } | |
| 10214 | |
| 10215 // If we aren't allowing precision loss then the third and fourth conditions | |
| 10216 // check that we aren't losing precision. | |
| 10217 // | |
| 10218 // Let [C] be the current type and [P] be the potential type. When we | |
| 10219 // aren't allowing precision loss -- which is the case for is-checks -- we | |
| 10220 // check that [! (C << P)] or [P << C]. The second check, that [P << C], is | |
| 10221 // analogous to part of the Dart Language Spec rule for type promotion under | |
| 10222 // is-checks (in the analogy [T] is [P] and [S] is [C]): | |
| 10223 // | |
| 10224 // An is-expression of the form [v is T] shows that [v] has type [T] iff | |
| 10225 // [T] is more specific than the type [S] of the expression [v] and both | |
| 10226 // [T != dynamic] and [S != dynamic]. | |
| 10227 // | |
| 10228 // It also covers an important case that is not applicable in the spec: | |
| 10229 // for union types, we want an is-check to promote from an union type to | |
| 10230 // (a subtype of) any of its members. | |
| 10231 // | |
| 10232 // The first check, that [! (C << P)], covers the case where [P] and [C] are | |
| 10233 // unrelated types; This case is not addressed in the spec for static types. | |
| 10234 if (currentType == null || | |
| 10235 allowPrecisionLoss || | |
| 10236 !currentType.isMoreSpecificThan(potentialType) || | |
| 10237 potentialType.isMoreSpecificThan(currentType)) { | |
| 10238 // TODO(scheglov) type propagation for instance/top-level fields | |
| 10239 // was disabled because it depends on the order or visiting. | |
| 10240 // If both field and its client are in the same unit, and we visit | |
| 10241 // the client before the field, then propagated type is not set yet. | |
| 10242 // if (element is PropertyInducingElement) { | |
| 10243 // PropertyInducingElement variable = element; | |
| 10244 // if (!variable.isConst && !variable.isFinal) { | |
| 10245 // return; | |
| 10246 // } | |
| 10247 // (variable as PropertyInducingElementImpl).propagatedType = | |
| 10248 // potentialType; | |
| 10249 // } | |
| 10250 _overrideManager.setType(element, potentialType); | |
| 10251 return potentialType; | |
| 10252 } | |
| 10253 return null; | |
| 10254 } | |
| 10255 | |
| 10256 /** | |
| 10257 * If the given [type] is valid, strongly more specific than the | |
| 10258 * existing static type of the given [expression], record it as a propagated | |
| 10259 * type of the given [expression]. Otherwise, reset it to `null`. | |
| 10260 * | |
| 10261 * If [hasOldPropagatedType] is `true` then the existing propagated type | |
| 10262 * should also is checked. | |
| 10263 */ | |
| 10264 void recordPropagatedTypeIfBetter(Expression expression, DartType type, | |
| 10265 [bool hasOldPropagatedType = false]) { | |
| 10266 // Ensure that propagated type invalid. | |
| 10267 if (type == null || type.isDynamic || type.isBottom) { | |
| 10268 if (!hasOldPropagatedType) { | |
| 10269 expression.propagatedType = null; | |
| 10270 } | |
| 10271 return; | |
| 10272 } | |
| 10273 // Ensure that propagated type is more specific than the static type. | |
| 10274 DartType staticType = expression.staticType; | |
| 10275 if (type == staticType || !type.isMoreSpecificThan(staticType)) { | |
| 10276 expression.propagatedType = null; | |
| 10277 return; | |
| 10278 } | |
| 10279 // Ensure that the new propagated type is more specific than the old one. | |
| 10280 if (hasOldPropagatedType) { | |
| 10281 DartType oldPropagatedType = expression.propagatedType; | |
| 10282 if (oldPropagatedType != null && | |
| 10283 !type.isMoreSpecificThan(oldPropagatedType)) { | |
| 10284 return; | |
| 10285 } | |
| 10286 } | |
| 10287 // OK | |
| 10288 expression.propagatedType = type; | |
| 10289 } | |
| 10290 | |
| 10291 @override | |
| 10292 Object visitAnnotation(Annotation node) { | |
| 10293 AstNode parent = node.parent; | |
| 10294 if (identical(parent, _enclosingClassDeclaration) || | |
| 10295 identical(parent, _enclosingFunctionTypeAlias)) { | |
| 10296 return null; | |
| 10297 } | |
| 10298 return super.visitAnnotation(node); | |
| 10299 } | |
| 10300 | |
| 10301 @override | |
| 10302 Object visitAsExpression(AsExpression node) { | |
| 10303 super.visitAsExpression(node); | |
| 10304 // Since an as-statement doesn't actually change the type, we don't | |
| 10305 // let it affect the propagated type when it would result in a loss | |
| 10306 // of precision. | |
| 10307 overrideExpression(node.expression, node.type.type, false, false); | |
| 10308 return null; | |
| 10309 } | |
| 10310 | |
| 10311 @override | |
| 10312 Object visitAssertStatement(AssertStatement node) { | |
| 10313 super.visitAssertStatement(node); | |
| 10314 _propagateTrueState(node.condition); | |
| 10315 return null; | |
| 10316 } | |
| 10317 | |
| 10318 @override | |
| 10319 Object visitBinaryExpression(BinaryExpression node) { | |
| 10320 sc.TokenType operatorType = node.operator.type; | |
| 10321 Expression leftOperand = node.leftOperand; | |
| 10322 Expression rightOperand = node.rightOperand; | |
| 10323 if (operatorType == sc.TokenType.AMPERSAND_AMPERSAND) { | |
| 10324 safelyVisit(leftOperand); | |
| 10325 if (rightOperand != null) { | |
| 10326 _overrideManager.enterScope(); | |
| 10327 try { | |
| 10328 _promoteManager.enterScope(); | |
| 10329 try { | |
| 10330 _propagateTrueState(leftOperand); | |
| 10331 // Type promotion. | |
| 10332 _promoteTypes(leftOperand); | |
| 10333 _clearTypePromotionsIfPotentiallyMutatedIn(leftOperand); | |
| 10334 _clearTypePromotionsIfPotentiallyMutatedIn(rightOperand); | |
| 10335 _clearTypePromotionsIfAccessedInClosureAndProtentiallyMutated( | |
| 10336 rightOperand); | |
| 10337 // Visit right operand. | |
| 10338 rightOperand.accept(this); | |
| 10339 } finally { | |
| 10340 _promoteManager.exitScope(); | |
| 10341 } | |
| 10342 } finally { | |
| 10343 _overrideManager.exitScope(); | |
| 10344 } | |
| 10345 } | |
| 10346 } else if (operatorType == sc.TokenType.BAR_BAR) { | |
| 10347 safelyVisit(leftOperand); | |
| 10348 if (rightOperand != null) { | |
| 10349 _overrideManager.enterScope(); | |
| 10350 try { | |
| 10351 _propagateFalseState(leftOperand); | |
| 10352 rightOperand.accept(this); | |
| 10353 } finally { | |
| 10354 _overrideManager.exitScope(); | |
| 10355 } | |
| 10356 } | |
| 10357 } else { | |
| 10358 safelyVisit(leftOperand); | |
| 10359 safelyVisit(rightOperand); | |
| 10360 } | |
| 10361 node.accept(elementResolver); | |
| 10362 node.accept(typeAnalyzer); | |
| 10363 return null; | |
| 10364 } | |
| 10365 | |
| 10366 @override | |
| 10367 Object visitBlockFunctionBody(BlockFunctionBody node) { | |
| 10368 safelyVisit(_commentBeforeFunction); | |
| 10369 _overrideManager.enterScope(); | |
| 10370 try { | |
| 10371 super.visitBlockFunctionBody(node); | |
| 10372 } finally { | |
| 10373 _overrideManager.exitScope(); | |
| 10374 } | |
| 10375 return null; | |
| 10376 } | |
| 10377 | |
| 10378 @override | |
| 10379 Object visitBreakStatement(BreakStatement node) { | |
| 10380 // | |
| 10381 // We do not visit the label because it needs to be visited in the context | |
| 10382 // of the statement. | |
| 10383 // | |
| 10384 node.accept(elementResolver); | |
| 10385 node.accept(typeAnalyzer); | |
| 10386 return null; | |
| 10387 } | |
| 10388 | |
| 10389 @override | |
| 10390 Object visitClassDeclaration(ClassDeclaration node) { | |
| 10391 // | |
| 10392 // Resolve the metadata in the library scope. | |
| 10393 // | |
| 10394 if (node.metadata != null) { | |
| 10395 node.metadata.accept(this); | |
| 10396 } | |
| 10397 _enclosingClassDeclaration = node; | |
| 10398 // | |
| 10399 // Continue the class resolution. | |
| 10400 // | |
| 10401 ClassElement outerType = enclosingClass; | |
| 10402 try { | |
| 10403 enclosingClass = node.element; | |
| 10404 typeAnalyzer.thisType = | |
| 10405 enclosingClass == null ? null : enclosingClass.type; | |
| 10406 super.visitClassDeclaration(node); | |
| 10407 node.accept(elementResolver); | |
| 10408 node.accept(typeAnalyzer); | |
| 10409 } finally { | |
| 10410 typeAnalyzer.thisType = outerType == null ? null : outerType.type; | |
| 10411 enclosingClass = outerType; | |
| 10412 _enclosingClassDeclaration = null; | |
| 10413 } | |
| 10414 return null; | |
| 10415 } | |
| 10416 | |
| 10417 /** | |
| 10418 * Implementation of this method should be synchronized with | |
| 10419 * [visitClassDeclaration]. | |
| 10420 */ | |
| 10421 visitClassDeclarationIncrementally(ClassDeclaration node) { | |
| 10422 // | |
| 10423 // Resolve the metadata in the library scope. | |
| 10424 // | |
| 10425 if (node.metadata != null) { | |
| 10426 node.metadata.accept(this); | |
| 10427 } | |
| 10428 _enclosingClassDeclaration = node; | |
| 10429 // | |
| 10430 // Continue the class resolution. | |
| 10431 // | |
| 10432 enclosingClass = node.element; | |
| 10433 typeAnalyzer.thisType = enclosingClass == null ? null : enclosingClass.type; | |
| 10434 node.accept(elementResolver); | |
| 10435 node.accept(typeAnalyzer); | |
| 10436 } | |
| 10437 | |
| 10438 @override | |
| 10439 Object visitComment(Comment node) { | |
| 10440 if (node.parent is FunctionDeclaration || | |
| 10441 node.parent is ConstructorDeclaration || | |
| 10442 node.parent is MethodDeclaration) { | |
| 10443 if (!identical(node, _commentBeforeFunction)) { | |
| 10444 _commentBeforeFunction = node; | |
| 10445 return null; | |
| 10446 } | |
| 10447 } | |
| 10448 super.visitComment(node); | |
| 10449 _commentBeforeFunction = null; | |
| 10450 return null; | |
| 10451 } | |
| 10452 | |
| 10453 @override | |
| 10454 Object visitCommentReference(CommentReference node) { | |
| 10455 // | |
| 10456 // We do not visit the identifier because it needs to be visited in the | |
| 10457 // context of the reference. | |
| 10458 // | |
| 10459 node.accept(elementResolver); | |
| 10460 node.accept(typeAnalyzer); | |
| 10461 return null; | |
| 10462 } | |
| 10463 | |
| 10464 @override | |
| 10465 Object visitCompilationUnit(CompilationUnit node) { | |
| 10466 // | |
| 10467 // TODO(brianwilkerson) The goal of the code below is to visit the | |
| 10468 // declarations in such an order that we can infer type information for | |
| 10469 // top-level variables before we visit references to them. This is better | |
| 10470 // than making no effort, but still doesn't completely satisfy that goal | |
| 10471 // (consider for example "final var a = b; final var b = 0;"; we'll infer a | |
| 10472 // type of 'int' for 'b', but not for 'a' because of the order of the | |
| 10473 // visits). Ideally we would create a dependency graph, but that would | |
| 10474 // require references to be resolved, which they are not. | |
| 10475 // | |
| 10476 _overrideManager.enterScope(); | |
| 10477 try { | |
| 10478 NodeList<Directive> directives = node.directives; | |
| 10479 int directiveCount = directives.length; | |
| 10480 for (int i = 0; i < directiveCount; i++) { | |
| 10481 directives[i].accept(this); | |
| 10482 } | |
| 10483 NodeList<CompilationUnitMember> declarations = node.declarations; | |
| 10484 int declarationCount = declarations.length; | |
| 10485 for (int i = 0; i < declarationCount; i++) { | |
| 10486 CompilationUnitMember declaration = declarations[i]; | |
| 10487 if (declaration is! ClassDeclaration) { | |
| 10488 declaration.accept(this); | |
| 10489 } | |
| 10490 } | |
| 10491 for (int i = 0; i < declarationCount; i++) { | |
| 10492 CompilationUnitMember declaration = declarations[i]; | |
| 10493 if (declaration is ClassDeclaration) { | |
| 10494 declaration.accept(this); | |
| 10495 } | |
| 10496 } | |
| 10497 } finally { | |
| 10498 _overrideManager.exitScope(); | |
| 10499 } | |
| 10500 node.accept(elementResolver); | |
| 10501 node.accept(typeAnalyzer); | |
| 10502 return null; | |
| 10503 } | |
| 10504 | |
| 10505 @override | |
| 10506 Object visitConditionalExpression(ConditionalExpression node) { | |
| 10507 Expression condition = node.condition; | |
| 10508 safelyVisit(condition); | |
| 10509 Expression thenExpression = node.thenExpression; | |
| 10510 if (thenExpression != null) { | |
| 10511 _overrideManager.enterScope(); | |
| 10512 try { | |
| 10513 _promoteManager.enterScope(); | |
| 10514 try { | |
| 10515 _propagateTrueState(condition); | |
| 10516 // Type promotion. | |
| 10517 _promoteTypes(condition); | |
| 10518 _clearTypePromotionsIfPotentiallyMutatedIn(thenExpression); | |
| 10519 _clearTypePromotionsIfAccessedInClosureAndProtentiallyMutated( | |
| 10520 thenExpression); | |
| 10521 // Visit "then" expression. | |
| 10522 thenExpression.accept(this); | |
| 10523 } finally { | |
| 10524 _promoteManager.exitScope(); | |
| 10525 } | |
| 10526 } finally { | |
| 10527 _overrideManager.exitScope(); | |
| 10528 } | |
| 10529 } | |
| 10530 Expression elseExpression = node.elseExpression; | |
| 10531 if (elseExpression != null) { | |
| 10532 _overrideManager.enterScope(); | |
| 10533 try { | |
| 10534 _propagateFalseState(condition); | |
| 10535 elseExpression.accept(this); | |
| 10536 } finally { | |
| 10537 _overrideManager.exitScope(); | |
| 10538 } | |
| 10539 } | |
| 10540 node.accept(elementResolver); | |
| 10541 node.accept(typeAnalyzer); | |
| 10542 bool thenIsAbrupt = _isAbruptTerminationExpression(thenExpression); | |
| 10543 bool elseIsAbrupt = _isAbruptTerminationExpression(elseExpression); | |
| 10544 if (elseIsAbrupt && !thenIsAbrupt) { | |
| 10545 _propagateTrueState(condition); | |
| 10546 _propagateState(thenExpression); | |
| 10547 } else if (thenIsAbrupt && !elseIsAbrupt) { | |
| 10548 _propagateFalseState(condition); | |
| 10549 _propagateState(elseExpression); | |
| 10550 } | |
| 10551 return null; | |
| 10552 } | |
| 10553 | |
| 10554 @override | |
| 10555 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
| 10556 ExecutableElement outerFunction = _enclosingFunction; | |
| 10557 try { | |
| 10558 _enclosingFunction = node.element; | |
| 10559 super.visitConstructorDeclaration(node); | |
| 10560 } finally { | |
| 10561 _enclosingFunction = outerFunction; | |
| 10562 } | |
| 10563 ConstructorElementImpl constructor = node.element; | |
| 10564 constructor.constantInitializers = | |
| 10565 new ConstantAstCloner().cloneNodeList(node.initializers); | |
| 10566 return null; | |
| 10567 } | |
| 10568 | |
| 10569 @override | |
| 10570 Object visitConstructorFieldInitializer(ConstructorFieldInitializer node) { | |
| 10571 // | |
| 10572 // We visit the expression, but do not visit the field name because it needs | |
| 10573 // to be visited in the context of the constructor field initializer node. | |
| 10574 // | |
| 10575 safelyVisit(node.expression); | |
| 10576 node.accept(elementResolver); | |
| 10577 node.accept(typeAnalyzer); | |
| 10578 return null; | |
| 10579 } | |
| 10580 | |
| 10581 @override | |
| 10582 Object visitConstructorName(ConstructorName node) { | |
| 10583 // | |
| 10584 // We do not visit either the type name, because it won't be visited anyway, | |
| 10585 // or the name, because it needs to be visited in the context of the | |
| 10586 // constructor name. | |
| 10587 // | |
| 10588 node.accept(elementResolver); | |
| 10589 node.accept(typeAnalyzer); | |
| 10590 return null; | |
| 10591 } | |
| 10592 | |
| 10593 @override | |
| 10594 Object visitContinueStatement(ContinueStatement node) { | |
| 10595 // | |
| 10596 // We do not visit the label because it needs to be visited in the context | |
| 10597 // of the statement. | |
| 10598 // | |
| 10599 node.accept(elementResolver); | |
| 10600 node.accept(typeAnalyzer); | |
| 10601 return null; | |
| 10602 } | |
| 10603 | |
| 10604 @override | |
| 10605 Object visitDefaultFormalParameter(DefaultFormalParameter node) { | |
| 10606 super.visitDefaultFormalParameter(node); | |
| 10607 FormalParameterList parent = node.parent; | |
| 10608 AstNode grandparent = parent.parent; | |
| 10609 if (grandparent is ConstructorDeclaration && | |
| 10610 grandparent.constKeyword != null) { | |
| 10611 // For const constructors, we need to clone the ASTs for default formal | |
| 10612 // parameters, so that we can use them during constant evaluation. | |
| 10613 ParameterElement element = node.element; | |
| 10614 (element as ConstVariableElement).constantInitializer = | |
| 10615 new ConstantAstCloner().cloneNode(node.defaultValue); | |
| 10616 } | |
| 10617 return null; | |
| 10618 } | |
| 10619 | |
| 10620 @override | |
| 10621 Object visitDoStatement(DoStatement node) { | |
| 10622 _overrideManager.enterScope(); | |
| 10623 try { | |
| 10624 super.visitDoStatement(node); | |
| 10625 } finally { | |
| 10626 _overrideManager.exitScope(); | |
| 10627 } | |
| 10628 // TODO(brianwilkerson) If the loop can only be exited because the condition | |
| 10629 // is false, then propagateFalseState(node.getCondition()); | |
| 10630 return null; | |
| 10631 } | |
| 10632 | |
| 10633 @override | |
| 10634 Object visitEmptyFunctionBody(EmptyFunctionBody node) { | |
| 10635 safelyVisit(_commentBeforeFunction); | |
| 10636 if (resolveOnlyCommentInFunctionBody) { | |
| 10637 return null; | |
| 10638 } | |
| 10639 return super.visitEmptyFunctionBody(node); | |
| 10640 } | |
| 10641 | |
| 10642 @override | |
| 10643 Object visitEnumDeclaration(EnumDeclaration node) { | |
| 10644 // | |
| 10645 // Resolve the metadata in the library scope | |
| 10646 // and associate the annotations with the element. | |
| 10647 // | |
| 10648 if (node.metadata != null) { | |
| 10649 node.metadata.accept(this); | |
| 10650 ElementResolver.setMetadata(node.element, node); | |
| 10651 } | |
| 10652 // | |
| 10653 // There is nothing else to do because everything else was resolved by the | |
| 10654 // element builder. | |
| 10655 // | |
| 10656 return null; | |
| 10657 } | |
| 10658 | |
| 10659 @override | |
| 10660 Object visitExpressionFunctionBody(ExpressionFunctionBody node) { | |
| 10661 safelyVisit(_commentBeforeFunction); | |
| 10662 if (resolveOnlyCommentInFunctionBody) { | |
| 10663 return null; | |
| 10664 } | |
| 10665 _overrideManager.enterScope(); | |
| 10666 try { | |
| 10667 super.visitExpressionFunctionBody(node); | |
| 10668 } finally { | |
| 10669 _overrideManager.exitScope(); | |
| 10670 } | |
| 10671 return null; | |
| 10672 } | |
| 10673 | |
| 10674 @override | |
| 10675 Object visitFieldDeclaration(FieldDeclaration node) { | |
| 10676 _overrideManager.enterScope(); | |
| 10677 try { | |
| 10678 super.visitFieldDeclaration(node); | |
| 10679 } finally { | |
| 10680 Map<VariableElement, DartType> overrides = | |
| 10681 _overrideManager.captureOverrides(node.fields); | |
| 10682 _overrideManager.exitScope(); | |
| 10683 _overrideManager.applyOverrides(overrides); | |
| 10684 } | |
| 10685 return null; | |
| 10686 } | |
| 10687 | |
| 10688 @override | |
| 10689 Object visitForEachStatement(ForEachStatement node) { | |
| 10690 _overrideManager.enterScope(); | |
| 10691 try { | |
| 10692 super.visitForEachStatement(node); | |
| 10693 } finally { | |
| 10694 _overrideManager.exitScope(); | |
| 10695 } | |
| 10696 return null; | |
| 10697 } | |
| 10698 | |
| 10699 @override | |
| 10700 void visitForEachStatementInScope(ForEachStatement node) { | |
| 10701 // | |
| 10702 // We visit the iterator before the loop variable because the loop variable | |
| 10703 // cannot be in scope while visiting the iterator. | |
| 10704 // | |
| 10705 Expression iterable = node.iterable; | |
| 10706 safelyVisit(iterable); | |
| 10707 DeclaredIdentifier loopVariable = node.loopVariable; | |
| 10708 SimpleIdentifier identifier = node.identifier; | |
| 10709 safelyVisit(loopVariable); | |
| 10710 safelyVisit(identifier); | |
| 10711 Statement body = node.body; | |
| 10712 if (body != null) { | |
| 10713 _overrideManager.enterScope(); | |
| 10714 try { | |
| 10715 if (loopVariable != null && iterable != null) { | |
| 10716 LocalVariableElement loopElement = loopVariable.element; | |
| 10717 if (loopElement != null) { | |
| 10718 DartType iteratorElementType = _getIteratorElementType(iterable); | |
| 10719 overrideVariable(loopElement, iteratorElementType, true); | |
| 10720 _recordPropagatedType(loopVariable.identifier, iteratorElementType); | |
| 10721 } | |
| 10722 } else if (identifier != null && iterable != null) { | |
| 10723 Element identifierElement = identifier.staticElement; | |
| 10724 if (identifierElement is VariableElement) { | |
| 10725 DartType iteratorElementType = _getIteratorElementType(iterable); | |
| 10726 overrideVariable(identifierElement, iteratorElementType, true); | |
| 10727 _recordPropagatedType(identifier, iteratorElementType); | |
| 10728 } | |
| 10729 } | |
| 10730 visitStatementInScope(body); | |
| 10731 } finally { | |
| 10732 _overrideManager.exitScope(); | |
| 10733 } | |
| 10734 } | |
| 10735 node.accept(elementResolver); | |
| 10736 node.accept(typeAnalyzer); | |
| 10737 } | |
| 10738 | |
| 10739 @override | |
| 10740 Object visitForStatement(ForStatement node) { | |
| 10741 _overrideManager.enterScope(); | |
| 10742 try { | |
| 10743 super.visitForStatement(node); | |
| 10744 } finally { | |
| 10745 _overrideManager.exitScope(); | |
| 10746 } | |
| 10747 return null; | |
| 10748 } | |
| 10749 | |
| 10750 @override | |
| 10751 void visitForStatementInScope(ForStatement node) { | |
| 10752 safelyVisit(node.variables); | |
| 10753 safelyVisit(node.initialization); | |
| 10754 safelyVisit(node.condition); | |
| 10755 _overrideManager.enterScope(); | |
| 10756 try { | |
| 10757 _propagateTrueState(node.condition); | |
| 10758 visitStatementInScope(node.body); | |
| 10759 node.updaters.accept(this); | |
| 10760 } finally { | |
| 10761 _overrideManager.exitScope(); | |
| 10762 } | |
| 10763 // TODO(brianwilkerson) If the loop can only be exited because the condition | |
| 10764 // is false, then propagateFalseState(condition); | |
| 10765 } | |
| 10766 | |
| 10767 @override | |
| 10768 Object visitFunctionDeclaration(FunctionDeclaration node) { | |
| 10769 ExecutableElement outerFunction = _enclosingFunction; | |
| 10770 try { | |
| 10771 SimpleIdentifier functionName = node.name; | |
| 10772 _enclosingFunction = functionName.staticElement as ExecutableElement; | |
| 10773 super.visitFunctionDeclaration(node); | |
| 10774 } finally { | |
| 10775 _enclosingFunction = outerFunction; | |
| 10776 } | |
| 10777 return null; | |
| 10778 } | |
| 10779 | |
| 10780 @override | |
| 10781 Object visitFunctionExpression(FunctionExpression node) { | |
| 10782 ExecutableElement outerFunction = _enclosingFunction; | |
| 10783 try { | |
| 10784 _enclosingFunction = node.element; | |
| 10785 _overrideManager.enterScope(); | |
| 10786 try { | |
| 10787 super.visitFunctionExpression(node); | |
| 10788 } finally { | |
| 10789 _overrideManager.exitScope(); | |
| 10790 } | |
| 10791 } finally { | |
| 10792 _enclosingFunction = outerFunction; | |
| 10793 } | |
| 10794 return null; | |
| 10795 } | |
| 10796 | |
| 10797 @override | |
| 10798 Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { | |
| 10799 safelyVisit(node.function); | |
| 10800 node.accept(elementResolver); | |
| 10801 _inferFunctionExpressionsParametersTypes(node.argumentList); | |
| 10802 safelyVisit(node.argumentList); | |
| 10803 node.accept(typeAnalyzer); | |
| 10804 return null; | |
| 10805 } | |
| 10806 | |
| 10807 @override | |
| 10808 Object visitFunctionTypeAlias(FunctionTypeAlias node) { | |
| 10809 // Resolve the metadata in the library scope. | |
| 10810 if (node.metadata != null) { | |
| 10811 node.metadata.accept(this); | |
| 10812 } | |
| 10813 FunctionTypeAlias outerAlias = _enclosingFunctionTypeAlias; | |
| 10814 _enclosingFunctionTypeAlias = node; | |
| 10815 try { | |
| 10816 super.visitFunctionTypeAlias(node); | |
| 10817 } finally { | |
| 10818 _enclosingFunctionTypeAlias = outerAlias; | |
| 10819 } | |
| 10820 return null; | |
| 10821 } | |
| 10822 | |
| 10823 @override | |
| 10824 Object visitHideCombinator(HideCombinator node) => null; | |
| 10825 | |
| 10826 @override | |
| 10827 Object visitIfStatement(IfStatement node) { | |
| 10828 Expression condition = node.condition; | |
| 10829 safelyVisit(condition); | |
| 10830 Map<VariableElement, DartType> thenOverrides = | |
| 10831 new HashMap<VariableElement, DartType>(); | |
| 10832 Statement thenStatement = node.thenStatement; | |
| 10833 if (thenStatement != null) { | |
| 10834 _overrideManager.enterScope(); | |
| 10835 try { | |
| 10836 _promoteManager.enterScope(); | |
| 10837 try { | |
| 10838 _propagateTrueState(condition); | |
| 10839 // Type promotion. | |
| 10840 _promoteTypes(condition); | |
| 10841 _clearTypePromotionsIfPotentiallyMutatedIn(thenStatement); | |
| 10842 _clearTypePromotionsIfAccessedInClosureAndProtentiallyMutated( | |
| 10843 thenStatement); | |
| 10844 // Visit "then". | |
| 10845 visitStatementInScope(thenStatement); | |
| 10846 } finally { | |
| 10847 _promoteManager.exitScope(); | |
| 10848 } | |
| 10849 } finally { | |
| 10850 thenOverrides = _overrideManager.captureLocalOverrides(); | |
| 10851 _overrideManager.exitScope(); | |
| 10852 } | |
| 10853 } | |
| 10854 Map<VariableElement, DartType> elseOverrides = | |
| 10855 new HashMap<VariableElement, DartType>(); | |
| 10856 Statement elseStatement = node.elseStatement; | |
| 10857 if (elseStatement != null) { | |
| 10858 _overrideManager.enterScope(); | |
| 10859 try { | |
| 10860 _propagateFalseState(condition); | |
| 10861 visitStatementInScope(elseStatement); | |
| 10862 } finally { | |
| 10863 elseOverrides = _overrideManager.captureLocalOverrides(); | |
| 10864 _overrideManager.exitScope(); | |
| 10865 } | |
| 10866 } | |
| 10867 node.accept(elementResolver); | |
| 10868 node.accept(typeAnalyzer); | |
| 10869 // Join overrides. | |
| 10870 bool thenIsAbrupt = _isAbruptTerminationStatement(thenStatement); | |
| 10871 bool elseIsAbrupt = _isAbruptTerminationStatement(elseStatement); | |
| 10872 if (elseIsAbrupt && !thenIsAbrupt) { | |
| 10873 _propagateTrueState(condition); | |
| 10874 _overrideManager.applyOverrides(thenOverrides); | |
| 10875 } else if (thenIsAbrupt && !elseIsAbrupt) { | |
| 10876 _propagateFalseState(condition); | |
| 10877 _overrideManager.applyOverrides(elseOverrides); | |
| 10878 } else if (!thenIsAbrupt && !elseIsAbrupt) { | |
| 10879 List<Map<VariableElement, DartType>> perBranchOverrides = | |
| 10880 new List<Map<VariableElement, DartType>>(); | |
| 10881 perBranchOverrides.add(thenOverrides); | |
| 10882 perBranchOverrides.add(elseOverrides); | |
| 10883 _overrideManager.mergeOverrides(perBranchOverrides); | |
| 10884 } | |
| 10885 return null; | |
| 10886 } | |
| 10887 | |
| 10888 @override | |
| 10889 Object visitLabel(Label node) => null; | |
| 10890 | |
| 10891 @override | |
| 10892 Object visitLibraryIdentifier(LibraryIdentifier node) => null; | |
| 10893 | |
| 10894 @override | |
| 10895 Object visitMethodDeclaration(MethodDeclaration node) { | |
| 10896 ExecutableElement outerFunction = _enclosingFunction; | |
| 10897 try { | |
| 10898 _enclosingFunction = node.element; | |
| 10899 super.visitMethodDeclaration(node); | |
| 10900 } finally { | |
| 10901 _enclosingFunction = outerFunction; | |
| 10902 } | |
| 10903 return null; | |
| 10904 } | |
| 10905 | |
| 10906 @override | |
| 10907 Object visitMethodInvocation(MethodInvocation node) { | |
| 10908 // | |
| 10909 // We visit the target and argument list, but do not visit the method name | |
| 10910 // because it needs to be visited in the context of the invocation. | |
| 10911 // | |
| 10912 safelyVisit(node.target); | |
| 10913 node.accept(elementResolver); | |
| 10914 _inferFunctionExpressionsParametersTypes(node.argumentList); | |
| 10915 safelyVisit(node.argumentList); | |
| 10916 node.accept(typeAnalyzer); | |
| 10917 return null; | |
| 10918 } | |
| 10919 | |
| 10920 @override | |
| 10921 Object visitNode(AstNode node) { | |
| 10922 node.visitChildren(this); | |
| 10923 node.accept(elementResolver); | |
| 10924 node.accept(typeAnalyzer); | |
| 10925 return null; | |
| 10926 } | |
| 10927 | |
| 10928 @override | |
| 10929 Object visitPrefixedIdentifier(PrefixedIdentifier node) { | |
| 10930 // | |
| 10931 // We visit the prefix, but do not visit the identifier because it needs to | |
| 10932 // be visited in the context of the prefix. | |
| 10933 // | |
| 10934 safelyVisit(node.prefix); | |
| 10935 node.accept(elementResolver); | |
| 10936 node.accept(typeAnalyzer); | |
| 10937 return null; | |
| 10938 } | |
| 10939 | |
| 10940 @override | |
| 10941 Object visitPropertyAccess(PropertyAccess node) { | |
| 10942 // | |
| 10943 // We visit the target, but do not visit the property name because it needs | |
| 10944 // to be visited in the context of the property access node. | |
| 10945 // | |
| 10946 safelyVisit(node.target); | |
| 10947 node.accept(elementResolver); | |
| 10948 node.accept(typeAnalyzer); | |
| 10949 return null; | |
| 10950 } | |
| 10951 | |
| 10952 @override | |
| 10953 Object visitRedirectingConstructorInvocation( | |
| 10954 RedirectingConstructorInvocation node) { | |
| 10955 // | |
| 10956 // We visit the argument list, but do not visit the optional identifier | |
| 10957 // because it needs to be visited in the context of the constructor | |
| 10958 // invocation. | |
| 10959 // | |
| 10960 safelyVisit(node.argumentList); | |
| 10961 node.accept(elementResolver); | |
| 10962 node.accept(typeAnalyzer); | |
| 10963 return null; | |
| 10964 } | |
| 10965 | |
| 10966 @override | |
| 10967 Object visitShowCombinator(ShowCombinator node) => null; | |
| 10968 | |
| 10969 @override | |
| 10970 Object visitSuperConstructorInvocation(SuperConstructorInvocation node) { | |
| 10971 // | |
| 10972 // We visit the argument list, but do not visit the optional identifier | |
| 10973 // because it needs to be visited in the context of the constructor | |
| 10974 // invocation. | |
| 10975 // | |
| 10976 safelyVisit(node.argumentList); | |
| 10977 node.accept(elementResolver); | |
| 10978 node.accept(typeAnalyzer); | |
| 10979 return null; | |
| 10980 } | |
| 10981 | |
| 10982 @override | |
| 10983 Object visitSwitchCase(SwitchCase node) { | |
| 10984 _overrideManager.enterScope(); | |
| 10985 try { | |
| 10986 super.visitSwitchCase(node); | |
| 10987 } finally { | |
| 10988 _overrideManager.exitScope(); | |
| 10989 } | |
| 10990 return null; | |
| 10991 } | |
| 10992 | |
| 10993 @override | |
| 10994 Object visitSwitchDefault(SwitchDefault node) { | |
| 10995 _overrideManager.enterScope(); | |
| 10996 try { | |
| 10997 super.visitSwitchDefault(node); | |
| 10998 } finally { | |
| 10999 _overrideManager.exitScope(); | |
| 11000 } | |
| 11001 return null; | |
| 11002 } | |
| 11003 | |
| 11004 @override | |
| 11005 Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | |
| 11006 _overrideManager.enterScope(); | |
| 11007 try { | |
| 11008 super.visitTopLevelVariableDeclaration(node); | |
| 11009 } finally { | |
| 11010 Map<VariableElement, DartType> overrides = | |
| 11011 _overrideManager.captureOverrides(node.variables); | |
| 11012 _overrideManager.exitScope(); | |
| 11013 _overrideManager.applyOverrides(overrides); | |
| 11014 } | |
| 11015 return null; | |
| 11016 } | |
| 11017 | |
| 11018 @override | |
| 11019 Object visitTypeName(TypeName node) => null; | |
| 11020 | |
| 11021 @override | |
| 11022 Object visitVariableDeclaration(VariableDeclaration node) { | |
| 11023 super.visitVariableDeclaration(node); | |
| 11024 VariableElement element = node.element; | |
| 11025 // Note: in addition to cloning the initializers for const variables, we | |
| 11026 // have to clone the initializers for non-static final fields (because if | |
| 11027 // they occur in a class with a const constructor, they will be needed to | |
| 11028 // evaluate the const constructor). | |
| 11029 if ((element.isConst || | |
| 11030 (element is FieldElement && | |
| 11031 element.isFinal && | |
| 11032 !element.isStatic)) && | |
| 11033 node.initializer != null) { | |
| 11034 (element as ConstVariableElement).constantInitializer = | |
| 11035 new ConstantAstCloner().cloneNode(node.initializer); | |
| 11036 } | |
| 11037 return null; | |
| 11038 } | |
| 11039 | |
| 11040 @override | |
| 11041 Object visitWhileStatement(WhileStatement node) { | |
| 11042 // Note: since we don't call the base class, we have to maintain | |
| 11043 // _implicitLabelScope ourselves. | |
| 11044 ImplicitLabelScope outerImplicitScope = _implicitLabelScope; | |
| 11045 try { | |
| 11046 _implicitLabelScope = _implicitLabelScope.nest(node); | |
| 11047 Expression condition = node.condition; | |
| 11048 safelyVisit(condition); | |
| 11049 Statement body = node.body; | |
| 11050 if (body != null) { | |
| 11051 _overrideManager.enterScope(); | |
| 11052 try { | |
| 11053 _propagateTrueState(condition); | |
| 11054 visitStatementInScope(body); | |
| 11055 } finally { | |
| 11056 _overrideManager.exitScope(); | |
| 11057 } | |
| 11058 } | |
| 11059 } finally { | |
| 11060 _implicitLabelScope = outerImplicitScope; | |
| 11061 } | |
| 11062 // TODO(brianwilkerson) If the loop can only be exited because the condition | |
| 11063 // is false, then propagateFalseState(condition); | |
| 11064 node.accept(elementResolver); | |
| 11065 node.accept(typeAnalyzer); | |
| 11066 return null; | |
| 11067 } | |
| 11068 | |
| 11069 /** | |
| 11070 * Checks each promoted variable in the current scope for compliance with the
following | |
| 11071 * specification statement: | |
| 11072 * | |
| 11073 * If the variable <i>v</i> is accessed by a closure in <i>s<sub>1</sub></i> t
hen the variable | |
| 11074 * <i>v</i> is not potentially mutated anywhere in the scope of <i>v</i>. | |
| 11075 */ | |
| 11076 void _clearTypePromotionsIfAccessedInClosureAndProtentiallyMutated( | |
| 11077 AstNode target) { | |
| 11078 for (Element element in _promoteManager.promotedElements) { | |
| 11079 if ((element as VariableElementImpl).isPotentiallyMutatedInScope) { | |
| 11080 if (_isVariableAccessedInClosure(element, target)) { | |
| 11081 _promoteManager.setType(element, null); | |
| 11082 } | |
| 11083 } | |
| 11084 } | |
| 11085 } | |
| 11086 | |
| 11087 /** | |
| 11088 * Checks each promoted variable in the current scope for compliance with the
following | |
| 11089 * specification statement: | |
| 11090 * | |
| 11091 * <i>v</i> is not potentially mutated in <i>s<sub>1</sub></i> or within a clo
sure. | |
| 11092 */ | |
| 11093 void _clearTypePromotionsIfPotentiallyMutatedIn(AstNode target) { | |
| 11094 for (Element element in _promoteManager.promotedElements) { | |
| 11095 if (_isVariablePotentiallyMutatedIn(element, target)) { | |
| 11096 _promoteManager.setType(element, null); | |
| 11097 } | |
| 11098 } | |
| 11099 } | |
| 11100 | |
| 11101 /** | |
| 11102 * The given expression is the expression used to compute the iterator for a f
or-each statement. | |
| 11103 * Attempt to compute the type of objects that will be assigned to the loop va
riable and return | |
| 11104 * that type. Return `null` if the type could not be determined. | |
| 11105 * | |
| 11106 * @param iterator the iterator for a for-each statement | |
| 11107 * @return the type of objects that will be assigned to the loop variable | |
| 11108 */ | |
| 11109 DartType _getIteratorElementType(Expression iteratorExpression) { | |
| 11110 DartType expressionType = iteratorExpression.bestType; | |
| 11111 if (expressionType is InterfaceType) { | |
| 11112 InterfaceType interfaceType = expressionType; | |
| 11113 FunctionType iteratorFunction = | |
| 11114 _inheritanceManager.lookupMemberType(interfaceType, "iterator"); | |
| 11115 if (iteratorFunction == null) { | |
| 11116 // TODO(brianwilkerson) Should we report this error? | |
| 11117 return null; | |
| 11118 } | |
| 11119 DartType iteratorType = iteratorFunction.returnType; | |
| 11120 if (iteratorType is InterfaceType) { | |
| 11121 InterfaceType iteratorInterfaceType = iteratorType; | |
| 11122 FunctionType currentFunction = _inheritanceManager.lookupMemberType( | |
| 11123 iteratorInterfaceType, "current"); | |
| 11124 if (currentFunction == null) { | |
| 11125 // TODO(brianwilkerson) Should we report this error? | |
| 11126 return null; | |
| 11127 } | |
| 11128 return currentFunction.returnType; | |
| 11129 } | |
| 11130 } | |
| 11131 return null; | |
| 11132 } | |
| 11133 | |
| 11134 /** | |
| 11135 * If given "mayBeClosure" is [FunctionExpression] without explicit parameters
types and its | |
| 11136 * required type is [FunctionType], then infer parameters types from [Function
Type]. | |
| 11137 */ | |
| 11138 void _inferFunctionExpressionParametersTypes( | |
| 11139 Expression mayBeClosure, DartType mayByFunctionType) { | |
| 11140 // prepare closure | |
| 11141 if (mayBeClosure is! FunctionExpression) { | |
| 11142 return; | |
| 11143 } | |
| 11144 FunctionExpression closure = mayBeClosure as FunctionExpression; | |
| 11145 // prepare expected closure type | |
| 11146 if (mayByFunctionType is! FunctionType) { | |
| 11147 return; | |
| 11148 } | |
| 11149 FunctionType expectedClosureType = mayByFunctionType as FunctionType; | |
| 11150 // If the expectedClosureType is not more specific than the static type, | |
| 11151 // return. | |
| 11152 DartType staticClosureType = | |
| 11153 (closure.element != null ? closure.element.type : null) as DartType; | |
| 11154 if (staticClosureType != null && | |
| 11155 !expectedClosureType.isMoreSpecificThan(staticClosureType)) { | |
| 11156 return; | |
| 11157 } | |
| 11158 // set propagated type for the closure | |
| 11159 closure.propagatedType = expectedClosureType; | |
| 11160 // set inferred types for parameters | |
| 11161 NodeList<FormalParameter> parameters = closure.parameters.parameters; | |
| 11162 List<ParameterElement> expectedParameters = expectedClosureType.parameters; | |
| 11163 for (int i = 0; | |
| 11164 i < parameters.length && i < expectedParameters.length; | |
| 11165 i++) { | |
| 11166 FormalParameter parameter = parameters[i]; | |
| 11167 ParameterElement element = parameter.element; | |
| 11168 DartType currentType = _overrideManager.getBestType(element); | |
| 11169 // may be override the type | |
| 11170 DartType expectedType = expectedParameters[i].type; | |
| 11171 if (currentType == null || expectedType.isMoreSpecificThan(currentType)) { | |
| 11172 _overrideManager.setType(element, expectedType); | |
| 11173 } | |
| 11174 } | |
| 11175 } | |
| 11176 | |
| 11177 /** | |
| 11178 * Try to infer types of parameters of the [FunctionExpression] arguments. | |
| 11179 */ | |
| 11180 void _inferFunctionExpressionsParametersTypes(ArgumentList argumentList) { | |
| 11181 for (Expression argument in argumentList.arguments) { | |
| 11182 ParameterElement parameter = argument.propagatedParameterElement; | |
| 11183 if (parameter == null) { | |
| 11184 parameter = argument.staticParameterElement; | |
| 11185 } | |
| 11186 if (parameter != null) { | |
| 11187 _inferFunctionExpressionParametersTypes(argument, parameter.type); | |
| 11188 } | |
| 11189 } | |
| 11190 } | |
| 11191 | |
| 11192 /** | |
| 11193 * Return `true` if the given expression terminates abruptly (that is, if any
expression | |
| 11194 * following the given expression will not be reached). | |
| 11195 * | |
| 11196 * @param expression the expression being tested | |
| 11197 * @return `true` if the given expression terminates abruptly | |
| 11198 */ | |
| 11199 bool _isAbruptTerminationExpression(Expression expression) { | |
| 11200 // TODO(brianwilkerson) This needs to be significantly improved. Ideally we | |
| 11201 // would eventually turn this into a method on Expression that returns a | |
| 11202 // termination indication (normal, abrupt with no exception, abrupt with an | |
| 11203 // exception). | |
| 11204 while (expression is ParenthesizedExpression) { | |
| 11205 expression = (expression as ParenthesizedExpression).expression; | |
| 11206 } | |
| 11207 return expression is ThrowExpression || expression is RethrowExpression; | |
| 11208 } | |
| 11209 | |
| 11210 /** | |
| 11211 * Return `true` if the given statement terminates abruptly (that is, if any s
tatement | |
| 11212 * following the given statement will not be reached). | |
| 11213 * | |
| 11214 * @param statement the statement being tested | |
| 11215 * @return `true` if the given statement terminates abruptly | |
| 11216 */ | |
| 11217 bool _isAbruptTerminationStatement(Statement statement) { | |
| 11218 // TODO(brianwilkerson) This needs to be significantly improved. Ideally we | |
| 11219 // would eventually turn this into a method on Statement that returns a | |
| 11220 // termination indication (normal, abrupt with no exception, abrupt with an | |
| 11221 // exception). | |
| 11222 // | |
| 11223 // collinsn: it is unsound to assume that [break] and [continue] are | |
| 11224 // "abrupt". See: https://code.google.com/p/dart/issues/detail?id=19929#c4 | |
| 11225 // (tests are included in TypePropagationTest.java). | |
| 11226 // In general, the difficulty is loopy control flow. | |
| 11227 // | |
| 11228 // In the presence of exceptions things become much more complicated, but | |
| 11229 // while we only use this to propagate at [if]-statement join points, | |
| 11230 // checking for [return] may work well enough in the common case. | |
| 11231 if (statement is ReturnStatement) { | |
| 11232 return true; | |
| 11233 } else if (statement is ExpressionStatement) { | |
| 11234 return _isAbruptTerminationExpression(statement.expression); | |
| 11235 } else if (statement is Block) { | |
| 11236 NodeList<Statement> statements = statement.statements; | |
| 11237 int size = statements.length; | |
| 11238 if (size == 0) { | |
| 11239 return false; | |
| 11240 } | |
| 11241 | |
| 11242 // This last-statement-is-return heuristic is unsound for adversarial | |
| 11243 // code, but probably works well in the common case: | |
| 11244 // | |
| 11245 // var x = 123; | |
| 11246 // var c = true; | |
| 11247 // L: if (c) { | |
| 11248 // x = "hello"; | |
| 11249 // c = false; | |
| 11250 // break L; | |
| 11251 // return; | |
| 11252 // } | |
| 11253 // print(x); | |
| 11254 // | |
| 11255 // Unsound to assume that [x = "hello";] never executed after the | |
| 11256 // if-statement. Of course, a dead-code analysis could point out that | |
| 11257 // [return] here is dead. | |
| 11258 return _isAbruptTerminationStatement(statements[size - 1]); | |
| 11259 } | |
| 11260 return false; | |
| 11261 } | |
| 11262 | |
| 11263 /** | |
| 11264 * Return `true` if the given variable is accessed within a closure in the giv
en | |
| 11265 * [AstNode] and also mutated somewhere in variable scope. This information is
only | |
| 11266 * available for local variables (including parameters). | |
| 11267 * | |
| 11268 * @param variable the variable to check | |
| 11269 * @param target the [AstNode] to check within | |
| 11270 * @return `true` if this variable is potentially mutated somewhere in the giv
en ASTNode | |
| 11271 */ | |
| 11272 bool _isVariableAccessedInClosure(Element variable, AstNode target) { | |
| 11273 _ResolverVisitor_isVariableAccessedInClosure visitor = | |
| 11274 new _ResolverVisitor_isVariableAccessedInClosure(variable); | |
| 11275 target.accept(visitor); | |
| 11276 return visitor.result; | |
| 11277 } | |
| 11278 | |
| 11279 /** | |
| 11280 * Return `true` if the given variable is potentially mutated somewhere in the
given | |
| 11281 * [AstNode]. This information is only available for local variables (includin
g parameters). | |
| 11282 * | |
| 11283 * @param variable the variable to check | |
| 11284 * @param target the [AstNode] to check within | |
| 11285 * @return `true` if this variable is potentially mutated somewhere in the giv
en ASTNode | |
| 11286 */ | |
| 11287 bool _isVariablePotentiallyMutatedIn(Element variable, AstNode target) { | |
| 11288 _ResolverVisitor_isVariablePotentiallyMutatedIn visitor = | |
| 11289 new _ResolverVisitor_isVariablePotentiallyMutatedIn(variable); | |
| 11290 target.accept(visitor); | |
| 11291 return visitor.result; | |
| 11292 } | |
| 11293 | |
| 11294 /** | |
| 11295 * If it is appropriate to do so, promotes the current type of the static elem
ent associated with | |
| 11296 * the given expression with the given type. Generally speaking, it is appropr
iate if the given | |
| 11297 * type is more specific than the current type. | |
| 11298 * | |
| 11299 * @param expression the expression used to access the static element whose ty
pes might be | |
| 11300 * promoted | |
| 11301 * @param potentialType the potential type of the elements | |
| 11302 */ | |
| 11303 void _promote(Expression expression, DartType potentialType) { | |
| 11304 VariableElement element = getPromotionStaticElement(expression); | |
| 11305 if (element != null) { | |
| 11306 // may be mutated somewhere in closure | |
| 11307 if (element.isPotentiallyMutatedInClosure) { | |
| 11308 return; | |
| 11309 } | |
| 11310 // prepare current variable type | |
| 11311 DartType type = _promoteManager.getType(element); | |
| 11312 if (type == null) { | |
| 11313 type = expression.staticType; | |
| 11314 } | |
| 11315 // Declared type should not be "dynamic". | |
| 11316 if (type == null || type.isDynamic) { | |
| 11317 return; | |
| 11318 } | |
| 11319 // Promoted type should not be "dynamic". | |
| 11320 if (potentialType == null || potentialType.isDynamic) { | |
| 11321 return; | |
| 11322 } | |
| 11323 // Promoted type should be more specific than declared. | |
| 11324 if (!potentialType.isMoreSpecificThan(type)) { | |
| 11325 return; | |
| 11326 } | |
| 11327 // Do promote type of variable. | |
| 11328 _promoteManager.setType(element, potentialType); | |
| 11329 } | |
| 11330 } | |
| 11331 | |
| 11332 /** | |
| 11333 * Promotes type information using given condition. | |
| 11334 */ | |
| 11335 void _promoteTypes(Expression condition) { | |
| 11336 if (condition is BinaryExpression) { | |
| 11337 BinaryExpression binary = condition; | |
| 11338 if (binary.operator.type == sc.TokenType.AMPERSAND_AMPERSAND) { | |
| 11339 Expression left = binary.leftOperand; | |
| 11340 Expression right = binary.rightOperand; | |
| 11341 _promoteTypes(left); | |
| 11342 _promoteTypes(right); | |
| 11343 _clearTypePromotionsIfPotentiallyMutatedIn(right); | |
| 11344 } | |
| 11345 } else if (condition is IsExpression) { | |
| 11346 IsExpression is2 = condition; | |
| 11347 if (is2.notOperator == null) { | |
| 11348 _promote(is2.expression, is2.type.type); | |
| 11349 } | |
| 11350 } else if (condition is ParenthesizedExpression) { | |
| 11351 _promoteTypes(condition.expression); | |
| 11352 } | |
| 11353 } | |
| 11354 | |
| 11355 /** | |
| 11356 * Propagate any type information that results from knowing that the given con
dition will have | |
| 11357 * been evaluated to 'false'. | |
| 11358 * | |
| 11359 * @param condition the condition that will have evaluated to 'false' | |
| 11360 */ | |
| 11361 void _propagateFalseState(Expression condition) { | |
| 11362 if (condition is BinaryExpression) { | |
| 11363 BinaryExpression binary = condition; | |
| 11364 if (binary.operator.type == sc.TokenType.BAR_BAR) { | |
| 11365 _propagateFalseState(binary.leftOperand); | |
| 11366 _propagateFalseState(binary.rightOperand); | |
| 11367 } | |
| 11368 } else if (condition is IsExpression) { | |
| 11369 IsExpression is2 = condition; | |
| 11370 if (is2.notOperator != null) { | |
| 11371 // Since an is-statement doesn't actually change the type, we don't | |
| 11372 // let it affect the propagated type when it would result in a loss | |
| 11373 // of precision. | |
| 11374 overrideExpression(is2.expression, is2.type.type, false, false); | |
| 11375 } | |
| 11376 } else if (condition is PrefixExpression) { | |
| 11377 PrefixExpression prefix = condition; | |
| 11378 if (prefix.operator.type == sc.TokenType.BANG) { | |
| 11379 _propagateTrueState(prefix.operand); | |
| 11380 } | |
| 11381 } else if (condition is ParenthesizedExpression) { | |
| 11382 _propagateFalseState(condition.expression); | |
| 11383 } | |
| 11384 } | |
| 11385 | |
| 11386 /** | |
| 11387 * Propagate any type information that results from knowing that the given exp
ression will have | |
| 11388 * been evaluated without altering the flow of execution. | |
| 11389 * | |
| 11390 * @param expression the expression that will have been evaluated | |
| 11391 */ | |
| 11392 void _propagateState(Expression expression) { | |
| 11393 // TODO(brianwilkerson) Implement this. | |
| 11394 } | |
| 11395 | |
| 11396 /** | |
| 11397 * Propagate any type information that results from knowing that the given con
dition will have | |
| 11398 * been evaluated to 'true'. | |
| 11399 * | |
| 11400 * @param condition the condition that will have evaluated to 'true' | |
| 11401 */ | |
| 11402 void _propagateTrueState(Expression condition) { | |
| 11403 if (condition is BinaryExpression) { | |
| 11404 BinaryExpression binary = condition; | |
| 11405 if (binary.operator.type == sc.TokenType.AMPERSAND_AMPERSAND) { | |
| 11406 _propagateTrueState(binary.leftOperand); | |
| 11407 _propagateTrueState(binary.rightOperand); | |
| 11408 } | |
| 11409 } else if (condition is IsExpression) { | |
| 11410 IsExpression is2 = condition; | |
| 11411 if (is2.notOperator == null) { | |
| 11412 // Since an is-statement doesn't actually change the type, we don't | |
| 11413 // let it affect the propagated type when it would result in a loss | |
| 11414 // of precision. | |
| 11415 overrideExpression(is2.expression, is2.type.type, false, false); | |
| 11416 } | |
| 11417 } else if (condition is PrefixExpression) { | |
| 11418 PrefixExpression prefix = condition; | |
| 11419 if (prefix.operator.type == sc.TokenType.BANG) { | |
| 11420 _propagateFalseState(prefix.operand); | |
| 11421 } | |
| 11422 } else if (condition is ParenthesizedExpression) { | |
| 11423 _propagateTrueState(condition.expression); | |
| 11424 } | |
| 11425 } | |
| 11426 | |
| 11427 /** | |
| 11428 * Record that the propagated type of the given node is the given type. | |
| 11429 * | |
| 11430 * @param expression the node whose type is to be recorded | |
| 11431 * @param type the propagated type of the node | |
| 11432 */ | |
| 11433 void _recordPropagatedType(Expression expression, DartType type) { | |
| 11434 if (type != null && !type.isDynamic) { | |
| 11435 expression.propagatedType = type; | |
| 11436 } | |
| 11437 } | |
| 11438 } | |
| 11439 | |
| 11440 /** | |
| 11441 * The abstract class `Scope` defines the behavior common to name scopes used by
the resolver | |
| 11442 * to determine which names are visible at any given point in the code. | |
| 11443 */ | |
| 11444 abstract class Scope { | |
| 11445 /** | |
| 11446 * The prefix used to mark an identifier as being private to its library. | |
| 11447 */ | |
| 11448 static int PRIVATE_NAME_PREFIX = 0x5F; | |
| 11449 | |
| 11450 /** | |
| 11451 * The suffix added to the declared name of a setter when looking up the sette
r. Used to | |
| 11452 * disambiguate between a getter and a setter that have the same name. | |
| 11453 */ | |
| 11454 static String SETTER_SUFFIX = "="; | |
| 11455 | |
| 11456 /** | |
| 11457 * The name used to look up the method used to implement the unary minus opera
tor. Used to | |
| 11458 * disambiguate between the unary and binary operators. | |
| 11459 */ | |
| 11460 static String UNARY_MINUS = "unary-"; | |
| 11461 | |
| 11462 /** | |
| 11463 * A table mapping names that are defined in this scope to the element represe
nting the thing | |
| 11464 * declared with that name. | |
| 11465 */ | |
| 11466 HashMap<String, Element> _definedNames = new HashMap<String, Element>(); | |
| 11467 | |
| 11468 /** | |
| 11469 * A flag indicating whether there are any names defined in this scope. | |
| 11470 */ | |
| 11471 bool _hasName = false; | |
| 11472 | |
| 11473 /** | |
| 11474 * Return the scope in which this scope is lexically enclosed. | |
| 11475 * | |
| 11476 * @return the scope in which this scope is lexically enclosed | |
| 11477 */ | |
| 11478 Scope get enclosingScope => null; | |
| 11479 | |
| 11480 /** | |
| 11481 * Return the listener that is to be informed when an error is encountered. | |
| 11482 * | |
| 11483 * @return the listener that is to be informed when an error is encountered | |
| 11484 */ | |
| 11485 AnalysisErrorListener get errorListener; | |
| 11486 | |
| 11487 /** | |
| 11488 * Add the given element to this scope. If there is already an element with th
e given name defined | |
| 11489 * in this scope, then an error will be generated and the original element wil
l continue to be | |
| 11490 * mapped to the name. If there is an element with the given name in an enclos
ing scope, then a | |
| 11491 * warning will be generated but the given element will hide the inherited ele
ment. | |
| 11492 * | |
| 11493 * @param element the element to be added to this scope | |
| 11494 */ | |
| 11495 void define(Element element) { | |
| 11496 String name = _getName(element); | |
| 11497 if (name != null && !name.isEmpty) { | |
| 11498 if (_definedNames.containsKey(name)) { | |
| 11499 errorListener | |
| 11500 .onError(getErrorForDuplicate(_definedNames[name], element)); | |
| 11501 } else { | |
| 11502 _definedNames[name] = element; | |
| 11503 _hasName = true; | |
| 11504 } | |
| 11505 } | |
| 11506 } | |
| 11507 | |
| 11508 /** | |
| 11509 * Add the given element to this scope without checking for duplication or hid
ing. | |
| 11510 * | |
| 11511 * @param name the name of the element to be added | |
| 11512 * @param element the element to be added to this scope | |
| 11513 */ | |
| 11514 void defineNameWithoutChecking(String name, Element element) { | |
| 11515 _definedNames[name] = element; | |
| 11516 _hasName = true; | |
| 11517 } | |
| 11518 | |
| 11519 /** | |
| 11520 * Add the given element to this scope without checking for duplication or hid
ing. | |
| 11521 * | |
| 11522 * @param element the element to be added to this scope | |
| 11523 */ | |
| 11524 void defineWithoutChecking(Element element) { | |
| 11525 _definedNames[_getName(element)] = element; | |
| 11526 _hasName = true; | |
| 11527 } | |
| 11528 | |
| 11529 /** | |
| 11530 * Return the error code to be used when reporting that a name being defined l
ocally conflicts | |
| 11531 * with another element of the same name in the local scope. | |
| 11532 * | |
| 11533 * @param existing the first element to be declared with the conflicting name | |
| 11534 * @param duplicate another element declared with the conflicting name | |
| 11535 * @return the error code used to report duplicate names within a scope | |
| 11536 */ | |
| 11537 AnalysisError getErrorForDuplicate(Element existing, Element duplicate) { | |
| 11538 // TODO(brianwilkerson) Customize the error message based on the types of | |
| 11539 // elements that share the same name. | |
| 11540 // TODO(jwren) There are 4 error codes for duplicate, but only 1 is being | |
| 11541 // generated. | |
| 11542 Source source = duplicate.source; | |
| 11543 return new AnalysisError(source, duplicate.nameOffset, | |
| 11544 duplicate.displayName.length, CompileTimeErrorCode.DUPLICATE_DEFINITION, | |
| 11545 [existing.displayName]); | |
| 11546 } | |
| 11547 | |
| 11548 /** | |
| 11549 * Return the source that contains the given identifier, or the source associa
ted with this scope | |
| 11550 * if the source containing the identifier could not be determined. | |
| 11551 * | |
| 11552 * @param identifier the identifier whose source is to be returned | |
| 11553 * @return the source that contains the given identifier | |
| 11554 */ | |
| 11555 Source getSource(AstNode node) { | |
| 11556 CompilationUnit unit = node.getAncestor((node) => node is CompilationUnit); | |
| 11557 if (unit != null) { | |
| 11558 CompilationUnitElement unitElement = unit.element; | |
| 11559 if (unitElement != null) { | |
| 11560 return unitElement.source; | |
| 11561 } | |
| 11562 } | |
| 11563 return null; | |
| 11564 } | |
| 11565 | |
| 11566 /** | |
| 11567 * Return the element with which the given name is associated, or `null` if th
e name is not | |
| 11568 * defined within this scope. | |
| 11569 * | |
| 11570 * @param identifier the identifier node to lookup element for, used to report
correct kind of a | |
| 11571 * problem and associate problem with | |
| 11572 * @param name the name associated with the element to be returned | |
| 11573 * @param referencingLibrary the library that contains the reference to the na
me, used to | |
| 11574 * implement library-level privacy | |
| 11575 * @return the element with which the given name is associated | |
| 11576 */ | |
| 11577 Element internalLookup( | |
| 11578 Identifier identifier, String name, LibraryElement referencingLibrary); | |
| 11579 | |
| 11580 /** | |
| 11581 * Return the element with which the given name is associated, or `null` if th
e name is not | |
| 11582 * defined within this scope. This method only returns elements that are direc
tly defined within | |
| 11583 * this scope, not elements that are defined in an enclosing scope. | |
| 11584 * | |
| 11585 * @param name the name associated with the element to be returned | |
| 11586 * @param referencingLibrary the library that contains the reference to the na
me, used to | |
| 11587 * implement library-level privacy | |
| 11588 * @return the element with which the given name is associated | |
| 11589 */ | |
| 11590 Element localLookup(String name, LibraryElement referencingLibrary) { | |
| 11591 if (_hasName) { | |
| 11592 return _definedNames[name]; | |
| 11593 } | |
| 11594 return null; | |
| 11595 } | |
| 11596 | |
| 11597 /** | |
| 11598 * Return the element with which the given identifier is associated, or `null`
if the name | |
| 11599 * is not defined within this scope. | |
| 11600 * | |
| 11601 * @param identifier the identifier associated with the element to be returned | |
| 11602 * @param referencingLibrary the library that contains the reference to the na
me, used to | |
| 11603 * implement library-level privacy | |
| 11604 * @return the element with which the given identifier is associated | |
| 11605 */ | |
| 11606 Element lookup(Identifier identifier, LibraryElement referencingLibrary) => | |
| 11607 internalLookup(identifier, identifier.name, referencingLibrary); | |
| 11608 | |
| 11609 /** | |
| 11610 * Return the name that will be used to look up the given element. | |
| 11611 * | |
| 11612 * @param element the element whose look-up name is to be returned | |
| 11613 * @return the name that will be used to look up the given element | |
| 11614 */ | |
| 11615 String _getName(Element element) { | |
| 11616 if (element is MethodElement) { | |
| 11617 MethodElement method = element; | |
| 11618 if (method.name == "-" && method.parameters.length == 0) { | |
| 11619 return UNARY_MINUS; | |
| 11620 } | |
| 11621 } | |
| 11622 return element.name; | |
| 11623 } | |
| 11624 | |
| 11625 /** | |
| 11626 * Return `true` if the given name is a library-private name. | |
| 11627 * | |
| 11628 * @param name the name being tested | |
| 11629 * @return `true` if the given name is a library-private name | |
| 11630 */ | |
| 11631 static bool isPrivateName(String name) => | |
| 11632 name != null && StringUtilities.startsWithChar(name, PRIVATE_NAME_PREFIX); | |
| 11633 } | |
| 11634 | |
| 11635 /** | |
| 11636 * The abstract class `ScopedVisitor` maintains name and label scopes as an AST
structure is | |
| 11637 * being visited. | |
| 11638 */ | |
| 11639 abstract class ScopedVisitor extends UnifyingAstVisitor<Object> { | |
| 11640 /** | |
| 11641 * The element for the library containing the compilation unit being visited. | |
| 11642 */ | |
| 11643 LibraryElement _definingLibrary; | |
| 11644 | |
| 11645 /** | |
| 11646 * The source representing the compilation unit being visited. | |
| 11647 */ | |
| 11648 final Source source; | |
| 11649 | |
| 11650 /** | |
| 11651 * The error listener that will be informed of any errors that are found durin
g resolution. | |
| 11652 */ | |
| 11653 AnalysisErrorListener _errorListener; | |
| 11654 | |
| 11655 /** | |
| 11656 * The scope used to resolve identifiers. | |
| 11657 */ | |
| 11658 Scope nameScope; | |
| 11659 | |
| 11660 /** | |
| 11661 * The object used to access the types from the core library. | |
| 11662 */ | |
| 11663 final TypeProvider typeProvider; | |
| 11664 | |
| 11665 /** | |
| 11666 * The scope used to resolve unlabeled `break` and `continue` statements. | |
| 11667 */ | |
| 11668 ImplicitLabelScope _implicitLabelScope = ImplicitLabelScope.ROOT; | |
| 11669 | |
| 11670 /** | |
| 11671 * The scope used to resolve labels for `break` and `continue` statements, or | |
| 11672 * `null` if no labels have been defined in the current context. | |
| 11673 */ | |
| 11674 LabelScope labelScope; | |
| 11675 | |
| 11676 /** | |
| 11677 * The class containing the AST nodes being visited, | |
| 11678 * or `null` if we are not in the scope of a class. | |
| 11679 */ | |
| 11680 ClassElement enclosingClass; | |
| 11681 | |
| 11682 /** | |
| 11683 * Initialize a newly created visitor to resolve the nodes in a compilation | |
| 11684 * unit. | |
| 11685 * | |
| 11686 * [definingLibrary] is the element for the library containing the | |
| 11687 * compilation unit being visited. | |
| 11688 * [source] is the source representing the compilation unit being visited. | |
| 11689 * [typeProvider] is the object used to access the types from the core | |
| 11690 * library. | |
| 11691 * [errorListener] is the error listener that will be informed of any errors | |
| 11692 * that are found during resolution. | |
| 11693 * [nameScope] is the scope used to resolve identifiers in the node that will | |
| 11694 * first be visited. If `null` or unspecified, a new [LibraryScope] will be | |
| 11695 * created based on [definingLibrary] and [typeProvider]. | |
| 11696 */ | |
| 11697 ScopedVisitor(LibraryElement definingLibrary, this.source, this.typeProvider, | |
| 11698 AnalysisErrorListener errorListener, {Scope nameScope}) { | |
| 11699 this._definingLibrary = definingLibrary; | |
| 11700 this._errorListener = errorListener; | |
| 11701 if (nameScope == null) { | |
| 11702 this.nameScope = new LibraryScope(definingLibrary, errorListener); | |
| 11703 } else { | |
| 11704 this.nameScope = nameScope; | |
| 11705 } | |
| 11706 } | |
| 11707 | |
| 11708 /** | |
| 11709 * Return the library element for the library containing the compilation unit
being resolved. | |
| 11710 * | |
| 11711 * @return the library element for the library containing the compilation unit
being resolved | |
| 11712 */ | |
| 11713 LibraryElement get definingLibrary => _definingLibrary; | |
| 11714 | |
| 11715 /** | |
| 11716 * Return the implicit label scope in which the current node is being | |
| 11717 * resolved. | |
| 11718 */ | |
| 11719 ImplicitLabelScope get implicitLabelScope => _implicitLabelScope; | |
| 11720 | |
| 11721 /** | |
| 11722 * Replaces the current [Scope] with the enclosing [Scope]. | |
| 11723 * | |
| 11724 * @return the enclosing [Scope]. | |
| 11725 */ | |
| 11726 Scope popNameScope() { | |
| 11727 nameScope = nameScope.enclosingScope; | |
| 11728 return nameScope; | |
| 11729 } | |
| 11730 | |
| 11731 /** | |
| 11732 * Pushes a new [Scope] into the visitor. | |
| 11733 * | |
| 11734 * @return the new [Scope]. | |
| 11735 */ | |
| 11736 Scope pushNameScope() { | |
| 11737 Scope newScope = new EnclosedScope(nameScope); | |
| 11738 nameScope = newScope; | |
| 11739 return nameScope; | |
| 11740 } | |
| 11741 | |
| 11742 /** | |
| 11743 * Report an error with the given error code and arguments. | |
| 11744 * | |
| 11745 * @param errorCode the error code of the error to be reported | |
| 11746 * @param node the node specifying the location of the error | |
| 11747 * @param arguments the arguments to the error, used to compose the error mess
age | |
| 11748 */ | |
| 11749 void reportErrorForNode(ErrorCode errorCode, AstNode node, | |
| 11750 [List<Object> arguments]) { | |
| 11751 _errorListener.onError(new AnalysisError( | |
| 11752 source, node.offset, node.length, errorCode, arguments)); | |
| 11753 } | |
| 11754 | |
| 11755 /** | |
| 11756 * Report an error with the given error code and arguments. | |
| 11757 * | |
| 11758 * @param errorCode the error code of the error to be reported | |
| 11759 * @param offset the offset of the location of the error | |
| 11760 * @param length the length of the location of the error | |
| 11761 * @param arguments the arguments to the error, used to compose the error mess
age | |
| 11762 */ | |
| 11763 void reportErrorForOffset(ErrorCode errorCode, int offset, int length, | |
| 11764 [List<Object> arguments]) { | |
| 11765 _errorListener.onError( | |
| 11766 new AnalysisError(source, offset, length, errorCode, arguments)); | |
| 11767 } | |
| 11768 | |
| 11769 /** | |
| 11770 * Report an error with the given error code and arguments. | |
| 11771 * | |
| 11772 * @param errorCode the error code of the error to be reported | |
| 11773 * @param token the token specifying the location of the error | |
| 11774 * @param arguments the arguments to the error, used to compose the error mess
age | |
| 11775 */ | |
| 11776 void reportErrorForToken(ErrorCode errorCode, sc.Token token, | |
| 11777 [List<Object> arguments]) { | |
| 11778 _errorListener.onError(new AnalysisError( | |
| 11779 source, token.offset, token.length, errorCode, arguments)); | |
| 11780 } | |
| 11781 | |
| 11782 /** | |
| 11783 * Visit the given AST node if it is not null. | |
| 11784 * | |
| 11785 * @param node the node to be visited | |
| 11786 */ | |
| 11787 void safelyVisit(AstNode node) { | |
| 11788 if (node != null) { | |
| 11789 node.accept(this); | |
| 11790 } | |
| 11791 } | |
| 11792 | |
| 11793 @override | |
| 11794 Object visitBlock(Block node) { | |
| 11795 Scope outerScope = nameScope; | |
| 11796 try { | |
| 11797 EnclosedScope enclosedScope = new EnclosedScope(nameScope); | |
| 11798 _hideNamesDefinedInBlock(enclosedScope, node); | |
| 11799 nameScope = enclosedScope; | |
| 11800 super.visitBlock(node); | |
| 11801 } finally { | |
| 11802 nameScope = outerScope; | |
| 11803 } | |
| 11804 return null; | |
| 11805 } | |
| 11806 | |
| 11807 @override | |
| 11808 Object visitBlockFunctionBody(BlockFunctionBody node) { | |
| 11809 ImplicitLabelScope implicitOuterScope = _implicitLabelScope; | |
| 11810 try { | |
| 11811 _implicitLabelScope = ImplicitLabelScope.ROOT; | |
| 11812 super.visitBlockFunctionBody(node); | |
| 11813 } finally { | |
| 11814 _implicitLabelScope = implicitOuterScope; | |
| 11815 } | |
| 11816 return null; | |
| 11817 } | |
| 11818 | |
| 11819 @override | |
| 11820 Object visitCatchClause(CatchClause node) { | |
| 11821 SimpleIdentifier exception = node.exceptionParameter; | |
| 11822 if (exception != null) { | |
| 11823 Scope outerScope = nameScope; | |
| 11824 try { | |
| 11825 nameScope = new EnclosedScope(nameScope); | |
| 11826 nameScope.define(exception.staticElement); | |
| 11827 SimpleIdentifier stackTrace = node.stackTraceParameter; | |
| 11828 if (stackTrace != null) { | |
| 11829 nameScope.define(stackTrace.staticElement); | |
| 11830 } | |
| 11831 super.visitCatchClause(node); | |
| 11832 } finally { | |
| 11833 nameScope = outerScope; | |
| 11834 } | |
| 11835 } else { | |
| 11836 super.visitCatchClause(node); | |
| 11837 } | |
| 11838 return null; | |
| 11839 } | |
| 11840 | |
| 11841 @override | |
| 11842 Object visitClassDeclaration(ClassDeclaration node) { | |
| 11843 ClassElement classElement = node.element; | |
| 11844 Scope outerScope = nameScope; | |
| 11845 try { | |
| 11846 if (classElement == null) { | |
| 11847 AnalysisEngine.instance.logger.logInformation( | |
| 11848 "Missing element for class declaration ${node.name.name} in ${defini
ngLibrary.source.fullName}", | |
| 11849 new CaughtException(new AnalysisException(), null)); | |
| 11850 super.visitClassDeclaration(node); | |
| 11851 } else { | |
| 11852 ClassElement outerClass = enclosingClass; | |
| 11853 try { | |
| 11854 enclosingClass = node.element; | |
| 11855 nameScope = new TypeParameterScope(nameScope, classElement); | |
| 11856 visitClassDeclarationInScope(node); | |
| 11857 nameScope = new ClassScope(nameScope, classElement); | |
| 11858 visitClassMembersInScope(node); | |
| 11859 } finally { | |
| 11860 enclosingClass = outerClass; | |
| 11861 } | |
| 11862 } | |
| 11863 } finally { | |
| 11864 nameScope = outerScope; | |
| 11865 } | |
| 11866 return null; | |
| 11867 } | |
| 11868 | |
| 11869 void visitClassDeclarationInScope(ClassDeclaration node) { | |
| 11870 safelyVisit(node.name); | |
| 11871 safelyVisit(node.typeParameters); | |
| 11872 safelyVisit(node.extendsClause); | |
| 11873 safelyVisit(node.withClause); | |
| 11874 safelyVisit(node.implementsClause); | |
| 11875 safelyVisit(node.nativeClause); | |
| 11876 } | |
| 11877 | |
| 11878 void visitClassMembersInScope(ClassDeclaration node) { | |
| 11879 safelyVisit(node.documentationComment); | |
| 11880 node.metadata.accept(this); | |
| 11881 node.members.accept(this); | |
| 11882 } | |
| 11883 | |
| 11884 @override | |
| 11885 Object visitClassTypeAlias(ClassTypeAlias node) { | |
| 11886 Scope outerScope = nameScope; | |
| 11887 try { | |
| 11888 ClassElement element = node.element; | |
| 11889 nameScope = | |
| 11890 new ClassScope(new TypeParameterScope(nameScope, element), element); | |
| 11891 super.visitClassTypeAlias(node); | |
| 11892 } finally { | |
| 11893 nameScope = outerScope; | |
| 11894 } | |
| 11895 return null; | |
| 11896 } | |
| 11897 | |
| 11898 @override | |
| 11899 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
| 11900 ConstructorElement constructorElement = node.element; | |
| 11901 Scope outerScope = nameScope; | |
| 11902 try { | |
| 11903 if (constructorElement == null) { | |
| 11904 StringBuffer buffer = new StringBuffer(); | |
| 11905 buffer.write("Missing element for constructor "); | |
| 11906 buffer.write(node.returnType.name); | |
| 11907 if (node.name != null) { | |
| 11908 buffer.write("."); | |
| 11909 buffer.write(node.name.name); | |
| 11910 } | |
| 11911 buffer.write(" in "); | |
| 11912 buffer.write(definingLibrary.source.fullName); | |
| 11913 AnalysisEngine.instance.logger.logInformation(buffer.toString(), | |
| 11914 new CaughtException(new AnalysisException(), null)); | |
| 11915 } else { | |
| 11916 nameScope = new FunctionScope(nameScope, constructorElement); | |
| 11917 } | |
| 11918 super.visitConstructorDeclaration(node); | |
| 11919 } finally { | |
| 11920 nameScope = outerScope; | |
| 11921 } | |
| 11922 return null; | |
| 11923 } | |
| 11924 | |
| 11925 @override | |
| 11926 Object visitDeclaredIdentifier(DeclaredIdentifier node) { | |
| 11927 VariableElement element = node.element; | |
| 11928 if (element != null) { | |
| 11929 nameScope.define(element); | |
| 11930 } | |
| 11931 super.visitDeclaredIdentifier(node); | |
| 11932 return null; | |
| 11933 } | |
| 11934 | |
| 11935 @override | |
| 11936 Object visitDoStatement(DoStatement node) { | |
| 11937 ImplicitLabelScope outerImplicitScope = _implicitLabelScope; | |
| 11938 try { | |
| 11939 _implicitLabelScope = _implicitLabelScope.nest(node); | |
| 11940 visitStatementInScope(node.body); | |
| 11941 safelyVisit(node.condition); | |
| 11942 } finally { | |
| 11943 _implicitLabelScope = outerImplicitScope; | |
| 11944 } | |
| 11945 return null; | |
| 11946 } | |
| 11947 | |
| 11948 @override | |
| 11949 Object visitForEachStatement(ForEachStatement node) { | |
| 11950 Scope outerNameScope = nameScope; | |
| 11951 ImplicitLabelScope outerImplicitScope = _implicitLabelScope; | |
| 11952 try { | |
| 11953 nameScope = new EnclosedScope(nameScope); | |
| 11954 _implicitLabelScope = _implicitLabelScope.nest(node); | |
| 11955 visitForEachStatementInScope(node); | |
| 11956 } finally { | |
| 11957 nameScope = outerNameScope; | |
| 11958 _implicitLabelScope = outerImplicitScope; | |
| 11959 } | |
| 11960 return null; | |
| 11961 } | |
| 11962 | |
| 11963 /** | |
| 11964 * Visit the given statement after it's scope has been created. This replaces
the normal call to | |
| 11965 * the inherited visit method so that ResolverVisitor can intervene when type
propagation is | |
| 11966 * enabled. | |
| 11967 * | |
| 11968 * @param node the statement to be visited | |
| 11969 */ | |
| 11970 void visitForEachStatementInScope(ForEachStatement node) { | |
| 11971 // | |
| 11972 // We visit the iterator before the loop variable because the loop variable | |
| 11973 // cannot be in scope while visiting the iterator. | |
| 11974 // | |
| 11975 safelyVisit(node.identifier); | |
| 11976 safelyVisit(node.iterable); | |
| 11977 safelyVisit(node.loopVariable); | |
| 11978 visitStatementInScope(node.body); | |
| 11979 } | |
| 11980 | |
| 11981 @override | |
| 11982 Object visitFormalParameterList(FormalParameterList node) { | |
| 11983 super.visitFormalParameterList(node); | |
| 11984 // We finished resolving function signature, now include formal parameters | |
| 11985 // scope. Note: we must not do this if the parent is a | |
| 11986 // FunctionTypedFormalParameter, because in that case we aren't finished | |
| 11987 // resolving the full function signature, just a part of it. | |
| 11988 if (nameScope is FunctionScope && | |
| 11989 node.parent is! FunctionTypedFormalParameter) { | |
| 11990 (nameScope as FunctionScope).defineParameters(); | |
| 11991 } | |
| 11992 if (nameScope is FunctionTypeScope) { | |
| 11993 (nameScope as FunctionTypeScope).defineParameters(); | |
| 11994 } | |
| 11995 return null; | |
| 11996 } | |
| 11997 | |
| 11998 @override | |
| 11999 Object visitForStatement(ForStatement node) { | |
| 12000 Scope outerNameScope = nameScope; | |
| 12001 ImplicitLabelScope outerImplicitScope = _implicitLabelScope; | |
| 12002 try { | |
| 12003 nameScope = new EnclosedScope(nameScope); | |
| 12004 _implicitLabelScope = _implicitLabelScope.nest(node); | |
| 12005 visitForStatementInScope(node); | |
| 12006 } finally { | |
| 12007 nameScope = outerNameScope; | |
| 12008 _implicitLabelScope = outerImplicitScope; | |
| 12009 } | |
| 12010 return null; | |
| 12011 } | |
| 12012 | |
| 12013 /** | |
| 12014 * Visit the given statement after it's scope has been created. This replaces
the normal call to | |
| 12015 * the inherited visit method so that ResolverVisitor can intervene when type
propagation is | |
| 12016 * enabled. | |
| 12017 * | |
| 12018 * @param node the statement to be visited | |
| 12019 */ | |
| 12020 void visitForStatementInScope(ForStatement node) { | |
| 12021 safelyVisit(node.variables); | |
| 12022 safelyVisit(node.initialization); | |
| 12023 safelyVisit(node.condition); | |
| 12024 node.updaters.accept(this); | |
| 12025 visitStatementInScope(node.body); | |
| 12026 } | |
| 12027 | |
| 12028 @override | |
| 12029 Object visitFunctionDeclaration(FunctionDeclaration node) { | |
| 12030 ExecutableElement functionElement = node.element; | |
| 12031 if (functionElement != null && | |
| 12032 functionElement.enclosingElement is! CompilationUnitElement) { | |
| 12033 nameScope.define(functionElement); | |
| 12034 } | |
| 12035 Scope outerScope = nameScope; | |
| 12036 try { | |
| 12037 if (functionElement == null) { | |
| 12038 AnalysisEngine.instance.logger.logInformation( | |
| 12039 "Missing element for top-level function ${node.name.name} in ${defin
ingLibrary.source.fullName}", | |
| 12040 new CaughtException(new AnalysisException(), null)); | |
| 12041 } else { | |
| 12042 nameScope = new FunctionScope(nameScope, functionElement); | |
| 12043 } | |
| 12044 super.visitFunctionDeclaration(node); | |
| 12045 } finally { | |
| 12046 nameScope = outerScope; | |
| 12047 } | |
| 12048 return null; | |
| 12049 } | |
| 12050 | |
| 12051 @override | |
| 12052 Object visitFunctionExpression(FunctionExpression node) { | |
| 12053 if (node.parent is FunctionDeclaration) { | |
| 12054 // We have already created a function scope and don't need to do so again. | |
| 12055 super.visitFunctionExpression(node); | |
| 12056 } else { | |
| 12057 Scope outerScope = nameScope; | |
| 12058 try { | |
| 12059 ExecutableElement functionElement = node.element; | |
| 12060 if (functionElement == null) { | |
| 12061 StringBuffer buffer = new StringBuffer(); | |
| 12062 buffer.write("Missing element for function "); | |
| 12063 AstNode parent = node.parent; | |
| 12064 while (parent != null) { | |
| 12065 if (parent is Declaration) { | |
| 12066 Element parentElement = parent.element; | |
| 12067 buffer.write(parentElement == null | |
| 12068 ? "<unknown> " | |
| 12069 : "${parentElement.name} "); | |
| 12070 } | |
| 12071 parent = parent.parent; | |
| 12072 } | |
| 12073 buffer.write("in "); | |
| 12074 buffer.write(definingLibrary.source.fullName); | |
| 12075 AnalysisEngine.instance.logger.logInformation(buffer.toString(), | |
| 12076 new CaughtException(new AnalysisException(), null)); | |
| 12077 } else { | |
| 12078 nameScope = new FunctionScope(nameScope, functionElement); | |
| 12079 } | |
| 12080 super.visitFunctionExpression(node); | |
| 12081 } finally { | |
| 12082 nameScope = outerScope; | |
| 12083 } | |
| 12084 } | |
| 12085 return null; | |
| 12086 } | |
| 12087 | |
| 12088 @override | |
| 12089 Object visitFunctionTypeAlias(FunctionTypeAlias node) { | |
| 12090 Scope outerScope = nameScope; | |
| 12091 try { | |
| 12092 nameScope = new FunctionTypeScope(nameScope, node.element); | |
| 12093 super.visitFunctionTypeAlias(node); | |
| 12094 } finally { | |
| 12095 nameScope = outerScope; | |
| 12096 } | |
| 12097 return null; | |
| 12098 } | |
| 12099 | |
| 12100 @override | |
| 12101 Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { | |
| 12102 Scope outerScope = nameScope; | |
| 12103 try { | |
| 12104 ParameterElement parameterElement = node.element; | |
| 12105 if (parameterElement == null) { | |
| 12106 AnalysisEngine.instance.logger.logInformation( | |
| 12107 "Missing element for function typed formal parameter ${node.identifi
er.name} in ${definingLibrary.source.fullName}", | |
| 12108 new CaughtException(new AnalysisException(), null)); | |
| 12109 } else { | |
| 12110 nameScope = new EnclosedScope(nameScope); | |
| 12111 for (TypeParameterElement typeParameter | |
| 12112 in parameterElement.typeParameters) { | |
| 12113 nameScope.define(typeParameter); | |
| 12114 } | |
| 12115 } | |
| 12116 super.visitFunctionTypedFormalParameter(node); | |
| 12117 } finally { | |
| 12118 nameScope = outerScope; | |
| 12119 } | |
| 12120 return null; | |
| 12121 } | |
| 12122 | |
| 12123 @override | |
| 12124 Object visitIfStatement(IfStatement node) { | |
| 12125 safelyVisit(node.condition); | |
| 12126 visitStatementInScope(node.thenStatement); | |
| 12127 visitStatementInScope(node.elseStatement); | |
| 12128 return null; | |
| 12129 } | |
| 12130 | |
| 12131 @override | |
| 12132 Object visitLabeledStatement(LabeledStatement node) { | |
| 12133 LabelScope outerScope = _addScopesFor(node.labels, node.unlabeled); | |
| 12134 try { | |
| 12135 super.visitLabeledStatement(node); | |
| 12136 } finally { | |
| 12137 labelScope = outerScope; | |
| 12138 } | |
| 12139 return null; | |
| 12140 } | |
| 12141 | |
| 12142 @override | |
| 12143 Object visitMethodDeclaration(MethodDeclaration node) { | |
| 12144 Scope outerScope = nameScope; | |
| 12145 try { | |
| 12146 ExecutableElement methodElement = node.element; | |
| 12147 if (methodElement == null) { | |
| 12148 AnalysisEngine.instance.logger.logInformation( | |
| 12149 "Missing element for method ${node.name.name} in ${definingLibrary.s
ource.fullName}", | |
| 12150 new CaughtException(new AnalysisException(), null)); | |
| 12151 } else { | |
| 12152 nameScope = new FunctionScope(nameScope, methodElement); | |
| 12153 } | |
| 12154 super.visitMethodDeclaration(node); | |
| 12155 } finally { | |
| 12156 nameScope = outerScope; | |
| 12157 } | |
| 12158 return null; | |
| 12159 } | |
| 12160 | |
| 12161 /** | |
| 12162 * Visit the given statement after it's scope has been created. This is used b
y ResolverVisitor to | |
| 12163 * correctly visit the 'then' and 'else' statements of an 'if' statement. | |
| 12164 * | |
| 12165 * @param node the statement to be visited | |
| 12166 */ | |
| 12167 void visitStatementInScope(Statement node) { | |
| 12168 if (node is Block) { | |
| 12169 // Don't create a scope around a block because the block will create it's | |
| 12170 // own scope. | |
| 12171 visitBlock(node); | |
| 12172 } else if (node != null) { | |
| 12173 Scope outerNameScope = nameScope; | |
| 12174 try { | |
| 12175 nameScope = new EnclosedScope(nameScope); | |
| 12176 node.accept(this); | |
| 12177 } finally { | |
| 12178 nameScope = outerNameScope; | |
| 12179 } | |
| 12180 } | |
| 12181 } | |
| 12182 | |
| 12183 @override | |
| 12184 Object visitSwitchCase(SwitchCase node) { | |
| 12185 node.expression.accept(this); | |
| 12186 Scope outerNameScope = nameScope; | |
| 12187 try { | |
| 12188 nameScope = new EnclosedScope(nameScope); | |
| 12189 node.statements.accept(this); | |
| 12190 } finally { | |
| 12191 nameScope = outerNameScope; | |
| 12192 } | |
| 12193 return null; | |
| 12194 } | |
| 12195 | |
| 12196 @override | |
| 12197 Object visitSwitchDefault(SwitchDefault node) { | |
| 12198 Scope outerNameScope = nameScope; | |
| 12199 try { | |
| 12200 nameScope = new EnclosedScope(nameScope); | |
| 12201 node.statements.accept(this); | |
| 12202 } finally { | |
| 12203 nameScope = outerNameScope; | |
| 12204 } | |
| 12205 return null; | |
| 12206 } | |
| 12207 | |
| 12208 @override | |
| 12209 Object visitSwitchStatement(SwitchStatement node) { | |
| 12210 LabelScope outerScope = labelScope; | |
| 12211 ImplicitLabelScope outerImplicitScope = _implicitLabelScope; | |
| 12212 try { | |
| 12213 _implicitLabelScope = _implicitLabelScope.nest(node); | |
| 12214 for (SwitchMember member in node.members) { | |
| 12215 for (Label label in member.labels) { | |
| 12216 SimpleIdentifier labelName = label.label; | |
| 12217 LabelElement labelElement = labelName.staticElement as LabelElement; | |
| 12218 labelScope = | |
| 12219 new LabelScope(labelScope, labelName.name, member, labelElement); | |
| 12220 } | |
| 12221 } | |
| 12222 super.visitSwitchStatement(node); | |
| 12223 } finally { | |
| 12224 labelScope = outerScope; | |
| 12225 _implicitLabelScope = outerImplicitScope; | |
| 12226 } | |
| 12227 return null; | |
| 12228 } | |
| 12229 | |
| 12230 @override | |
| 12231 Object visitVariableDeclaration(VariableDeclaration node) { | |
| 12232 super.visitVariableDeclaration(node); | |
| 12233 if (node.parent.parent is! TopLevelVariableDeclaration && | |
| 12234 node.parent.parent is! FieldDeclaration) { | |
| 12235 VariableElement element = node.element; | |
| 12236 if (element != null) { | |
| 12237 nameScope.define(element); | |
| 12238 } | |
| 12239 } | |
| 12240 return null; | |
| 12241 } | |
| 12242 | |
| 12243 @override | |
| 12244 Object visitWhileStatement(WhileStatement node) { | |
| 12245 safelyVisit(node.condition); | |
| 12246 ImplicitLabelScope outerImplicitScope = _implicitLabelScope; | |
| 12247 try { | |
| 12248 _implicitLabelScope = _implicitLabelScope.nest(node); | |
| 12249 visitStatementInScope(node.body); | |
| 12250 } finally { | |
| 12251 _implicitLabelScope = outerImplicitScope; | |
| 12252 } | |
| 12253 return null; | |
| 12254 } | |
| 12255 | |
| 12256 /** | |
| 12257 * Add scopes for each of the given labels. | |
| 12258 * | |
| 12259 * @param labels the labels for which new scopes are to be added | |
| 12260 * @return the scope that was in effect before the new scopes were added | |
| 12261 */ | |
| 12262 LabelScope _addScopesFor(NodeList<Label> labels, AstNode node) { | |
| 12263 LabelScope outerScope = labelScope; | |
| 12264 for (Label label in labels) { | |
| 12265 SimpleIdentifier labelNameNode = label.label; | |
| 12266 String labelName = labelNameNode.name; | |
| 12267 LabelElement labelElement = labelNameNode.staticElement as LabelElement; | |
| 12268 labelScope = new LabelScope(labelScope, labelName, node, labelElement); | |
| 12269 } | |
| 12270 return outerScope; | |
| 12271 } | |
| 12272 | |
| 12273 /** | |
| 12274 * Marks the local declarations of the given [Block] hidden in the enclosing s
cope. | |
| 12275 * According to the scoping rules name is hidden if block defines it, but name
is defined after | |
| 12276 * its declaration statement. | |
| 12277 */ | |
| 12278 void _hideNamesDefinedInBlock(EnclosedScope scope, Block block) { | |
| 12279 NodeList<Statement> statements = block.statements; | |
| 12280 int statementCount = statements.length; | |
| 12281 for (int i = 0; i < statementCount; i++) { | |
| 12282 Statement statement = statements[i]; | |
| 12283 if (statement is VariableDeclarationStatement) { | |
| 12284 VariableDeclarationStatement vds = statement; | |
| 12285 NodeList<VariableDeclaration> variables = vds.variables.variables; | |
| 12286 int variableCount = variables.length; | |
| 12287 for (int j = 0; j < variableCount; j++) { | |
| 12288 scope.hide(variables[j].element); | |
| 12289 } | |
| 12290 } else if (statement is FunctionDeclarationStatement) { | |
| 12291 FunctionDeclarationStatement fds = statement; | |
| 12292 scope.hide(fds.functionDeclaration.element); | |
| 12293 } | |
| 12294 } | |
| 12295 } | |
| 12296 } | |
| 12297 | |
| 12298 /** | |
| 12299 * Instances of this class manage the knowledge of what the set of subtypes are
for a given type. | |
| 12300 */ | |
| 12301 class SubtypeManager { | |
| 12302 /** | |
| 12303 * A map between [ClassElement]s and a set of [ClassElement]s that are subtype
s of the | |
| 12304 * key. | |
| 12305 */ | |
| 12306 HashMap<ClassElement, HashSet<ClassElement>> _subtypeMap = | |
| 12307 new HashMap<ClassElement, HashSet<ClassElement>>(); | |
| 12308 | |
| 12309 /** | |
| 12310 * The set of all [LibraryElement]s that have been visited by the manager. Thi
s is used both | |
| 12311 * to prevent infinite loops in the recursive methods, and also as a marker fo
r the scope of the | |
| 12312 * libraries visited by this manager. | |
| 12313 */ | |
| 12314 HashSet<LibraryElement> _visitedLibraries = new HashSet<LibraryElement>(); | |
| 12315 | |
| 12316 /** | |
| 12317 * Given some [ClassElement], return the set of all subtypes, and subtypes of
subtypes. | |
| 12318 * | |
| 12319 * @param classElement the class to recursively return the set of subtypes of | |
| 12320 */ | |
| 12321 HashSet<ClassElement> computeAllSubtypes(ClassElement classElement) { | |
| 12322 // Ensure that we have generated the subtype map for the library | |
| 12323 _computeSubtypesInLibrary(classElement.library); | |
| 12324 // use the subtypeMap to compute the set of all subtypes and subtype's | |
| 12325 // subtypes | |
| 12326 HashSet<ClassElement> allSubtypes = new HashSet<ClassElement>(); | |
| 12327 _safelyComputeAllSubtypes( | |
| 12328 classElement, new HashSet<ClassElement>(), allSubtypes); | |
| 12329 return allSubtypes; | |
| 12330 } | |
| 12331 | |
| 12332 /** | |
| 12333 * Given some [LibraryElement], visit all of the types in the library, the pas
sed library, | |
| 12334 * and any imported libraries, will be in the [visitedLibraries] set. | |
| 12335 * | |
| 12336 * @param libraryElement the library to visit, it it hasn't been visited alrea
dy | |
| 12337 */ | |
| 12338 void ensureLibraryVisited(LibraryElement libraryElement) { | |
| 12339 _computeSubtypesInLibrary(libraryElement); | |
| 12340 } | |
| 12341 | |
| 12342 /** | |
| 12343 * Given some [ClassElement], this method adds all of the pairs combinations o
f itself and | |
| 12344 * all of its supertypes to the [subtypeMap] map. | |
| 12345 * | |
| 12346 * @param classElement the class element | |
| 12347 */ | |
| 12348 void _computeSubtypesInClass(ClassElement classElement) { | |
| 12349 InterfaceType supertypeType = classElement.supertype; | |
| 12350 if (supertypeType != null) { | |
| 12351 ClassElement supertypeElement = supertypeType.element; | |
| 12352 if (supertypeElement != null) { | |
| 12353 _putInSubtypeMap(supertypeElement, classElement); | |
| 12354 } | |
| 12355 } | |
| 12356 List<InterfaceType> interfaceTypes = classElement.interfaces; | |
| 12357 for (InterfaceType interfaceType in interfaceTypes) { | |
| 12358 ClassElement interfaceElement = interfaceType.element; | |
| 12359 if (interfaceElement != null) { | |
| 12360 _putInSubtypeMap(interfaceElement, classElement); | |
| 12361 } | |
| 12362 } | |
| 12363 List<InterfaceType> mixinTypes = classElement.mixins; | |
| 12364 for (InterfaceType mixinType in mixinTypes) { | |
| 12365 ClassElement mixinElement = mixinType.element; | |
| 12366 if (mixinElement != null) { | |
| 12367 _putInSubtypeMap(mixinElement, classElement); | |
| 12368 } | |
| 12369 } | |
| 12370 } | |
| 12371 | |
| 12372 /** | |
| 12373 * Given some [CompilationUnitElement], this method calls | |
| 12374 * [computeAllSubtypes] on all of the [ClassElement]s in the | |
| 12375 * compilation unit. | |
| 12376 * | |
| 12377 * @param unitElement the compilation unit element | |
| 12378 */ | |
| 12379 void _computeSubtypesInCompilationUnit(CompilationUnitElement unitElement) { | |
| 12380 List<ClassElement> classElements = unitElement.types; | |
| 12381 for (ClassElement classElement in classElements) { | |
| 12382 _computeSubtypesInClass(classElement); | |
| 12383 } | |
| 12384 } | |
| 12385 | |
| 12386 /** | |
| 12387 * Given some [LibraryElement], this method calls | |
| 12388 * [computeAllSubtypes] on all of the [ClassElement]s in the | |
| 12389 * compilation unit, and itself for all imported and exported libraries. All v
isited libraries are | |
| 12390 * added to the [visitedLibraries] set. | |
| 12391 * | |
| 12392 * @param libraryElement the library element | |
| 12393 */ | |
| 12394 void _computeSubtypesInLibrary(LibraryElement libraryElement) { | |
| 12395 if (libraryElement == null || _visitedLibraries.contains(libraryElement)) { | |
| 12396 return; | |
| 12397 } | |
| 12398 _visitedLibraries.add(libraryElement); | |
| 12399 _computeSubtypesInCompilationUnit(libraryElement.definingCompilationUnit); | |
| 12400 List<CompilationUnitElement> parts = libraryElement.parts; | |
| 12401 for (CompilationUnitElement part in parts) { | |
| 12402 _computeSubtypesInCompilationUnit(part); | |
| 12403 } | |
| 12404 List<LibraryElement> imports = libraryElement.importedLibraries; | |
| 12405 for (LibraryElement importElt in imports) { | |
| 12406 _computeSubtypesInLibrary(importElt.library); | |
| 12407 } | |
| 12408 List<LibraryElement> exports = libraryElement.exportedLibraries; | |
| 12409 for (LibraryElement exportElt in exports) { | |
| 12410 _computeSubtypesInLibrary(exportElt.library); | |
| 12411 } | |
| 12412 } | |
| 12413 | |
| 12414 /** | |
| 12415 * Add some key/ value pair into the [subtypeMap] map. | |
| 12416 * | |
| 12417 * @param supertypeElement the key for the [subtypeMap] map | |
| 12418 * @param subtypeElement the value for the [subtypeMap] map | |
| 12419 */ | |
| 12420 void _putInSubtypeMap( | |
| 12421 ClassElement supertypeElement, ClassElement subtypeElement) { | |
| 12422 HashSet<ClassElement> subtypes = _subtypeMap[supertypeElement]; | |
| 12423 if (subtypes == null) { | |
| 12424 subtypes = new HashSet<ClassElement>(); | |
| 12425 _subtypeMap[supertypeElement] = subtypes; | |
| 12426 } | |
| 12427 subtypes.add(subtypeElement); | |
| 12428 } | |
| 12429 | |
| 12430 /** | |
| 12431 * Given some [ClassElement] and a [HashSet<ClassElement>], this method recurs
ively | |
| 12432 * adds all of the subtypes of the [ClassElement] to the passed array. | |
| 12433 * | |
| 12434 * @param classElement the type to compute the set of subtypes of | |
| 12435 * @param visitedClasses the set of class elements that this method has alread
y recursively seen | |
| 12436 * @param allSubtypes the computed set of subtypes of the passed class element | |
| 12437 */ | |
| 12438 void _safelyComputeAllSubtypes(ClassElement classElement, | |
| 12439 HashSet<ClassElement> visitedClasses, HashSet<ClassElement> allSubtypes) { | |
| 12440 if (!visitedClasses.add(classElement)) { | |
| 12441 // if this class has already been called on this class element | |
| 12442 return; | |
| 12443 } | |
| 12444 HashSet<ClassElement> subtypes = _subtypeMap[classElement]; | |
| 12445 if (subtypes == null) { | |
| 12446 return; | |
| 12447 } | |
| 12448 for (ClassElement subtype in subtypes) { | |
| 12449 _safelyComputeAllSubtypes(subtype, visitedClasses, allSubtypes); | |
| 12450 } | |
| 12451 allSubtypes.addAll(subtypes); | |
| 12452 } | |
| 12453 } | |
| 12454 | |
| 12455 /** | |
| 12456 * Instances of the class `ToDoFinder` find to-do comments in Dart code. | |
| 12457 */ | |
| 12458 class ToDoFinder { | |
| 12459 /** | |
| 12460 * The error reporter by which to-do comments will be reported. | |
| 12461 */ | |
| 12462 final ErrorReporter _errorReporter; | |
| 12463 | |
| 12464 /** | |
| 12465 * Initialize a newly created to-do finder to report to-do comments to the giv
en reporter. | |
| 12466 * | |
| 12467 * @param errorReporter the error reporter by which to-do comments will be rep
orted | |
| 12468 */ | |
| 12469 ToDoFinder(this._errorReporter); | |
| 12470 | |
| 12471 /** | |
| 12472 * Search the comments in the given compilation unit for to-do comments and re
port an error for | |
| 12473 * each. | |
| 12474 * | |
| 12475 * @param unit the compilation unit containing the to-do comments | |
| 12476 */ | |
| 12477 void findIn(CompilationUnit unit) { | |
| 12478 _gatherTodoComments(unit.beginToken); | |
| 12479 } | |
| 12480 | |
| 12481 /** | |
| 12482 * Search the comment tokens reachable from the given token and create errors
for each to-do | |
| 12483 * comment. | |
| 12484 * | |
| 12485 * @param token the head of the list of tokens being searched | |
| 12486 */ | |
| 12487 void _gatherTodoComments(sc.Token token) { | |
| 12488 while (token != null && token.type != sc.TokenType.EOF) { | |
| 12489 sc.Token commentToken = token.precedingComments; | |
| 12490 while (commentToken != null) { | |
| 12491 if (commentToken.type == sc.TokenType.SINGLE_LINE_COMMENT || | |
| 12492 commentToken.type == sc.TokenType.MULTI_LINE_COMMENT) { | |
| 12493 _scrapeTodoComment(commentToken); | |
| 12494 } | |
| 12495 commentToken = commentToken.next; | |
| 12496 } | |
| 12497 token = token.next; | |
| 12498 } | |
| 12499 } | |
| 12500 | |
| 12501 /** | |
| 12502 * Look for user defined tasks in comments and convert them into info level an
alysis issues. | |
| 12503 * | |
| 12504 * @param commentToken the comment token to analyze | |
| 12505 */ | |
| 12506 void _scrapeTodoComment(sc.Token commentToken) { | |
| 12507 JavaPatternMatcher matcher = | |
| 12508 new JavaPatternMatcher(TodoCode.TODO_REGEX, commentToken.lexeme); | |
| 12509 if (matcher.find()) { | |
| 12510 int offset = | |
| 12511 commentToken.offset + matcher.start() + matcher.group(1).length; | |
| 12512 int length = matcher.group(2).length; | |
| 12513 _errorReporter.reportErrorForOffset( | |
| 12514 TodoCode.TODO, offset, length, [matcher.group(2)]); | |
| 12515 } | |
| 12516 } | |
| 12517 } | |
| 12518 | |
| 12519 /** | |
| 12520 * Instances of the class `TypeOverrideManager` manage the ability to override t
he type of an | |
| 12521 * element within a given context. | |
| 12522 */ | |
| 12523 class TypeOverrideManager { | |
| 12524 /** | |
| 12525 * The current override scope, or `null` if no scope has been entered. | |
| 12526 */ | |
| 12527 TypeOverrideManager_TypeOverrideScope currentScope; | |
| 12528 | |
| 12529 /** | |
| 12530 * Apply a set of overrides that were previously captured. | |
| 12531 * | |
| 12532 * @param overrides the overrides to be applied | |
| 12533 */ | |
| 12534 void applyOverrides(Map<VariableElement, DartType> overrides) { | |
| 12535 if (currentScope == null) { | |
| 12536 throw new IllegalStateException("Cannot apply overrides without a scope"); | |
| 12537 } | |
| 12538 currentScope.applyOverrides(overrides); | |
| 12539 } | |
| 12540 | |
| 12541 /** | |
| 12542 * Return a table mapping the elements whose type is overridden in the current
scope to the | |
| 12543 * overriding type. | |
| 12544 * | |
| 12545 * @return the overrides in the current scope | |
| 12546 */ | |
| 12547 Map<VariableElement, DartType> captureLocalOverrides() { | |
| 12548 if (currentScope == null) { | |
| 12549 throw new IllegalStateException( | |
| 12550 "Cannot capture local overrides without a scope"); | |
| 12551 } | |
| 12552 return currentScope.captureLocalOverrides(); | |
| 12553 } | |
| 12554 | |
| 12555 /** | |
| 12556 * Return a map from the elements for the variables in the given list that hav
e their types | |
| 12557 * overridden to the overriding type. | |
| 12558 * | |
| 12559 * @param variableList the list of variables whose overriding types are to be
captured | |
| 12560 * @return a table mapping elements to their overriding types | |
| 12561 */ | |
| 12562 Map<VariableElement, DartType> captureOverrides( | |
| 12563 VariableDeclarationList variableList) { | |
| 12564 if (currentScope == null) { | |
| 12565 throw new IllegalStateException( | |
| 12566 "Cannot capture overrides without a scope"); | |
| 12567 } | |
| 12568 return currentScope.captureOverrides(variableList); | |
| 12569 } | |
| 12570 | |
| 12571 /** | |
| 12572 * Enter a new override scope. | |
| 12573 */ | |
| 12574 void enterScope() { | |
| 12575 currentScope = new TypeOverrideManager_TypeOverrideScope(currentScope); | |
| 12576 } | |
| 12577 | |
| 12578 /** | |
| 12579 * Exit the current override scope. | |
| 12580 */ | |
| 12581 void exitScope() { | |
| 12582 if (currentScope == null) { | |
| 12583 throw new IllegalStateException("No scope to exit"); | |
| 12584 } | |
| 12585 currentScope = currentScope._outerScope; | |
| 12586 } | |
| 12587 | |
| 12588 /** | |
| 12589 * Return the best type information available for the given element. If the ty
pe of the element | |
| 12590 * has been overridden, then return the overriding type. Otherwise, return the
static type. | |
| 12591 * | |
| 12592 * @param element the element for which type information is to be returned | |
| 12593 * @return the best type information available for the given element | |
| 12594 */ | |
| 12595 DartType getBestType(VariableElement element) { | |
| 12596 DartType bestType = getType(element); | |
| 12597 return bestType == null ? element.type : bestType; | |
| 12598 } | |
| 12599 | |
| 12600 /** | |
| 12601 * Return the overridden type of the given element, or `null` if the type of t
he element has | |
| 12602 * not been overridden. | |
| 12603 * | |
| 12604 * @param element the element whose type might have been overridden | |
| 12605 * @return the overridden type of the given element | |
| 12606 */ | |
| 12607 DartType getType(Element element) { | |
| 12608 if (currentScope == null) { | |
| 12609 return null; | |
| 12610 } | |
| 12611 return currentScope.getType(element); | |
| 12612 } | |
| 12613 | |
| 12614 /** | |
| 12615 * Update overrides assuming [perBranchOverrides] is the collection of | |
| 12616 * per-branch overrides for *all* branches flowing into a join point. | |
| 12617 * | |
| 12618 * If a variable type in any of branches is not the same as its type before | |
| 12619 * the branching, then its propagated type is reset to `null`. | |
| 12620 */ | |
| 12621 void mergeOverrides(List<Map<VariableElement, DartType>> perBranchOverrides) { | |
| 12622 for (Map<VariableElement, DartType> branch in perBranchOverrides) { | |
| 12623 branch.forEach((VariableElement variable, DartType branchType) { | |
| 12624 DartType currentType = currentScope.getType(variable); | |
| 12625 if (currentType != branchType) { | |
| 12626 currentScope.resetType(variable); | |
| 12627 } | |
| 12628 }); | |
| 12629 } | |
| 12630 } | |
| 12631 | |
| 12632 /** | |
| 12633 * Set the overridden type of the given element to the given type | |
| 12634 * | |
| 12635 * @param element the element whose type might have been overridden | |
| 12636 * @param type the overridden type of the given element | |
| 12637 */ | |
| 12638 void setType(VariableElement element, DartType type) { | |
| 12639 if (currentScope == null) { | |
| 12640 throw new IllegalStateException("Cannot override without a scope"); | |
| 12641 } | |
| 12642 currentScope.setType(element, type); | |
| 12643 } | |
| 12644 } | |
| 12645 | |
| 12646 /** | |
| 12647 * Instances of the class `TypeOverrideScope` represent a scope in which the typ
es of | |
| 12648 * elements can be overridden. | |
| 12649 */ | |
| 12650 class TypeOverrideManager_TypeOverrideScope { | |
| 12651 /** | |
| 12652 * The outer scope in which types might be overridden. | |
| 12653 */ | |
| 12654 final TypeOverrideManager_TypeOverrideScope _outerScope; | |
| 12655 | |
| 12656 /** | |
| 12657 * A table mapping elements to the overridden type of that element. | |
| 12658 */ | |
| 12659 Map<VariableElement, DartType> _overridenTypes = | |
| 12660 new HashMap<VariableElement, DartType>(); | |
| 12661 | |
| 12662 /** | |
| 12663 * Initialize a newly created scope to be an empty child of the given scope. | |
| 12664 * | |
| 12665 * @param outerScope the outer scope in which types might be overridden | |
| 12666 */ | |
| 12667 TypeOverrideManager_TypeOverrideScope(this._outerScope); | |
| 12668 | |
| 12669 /** | |
| 12670 * Apply a set of overrides that were previously captured. | |
| 12671 * | |
| 12672 * @param overrides the overrides to be applied | |
| 12673 */ | |
| 12674 void applyOverrides(Map<VariableElement, DartType> overrides) { | |
| 12675 _overridenTypes.addAll(overrides); | |
| 12676 } | |
| 12677 | |
| 12678 /** | |
| 12679 * Return a table mapping the elements whose type is overridden in the current
scope to the | |
| 12680 * overriding type. | |
| 12681 * | |
| 12682 * @return the overrides in the current scope | |
| 12683 */ | |
| 12684 Map<VariableElement, DartType> captureLocalOverrides() => _overridenTypes; | |
| 12685 | |
| 12686 /** | |
| 12687 * Return a map from the elements for the variables in the given list that hav
e their types | |
| 12688 * overridden to the overriding type. | |
| 12689 * | |
| 12690 * @param variableList the list of variables whose overriding types are to be
captured | |
| 12691 * @return a table mapping elements to their overriding types | |
| 12692 */ | |
| 12693 Map<VariableElement, DartType> captureOverrides( | |
| 12694 VariableDeclarationList variableList) { | |
| 12695 Map<VariableElement, DartType> overrides = | |
| 12696 new HashMap<VariableElement, DartType>(); | |
| 12697 if (variableList.isConst || variableList.isFinal) { | |
| 12698 for (VariableDeclaration variable in variableList.variables) { | |
| 12699 VariableElement element = variable.element; | |
| 12700 if (element != null) { | |
| 12701 DartType type = _overridenTypes[element]; | |
| 12702 if (type != null) { | |
| 12703 overrides[element] = type; | |
| 12704 } | |
| 12705 } | |
| 12706 } | |
| 12707 } | |
| 12708 return overrides; | |
| 12709 } | |
| 12710 | |
| 12711 /** | |
| 12712 * Return the overridden type of the given element, or `null` if the type of t
he element | |
| 12713 * has not been overridden. | |
| 12714 * | |
| 12715 * @param element the element whose type might have been overridden | |
| 12716 * @return the overridden type of the given element | |
| 12717 */ | |
| 12718 DartType getType(Element element) { | |
| 12719 if (element is PropertyAccessorElement) { | |
| 12720 element = (element as PropertyAccessorElement).variable; | |
| 12721 } | |
| 12722 DartType type = _overridenTypes[element]; | |
| 12723 if (_overridenTypes.containsKey(element)) { | |
| 12724 return type; | |
| 12725 } | |
| 12726 if (type != null) { | |
| 12727 return type; | |
| 12728 } else if (_outerScope != null) { | |
| 12729 return _outerScope.getType(element); | |
| 12730 } | |
| 12731 return null; | |
| 12732 } | |
| 12733 | |
| 12734 /** | |
| 12735 * Clears the overridden type of the given [element]. | |
| 12736 */ | |
| 12737 void resetType(VariableElement element) { | |
| 12738 _overridenTypes[element] = null; | |
| 12739 } | |
| 12740 | |
| 12741 /** | |
| 12742 * Set the overridden type of the given element to the given type | |
| 12743 * | |
| 12744 * @param element the element whose type might have been overridden | |
| 12745 * @param type the overridden type of the given element | |
| 12746 */ | |
| 12747 void setType(VariableElement element, DartType type) { | |
| 12748 _overridenTypes[element] = type; | |
| 12749 } | |
| 12750 } | |
| 12751 | |
| 12752 /** | |
| 12753 * Instances of the class `TypeParameterScope` implement the scope defined by th
e type | |
| 12754 * parameters in a class. | |
| 12755 */ | |
| 12756 class TypeParameterScope extends EnclosedScope { | |
| 12757 /** | |
| 12758 * Initialize a newly created scope enclosed within another scope. | |
| 12759 * | |
| 12760 * @param enclosingScope the scope in which this scope is lexically enclosed | |
| 12761 * @param typeElement the element representing the type represented by this sc
ope | |
| 12762 */ | |
| 12763 TypeParameterScope(Scope enclosingScope, ClassElement typeElement) | |
| 12764 : super(enclosingScope) { | |
| 12765 if (typeElement == null) { | |
| 12766 throw new IllegalArgumentException("class element cannot be null"); | |
| 12767 } | |
| 12768 _defineTypeParameters(typeElement); | |
| 12769 } | |
| 12770 | |
| 12771 /** | |
| 12772 * Define the type parameters for the class. | |
| 12773 * | |
| 12774 * @param typeElement the element representing the type represented by this sc
ope | |
| 12775 */ | |
| 12776 void _defineTypeParameters(ClassElement typeElement) { | |
| 12777 for (TypeParameterElement typeParameter in typeElement.typeParameters) { | |
| 12778 define(typeParameter); | |
| 12779 } | |
| 12780 } | |
| 12781 } | |
| 12782 | |
| 12783 /** | |
| 12784 * Instances of the class `TypePromotionManager` manage the ability to promote t
ypes of local | |
| 12785 * variables and formal parameters from their declared types based on control fl
ow. | |
| 12786 */ | |
| 12787 class TypePromotionManager { | |
| 12788 /** | |
| 12789 * The current promotion scope, or `null` if no scope has been entered. | |
| 12790 */ | |
| 12791 TypePromotionManager_TypePromoteScope currentScope; | |
| 12792 | |
| 12793 /** | |
| 12794 * Returns the elements with promoted types. | |
| 12795 */ | |
| 12796 Iterable<Element> get promotedElements => currentScope.promotedElements; | |
| 12797 | |
| 12798 /** | |
| 12799 * Enter a new promotions scope. | |
| 12800 */ | |
| 12801 void enterScope() { | |
| 12802 currentScope = new TypePromotionManager_TypePromoteScope(currentScope); | |
| 12803 } | |
| 12804 | |
| 12805 /** | |
| 12806 * Exit the current promotion scope. | |
| 12807 */ | |
| 12808 void exitScope() { | |
| 12809 if (currentScope == null) { | |
| 12810 throw new IllegalStateException("No scope to exit"); | |
| 12811 } | |
| 12812 currentScope = currentScope._outerScope; | |
| 12813 } | |
| 12814 | |
| 12815 /** | |
| 12816 * Returns static type of the given variable - declared or promoted. | |
| 12817 * | |
| 12818 * @return the static type of the given variable - declared or promoted | |
| 12819 */ | |
| 12820 DartType getStaticType(VariableElement variable) { | |
| 12821 DartType staticType = getType(variable); | |
| 12822 if (staticType == null) { | |
| 12823 staticType = variable.type; | |
| 12824 } | |
| 12825 return staticType; | |
| 12826 } | |
| 12827 | |
| 12828 /** | |
| 12829 * Return the promoted type of the given element, or `null` if the type of the
element has | |
| 12830 * not been promoted. | |
| 12831 * | |
| 12832 * @param element the element whose type might have been promoted | |
| 12833 * @return the promoted type of the given element | |
| 12834 */ | |
| 12835 DartType getType(Element element) { | |
| 12836 if (currentScope == null) { | |
| 12837 return null; | |
| 12838 } | |
| 12839 return currentScope.getType(element); | |
| 12840 } | |
| 12841 | |
| 12842 /** | |
| 12843 * Set the promoted type of the given element to the given type. | |
| 12844 * | |
| 12845 * @param element the element whose type might have been promoted | |
| 12846 * @param type the promoted type of the given element | |
| 12847 */ | |
| 12848 void setType(Element element, DartType type) { | |
| 12849 if (currentScope == null) { | |
| 12850 throw new IllegalStateException("Cannot promote without a scope"); | |
| 12851 } | |
| 12852 currentScope.setType(element, type); | |
| 12853 } | |
| 12854 } | |
| 12855 | |
| 12856 /** | |
| 12857 * Instances of the class `TypePromoteScope` represent a scope in which the type
s of | |
| 12858 * elements can be promoted. | |
| 12859 */ | |
| 12860 class TypePromotionManager_TypePromoteScope { | |
| 12861 /** | |
| 12862 * The outer scope in which types might be promoter. | |
| 12863 */ | |
| 12864 final TypePromotionManager_TypePromoteScope _outerScope; | |
| 12865 | |
| 12866 /** | |
| 12867 * A table mapping elements to the promoted type of that element. | |
| 12868 */ | |
| 12869 HashMap<Element, DartType> _promotedTypes = new HashMap<Element, DartType>(); | |
| 12870 | |
| 12871 /** | |
| 12872 * Initialize a newly created scope to be an empty child of the given scope. | |
| 12873 * | |
| 12874 * @param outerScope the outer scope in which types might be promoted | |
| 12875 */ | |
| 12876 TypePromotionManager_TypePromoteScope(this._outerScope); | |
| 12877 | |
| 12878 /** | |
| 12879 * Returns the elements with promoted types. | |
| 12880 */ | |
| 12881 Iterable<Element> get promotedElements => _promotedTypes.keys.toSet(); | |
| 12882 | |
| 12883 /** | |
| 12884 * Return the promoted type of the given element, or `null` if the type of the
element has | |
| 12885 * not been promoted. | |
| 12886 * | |
| 12887 * @param element the element whose type might have been promoted | |
| 12888 * @return the promoted type of the given element | |
| 12889 */ | |
| 12890 DartType getType(Element element) { | |
| 12891 DartType type = _promotedTypes[element]; | |
| 12892 if (type == null && element is PropertyAccessorElement) { | |
| 12893 type = _promotedTypes[element.variable]; | |
| 12894 } | |
| 12895 if (type != null) { | |
| 12896 return type; | |
| 12897 } else if (_outerScope != null) { | |
| 12898 return _outerScope.getType(element); | |
| 12899 } | |
| 12900 return null; | |
| 12901 } | |
| 12902 | |
| 12903 /** | |
| 12904 * Set the promoted type of the given element to the given type. | |
| 12905 * | |
| 12906 * @param element the element whose type might have been promoted | |
| 12907 * @param type the promoted type of the given element | |
| 12908 */ | |
| 12909 void setType(Element element, DartType type) { | |
| 12910 _promotedTypes[element] = type; | |
| 12911 } | |
| 12912 } | |
| 12913 | |
| 12914 /** | |
| 12915 * The interface `TypeProvider` defines the behavior of objects that provide acc
ess to types | |
| 12916 * defined by the language. | |
| 12917 */ | |
| 12918 abstract class TypeProvider { | |
| 12919 /** | |
| 12920 * Return the type representing the built-in type 'bool'. | |
| 12921 */ | |
| 12922 InterfaceType get boolType; | |
| 12923 | |
| 12924 /** | |
| 12925 * Return the type representing the type 'bottom'. | |
| 12926 */ | |
| 12927 DartType get bottomType; | |
| 12928 | |
| 12929 /** | |
| 12930 * Return the type representing the built-in type 'Deprecated'. | |
| 12931 */ | |
| 12932 InterfaceType get deprecatedType; | |
| 12933 | |
| 12934 /** | |
| 12935 * Return the type representing the built-in type 'double'. | |
| 12936 */ | |
| 12937 InterfaceType get doubleType; | |
| 12938 | |
| 12939 /** | |
| 12940 * Return the type representing the built-in type 'dynamic'. | |
| 12941 */ | |
| 12942 DartType get dynamicType; | |
| 12943 | |
| 12944 /** | |
| 12945 * Return the type representing the built-in type 'Function'. | |
| 12946 */ | |
| 12947 InterfaceType get functionType; | |
| 12948 | |
| 12949 /** | |
| 12950 * Return the type representing 'Future<dynamic>'. | |
| 12951 */ | |
| 12952 InterfaceType get futureDynamicType; | |
| 12953 | |
| 12954 /** | |
| 12955 * Return the type representing 'Future<Null>'. | |
| 12956 */ | |
| 12957 InterfaceType get futureNullType; | |
| 12958 | |
| 12959 /** | |
| 12960 * Return the type representing the built-in type 'Future'. | |
| 12961 */ | |
| 12962 InterfaceType get futureType; | |
| 12963 | |
| 12964 /** | |
| 12965 * Return the type representing the built-in type 'int'. | |
| 12966 */ | |
| 12967 InterfaceType get intType; | |
| 12968 | |
| 12969 /** | |
| 12970 * Return the type representing the type 'Iterable<dynamic>'. | |
| 12971 */ | |
| 12972 InterfaceType get iterableDynamicType; | |
| 12973 | |
| 12974 /** | |
| 12975 * Return the type representing the built-in type 'Iterable'. | |
| 12976 */ | |
| 12977 InterfaceType get iterableType; | |
| 12978 | |
| 12979 /** | |
| 12980 * Return the type representing the built-in type 'List'. | |
| 12981 */ | |
| 12982 InterfaceType get listType; | |
| 12983 | |
| 12984 /** | |
| 12985 * Return the type representing the built-in type 'Map'. | |
| 12986 */ | |
| 12987 InterfaceType get mapType; | |
| 12988 | |
| 12989 /** | |
| 12990 * Return a list containing all of the types that cannot be either extended or | |
| 12991 * implemented. | |
| 12992 */ | |
| 12993 List<InterfaceType> get nonSubtypableTypes; | |
| 12994 | |
| 12995 /** | |
| 12996 * Return a [DartObjectImpl] representing the `null` object. | |
| 12997 */ | |
| 12998 DartObjectImpl get nullObject; | |
| 12999 | |
| 13000 /** | |
| 13001 * Return the type representing the built-in type 'Null'. | |
| 13002 */ | |
| 13003 InterfaceType get nullType; | |
| 13004 | |
| 13005 /** | |
| 13006 * Return the type representing the built-in type 'num'. | |
| 13007 */ | |
| 13008 InterfaceType get numType; | |
| 13009 | |
| 13010 /** | |
| 13011 * Return the type representing the built-in type 'Object'. | |
| 13012 */ | |
| 13013 InterfaceType get objectType; | |
| 13014 | |
| 13015 /** | |
| 13016 * Return the type representing the built-in type 'StackTrace'. | |
| 13017 */ | |
| 13018 InterfaceType get stackTraceType; | |
| 13019 | |
| 13020 /** | |
| 13021 * Return the type representing 'Stream<dynamic>'. | |
| 13022 */ | |
| 13023 InterfaceType get streamDynamicType; | |
| 13024 | |
| 13025 /** | |
| 13026 * Return the type representing the built-in type 'Stream'. | |
| 13027 */ | |
| 13028 InterfaceType get streamType; | |
| 13029 | |
| 13030 /** | |
| 13031 * Return the type representing the built-in type 'String'. | |
| 13032 */ | |
| 13033 InterfaceType get stringType; | |
| 13034 | |
| 13035 /** | |
| 13036 * Return the type representing the built-in type 'Symbol'. | |
| 13037 */ | |
| 13038 InterfaceType get symbolType; | |
| 13039 | |
| 13040 /** | |
| 13041 * Return the type representing the built-in type 'Type'. | |
| 13042 */ | |
| 13043 InterfaceType get typeType; | |
| 13044 | |
| 13045 /** | |
| 13046 * Return the type representing typenames that can't be resolved. | |
| 13047 */ | |
| 13048 DartType get undefinedType; | |
| 13049 } | |
| 13050 | |
| 13051 /** | |
| 13052 * Instances of the class `TypeProviderImpl` provide access to types defined by
the language | |
| 13053 * by looking for those types in the element model for the core library. | |
| 13054 */ | |
| 13055 class TypeProviderImpl implements TypeProvider { | |
| 13056 /** | |
| 13057 * The type representing the built-in type 'bool'. | |
| 13058 */ | |
| 13059 InterfaceType _boolType; | |
| 13060 | |
| 13061 /** | |
| 13062 * The type representing the type 'bottom'. | |
| 13063 */ | |
| 13064 DartType _bottomType; | |
| 13065 | |
| 13066 /** | |
| 13067 * The type representing the built-in type 'double'. | |
| 13068 */ | |
| 13069 InterfaceType _doubleType; | |
| 13070 | |
| 13071 /** | |
| 13072 * The type representing the built-in type 'Deprecated'. | |
| 13073 */ | |
| 13074 InterfaceType _deprecatedType; | |
| 13075 | |
| 13076 /** | |
| 13077 * The type representing the built-in type 'dynamic'. | |
| 13078 */ | |
| 13079 DartType _dynamicType; | |
| 13080 | |
| 13081 /** | |
| 13082 * The type representing the built-in type 'Function'. | |
| 13083 */ | |
| 13084 InterfaceType _functionType; | |
| 13085 | |
| 13086 /** | |
| 13087 * The type representing 'Future<dynamic>'. | |
| 13088 */ | |
| 13089 InterfaceType _futureDynamicType; | |
| 13090 | |
| 13091 /** | |
| 13092 * The type representing 'Future<Null>'. | |
| 13093 */ | |
| 13094 InterfaceType _futureNullType; | |
| 13095 | |
| 13096 /** | |
| 13097 * The type representing the built-in type 'Future'. | |
| 13098 */ | |
| 13099 InterfaceType _futureType; | |
| 13100 | |
| 13101 /** | |
| 13102 * The type representing the built-in type 'int'. | |
| 13103 */ | |
| 13104 InterfaceType _intType; | |
| 13105 | |
| 13106 /** | |
| 13107 * The type representing 'Iterable<dynamic>'. | |
| 13108 */ | |
| 13109 InterfaceType _iterableDynamicType; | |
| 13110 | |
| 13111 /** | |
| 13112 * The type representing the built-in type 'Iterable'. | |
| 13113 */ | |
| 13114 InterfaceType _iterableType; | |
| 13115 | |
| 13116 /** | |
| 13117 * The type representing the built-in type 'List'. | |
| 13118 */ | |
| 13119 InterfaceType _listType; | |
| 13120 | |
| 13121 /** | |
| 13122 * The type representing the built-in type 'Map'. | |
| 13123 */ | |
| 13124 InterfaceType _mapType; | |
| 13125 | |
| 13126 /** | |
| 13127 * An shared object representing the value 'null'. | |
| 13128 */ | |
| 13129 DartObjectImpl _nullObject; | |
| 13130 | |
| 13131 /** | |
| 13132 * The type representing the type 'Null'. | |
| 13133 */ | |
| 13134 InterfaceType _nullType; | |
| 13135 | |
| 13136 /** | |
| 13137 * The type representing the built-in type 'num'. | |
| 13138 */ | |
| 13139 InterfaceType _numType; | |
| 13140 | |
| 13141 /** | |
| 13142 * The type representing the built-in type 'Object'. | |
| 13143 */ | |
| 13144 InterfaceType _objectType; | |
| 13145 | |
| 13146 /** | |
| 13147 * The type representing the built-in type 'StackTrace'. | |
| 13148 */ | |
| 13149 InterfaceType _stackTraceType; | |
| 13150 | |
| 13151 /** | |
| 13152 * The type representing 'Stream<dynamic>'. | |
| 13153 */ | |
| 13154 InterfaceType _streamDynamicType; | |
| 13155 | |
| 13156 /** | |
| 13157 * The type representing the built-in type 'Stream'. | |
| 13158 */ | |
| 13159 InterfaceType _streamType; | |
| 13160 | |
| 13161 /** | |
| 13162 * The type representing the built-in type 'String'. | |
| 13163 */ | |
| 13164 InterfaceType _stringType; | |
| 13165 | |
| 13166 /** | |
| 13167 * The type representing the built-in type 'Symbol'. | |
| 13168 */ | |
| 13169 InterfaceType _symbolType; | |
| 13170 | |
| 13171 /** | |
| 13172 * The type representing the built-in type 'Type'. | |
| 13173 */ | |
| 13174 InterfaceType _typeType; | |
| 13175 | |
| 13176 /** | |
| 13177 * The type representing typenames that can't be resolved. | |
| 13178 */ | |
| 13179 DartType _undefinedType; | |
| 13180 | |
| 13181 /** | |
| 13182 * Initialize a newly created type provider to provide the types defined in | |
| 13183 * the given [coreLibrary] and [asyncLibrary]. | |
| 13184 */ | |
| 13185 TypeProviderImpl(LibraryElement coreLibrary, LibraryElement asyncLibrary) { | |
| 13186 Namespace coreNamespace = | |
| 13187 new NamespaceBuilder().createPublicNamespaceForLibrary(coreLibrary); | |
| 13188 Namespace asyncNamespace = | |
| 13189 new NamespaceBuilder().createPublicNamespaceForLibrary(asyncLibrary); | |
| 13190 _initializeFrom(coreNamespace, asyncNamespace); | |
| 13191 } | |
| 13192 | |
| 13193 /** | |
| 13194 * Initialize a newly created type provider to provide the types defined in | |
| 13195 * the given [Namespace]s. | |
| 13196 */ | |
| 13197 TypeProviderImpl.forNamespaces( | |
| 13198 Namespace coreNamespace, Namespace asyncNamespace) { | |
| 13199 _initializeFrom(coreNamespace, asyncNamespace); | |
| 13200 } | |
| 13201 | |
| 13202 @override | |
| 13203 InterfaceType get boolType => _boolType; | |
| 13204 | |
| 13205 @override | |
| 13206 DartType get bottomType => _bottomType; | |
| 13207 | |
| 13208 @override | |
| 13209 InterfaceType get deprecatedType => _deprecatedType; | |
| 13210 | |
| 13211 @override | |
| 13212 InterfaceType get doubleType => _doubleType; | |
| 13213 | |
| 13214 @override | |
| 13215 DartType get dynamicType => _dynamicType; | |
| 13216 | |
| 13217 @override | |
| 13218 InterfaceType get functionType => _functionType; | |
| 13219 | |
| 13220 @override | |
| 13221 InterfaceType get futureDynamicType => _futureDynamicType; | |
| 13222 | |
| 13223 @override | |
| 13224 InterfaceType get futureNullType => _futureNullType; | |
| 13225 | |
| 13226 @override | |
| 13227 InterfaceType get futureType => _futureType; | |
| 13228 | |
| 13229 @override | |
| 13230 InterfaceType get intType => _intType; | |
| 13231 | |
| 13232 @override | |
| 13233 InterfaceType get iterableDynamicType => _iterableDynamicType; | |
| 13234 | |
| 13235 @override | |
| 13236 InterfaceType get iterableType => _iterableType; | |
| 13237 | |
| 13238 @override | |
| 13239 InterfaceType get listType => _listType; | |
| 13240 | |
| 13241 @override | |
| 13242 InterfaceType get mapType => _mapType; | |
| 13243 | |
| 13244 @override | |
| 13245 List<InterfaceType> get nonSubtypableTypes => <InterfaceType>[ | |
| 13246 nullType, | |
| 13247 numType, | |
| 13248 intType, | |
| 13249 doubleType, | |
| 13250 boolType, | |
| 13251 stringType | |
| 13252 ]; | |
| 13253 | |
| 13254 @override | |
| 13255 DartObjectImpl get nullObject { | |
| 13256 if (_nullObject == null) { | |
| 13257 _nullObject = new DartObjectImpl(nullType, NullState.NULL_STATE); | |
| 13258 } | |
| 13259 return _nullObject; | |
| 13260 } | |
| 13261 | |
| 13262 @override | |
| 13263 InterfaceType get nullType => _nullType; | |
| 13264 | |
| 13265 @override | |
| 13266 InterfaceType get numType => _numType; | |
| 13267 | |
| 13268 @override | |
| 13269 InterfaceType get objectType => _objectType; | |
| 13270 | |
| 13271 @override | |
| 13272 InterfaceType get stackTraceType => _stackTraceType; | |
| 13273 | |
| 13274 @override | |
| 13275 InterfaceType get streamDynamicType => _streamDynamicType; | |
| 13276 | |
| 13277 @override | |
| 13278 InterfaceType get streamType => _streamType; | |
| 13279 | |
| 13280 @override | |
| 13281 InterfaceType get stringType => _stringType; | |
| 13282 | |
| 13283 @override | |
| 13284 InterfaceType get symbolType => _symbolType; | |
| 13285 | |
| 13286 @override | |
| 13287 InterfaceType get typeType => _typeType; | |
| 13288 | |
| 13289 @override | |
| 13290 DartType get undefinedType => _undefinedType; | |
| 13291 | |
| 13292 /** | |
| 13293 * Return the type with the given name from the given namespace, or `null` if
there is no | |
| 13294 * class with the given name. | |
| 13295 * | |
| 13296 * @param namespace the namespace in which to search for the given name | |
| 13297 * @param typeName the name of the type being searched for | |
| 13298 * @return the type that was found | |
| 13299 */ | |
| 13300 InterfaceType _getType(Namespace namespace, String typeName) { | |
| 13301 Element element = namespace.get(typeName); | |
| 13302 if (element == null) { | |
| 13303 AnalysisEngine.instance.logger | |
| 13304 .logInformation("No definition of type $typeName"); | |
| 13305 return null; | |
| 13306 } | |
| 13307 return (element as ClassElement).type; | |
| 13308 } | |
| 13309 | |
| 13310 /** | |
| 13311 * Initialize the types provided by this type provider from the given | |
| 13312 * [Namespace]s. | |
| 13313 */ | |
| 13314 void _initializeFrom(Namespace coreNamespace, Namespace asyncNamespace) { | |
| 13315 _boolType = _getType(coreNamespace, "bool"); | |
| 13316 _bottomType = BottomTypeImpl.instance; | |
| 13317 _deprecatedType = _getType(coreNamespace, "Deprecated"); | |
| 13318 _doubleType = _getType(coreNamespace, "double"); | |
| 13319 _dynamicType = DynamicTypeImpl.instance; | |
| 13320 _functionType = _getType(coreNamespace, "Function"); | |
| 13321 _futureType = _getType(asyncNamespace, "Future"); | |
| 13322 _intType = _getType(coreNamespace, "int"); | |
| 13323 _iterableType = _getType(coreNamespace, "Iterable"); | |
| 13324 _listType = _getType(coreNamespace, "List"); | |
| 13325 _mapType = _getType(coreNamespace, "Map"); | |
| 13326 _nullType = _getType(coreNamespace, "Null"); | |
| 13327 _numType = _getType(coreNamespace, "num"); | |
| 13328 _objectType = _getType(coreNamespace, "Object"); | |
| 13329 _stackTraceType = _getType(coreNamespace, "StackTrace"); | |
| 13330 _streamType = _getType(asyncNamespace, "Stream"); | |
| 13331 _stringType = _getType(coreNamespace, "String"); | |
| 13332 _symbolType = _getType(coreNamespace, "Symbol"); | |
| 13333 _typeType = _getType(coreNamespace, "Type"); | |
| 13334 _undefinedType = UndefinedTypeImpl.instance; | |
| 13335 _futureDynamicType = _futureType.substitute4(<DartType>[_dynamicType]); | |
| 13336 _futureNullType = _futureType.substitute4(<DartType>[_nullType]); | |
| 13337 _iterableDynamicType = _iterableType.substitute4(<DartType>[_dynamicType]); | |
| 13338 _streamDynamicType = _streamType.substitute4(<DartType>[_dynamicType]); | |
| 13339 } | |
| 13340 } | |
| 13341 | |
| 13342 /** | |
| 13343 * Instances of the class `TypeResolverVisitor` are used to resolve the types as
sociated with | |
| 13344 * the elements in the element model. This includes the types of superclasses, m
ixins, interfaces, | |
| 13345 * fields, methods, parameters, and local variables. As a side-effect, this also
finishes building | |
| 13346 * the type hierarchy. | |
| 13347 */ | |
| 13348 class TypeResolverVisitor extends ScopedVisitor { | |
| 13349 /** | |
| 13350 * The type representing the type 'dynamic'. | |
| 13351 */ | |
| 13352 DartType _dynamicType; | |
| 13353 | |
| 13354 /** | |
| 13355 * The type representing typenames that can't be resolved. | |
| 13356 */ | |
| 13357 DartType _undefinedType; | |
| 13358 | |
| 13359 /** | |
| 13360 * The flag specifying if currently visited class references 'super' expressio
n. | |
| 13361 */ | |
| 13362 bool _hasReferenceToSuper = false; | |
| 13363 | |
| 13364 /** | |
| 13365 * Initialize a newly created visitor to resolve the nodes in an AST node. | |
| 13366 * | |
| 13367 * [definingLibrary] is the element for the library containing the node being | |
| 13368 * visited. | |
| 13369 * [source] is the source representing the compilation unit containing the | |
| 13370 * node being visited. | |
| 13371 * [typeProvider] is the object used to access the types from the core | |
| 13372 * library. | |
| 13373 * [errorListener] is the error listener that will be informed of any errors | |
| 13374 * that are found during resolution. | |
| 13375 * [nameScope] is the scope used to resolve identifiers in the node that will | |
| 13376 * first be visited. If `null` or unspecified, a new [LibraryScope] will be | |
| 13377 * created based on [definingLibrary] and [typeProvider]. | |
| 13378 */ | |
| 13379 TypeResolverVisitor(LibraryElement definingLibrary, Source source, | |
| 13380 TypeProvider typeProvider, AnalysisErrorListener errorListener, | |
| 13381 {Scope nameScope}) | |
| 13382 : super(definingLibrary, source, typeProvider, errorListener, | |
| 13383 nameScope: nameScope) { | |
| 13384 _dynamicType = typeProvider.dynamicType; | |
| 13385 _undefinedType = typeProvider.undefinedType; | |
| 13386 } | |
| 13387 | |
| 13388 @override | |
| 13389 Object visitAnnotation(Annotation node) { | |
| 13390 // | |
| 13391 // Visit annotations, if the annotation is @proxy, on a class, and "proxy" | |
| 13392 // resolves to the proxy annotation in dart.core, then create create the | |
| 13393 // ElementAnnotationImpl and set it as the metadata on the enclosing class. | |
| 13394 // | |
| 13395 // Element resolution is done in the ElementResolver, and this work will be | |
| 13396 // done in the general case for all annotations in the ElementResolver. | |
| 13397 // The reason we resolve this particular element early is so that | |
| 13398 // ClassElement.isProxy() returns the correct information during all | |
| 13399 // phases of the ElementResolver. | |
| 13400 // | |
| 13401 super.visitAnnotation(node); | |
| 13402 Identifier identifier = node.name; | |
| 13403 if (identifier.name.endsWith(ElementAnnotationImpl.PROXY_VARIABLE_NAME) && | |
| 13404 node.parent is ClassDeclaration) { | |
| 13405 Element element = nameScope.lookup(identifier, definingLibrary); | |
| 13406 if (element != null && | |
| 13407 element.library.isDartCore && | |
| 13408 element is PropertyAccessorElement) { | |
| 13409 // This is the @proxy from dart.core | |
| 13410 ClassDeclaration classDeclaration = node.parent as ClassDeclaration; | |
| 13411 ElementAnnotationImpl elementAnnotation = | |
| 13412 new ElementAnnotationImpl(element); | |
| 13413 node.elementAnnotation = elementAnnotation; | |
| 13414 (classDeclaration.element as ClassElementImpl).metadata = | |
| 13415 <ElementAnnotationImpl>[elementAnnotation]; | |
| 13416 } | |
| 13417 } | |
| 13418 return null; | |
| 13419 } | |
| 13420 | |
| 13421 @override | |
| 13422 Object visitCatchClause(CatchClause node) { | |
| 13423 super.visitCatchClause(node); | |
| 13424 SimpleIdentifier exception = node.exceptionParameter; | |
| 13425 if (exception != null) { | |
| 13426 // If an 'on' clause is provided the type of the exception parameter is | |
| 13427 // the type in the 'on' clause. Otherwise, the type of the exception | |
| 13428 // parameter is 'Object'. | |
| 13429 TypeName exceptionTypeName = node.exceptionType; | |
| 13430 DartType exceptionType; | |
| 13431 if (exceptionTypeName == null) { | |
| 13432 exceptionType = typeProvider.dynamicType; | |
| 13433 } else { | |
| 13434 exceptionType = _getType(exceptionTypeName); | |
| 13435 } | |
| 13436 _recordType(exception, exceptionType); | |
| 13437 Element element = exception.staticElement; | |
| 13438 if (element is VariableElementImpl) { | |
| 13439 element.type = exceptionType; | |
| 13440 } else { | |
| 13441 // TODO(brianwilkerson) Report the internal error | |
| 13442 } | |
| 13443 } | |
| 13444 SimpleIdentifier stackTrace = node.stackTraceParameter; | |
| 13445 if (stackTrace != null) { | |
| 13446 _recordType(stackTrace, typeProvider.stackTraceType); | |
| 13447 Element element = stackTrace.staticElement; | |
| 13448 if (element is VariableElementImpl) { | |
| 13449 element.type = typeProvider.stackTraceType; | |
| 13450 } else { | |
| 13451 // TODO(brianwilkerson) Report the internal error | |
| 13452 } | |
| 13453 } | |
| 13454 return null; | |
| 13455 } | |
| 13456 | |
| 13457 @override | |
| 13458 Object visitClassDeclaration(ClassDeclaration node) { | |
| 13459 _hasReferenceToSuper = false; | |
| 13460 super.visitClassDeclaration(node); | |
| 13461 ClassElementImpl classElement = _getClassElement(node.name); | |
| 13462 if (classElement != null) { | |
| 13463 classElement.hasReferenceToSuper = _hasReferenceToSuper; | |
| 13464 } | |
| 13465 return null; | |
| 13466 } | |
| 13467 | |
| 13468 @override | |
| 13469 void visitClassDeclarationInScope(ClassDeclaration node) { | |
| 13470 super.visitClassDeclarationInScope(node); | |
| 13471 ExtendsClause extendsClause = node.extendsClause; | |
| 13472 WithClause withClause = node.withClause; | |
| 13473 ImplementsClause implementsClause = node.implementsClause; | |
| 13474 ClassElementImpl classElement = _getClassElement(node.name); | |
| 13475 InterfaceType superclassType = null; | |
| 13476 if (extendsClause != null) { | |
| 13477 ErrorCode errorCode = (withClause == null | |
| 13478 ? CompileTimeErrorCode.EXTENDS_NON_CLASS | |
| 13479 : CompileTimeErrorCode.MIXIN_WITH_NON_CLASS_SUPERCLASS); | |
| 13480 superclassType = _resolveType(extendsClause.superclass, errorCode, | |
| 13481 CompileTimeErrorCode.EXTENDS_ENUM, errorCode); | |
| 13482 if (!identical(superclassType, typeProvider.objectType)) { | |
| 13483 classElement.validMixin = false; | |
| 13484 } | |
| 13485 } | |
| 13486 if (classElement != null) { | |
| 13487 if (superclassType == null) { | |
| 13488 InterfaceType objectType = typeProvider.objectType; | |
| 13489 if (!identical(classElement.type, objectType)) { | |
| 13490 superclassType = objectType; | |
| 13491 } | |
| 13492 } | |
| 13493 classElement.supertype = superclassType; | |
| 13494 } | |
| 13495 _resolve(classElement, withClause, implementsClause); | |
| 13496 return null; | |
| 13497 } | |
| 13498 | |
| 13499 @override | |
| 13500 void visitClassMembersInScope(ClassDeclaration node) { | |
| 13501 // | |
| 13502 // Process field declarations before constructors and methods so that the | |
| 13503 // types of field formal parameters can be correctly resolved. | |
| 13504 // | |
| 13505 List<ClassMember> nonFields = new List<ClassMember>(); | |
| 13506 node.visitChildren( | |
| 13507 new _TypeResolverVisitor_visitClassMembersInScope(this, nonFields)); | |
| 13508 int count = nonFields.length; | |
| 13509 for (int i = 0; i < count; i++) { | |
| 13510 nonFields[i].accept(this); | |
| 13511 } | |
| 13512 } | |
| 13513 | |
| 13514 @override | |
| 13515 Object visitClassTypeAlias(ClassTypeAlias node) { | |
| 13516 super.visitClassTypeAlias(node); | |
| 13517 ErrorCode errorCode = CompileTimeErrorCode.MIXIN_WITH_NON_CLASS_SUPERCLASS; | |
| 13518 InterfaceType superclassType = _resolveType(node.superclass, errorCode, | |
| 13519 CompileTimeErrorCode.EXTENDS_ENUM, errorCode); | |
| 13520 if (superclassType == null) { | |
| 13521 superclassType = typeProvider.objectType; | |
| 13522 } | |
| 13523 ClassElementImpl classElement = _getClassElement(node.name); | |
| 13524 if (classElement != null) { | |
| 13525 classElement.supertype = superclassType; | |
| 13526 } | |
| 13527 _resolve(classElement, node.withClause, node.implementsClause); | |
| 13528 return null; | |
| 13529 } | |
| 13530 | |
| 13531 @override | |
| 13532 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
| 13533 super.visitConstructorDeclaration(node); | |
| 13534 ExecutableElementImpl element = node.element as ExecutableElementImpl; | |
| 13535 if (element == null) { | |
| 13536 ClassDeclaration classNode = | |
| 13537 node.getAncestor((node) => node is ClassDeclaration); | |
| 13538 StringBuffer buffer = new StringBuffer(); | |
| 13539 buffer.write("The element for the constructor "); | |
| 13540 buffer.write(node.name == null ? "<unnamed>" : node.name.name); | |
| 13541 buffer.write(" in "); | |
| 13542 if (classNode == null) { | |
| 13543 buffer.write("<unknown class>"); | |
| 13544 } else { | |
| 13545 buffer.write(classNode.name.name); | |
| 13546 } | |
| 13547 buffer.write(" in "); | |
| 13548 buffer.write(source.fullName); | |
| 13549 buffer.write(" was not set while trying to resolve types."); | |
| 13550 AnalysisEngine.instance.logger.logError(buffer.toString(), | |
| 13551 new CaughtException(new AnalysisException(), null)); | |
| 13552 } else { | |
| 13553 ClassElement definingClass = element.enclosingElement as ClassElement; | |
| 13554 element.returnType = definingClass.type; | |
| 13555 FunctionTypeImpl type = new FunctionTypeImpl(element); | |
| 13556 type.typeArguments = definingClass.type.typeArguments; | |
| 13557 element.type = type; | |
| 13558 } | |
| 13559 return null; | |
| 13560 } | |
| 13561 | |
| 13562 @override | |
| 13563 Object visitDeclaredIdentifier(DeclaredIdentifier node) { | |
| 13564 super.visitDeclaredIdentifier(node); | |
| 13565 DartType declaredType; | |
| 13566 TypeName typeName = node.type; | |
| 13567 if (typeName == null) { | |
| 13568 declaredType = _dynamicType; | |
| 13569 } else { | |
| 13570 declaredType = _getType(typeName); | |
| 13571 } | |
| 13572 LocalVariableElementImpl element = node.element as LocalVariableElementImpl; | |
| 13573 element.type = declaredType; | |
| 13574 return null; | |
| 13575 } | |
| 13576 | |
| 13577 @override | |
| 13578 Object visitFieldFormalParameter(FieldFormalParameter node) { | |
| 13579 super.visitFieldFormalParameter(node); | |
| 13580 Element element = node.identifier.staticElement; | |
| 13581 if (element is ParameterElementImpl) { | |
| 13582 ParameterElementImpl parameter = element; | |
| 13583 FormalParameterList parameterList = node.parameters; | |
| 13584 if (parameterList == null) { | |
| 13585 DartType type; | |
| 13586 TypeName typeName = node.type; | |
| 13587 if (typeName == null) { | |
| 13588 type = _dynamicType; | |
| 13589 if (parameter is FieldFormalParameterElement) { | |
| 13590 FieldElement fieldElement = | |
| 13591 (parameter as FieldFormalParameterElement).field; | |
| 13592 if (fieldElement != null) { | |
| 13593 type = fieldElement.type; | |
| 13594 } | |
| 13595 } | |
| 13596 } else { | |
| 13597 type = _getType(typeName); | |
| 13598 } | |
| 13599 parameter.type = type; | |
| 13600 } else { | |
| 13601 _setFunctionTypedParameterType(parameter, node.type, node.parameters); | |
| 13602 } | |
| 13603 } else { | |
| 13604 // TODO(brianwilkerson) Report this internal error | |
| 13605 } | |
| 13606 return null; | |
| 13607 } | |
| 13608 | |
| 13609 @override | |
| 13610 Object visitFunctionDeclaration(FunctionDeclaration node) { | |
| 13611 super.visitFunctionDeclaration(node); | |
| 13612 ExecutableElementImpl element = node.element as ExecutableElementImpl; | |
| 13613 if (element == null) { | |
| 13614 StringBuffer buffer = new StringBuffer(); | |
| 13615 buffer.write("The element for the top-level function "); | |
| 13616 buffer.write(node.name); | |
| 13617 buffer.write(" in "); | |
| 13618 buffer.write(source.fullName); | |
| 13619 buffer.write(" was not set while trying to resolve types."); | |
| 13620 AnalysisEngine.instance.logger.logError(buffer.toString(), | |
| 13621 new CaughtException(new AnalysisException(), null)); | |
| 13622 } | |
| 13623 element.returnType = _computeReturnType(node.returnType); | |
| 13624 FunctionTypeImpl type = new FunctionTypeImpl(element); | |
| 13625 ClassElement definingClass = | |
| 13626 element.getAncestor((element) => element is ClassElement); | |
| 13627 if (definingClass != null) { | |
| 13628 type.typeArguments = definingClass.type.typeArguments; | |
| 13629 } | |
| 13630 element.type = type; | |
| 13631 return null; | |
| 13632 } | |
| 13633 | |
| 13634 @override | |
| 13635 Object visitFunctionTypeAlias(FunctionTypeAlias node) { | |
| 13636 FunctionTypeAliasElementImpl element = | |
| 13637 node.element as FunctionTypeAliasElementImpl; | |
| 13638 super.visitFunctionTypeAlias(node); | |
| 13639 element.returnType = _computeReturnType(node.returnType); | |
| 13640 return null; | |
| 13641 } | |
| 13642 | |
| 13643 @override | |
| 13644 Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { | |
| 13645 super.visitFunctionTypedFormalParameter(node); | |
| 13646 Element element = node.identifier.staticElement; | |
| 13647 if (element is ParameterElementImpl) { | |
| 13648 _setFunctionTypedParameterType(element, node.returnType, node.parameters); | |
| 13649 } else { | |
| 13650 // TODO(brianwilkerson) Report this internal error | |
| 13651 } | |
| 13652 return null; | |
| 13653 } | |
| 13654 | |
| 13655 @override | |
| 13656 Object visitMethodDeclaration(MethodDeclaration node) { | |
| 13657 super.visitMethodDeclaration(node); | |
| 13658 ExecutableElementImpl element = node.element as ExecutableElementImpl; | |
| 13659 if (element == null) { | |
| 13660 ClassDeclaration classNode = | |
| 13661 node.getAncestor((node) => node is ClassDeclaration); | |
| 13662 StringBuffer buffer = new StringBuffer(); | |
| 13663 buffer.write("The element for the method "); | |
| 13664 buffer.write(node.name.name); | |
| 13665 buffer.write(" in "); | |
| 13666 if (classNode == null) { | |
| 13667 buffer.write("<unknown class>"); | |
| 13668 } else { | |
| 13669 buffer.write(classNode.name.name); | |
| 13670 } | |
| 13671 buffer.write(" in "); | |
| 13672 buffer.write(source.fullName); | |
| 13673 buffer.write(" was not set while trying to resolve types."); | |
| 13674 AnalysisEngine.instance.logger.logError(buffer.toString(), | |
| 13675 new CaughtException(new AnalysisException(), null)); | |
| 13676 } | |
| 13677 element.returnType = _computeReturnType(node.returnType); | |
| 13678 FunctionTypeImpl type = new FunctionTypeImpl(element); | |
| 13679 ClassElement definingClass = | |
| 13680 element.getAncestor((element) => element is ClassElement); | |
| 13681 if (definingClass != null) { | |
| 13682 type.typeArguments = definingClass.type.typeArguments; | |
| 13683 } | |
| 13684 element.type = type; | |
| 13685 if (element is PropertyAccessorElement) { | |
| 13686 PropertyAccessorElement accessor = element as PropertyAccessorElement; | |
| 13687 PropertyInducingElementImpl variable = | |
| 13688 accessor.variable as PropertyInducingElementImpl; | |
| 13689 if (accessor.isGetter) { | |
| 13690 variable.type = type.returnType; | |
| 13691 } else if (variable.type == null) { | |
| 13692 List<DartType> parameterTypes = type.normalParameterTypes; | |
| 13693 if (parameterTypes != null && parameterTypes.length > 0) { | |
| 13694 variable.type = parameterTypes[0]; | |
| 13695 } | |
| 13696 } | |
| 13697 } | |
| 13698 return null; | |
| 13699 } | |
| 13700 | |
| 13701 @override | |
| 13702 Object visitSimpleFormalParameter(SimpleFormalParameter node) { | |
| 13703 super.visitSimpleFormalParameter(node); | |
| 13704 DartType declaredType; | |
| 13705 TypeName typeName = node.type; | |
| 13706 if (typeName == null) { | |
| 13707 declaredType = _dynamicType; | |
| 13708 } else { | |
| 13709 declaredType = _getType(typeName); | |
| 13710 } | |
| 13711 Element element = node.identifier.staticElement; | |
| 13712 if (element is ParameterElement) { | |
| 13713 (element as ParameterElementImpl).type = declaredType; | |
| 13714 } else { | |
| 13715 // TODO(brianwilkerson) Report the internal error. | |
| 13716 } | |
| 13717 return null; | |
| 13718 } | |
| 13719 | |
| 13720 @override | |
| 13721 Object visitSuperExpression(SuperExpression node) { | |
| 13722 _hasReferenceToSuper = true; | |
| 13723 return super.visitSuperExpression(node); | |
| 13724 } | |
| 13725 | |
| 13726 @override | |
| 13727 Object visitTypeName(TypeName node) { | |
| 13728 super.visitTypeName(node); | |
| 13729 Identifier typeName = node.name; | |
| 13730 TypeArgumentList argumentList = node.typeArguments; | |
| 13731 Element element = nameScope.lookup(typeName, definingLibrary); | |
| 13732 if (element == null) { | |
| 13733 // | |
| 13734 // Check to see whether the type name is either 'dynamic' or 'void', | |
| 13735 // neither of which are in the name scope and hence will not be found by | |
| 13736 // normal means. | |
| 13737 // | |
| 13738 if (typeName.name == _dynamicType.name) { | |
| 13739 _setElement(typeName, _dynamicType.element); | |
| 13740 if (argumentList != null) { | |
| 13741 // TODO(brianwilkerson) Report this error | |
| 13742 // reporter.reportError(StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGU
MENTS, node, dynamicType.getName(), 0, argumentList.getArguments().size()); | |
| 13743 } | |
| 13744 typeName.staticType = _dynamicType; | |
| 13745 node.type = _dynamicType; | |
| 13746 return null; | |
| 13747 } | |
| 13748 VoidTypeImpl voidType = VoidTypeImpl.instance; | |
| 13749 if (typeName.name == voidType.name) { | |
| 13750 // There is no element for 'void'. | |
| 13751 if (argumentList != null) { | |
| 13752 // TODO(brianwilkerson) Report this error | |
| 13753 // reporter.reportError(StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGU
MENTS, node, voidType.getName(), 0, argumentList.getArguments().size()); | |
| 13754 } | |
| 13755 typeName.staticType = voidType; | |
| 13756 node.type = voidType; | |
| 13757 return null; | |
| 13758 } | |
| 13759 // | |
| 13760 // If not, the look to see whether we might have created the wrong AST | |
| 13761 // structure for a constructor name. If so, fix the AST structure and then | |
| 13762 // proceed. | |
| 13763 // | |
| 13764 AstNode parent = node.parent; | |
| 13765 if (typeName is PrefixedIdentifier && | |
| 13766 parent is ConstructorName && | |
| 13767 argumentList == null) { | |
| 13768 ConstructorName name = parent; | |
| 13769 if (name.name == null) { | |
| 13770 PrefixedIdentifier prefixedIdentifier = | |
| 13771 typeName as PrefixedIdentifier; | |
| 13772 SimpleIdentifier prefix = prefixedIdentifier.prefix; | |
| 13773 element = nameScope.lookup(prefix, definingLibrary); | |
| 13774 if (element is PrefixElement) { | |
| 13775 if (parent.parent is InstanceCreationExpression && | |
| 13776 (parent.parent as InstanceCreationExpression).isConst) { | |
| 13777 // If, if this is a const expression, then generate a | |
| 13778 // CompileTimeErrorCode.CONST_WITH_NON_TYPE error. | |
| 13779 reportErrorForNode(CompileTimeErrorCode.CONST_WITH_NON_TYPE, | |
| 13780 prefixedIdentifier.identifier, | |
| 13781 [prefixedIdentifier.identifier.name]); | |
| 13782 } else { | |
| 13783 // Else, if this expression is a new expression, report a | |
| 13784 // NEW_WITH_NON_TYPE warning. | |
| 13785 reportErrorForNode(StaticWarningCode.NEW_WITH_NON_TYPE, | |
| 13786 prefixedIdentifier.identifier, | |
| 13787 [prefixedIdentifier.identifier.name]); | |
| 13788 } | |
| 13789 _setElement(prefix, element); | |
| 13790 return null; | |
| 13791 } else if (element != null) { | |
| 13792 // | |
| 13793 // Rewrite the constructor name. The parser, when it sees a | |
| 13794 // constructor named "a.b", cannot tell whether "a" is a prefix and | |
| 13795 // "b" is a class name, or whether "a" is a class name and "b" is a | |
| 13796 // constructor name. It arbitrarily chooses the former, but in this | |
| 13797 // case was wrong. | |
| 13798 // | |
| 13799 name.name = prefixedIdentifier.identifier; | |
| 13800 name.period = prefixedIdentifier.period; | |
| 13801 node.name = prefix; | |
| 13802 typeName = prefix; | |
| 13803 } | |
| 13804 } | |
| 13805 } | |
| 13806 } | |
| 13807 // check element | |
| 13808 bool elementValid = element is! MultiplyDefinedElement; | |
| 13809 if (elementValid && | |
| 13810 element is! ClassElement && | |
| 13811 _isTypeNameInInstanceCreationExpression(node)) { | |
| 13812 SimpleIdentifier typeNameSimple = _getTypeSimpleIdentifier(typeName); | |
| 13813 InstanceCreationExpression creation = | |
| 13814 node.parent.parent as InstanceCreationExpression; | |
| 13815 if (creation.isConst) { | |
| 13816 if (element == null) { | |
| 13817 reportErrorForNode( | |
| 13818 CompileTimeErrorCode.UNDEFINED_CLASS, typeNameSimple, [typeName]); | |
| 13819 } else { | |
| 13820 reportErrorForNode(CompileTimeErrorCode.CONST_WITH_NON_TYPE, | |
| 13821 typeNameSimple, [typeName]); | |
| 13822 } | |
| 13823 elementValid = false; | |
| 13824 } else { | |
| 13825 if (element != null) { | |
| 13826 reportErrorForNode( | |
| 13827 StaticWarningCode.NEW_WITH_NON_TYPE, typeNameSimple, [typeName]); | |
| 13828 elementValid = false; | |
| 13829 } | |
| 13830 } | |
| 13831 } | |
| 13832 if (elementValid && element == null) { | |
| 13833 // We couldn't resolve the type name. | |
| 13834 // TODO(jwren) Consider moving the check for | |
| 13835 // CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE from the | |
| 13836 // ErrorVerifier, so that we don't have two errors on a built in | |
| 13837 // identifier being used as a class name. | |
| 13838 // See CompileTimeErrorCodeTest.test_builtInIdentifierAsType(). | |
| 13839 SimpleIdentifier typeNameSimple = _getTypeSimpleIdentifier(typeName); | |
| 13840 RedirectingConstructorKind redirectingConstructorKind; | |
| 13841 if (_isBuiltInIdentifier(node) && _isTypeAnnotation(node)) { | |
| 13842 reportErrorForNode(CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE, | |
| 13843 typeName, [typeName.name]); | |
| 13844 } else if (typeNameSimple.name == "boolean") { | |
| 13845 reportErrorForNode( | |
| 13846 StaticWarningCode.UNDEFINED_CLASS_BOOLEAN, typeNameSimple, []); | |
| 13847 } else if (_isTypeNameInCatchClause(node)) { | |
| 13848 reportErrorForNode(StaticWarningCode.NON_TYPE_IN_CATCH_CLAUSE, typeName, | |
| 13849 [typeName.name]); | |
| 13850 } else if (_isTypeNameInAsExpression(node)) { | |
| 13851 reportErrorForNode( | |
| 13852 StaticWarningCode.CAST_TO_NON_TYPE, typeName, [typeName.name]); | |
| 13853 } else if (_isTypeNameInIsExpression(node)) { | |
| 13854 reportErrorForNode(StaticWarningCode.TYPE_TEST_WITH_UNDEFINED_NAME, | |
| 13855 typeName, [typeName.name]); | |
| 13856 } else if ((redirectingConstructorKind = | |
| 13857 _getRedirectingConstructorKind(node)) != | |
| 13858 null) { | |
| 13859 ErrorCode errorCode = (redirectingConstructorKind == | |
| 13860 RedirectingConstructorKind.CONST | |
| 13861 ? CompileTimeErrorCode.REDIRECT_TO_NON_CLASS | |
| 13862 : StaticWarningCode.REDIRECT_TO_NON_CLASS); | |
| 13863 reportErrorForNode(errorCode, typeName, [typeName.name]); | |
| 13864 } else if (_isTypeNameInTypeArgumentList(node)) { | |
| 13865 reportErrorForNode(StaticTypeWarningCode.NON_TYPE_AS_TYPE_ARGUMENT, | |
| 13866 typeName, [typeName.name]); | |
| 13867 } else { | |
| 13868 reportErrorForNode( | |
| 13869 StaticWarningCode.UNDEFINED_CLASS, typeName, [typeName.name]); | |
| 13870 } | |
| 13871 elementValid = false; | |
| 13872 } | |
| 13873 if (!elementValid) { | |
| 13874 if (element is MultiplyDefinedElement) { | |
| 13875 _setElement(typeName, element); | |
| 13876 } else { | |
| 13877 _setElement(typeName, _dynamicType.element); | |
| 13878 } | |
| 13879 typeName.staticType = _undefinedType; | |
| 13880 node.type = _undefinedType; | |
| 13881 return null; | |
| 13882 } | |
| 13883 DartType type = null; | |
| 13884 if (element is ClassElement) { | |
| 13885 _setElement(typeName, element); | |
| 13886 type = element.type; | |
| 13887 } else if (element is FunctionTypeAliasElement) { | |
| 13888 _setElement(typeName, element); | |
| 13889 type = element.type; | |
| 13890 } else if (element is TypeParameterElement) { | |
| 13891 _setElement(typeName, element); | |
| 13892 type = element.type; | |
| 13893 if (argumentList != null) { | |
| 13894 // Type parameters cannot have type arguments. | |
| 13895 // TODO(brianwilkerson) Report this error. | |
| 13896 // resolver.reportError(ResolverErrorCode.?, keyType); | |
| 13897 } | |
| 13898 } else if (element is MultiplyDefinedElement) { | |
| 13899 List<Element> elements = element.conflictingElements; | |
| 13900 type = _getTypeWhenMultiplyDefined(elements); | |
| 13901 if (type != null) { | |
| 13902 node.type = type; | |
| 13903 } | |
| 13904 } else { | |
| 13905 // The name does not represent a type. | |
| 13906 RedirectingConstructorKind redirectingConstructorKind; | |
| 13907 if (_isTypeNameInCatchClause(node)) { | |
| 13908 reportErrorForNode(StaticWarningCode.NON_TYPE_IN_CATCH_CLAUSE, typeName, | |
| 13909 [typeName.name]); | |
| 13910 } else if (_isTypeNameInAsExpression(node)) { | |
| 13911 reportErrorForNode( | |
| 13912 StaticWarningCode.CAST_TO_NON_TYPE, typeName, [typeName.name]); | |
| 13913 } else if (_isTypeNameInIsExpression(node)) { | |
| 13914 reportErrorForNode(StaticWarningCode.TYPE_TEST_WITH_NON_TYPE, typeName, | |
| 13915 [typeName.name]); | |
| 13916 } else if ((redirectingConstructorKind = | |
| 13917 _getRedirectingConstructorKind(node)) != | |
| 13918 null) { | |
| 13919 ErrorCode errorCode = (redirectingConstructorKind == | |
| 13920 RedirectingConstructorKind.CONST | |
| 13921 ? CompileTimeErrorCode.REDIRECT_TO_NON_CLASS | |
| 13922 : StaticWarningCode.REDIRECT_TO_NON_CLASS); | |
| 13923 reportErrorForNode(errorCode, typeName, [typeName.name]); | |
| 13924 } else if (_isTypeNameInTypeArgumentList(node)) { | |
| 13925 reportErrorForNode(StaticTypeWarningCode.NON_TYPE_AS_TYPE_ARGUMENT, | |
| 13926 typeName, [typeName.name]); | |
| 13927 } else { | |
| 13928 AstNode parent = typeName.parent; | |
| 13929 while (parent is TypeName) { | |
| 13930 parent = parent.parent; | |
| 13931 } | |
| 13932 if (parent is ExtendsClause || | |
| 13933 parent is ImplementsClause || | |
| 13934 parent is WithClause || | |
| 13935 parent is ClassTypeAlias) { | |
| 13936 // Ignored. The error will be reported elsewhere. | |
| 13937 } else { | |
| 13938 reportErrorForNode( | |
| 13939 StaticWarningCode.NOT_A_TYPE, typeName, [typeName.name]); | |
| 13940 } | |
| 13941 } | |
| 13942 _setElement(typeName, _dynamicType.element); | |
| 13943 typeName.staticType = _dynamicType; | |
| 13944 node.type = _dynamicType; | |
| 13945 return null; | |
| 13946 } | |
| 13947 if (argumentList != null) { | |
| 13948 NodeList<TypeName> arguments = argumentList.arguments; | |
| 13949 int argumentCount = arguments.length; | |
| 13950 List<DartType> parameters = _getTypeArguments(type); | |
| 13951 int parameterCount = parameters.length; | |
| 13952 List<DartType> typeArguments = new List<DartType>(parameterCount); | |
| 13953 if (argumentCount == parameterCount) { | |
| 13954 for (int i = 0; i < parameterCount; i++) { | |
| 13955 TypeName argumentTypeName = arguments[i]; | |
| 13956 DartType argumentType = _getType(argumentTypeName); | |
| 13957 if (argumentType == null) { | |
| 13958 argumentType = _dynamicType; | |
| 13959 } | |
| 13960 typeArguments[i] = argumentType; | |
| 13961 } | |
| 13962 } else { | |
| 13963 reportErrorForNode(_getInvalidTypeParametersErrorCode(node), node, [ | |
| 13964 typeName.name, | |
| 13965 parameterCount, | |
| 13966 argumentCount | |
| 13967 ]); | |
| 13968 for (int i = 0; i < parameterCount; i++) { | |
| 13969 typeArguments[i] = _dynamicType; | |
| 13970 } | |
| 13971 } | |
| 13972 if (type is InterfaceTypeImpl) { | |
| 13973 InterfaceTypeImpl interfaceType = type as InterfaceTypeImpl; | |
| 13974 type = interfaceType.substitute4(typeArguments); | |
| 13975 } else if (type is FunctionTypeImpl) { | |
| 13976 FunctionTypeImpl functionType = type as FunctionTypeImpl; | |
| 13977 type = functionType.substitute3(typeArguments); | |
| 13978 } else { | |
| 13979 // TODO(brianwilkerson) Report this internal error. | |
| 13980 } | |
| 13981 } else { | |
| 13982 // | |
| 13983 // Check for the case where there are no type arguments given for a | |
| 13984 // parameterized type. | |
| 13985 // | |
| 13986 List<DartType> parameters = _getTypeArguments(type); | |
| 13987 int parameterCount = parameters.length; | |
| 13988 if (parameterCount > 0) { | |
| 13989 DynamicTypeImpl dynamicType = DynamicTypeImpl.instance; | |
| 13990 List<DartType> arguments = new List<DartType>(parameterCount); | |
| 13991 for (int i = 0; i < parameterCount; i++) { | |
| 13992 arguments[i] = dynamicType; | |
| 13993 } | |
| 13994 type = type.substitute2(arguments, parameters); | |
| 13995 } | |
| 13996 } | |
| 13997 typeName.staticType = type; | |
| 13998 node.type = type; | |
| 13999 return null; | |
| 14000 } | |
| 14001 | |
| 14002 @override | |
| 14003 Object visitTypeParameter(TypeParameter node) { | |
| 14004 super.visitTypeParameter(node); | |
| 14005 TypeName bound = node.bound; | |
| 14006 if (bound != null) { | |
| 14007 TypeParameterElementImpl typeParameter = | |
| 14008 node.name.staticElement as TypeParameterElementImpl; | |
| 14009 if (typeParameter != null) { | |
| 14010 typeParameter.bound = bound.type; | |
| 14011 } | |
| 14012 } | |
| 14013 return null; | |
| 14014 } | |
| 14015 | |
| 14016 @override | |
| 14017 Object visitVariableDeclaration(VariableDeclaration node) { | |
| 14018 super.visitVariableDeclaration(node); | |
| 14019 DartType declaredType; | |
| 14020 TypeName typeName = (node.parent as VariableDeclarationList).type; | |
| 14021 if (typeName == null) { | |
| 14022 declaredType = _dynamicType; | |
| 14023 } else { | |
| 14024 declaredType = _getType(typeName); | |
| 14025 } | |
| 14026 Element element = node.name.staticElement; | |
| 14027 if (element is VariableElement) { | |
| 14028 (element as VariableElementImpl).type = declaredType; | |
| 14029 if (element is PropertyInducingElement) { | |
| 14030 PropertyInducingElement variableElement = element; | |
| 14031 PropertyAccessorElementImpl getter = | |
| 14032 variableElement.getter as PropertyAccessorElementImpl; | |
| 14033 getter.returnType = declaredType; | |
| 14034 FunctionTypeImpl getterType = new FunctionTypeImpl(getter); | |
| 14035 ClassElement definingClass = | |
| 14036 element.getAncestor((element) => element is ClassElement); | |
| 14037 if (definingClass != null) { | |
| 14038 getterType.typeArguments = definingClass.type.typeArguments; | |
| 14039 } | |
| 14040 getter.type = getterType; | |
| 14041 PropertyAccessorElementImpl setter = | |
| 14042 variableElement.setter as PropertyAccessorElementImpl; | |
| 14043 if (setter != null) { | |
| 14044 List<ParameterElement> parameters = setter.parameters; | |
| 14045 if (parameters.length > 0) { | |
| 14046 (parameters[0] as ParameterElementImpl).type = declaredType; | |
| 14047 } | |
| 14048 setter.returnType = VoidTypeImpl.instance; | |
| 14049 FunctionTypeImpl setterType = new FunctionTypeImpl(setter); | |
| 14050 if (definingClass != null) { | |
| 14051 setterType.typeArguments = definingClass.type.typeArguments; | |
| 14052 } | |
| 14053 setter.type = setterType; | |
| 14054 } | |
| 14055 } | |
| 14056 } else { | |
| 14057 // TODO(brianwilkerson) Report the internal error. | |
| 14058 } | |
| 14059 return null; | |
| 14060 } | |
| 14061 | |
| 14062 /** | |
| 14063 * Given a type name representing the return type of a function, compute the r
eturn type of the | |
| 14064 * function. | |
| 14065 * | |
| 14066 * @param returnType the type name representing the return type of the functio
n | |
| 14067 * @return the return type that was computed | |
| 14068 */ | |
| 14069 DartType _computeReturnType(TypeName returnType) { | |
| 14070 if (returnType == null) { | |
| 14071 return _dynamicType; | |
| 14072 } else { | |
| 14073 return returnType.type; | |
| 14074 } | |
| 14075 } | |
| 14076 | |
| 14077 /** | |
| 14078 * Return the class element that represents the class whose name was provided. | |
| 14079 * | |
| 14080 * @param identifier the name from the declaration of a class | |
| 14081 * @return the class element that represents the class | |
| 14082 */ | |
| 14083 ClassElementImpl _getClassElement(SimpleIdentifier identifier) { | |
| 14084 // TODO(brianwilkerson) Seems like we should be using | |
| 14085 // ClassDeclaration.getElement(). | |
| 14086 if (identifier == null) { | |
| 14087 // TODO(brianwilkerson) Report this | |
| 14088 // Internal error: We should never build a class declaration without a | |
| 14089 // name. | |
| 14090 return null; | |
| 14091 } | |
| 14092 Element element = identifier.staticElement; | |
| 14093 if (element is! ClassElementImpl) { | |
| 14094 // TODO(brianwilkerson) Report this | |
| 14095 // Internal error: Failed to create an element for a class declaration. | |
| 14096 return null; | |
| 14097 } | |
| 14098 return element as ClassElementImpl; | |
| 14099 } | |
| 14100 | |
| 14101 /** | |
| 14102 * Return an array containing all of the elements associated with the paramete
rs in the given | |
| 14103 * list. | |
| 14104 * | |
| 14105 * @param parameterList the list of parameters whose elements are to be return
ed | |
| 14106 * @return the elements associated with the parameters | |
| 14107 */ | |
| 14108 List<ParameterElement> _getElements(FormalParameterList parameterList) { | |
| 14109 List<ParameterElement> elements = new List<ParameterElement>(); | |
| 14110 for (FormalParameter parameter in parameterList.parameters) { | |
| 14111 ParameterElement element = | |
| 14112 parameter.identifier.staticElement as ParameterElement; | |
| 14113 // TODO(brianwilkerson) Understand why the element would be null. | |
| 14114 if (element != null) { | |
| 14115 elements.add(element); | |
| 14116 } | |
| 14117 } | |
| 14118 return elements; | |
| 14119 } | |
| 14120 | |
| 14121 /** | |
| 14122 * The number of type arguments in the given type name does not match the numb
er of parameters in | |
| 14123 * the corresponding class element. Return the error code that should be used
to report this | |
| 14124 * error. | |
| 14125 * | |
| 14126 * @param node the type name with the wrong number of type arguments | |
| 14127 * @return the error code that should be used to report that the wrong number
of type arguments | |
| 14128 * were provided | |
| 14129 */ | |
| 14130 ErrorCode _getInvalidTypeParametersErrorCode(TypeName node) { | |
| 14131 AstNode parent = node.parent; | |
| 14132 if (parent is ConstructorName) { | |
| 14133 parent = parent.parent; | |
| 14134 if (parent is InstanceCreationExpression) { | |
| 14135 if (parent.isConst) { | |
| 14136 return CompileTimeErrorCode.CONST_WITH_INVALID_TYPE_PARAMETERS; | |
| 14137 } else { | |
| 14138 return StaticWarningCode.NEW_WITH_INVALID_TYPE_PARAMETERS; | |
| 14139 } | |
| 14140 } | |
| 14141 } | |
| 14142 return StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS; | |
| 14143 } | |
| 14144 | |
| 14145 /** | |
| 14146 * Checks if the given type name is the target in a redirected constructor. | |
| 14147 * | |
| 14148 * @param typeName the type name to analyze | |
| 14149 * @return some [RedirectingConstructorKind] if the given type name is used as
the type in a | |
| 14150 * redirected constructor, or `null` otherwise | |
| 14151 */ | |
| 14152 RedirectingConstructorKind _getRedirectingConstructorKind(TypeName typeName) { | |
| 14153 AstNode parent = typeName.parent; | |
| 14154 if (parent is ConstructorName) { | |
| 14155 ConstructorName constructorName = parent as ConstructorName; | |
| 14156 parent = constructorName.parent; | |
| 14157 if (parent is ConstructorDeclaration) { | |
| 14158 if (identical(parent.redirectedConstructor, constructorName)) { | |
| 14159 if (parent.constKeyword != null) { | |
| 14160 return RedirectingConstructorKind.CONST; | |
| 14161 } | |
| 14162 return RedirectingConstructorKind.NORMAL; | |
| 14163 } | |
| 14164 } | |
| 14165 } | |
| 14166 return null; | |
| 14167 } | |
| 14168 | |
| 14169 /** | |
| 14170 * Return the type represented by the given type name. | |
| 14171 * | |
| 14172 * @param typeName the type name representing the type to be returned | |
| 14173 * @return the type represented by the type name | |
| 14174 */ | |
| 14175 DartType _getType(TypeName typeName) { | |
| 14176 DartType type = typeName.type; | |
| 14177 if (type == null) { | |
| 14178 return _undefinedType; | |
| 14179 } | |
| 14180 return type; | |
| 14181 } | |
| 14182 | |
| 14183 /** | |
| 14184 * Return the type arguments associated with the given type. | |
| 14185 * | |
| 14186 * @param type the type whole type arguments are to be returned | |
| 14187 * @return the type arguments associated with the given type | |
| 14188 */ | |
| 14189 List<DartType> _getTypeArguments(DartType type) { | |
| 14190 if (type is InterfaceType) { | |
| 14191 return type.typeArguments; | |
| 14192 } else if (type is FunctionType) { | |
| 14193 return type.typeArguments; | |
| 14194 } | |
| 14195 return DartType.EMPTY_LIST; | |
| 14196 } | |
| 14197 | |
| 14198 /** | |
| 14199 * Returns the simple identifier of the given (may be qualified) type name. | |
| 14200 * | |
| 14201 * @param typeName the (may be qualified) qualified type name | |
| 14202 * @return the simple identifier of the given (may be qualified) type name. | |
| 14203 */ | |
| 14204 SimpleIdentifier _getTypeSimpleIdentifier(Identifier typeName) { | |
| 14205 if (typeName is SimpleIdentifier) { | |
| 14206 return typeName; | |
| 14207 } else { | |
| 14208 return (typeName as PrefixedIdentifier).identifier; | |
| 14209 } | |
| 14210 } | |
| 14211 | |
| 14212 /** | |
| 14213 * Given the multiple elements to which a single name could potentially be res
olved, return the | |
| 14214 * single interface type that should be used, or `null` if there is no clear c
hoice. | |
| 14215 * | |
| 14216 * @param elements the elements to which a single name could potentially be re
solved | |
| 14217 * @return the single interface type that should be used for the type name | |
| 14218 */ | |
| 14219 InterfaceType _getTypeWhenMultiplyDefined(List<Element> elements) { | |
| 14220 InterfaceType type = null; | |
| 14221 for (Element element in elements) { | |
| 14222 if (element is ClassElement) { | |
| 14223 if (type != null) { | |
| 14224 return null; | |
| 14225 } | |
| 14226 type = element.type; | |
| 14227 } | |
| 14228 } | |
| 14229 return type; | |
| 14230 } | |
| 14231 | |
| 14232 /** | |
| 14233 * Checks if the given type name is used as the type in an as expression. | |
| 14234 * | |
| 14235 * @param typeName the type name to analyzer | |
| 14236 * @return `true` if the given type name is used as the type in an as expressi
on | |
| 14237 */ | |
| 14238 bool _isTypeNameInAsExpression(TypeName typeName) { | |
| 14239 AstNode parent = typeName.parent; | |
| 14240 if (parent is AsExpression) { | |
| 14241 AsExpression asExpression = parent; | |
| 14242 return identical(asExpression.type, typeName); | |
| 14243 } | |
| 14244 return false; | |
| 14245 } | |
| 14246 | |
| 14247 /** | |
| 14248 * Checks if the given type name is used as the exception type in a catch clau
se. | |
| 14249 * | |
| 14250 * @param typeName the type name to analyzer | |
| 14251 * @return `true` if the given type name is used as the exception type in a ca
tch clause | |
| 14252 */ | |
| 14253 bool _isTypeNameInCatchClause(TypeName typeName) { | |
| 14254 AstNode parent = typeName.parent; | |
| 14255 if (parent is CatchClause) { | |
| 14256 CatchClause catchClause = parent; | |
| 14257 return identical(catchClause.exceptionType, typeName); | |
| 14258 } | |
| 14259 return false; | |
| 14260 } | |
| 14261 | |
| 14262 /** | |
| 14263 * Checks if the given type name is used as the type in an instance creation e
xpression. | |
| 14264 * | |
| 14265 * @param typeName the type name to analyzer | |
| 14266 * @return `true` if the given type name is used as the type in an instance cr
eation | |
| 14267 * expression | |
| 14268 */ | |
| 14269 bool _isTypeNameInInstanceCreationExpression(TypeName typeName) { | |
| 14270 AstNode parent = typeName.parent; | |
| 14271 if (parent is ConstructorName && | |
| 14272 parent.parent is InstanceCreationExpression) { | |
| 14273 ConstructorName constructorName = parent; | |
| 14274 return constructorName != null && | |
| 14275 identical(constructorName.type, typeName); | |
| 14276 } | |
| 14277 return false; | |
| 14278 } | |
| 14279 | |
| 14280 /** | |
| 14281 * Checks if the given type name is used as the type in an is expression. | |
| 14282 * | |
| 14283 * @param typeName the type name to analyzer | |
| 14284 * @return `true` if the given type name is used as the type in an is expressi
on | |
| 14285 */ | |
| 14286 bool _isTypeNameInIsExpression(TypeName typeName) { | |
| 14287 AstNode parent = typeName.parent; | |
| 14288 if (parent is IsExpression) { | |
| 14289 IsExpression isExpression = parent; | |
| 14290 return identical(isExpression.type, typeName); | |
| 14291 } | |
| 14292 return false; | |
| 14293 } | |
| 14294 | |
| 14295 /** | |
| 14296 * Checks if the given type name used in a type argument list. | |
| 14297 * | |
| 14298 * @param typeName the type name to analyzer | |
| 14299 * @return `true` if the given type name is in a type argument list | |
| 14300 */ | |
| 14301 bool _isTypeNameInTypeArgumentList(TypeName typeName) => | |
| 14302 typeName.parent is TypeArgumentList; | |
| 14303 | |
| 14304 /** | |
| 14305 * Record that the static type of the given node is the given type. | |
| 14306 * | |
| 14307 * @param expression the node whose type is to be recorded | |
| 14308 * @param type the static type of the node | |
| 14309 */ | |
| 14310 Object _recordType(Expression expression, DartType type) { | |
| 14311 if (type == null) { | |
| 14312 expression.staticType = _dynamicType; | |
| 14313 } else { | |
| 14314 expression.staticType = type; | |
| 14315 } | |
| 14316 return null; | |
| 14317 } | |
| 14318 | |
| 14319 /** | |
| 14320 * Resolve the types in the given with and implements clauses and associate th
ose types with the | |
| 14321 * given class element. | |
| 14322 * | |
| 14323 * @param classElement the class element with which the mixin and interface ty
pes are to be | |
| 14324 * associated | |
| 14325 * @param withClause the with clause to be resolved | |
| 14326 * @param implementsClause the implements clause to be resolved | |
| 14327 */ | |
| 14328 void _resolve(ClassElementImpl classElement, WithClause withClause, | |
| 14329 ImplementsClause implementsClause) { | |
| 14330 if (withClause != null) { | |
| 14331 List<InterfaceType> mixinTypes = _resolveTypes(withClause.mixinTypes, | |
| 14332 CompileTimeErrorCode.MIXIN_OF_NON_CLASS, | |
| 14333 CompileTimeErrorCode.MIXIN_OF_ENUM, | |
| 14334 CompileTimeErrorCode.MIXIN_OF_NON_CLASS); | |
| 14335 if (classElement != null) { | |
| 14336 classElement.mixins = mixinTypes; | |
| 14337 classElement.withClauseRange = | |
| 14338 new SourceRange(withClause.offset, withClause.length); | |
| 14339 } | |
| 14340 } | |
| 14341 if (implementsClause != null) { | |
| 14342 NodeList<TypeName> interfaces = implementsClause.interfaces; | |
| 14343 List<InterfaceType> interfaceTypes = _resolveTypes(interfaces, | |
| 14344 CompileTimeErrorCode.IMPLEMENTS_NON_CLASS, | |
| 14345 CompileTimeErrorCode.IMPLEMENTS_ENUM, | |
| 14346 CompileTimeErrorCode.IMPLEMENTS_DYNAMIC); | |
| 14347 if (classElement != null) { | |
| 14348 classElement.interfaces = interfaceTypes; | |
| 14349 } | |
| 14350 // TODO(brianwilkerson) Move the following checks to ErrorVerifier. | |
| 14351 int count = interfaces.length; | |
| 14352 List<bool> detectedRepeatOnIndex = new List<bool>.filled(count, false); | |
| 14353 for (int i = 0; i < detectedRepeatOnIndex.length; i++) { | |
| 14354 detectedRepeatOnIndex[i] = false; | |
| 14355 } | |
| 14356 for (int i = 0; i < count; i++) { | |
| 14357 TypeName typeName = interfaces[i]; | |
| 14358 if (!detectedRepeatOnIndex[i]) { | |
| 14359 Element element = typeName.name.staticElement; | |
| 14360 for (int j = i + 1; j < count; j++) { | |
| 14361 TypeName typeName2 = interfaces[j]; | |
| 14362 Identifier identifier2 = typeName2.name; | |
| 14363 String name2 = identifier2.name; | |
| 14364 Element element2 = identifier2.staticElement; | |
| 14365 if (element != null && element == element2) { | |
| 14366 detectedRepeatOnIndex[j] = true; | |
| 14367 reportErrorForNode( | |
| 14368 CompileTimeErrorCode.IMPLEMENTS_REPEATED, typeName2, [name2]); | |
| 14369 } | |
| 14370 } | |
| 14371 } | |
| 14372 } | |
| 14373 } | |
| 14374 } | |
| 14375 | |
| 14376 /** | |
| 14377 * Return the type specified by the given name. | |
| 14378 * | |
| 14379 * @param typeName the type name specifying the type to be returned | |
| 14380 * @param nonTypeError the error to produce if the type name is defined to be
something other than | |
| 14381 * a type | |
| 14382 * @param enumTypeError the error to produce if the type name is defined to be
an enum | |
| 14383 * @param dynamicTypeError the error to produce if the type name is "dynamic" | |
| 14384 * @return the type specified by the type name | |
| 14385 */ | |
| 14386 InterfaceType _resolveType(TypeName typeName, ErrorCode nonTypeError, | |
| 14387 ErrorCode enumTypeError, ErrorCode dynamicTypeError) { | |
| 14388 DartType type = typeName.type; | |
| 14389 if (type is InterfaceType) { | |
| 14390 ClassElement element = type.element; | |
| 14391 if (element != null && element.isEnum) { | |
| 14392 reportErrorForNode(enumTypeError, typeName); | |
| 14393 return null; | |
| 14394 } | |
| 14395 return type; | |
| 14396 } | |
| 14397 // If the type is not an InterfaceType, then visitTypeName() sets the type | |
| 14398 // to be a DynamicTypeImpl | |
| 14399 Identifier name = typeName.name; | |
| 14400 if (name.name == sc.Keyword.DYNAMIC.syntax) { | |
| 14401 reportErrorForNode(dynamicTypeError, name, [name.name]); | |
| 14402 } else { | |
| 14403 reportErrorForNode(nonTypeError, name, [name.name]); | |
| 14404 } | |
| 14405 return null; | |
| 14406 } | |
| 14407 | |
| 14408 /** | |
| 14409 * Resolve the types in the given list of type names. | |
| 14410 * | |
| 14411 * @param typeNames the type names to be resolved | |
| 14412 * @param nonTypeError the error to produce if the type name is defined to be
something other than | |
| 14413 * a type | |
| 14414 * @param enumTypeError the error to produce if the type name is defined to be
an enum | |
| 14415 * @param dynamicTypeError the error to produce if the type name is "dynamic" | |
| 14416 * @return an array containing all of the types that were resolved. | |
| 14417 */ | |
| 14418 List<InterfaceType> _resolveTypes(NodeList<TypeName> typeNames, | |
| 14419 ErrorCode nonTypeError, ErrorCode enumTypeError, | |
| 14420 ErrorCode dynamicTypeError) { | |
| 14421 List<InterfaceType> types = new List<InterfaceType>(); | |
| 14422 for (TypeName typeName in typeNames) { | |
| 14423 InterfaceType type = | |
| 14424 _resolveType(typeName, nonTypeError, enumTypeError, dynamicTypeError); | |
| 14425 if (type != null) { | |
| 14426 types.add(type); | |
| 14427 } | |
| 14428 } | |
| 14429 return types; | |
| 14430 } | |
| 14431 | |
| 14432 void _setElement(Identifier typeName, Element element) { | |
| 14433 if (element != null) { | |
| 14434 if (typeName is SimpleIdentifier) { | |
| 14435 typeName.staticElement = element; | |
| 14436 } else if (typeName is PrefixedIdentifier) { | |
| 14437 PrefixedIdentifier identifier = typeName; | |
| 14438 identifier.identifier.staticElement = element; | |
| 14439 SimpleIdentifier prefix = identifier.prefix; | |
| 14440 Element prefixElement = nameScope.lookup(prefix, definingLibrary); | |
| 14441 if (prefixElement != null) { | |
| 14442 prefix.staticElement = prefixElement; | |
| 14443 } | |
| 14444 } | |
| 14445 } | |
| 14446 } | |
| 14447 | |
| 14448 /** | |
| 14449 * Given a parameter element, create a function type based on the given return
type and parameter | |
| 14450 * list and associate the created type with the element. | |
| 14451 * | |
| 14452 * @param element the parameter element whose type is to be set | |
| 14453 * @param returnType the (possibly `null`) return type of the function | |
| 14454 * @param parameterList the list of parameters to the function | |
| 14455 */ | |
| 14456 void _setFunctionTypedParameterType(ParameterElementImpl element, | |
| 14457 TypeName returnType, FormalParameterList parameterList) { | |
| 14458 List<ParameterElement> parameters = _getElements(parameterList); | |
| 14459 FunctionTypeAliasElementImpl aliasElement = | |
| 14460 new FunctionTypeAliasElementImpl.forNode(null); | |
| 14461 aliasElement.synthetic = true; | |
| 14462 aliasElement.shareParameters(parameters); | |
| 14463 aliasElement.returnType = _computeReturnType(returnType); | |
| 14464 // FunctionTypeAliasElementImpl assumes the enclosing element is a | |
| 14465 // CompilationUnitElement (because non-synthetic function types can only be | |
| 14466 // declared at top level), so to avoid breaking things, go find the | |
| 14467 // compilation unit element. | |
| 14468 aliasElement.enclosingElement = | |
| 14469 element.getAncestor((element) => element is CompilationUnitElement); | |
| 14470 FunctionTypeImpl type = new FunctionTypeImpl.forTypedef(aliasElement); | |
| 14471 ClassElement definingClass = | |
| 14472 element.getAncestor((element) => element is ClassElement); | |
| 14473 if (definingClass != null) { | |
| 14474 aliasElement.shareTypeParameters(definingClass.typeParameters); | |
| 14475 type.typeArguments = definingClass.type.typeArguments; | |
| 14476 } else { | |
| 14477 FunctionTypeAliasElement alias = | |
| 14478 element.getAncestor((element) => element is FunctionTypeAliasElement); | |
| 14479 while (alias != null && alias.isSynthetic) { | |
| 14480 alias = | |
| 14481 alias.getAncestor((element) => element is FunctionTypeAliasElement); | |
| 14482 } | |
| 14483 if (alias != null) { | |
| 14484 aliasElement.typeParameters = alias.typeParameters; | |
| 14485 type.typeArguments = alias.type.typeArguments; | |
| 14486 } else { | |
| 14487 type.typeArguments = DartType.EMPTY_LIST; | |
| 14488 } | |
| 14489 } | |
| 14490 element.type = type; | |
| 14491 } | |
| 14492 | |
| 14493 /** | |
| 14494 * @return `true` if the name of the given [TypeName] is an built-in identifie
r. | |
| 14495 */ | |
| 14496 static bool _isBuiltInIdentifier(TypeName node) { | |
| 14497 sc.Token token = node.name.beginToken; | |
| 14498 return token.type == sc.TokenType.KEYWORD; | |
| 14499 } | |
| 14500 | |
| 14501 /** | |
| 14502 * @return `true` if given [TypeName] is used as a type annotation. | |
| 14503 */ | |
| 14504 static bool _isTypeAnnotation(TypeName node) { | |
| 14505 AstNode parent = node.parent; | |
| 14506 if (parent is VariableDeclarationList) { | |
| 14507 return identical(parent.type, node); | |
| 14508 } | |
| 14509 if (parent is FieldFormalParameter) { | |
| 14510 return identical(parent.type, node); | |
| 14511 } | |
| 14512 if (parent is SimpleFormalParameter) { | |
| 14513 return identical(parent.type, node); | |
| 14514 } | |
| 14515 return false; | |
| 14516 } | |
| 14517 } | |
| 14518 | |
| 14519 /** | |
| 14520 * The interface `TypeSystem` defines the behavior of an object representing | |
| 14521 * the type system. This provides a common location to put methods that act on | |
| 14522 * types but may need access to more global data structures, and it paves the | |
| 14523 * way for a possible future where we may wish to make the type system | |
| 14524 * pluggable. | |
| 14525 */ | |
| 14526 abstract class TypeSystem { | |
| 14527 /** | |
| 14528 * Return the [TypeProvider] associated with this [TypeSystem]. | |
| 14529 */ | |
| 14530 TypeProvider get typeProvider; | |
| 14531 | |
| 14532 /** | |
| 14533 * Compute the least upper bound of two types. | |
| 14534 */ | |
| 14535 DartType getLeastUpperBound(DartType type1, DartType type2); | |
| 14536 } | |
| 14537 | |
| 14538 /** | |
| 14539 * Implementation of [TypeSystem] using the rules in the Dart specification. | |
| 14540 */ | |
| 14541 class TypeSystemImpl implements TypeSystem { | |
| 14542 @override | |
| 14543 final TypeProvider typeProvider; | |
| 14544 | |
| 14545 TypeSystemImpl(this.typeProvider); | |
| 14546 | |
| 14547 @override | |
| 14548 DartType getLeastUpperBound(DartType type1, DartType type2) { | |
| 14549 // The least upper bound relation is reflexive. | |
| 14550 if (identical(type1, type2)) { | |
| 14551 return type1; | |
| 14552 } | |
| 14553 // The least upper bound of dynamic and any type T is dynamic. | |
| 14554 if (type1.isDynamic) { | |
| 14555 return type1; | |
| 14556 } | |
| 14557 if (type2.isDynamic) { | |
| 14558 return type2; | |
| 14559 } | |
| 14560 // The least upper bound of void and any type T != dynamic is void. | |
| 14561 if (type1.isVoid) { | |
| 14562 return type1; | |
| 14563 } | |
| 14564 if (type2.isVoid) { | |
| 14565 return type2; | |
| 14566 } | |
| 14567 // The least upper bound of bottom and any type T is T. | |
| 14568 if (type1.isBottom) { | |
| 14569 return type2; | |
| 14570 } | |
| 14571 if (type2.isBottom) { | |
| 14572 return type1; | |
| 14573 } | |
| 14574 // Let U be a type variable with upper bound B. The least upper bound of U | |
| 14575 // and a type T is the least upper bound of B and T. | |
| 14576 while (type1 is TypeParameterType) { | |
| 14577 // TODO(paulberry): is this correct in the complex of F-bounded | |
| 14578 // polymorphism? | |
| 14579 DartType bound = (type1 as TypeParameterType).element.bound; | |
| 14580 if (bound == null) { | |
| 14581 bound = typeProvider.objectType; | |
| 14582 } | |
| 14583 type1 = bound; | |
| 14584 } | |
| 14585 while (type2 is TypeParameterType) { | |
| 14586 // TODO(paulberry): is this correct in the context of F-bounded | |
| 14587 // polymorphism? | |
| 14588 DartType bound = (type2 as TypeParameterType).element.bound; | |
| 14589 if (bound == null) { | |
| 14590 bound = typeProvider.objectType; | |
| 14591 } | |
| 14592 type2 = bound; | |
| 14593 } | |
| 14594 // The least upper bound of a function type and an interface type T is the | |
| 14595 // least upper bound of Function and T. | |
| 14596 if (type1 is FunctionType && type2 is InterfaceType) { | |
| 14597 type1 = typeProvider.functionType; | |
| 14598 } | |
| 14599 if (type2 is FunctionType && type1 is InterfaceType) { | |
| 14600 type2 = typeProvider.functionType; | |
| 14601 } | |
| 14602 | |
| 14603 // At this point type1 and type2 should both either be interface types or | |
| 14604 // function types. | |
| 14605 if (type1 is InterfaceType && type2 is InterfaceType) { | |
| 14606 InterfaceType result = | |
| 14607 InterfaceTypeImpl.computeLeastUpperBound(type1, type2); | |
| 14608 if (result == null) { | |
| 14609 return typeProvider.dynamicType; | |
| 14610 } | |
| 14611 return result; | |
| 14612 } else if (type1 is FunctionType && type2 is FunctionType) { | |
| 14613 FunctionType result = | |
| 14614 FunctionTypeImpl.computeLeastUpperBound(type1, type2); | |
| 14615 if (result == null) { | |
| 14616 return typeProvider.functionType; | |
| 14617 } | |
| 14618 return result; | |
| 14619 } else { | |
| 14620 // Should never happen. As a defensive measure, return the dynamic type. | |
| 14621 assert(false); | |
| 14622 return typeProvider.dynamicType; | |
| 14623 } | |
| 14624 } | |
| 14625 } | |
| 14626 | |
| 14627 /** | |
| 14628 * Instances of the class [UnusedLocalElementsVerifier] traverse an element | |
| 14629 * structure looking for cases of [HintCode.UNUSED_ELEMENT], | |
| 14630 * [HintCode.UNUSED_FIELD], [HintCode.UNUSED_LOCAL_VARIABLE], etc. | |
| 14631 */ | |
| 14632 class UnusedLocalElementsVerifier extends RecursiveElementVisitor { | |
| 14633 /** | |
| 14634 * The error listener to which errors will be reported. | |
| 14635 */ | |
| 14636 final AnalysisErrorListener _errorListener; | |
| 14637 | |
| 14638 /** | |
| 14639 * The elements know to be used. | |
| 14640 */ | |
| 14641 final UsedLocalElements _usedElements; | |
| 14642 | |
| 14643 /** | |
| 14644 * Create a new instance of the [UnusedLocalElementsVerifier]. | |
| 14645 */ | |
| 14646 UnusedLocalElementsVerifier(this._errorListener, this._usedElements); | |
| 14647 | |
| 14648 @override | |
| 14649 visitClassElement(ClassElement element) { | |
| 14650 if (!_isUsedElement(element)) { | |
| 14651 _reportErrorForElement(HintCode.UNUSED_ELEMENT, element, [ | |
| 14652 element.kind.displayName, | |
| 14653 element.displayName | |
| 14654 ]); | |
| 14655 } | |
| 14656 super.visitClassElement(element); | |
| 14657 } | |
| 14658 | |
| 14659 @override | |
| 14660 visitFieldElement(FieldElement element) { | |
| 14661 if (!_isReadMember(element)) { | |
| 14662 _reportErrorForElement( | |
| 14663 HintCode.UNUSED_FIELD, element, [element.displayName]); | |
| 14664 } | |
| 14665 super.visitFieldElement(element); | |
| 14666 } | |
| 14667 | |
| 14668 @override | |
| 14669 visitFunctionElement(FunctionElement element) { | |
| 14670 if (!_isUsedElement(element)) { | |
| 14671 _reportErrorForElement(HintCode.UNUSED_ELEMENT, element, [ | |
| 14672 element.kind.displayName, | |
| 14673 element.displayName | |
| 14674 ]); | |
| 14675 } | |
| 14676 super.visitFunctionElement(element); | |
| 14677 } | |
| 14678 | |
| 14679 @override | |
| 14680 visitFunctionTypeAliasElement(FunctionTypeAliasElement element) { | |
| 14681 if (!_isUsedElement(element)) { | |
| 14682 _reportErrorForElement(HintCode.UNUSED_ELEMENT, element, [ | |
| 14683 element.kind.displayName, | |
| 14684 element.displayName | |
| 14685 ]); | |
| 14686 } | |
| 14687 super.visitFunctionTypeAliasElement(element); | |
| 14688 } | |
| 14689 | |
| 14690 @override | |
| 14691 visitLocalVariableElement(LocalVariableElement element) { | |
| 14692 if (!_isUsedElement(element) && !_isNamedUnderscore(element)) { | |
| 14693 HintCode errorCode; | |
| 14694 if (_usedElements.isCatchException(element)) { | |
| 14695 errorCode = HintCode.UNUSED_CATCH_CLAUSE; | |
| 14696 } else if (_usedElements.isCatchStackTrace(element)) { | |
| 14697 errorCode = HintCode.UNUSED_CATCH_STACK; | |
| 14698 } else { | |
| 14699 errorCode = HintCode.UNUSED_LOCAL_VARIABLE; | |
| 14700 } | |
| 14701 _reportErrorForElement(errorCode, element, [element.displayName]); | |
| 14702 } | |
| 14703 } | |
| 14704 | |
| 14705 @override | |
| 14706 visitMethodElement(MethodElement element) { | |
| 14707 if (!_isUsedMember(element)) { | |
| 14708 _reportErrorForElement(HintCode.UNUSED_ELEMENT, element, [ | |
| 14709 element.kind.displayName, | |
| 14710 element.displayName | |
| 14711 ]); | |
| 14712 } | |
| 14713 super.visitMethodElement(element); | |
| 14714 } | |
| 14715 | |
| 14716 @override | |
| 14717 visitPropertyAccessorElement(PropertyAccessorElement element) { | |
| 14718 if (!_isUsedMember(element)) { | |
| 14719 _reportErrorForElement(HintCode.UNUSED_ELEMENT, element, [ | |
| 14720 element.kind.displayName, | |
| 14721 element.displayName | |
| 14722 ]); | |
| 14723 } | |
| 14724 super.visitPropertyAccessorElement(element); | |
| 14725 } | |
| 14726 | |
| 14727 bool _isNamedUnderscore(LocalVariableElement element) { | |
| 14728 String name = element.name; | |
| 14729 if (name != null) { | |
| 14730 for (int index = name.length - 1; index >= 0; --index) { | |
| 14731 if (name.codeUnitAt(index) != 0x5F) { | |
| 14732 // 0x5F => '_' | |
| 14733 return false; | |
| 14734 } | |
| 14735 } | |
| 14736 return true; | |
| 14737 } | |
| 14738 return false; | |
| 14739 } | |
| 14740 | |
| 14741 bool _isReadMember(Element element) { | |
| 14742 if (element.isPublic) { | |
| 14743 return true; | |
| 14744 } | |
| 14745 if (element.isSynthetic) { | |
| 14746 return true; | |
| 14747 } | |
| 14748 return _usedElements.readMembers.contains(element.displayName); | |
| 14749 } | |
| 14750 | |
| 14751 bool _isUsedElement(Element element) { | |
| 14752 if (element.isSynthetic) { | |
| 14753 return true; | |
| 14754 } | |
| 14755 if (element is LocalVariableElement || | |
| 14756 element is FunctionElement && !element.isStatic) { | |
| 14757 // local variable or function | |
| 14758 } else { | |
| 14759 if (element.isPublic) { | |
| 14760 return true; | |
| 14761 } | |
| 14762 } | |
| 14763 return _usedElements.elements.contains(element); | |
| 14764 } | |
| 14765 | |
| 14766 bool _isUsedMember(Element element) { | |
| 14767 if (element.isPublic) { | |
| 14768 return true; | |
| 14769 } | |
| 14770 if (element.isSynthetic) { | |
| 14771 return true; | |
| 14772 } | |
| 14773 if (_usedElements.members.contains(element.displayName)) { | |
| 14774 return true; | |
| 14775 } | |
| 14776 return _usedElements.elements.contains(element); | |
| 14777 } | |
| 14778 | |
| 14779 void _reportErrorForElement( | |
| 14780 ErrorCode errorCode, Element element, List<Object> arguments) { | |
| 14781 if (element != null) { | |
| 14782 _errorListener.onError(new AnalysisError(element.source, | |
| 14783 element.nameOffset, element.displayName.length, errorCode, | |
| 14784 arguments)); | |
| 14785 } | |
| 14786 } | |
| 14787 } | |
| 14788 | |
| 14789 /** | |
| 14790 * A container with information about used imports prefixes and used imported | |
| 14791 * elements. | |
| 14792 */ | |
| 14793 class UsedImportedElements { | |
| 14794 /** | |
| 14795 * The set of referenced [PrefixElement]s. | |
| 14796 */ | |
| 14797 final Set<PrefixElement> prefixes = new HashSet<PrefixElement>(); | |
| 14798 | |
| 14799 /** | |
| 14800 * The set of referenced top-level [Element]s. | |
| 14801 */ | |
| 14802 final Set<Element> elements = new HashSet<Element>(); | |
| 14803 } | |
| 14804 | |
| 14805 /** | |
| 14806 * A container with sets of used [Element]s. | |
| 14807 * All these elements are defined in a single compilation unit or a library. | |
| 14808 */ | |
| 14809 class UsedLocalElements { | |
| 14810 /** | |
| 14811 * Resolved, locally defined elements that are used or potentially can be | |
| 14812 * used. | |
| 14813 */ | |
| 14814 final HashSet<Element> elements = new HashSet<Element>(); | |
| 14815 | |
| 14816 /** | |
| 14817 * [LocalVariableElement]s that represent exceptions in [CatchClause]s. | |
| 14818 */ | |
| 14819 final HashSet<LocalVariableElement> catchExceptionElements = | |
| 14820 new HashSet<LocalVariableElement>(); | |
| 14821 | |
| 14822 /** | |
| 14823 * [LocalVariableElement]s that represent stack traces in [CatchClause]s. | |
| 14824 */ | |
| 14825 final HashSet<LocalVariableElement> catchStackTraceElements = | |
| 14826 new HashSet<LocalVariableElement>(); | |
| 14827 | |
| 14828 /** | |
| 14829 * Names of resolved or unresolved class members that are referenced in the | |
| 14830 * library. | |
| 14831 */ | |
| 14832 final HashSet<String> members = new HashSet<String>(); | |
| 14833 | |
| 14834 /** | |
| 14835 * Names of resolved or unresolved class members that are read in the | |
| 14836 * library. | |
| 14837 */ | |
| 14838 final HashSet<String> readMembers = new HashSet<String>(); | |
| 14839 | |
| 14840 UsedLocalElements(); | |
| 14841 | |
| 14842 factory UsedLocalElements.merge(List<UsedLocalElements> parts) { | |
| 14843 UsedLocalElements result = new UsedLocalElements(); | |
| 14844 for (UsedLocalElements part in parts) { | |
| 14845 result.elements.addAll(part.elements); | |
| 14846 result.catchExceptionElements.addAll(part.catchExceptionElements); | |
| 14847 result.catchStackTraceElements.addAll(part.catchStackTraceElements); | |
| 14848 result.members.addAll(part.members); | |
| 14849 result.readMembers.addAll(part.readMembers); | |
| 14850 } | |
| 14851 return result; | |
| 14852 } | |
| 14853 | |
| 14854 void addCatchException(LocalVariableElement element) { | |
| 14855 if (element != null) { | |
| 14856 catchExceptionElements.add(element); | |
| 14857 } | |
| 14858 } | |
| 14859 | |
| 14860 void addCatchStackTrace(LocalVariableElement element) { | |
| 14861 if (element != null) { | |
| 14862 catchStackTraceElements.add(element); | |
| 14863 } | |
| 14864 } | |
| 14865 | |
| 14866 void addElement(Element element) { | |
| 14867 if (element != null) { | |
| 14868 elements.add(element); | |
| 14869 } | |
| 14870 } | |
| 14871 | |
| 14872 bool isCatchException(LocalVariableElement element) { | |
| 14873 return catchExceptionElements.contains(element); | |
| 14874 } | |
| 14875 | |
| 14876 bool isCatchStackTrace(LocalVariableElement element) { | |
| 14877 return catchStackTraceElements.contains(element); | |
| 14878 } | |
| 14879 } | |
| 14880 | |
| 14881 /** | |
| 14882 * Instances of the class `VariableResolverVisitor` are used to resolve | |
| 14883 * [SimpleIdentifier]s to local variables and formal parameters. | |
| 14884 */ | |
| 14885 class VariableResolverVisitor extends ScopedVisitor { | |
| 14886 /** | |
| 14887 * The method or function that we are currently visiting, or `null` if we are
not inside a | |
| 14888 * method or function. | |
| 14889 */ | |
| 14890 ExecutableElement _enclosingFunction; | |
| 14891 | |
| 14892 /** | |
| 14893 * Initialize a newly created visitor to resolve the nodes in an AST node. | |
| 14894 * | |
| 14895 * [definingLibrary] is the element for the library containing the node being | |
| 14896 * visited. | |
| 14897 * [source] is the source representing the compilation unit containing the | |
| 14898 * node being visited | |
| 14899 * [typeProvider] is the object used to access the types from the core | |
| 14900 * library. | |
| 14901 * [errorListener] is the error listener that will be informed of any errors | |
| 14902 * that are found during resolution. | |
| 14903 * [nameScope] is the scope used to resolve identifiers in the node that will | |
| 14904 * first be visited. If `null` or unspecified, a new [LibraryScope] will be | |
| 14905 * created based on [definingLibrary] and [typeProvider]. | |
| 14906 */ | |
| 14907 VariableResolverVisitor(LibraryElement definingLibrary, Source source, | |
| 14908 TypeProvider typeProvider, AnalysisErrorListener errorListener, | |
| 14909 {Scope nameScope}) | |
| 14910 : super(definingLibrary, source, typeProvider, errorListener, | |
| 14911 nameScope: nameScope); | |
| 14912 | |
| 14913 /** | |
| 14914 * Initialize a newly created visitor to resolve the nodes in a compilation un
it. | |
| 14915 * | |
| 14916 * @param library the library containing the compilation unit being resolved | |
| 14917 * @param source the source representing the compilation unit being visited | |
| 14918 * @param typeProvider the object used to access the types from the core libra
ry | |
| 14919 * | |
| 14920 * Deprecated. Please use unnamed constructor instead. | |
| 14921 */ | |
| 14922 @deprecated | |
| 14923 VariableResolverVisitor.con1( | |
| 14924 Library library, Source source, TypeProvider typeProvider) | |
| 14925 : this( | |
| 14926 library.libraryElement, source, typeProvider, library.errorListener, | |
| 14927 nameScope: library.libraryScope); | |
| 14928 | |
| 14929 @override | |
| 14930 Object visitExportDirective(ExportDirective node) => null; | |
| 14931 | |
| 14932 @override | |
| 14933 Object visitFunctionDeclaration(FunctionDeclaration node) { | |
| 14934 ExecutableElement outerFunction = _enclosingFunction; | |
| 14935 try { | |
| 14936 _enclosingFunction = node.element; | |
| 14937 return super.visitFunctionDeclaration(node); | |
| 14938 } finally { | |
| 14939 _enclosingFunction = outerFunction; | |
| 14940 } | |
| 14941 } | |
| 14942 | |
| 14943 @override | |
| 14944 Object visitFunctionExpression(FunctionExpression node) { | |
| 14945 if (node.parent is! FunctionDeclaration) { | |
| 14946 ExecutableElement outerFunction = _enclosingFunction; | |
| 14947 try { | |
| 14948 _enclosingFunction = node.element; | |
| 14949 return super.visitFunctionExpression(node); | |
| 14950 } finally { | |
| 14951 _enclosingFunction = outerFunction; | |
| 14952 } | |
| 14953 } else { | |
| 14954 return super.visitFunctionExpression(node); | |
| 14955 } | |
| 14956 } | |
| 14957 | |
| 14958 @override | |
| 14959 Object visitImportDirective(ImportDirective node) => null; | |
| 14960 | |
| 14961 @override | |
| 14962 Object visitMethodDeclaration(MethodDeclaration node) { | |
| 14963 ExecutableElement outerFunction = _enclosingFunction; | |
| 14964 try { | |
| 14965 _enclosingFunction = node.element; | |
| 14966 return super.visitMethodDeclaration(node); | |
| 14967 } finally { | |
| 14968 _enclosingFunction = outerFunction; | |
| 14969 } | |
| 14970 } | |
| 14971 | |
| 14972 @override | |
| 14973 Object visitSimpleIdentifier(SimpleIdentifier node) { | |
| 14974 // Ignore if already resolved - declaration or type. | |
| 14975 if (node.staticElement != null) { | |
| 14976 return null; | |
| 14977 } | |
| 14978 // Ignore if qualified. | |
| 14979 AstNode parent = node.parent; | |
| 14980 if (parent is PrefixedIdentifier && identical(parent.identifier, node)) { | |
| 14981 return null; | |
| 14982 } | |
| 14983 if (parent is PropertyAccess && identical(parent.propertyName, node)) { | |
| 14984 return null; | |
| 14985 } | |
| 14986 if (parent is MethodInvocation && | |
| 14987 identical(parent.methodName, node) && | |
| 14988 parent.realTarget != null) { | |
| 14989 return null; | |
| 14990 } | |
| 14991 if (parent is ConstructorName) { | |
| 14992 return null; | |
| 14993 } | |
| 14994 if (parent is Label) { | |
| 14995 return null; | |
| 14996 } | |
| 14997 // Prepare VariableElement. | |
| 14998 Element element = nameScope.lookup(node, definingLibrary); | |
| 14999 if (element is! VariableElement) { | |
| 15000 return null; | |
| 15001 } | |
| 15002 // Must be local or parameter. | |
| 15003 ElementKind kind = element.kind; | |
| 15004 if (kind == ElementKind.LOCAL_VARIABLE) { | |
| 15005 node.staticElement = element; | |
| 15006 LocalVariableElementImpl variableImpl = | |
| 15007 element as LocalVariableElementImpl; | |
| 15008 if (node.inSetterContext()) { | |
| 15009 variableImpl.markPotentiallyMutatedInScope(); | |
| 15010 if (element.enclosingElement != _enclosingFunction) { | |
| 15011 variableImpl.markPotentiallyMutatedInClosure(); | |
| 15012 } | |
| 15013 } | |
| 15014 } else if (kind == ElementKind.PARAMETER) { | |
| 15015 node.staticElement = element; | |
| 15016 if (node.inSetterContext()) { | |
| 15017 ParameterElementImpl parameterImpl = element as ParameterElementImpl; | |
| 15018 parameterImpl.markPotentiallyMutatedInScope(); | |
| 15019 // If we are in some closure, check if it is not the same as where | |
| 15020 // variable is declared. | |
| 15021 if (_enclosingFunction != null && | |
| 15022 (element.enclosingElement != _enclosingFunction)) { | |
| 15023 parameterImpl.markPotentiallyMutatedInClosure(); | |
| 15024 } | |
| 15025 } | |
| 15026 } | |
| 15027 return null; | |
| 15028 } | |
| 15029 } | |
| 15030 | |
| 15031 class _ConstantVerifier_validateInitializerExpression extends ConstantVisitor { | |
| 15032 final ConstantVerifier verifier; | |
| 15033 | |
| 15034 List<ParameterElement> parameterElements; | |
| 15035 | |
| 15036 _ConstantVerifier_validateInitializerExpression(TypeProvider typeProvider, | |
| 15037 ErrorReporter errorReporter, this.verifier, this.parameterElements, | |
| 15038 DeclaredVariables declaredVariables) | |
| 15039 : super(new ConstantEvaluationEngine(typeProvider, declaredVariables), | |
| 15040 errorReporter); | |
| 15041 | |
| 15042 @override | |
| 15043 DartObjectImpl visitSimpleIdentifier(SimpleIdentifier node) { | |
| 15044 Element element = node.staticElement; | |
| 15045 for (ParameterElement parameterElement in parameterElements) { | |
| 15046 if (identical(parameterElement, element) && parameterElement != null) { | |
| 15047 DartType type = parameterElement.type; | |
| 15048 if (type != null) { | |
| 15049 if (type.isDynamic) { | |
| 15050 return new DartObjectImpl( | |
| 15051 verifier._typeProvider.objectType, DynamicState.DYNAMIC_STATE); | |
| 15052 } else if (type.isSubtypeOf(verifier._boolType)) { | |
| 15053 return new DartObjectImpl( | |
| 15054 verifier._typeProvider.boolType, BoolState.UNKNOWN_VALUE); | |
| 15055 } else if (type.isSubtypeOf(verifier._typeProvider.doubleType)) { | |
| 15056 return new DartObjectImpl( | |
| 15057 verifier._typeProvider.doubleType, DoubleState.UNKNOWN_VALUE); | |
| 15058 } else if (type.isSubtypeOf(verifier._intType)) { | |
| 15059 return new DartObjectImpl( | |
| 15060 verifier._typeProvider.intType, IntState.UNKNOWN_VALUE); | |
| 15061 } else if (type.isSubtypeOf(verifier._numType)) { | |
| 15062 return new DartObjectImpl( | |
| 15063 verifier._typeProvider.numType, NumState.UNKNOWN_VALUE); | |
| 15064 } else if (type.isSubtypeOf(verifier._stringType)) { | |
| 15065 return new DartObjectImpl( | |
| 15066 verifier._typeProvider.stringType, StringState.UNKNOWN_VALUE); | |
| 15067 } | |
| 15068 // | |
| 15069 // We don't test for other types of objects (such as List, Map, | |
| 15070 // Function or Type) because there are no operations allowed on such | |
| 15071 // types other than '==' and '!=', which means that we don't need to | |
| 15072 // know the type when there is no specific data about the state of | |
| 15073 // such objects. | |
| 15074 // | |
| 15075 } | |
| 15076 return new DartObjectImpl( | |
| 15077 type is InterfaceType ? type : verifier._typeProvider.objectType, | |
| 15078 GenericState.UNKNOWN_VALUE); | |
| 15079 } | |
| 15080 } | |
| 15081 return super.visitSimpleIdentifier(node); | |
| 15082 } | |
| 15083 } | |
| 15084 | |
| 15085 class _ElementBuilder_visitClassDeclaration extends UnifyingAstVisitor<Object> { | |
| 15086 final ElementBuilder builder; | |
| 15087 | |
| 15088 List<ClassMember> nonFields; | |
| 15089 | |
| 15090 _ElementBuilder_visitClassDeclaration(this.builder, this.nonFields) : super(); | |
| 15091 | |
| 15092 @override | |
| 15093 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
| 15094 nonFields.add(node); | |
| 15095 return null; | |
| 15096 } | |
| 15097 | |
| 15098 @override | |
| 15099 Object visitMethodDeclaration(MethodDeclaration node) { | |
| 15100 nonFields.add(node); | |
| 15101 return null; | |
| 15102 } | |
| 15103 | |
| 15104 @override | |
| 15105 Object visitNode(AstNode node) => node.accept(builder); | |
| 15106 } | |
| 15107 | |
| 15108 class _ResolverVisitor_isVariableAccessedInClosure | |
| 15109 extends RecursiveAstVisitor<Object> { | |
| 15110 final Element variable; | |
| 15111 | |
| 15112 bool result = false; | |
| 15113 | |
| 15114 bool _inClosure = false; | |
| 15115 | |
| 15116 _ResolverVisitor_isVariableAccessedInClosure(this.variable); | |
| 15117 | |
| 15118 @override | |
| 15119 Object visitFunctionExpression(FunctionExpression node) { | |
| 15120 bool inClosure = this._inClosure; | |
| 15121 try { | |
| 15122 this._inClosure = true; | |
| 15123 return super.visitFunctionExpression(node); | |
| 15124 } finally { | |
| 15125 this._inClosure = inClosure; | |
| 15126 } | |
| 15127 } | |
| 15128 | |
| 15129 @override | |
| 15130 Object visitSimpleIdentifier(SimpleIdentifier node) { | |
| 15131 if (result) { | |
| 15132 return null; | |
| 15133 } | |
| 15134 if (_inClosure && identical(node.staticElement, variable)) { | |
| 15135 result = true; | |
| 15136 } | |
| 15137 return null; | |
| 15138 } | |
| 15139 } | |
| 15140 | |
| 15141 class _ResolverVisitor_isVariablePotentiallyMutatedIn | |
| 15142 extends RecursiveAstVisitor<Object> { | |
| 15143 final Element variable; | |
| 15144 | |
| 15145 bool result = false; | |
| 15146 | |
| 15147 _ResolverVisitor_isVariablePotentiallyMutatedIn(this.variable); | |
| 15148 | |
| 15149 @override | |
| 15150 Object visitSimpleIdentifier(SimpleIdentifier node) { | |
| 15151 if (result) { | |
| 15152 return null; | |
| 15153 } | |
| 15154 if (identical(node.staticElement, variable)) { | |
| 15155 if (node.inSetterContext()) { | |
| 15156 result = true; | |
| 15157 } | |
| 15158 } | |
| 15159 return null; | |
| 15160 } | |
| 15161 } | |
| 15162 | |
| 15163 class _TypeResolverVisitor_visitClassMembersInScope | |
| 15164 extends UnifyingAstVisitor<Object> { | |
| 15165 final TypeResolverVisitor TypeResolverVisitor_this; | |
| 15166 | |
| 15167 List<ClassMember> nonFields; | |
| 15168 | |
| 15169 _TypeResolverVisitor_visitClassMembersInScope( | |
| 15170 this.TypeResolverVisitor_this, this.nonFields) | |
| 15171 : super(); | |
| 15172 | |
| 15173 @override | |
| 15174 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
| 15175 nonFields.add(node); | |
| 15176 return null; | |
| 15177 } | |
| 15178 | |
| 15179 @override | |
| 15180 Object visitExtendsClause(ExtendsClause node) => null; | |
| 15181 | |
| 15182 @override | |
| 15183 Object visitImplementsClause(ImplementsClause node) => null; | |
| 15184 | |
| 15185 @override | |
| 15186 Object visitMethodDeclaration(MethodDeclaration node) { | |
| 15187 nonFields.add(node); | |
| 15188 return null; | |
| 15189 } | |
| 15190 | |
| 15191 @override | |
| 15192 Object visitNode(AstNode node) => node.accept(TypeResolverVisitor_this); | |
| 15193 | |
| 15194 @override | |
| 15195 Object visitWithClause(WithClause node) => null; | |
| 15196 } | |
| OLD | NEW |