| 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.error_verifier; |
| 6 |
| 7 import 'dart:collection'; |
| 8 import "dart:math" as math; |
| 9 |
| 10 import 'java_engine.dart'; |
| 11 import 'error.dart'; |
| 12 import 'scanner.dart' as sc; |
| 13 import 'utilities_dart.dart'; |
| 14 import 'ast.dart'; |
| 15 import 'parser.dart' show Parser, ParserErrorCode; |
| 16 import 'sdk.dart' show DartSdk, SdkLibrary; |
| 17 import 'element.dart'; |
| 18 import 'constant.dart'; |
| 19 import 'resolver.dart'; |
| 20 import 'element_resolver.dart'; |
| 21 |
| 22 /** |
| 23 * Instances of the class `ErrorVerifier` traverse an AST structure looking for
additional |
| 24 * errors and warnings not covered by the parser and resolver. |
| 25 */ |
| 26 class ErrorVerifier extends RecursiveAstVisitor<Object> { |
| 27 /** |
| 28 * Return the static type of the given expression that is to be used for type
analysis. |
| 29 * |
| 30 * @param expression the expression whose type is to be returned |
| 31 * @return the static type of the given expression |
| 32 */ |
| 33 static DartType getStaticType(Expression expression) { |
| 34 DartType type = expression.staticType; |
| 35 if (type == null) { |
| 36 // TODO(brianwilkerson) This should never happen. |
| 37 return DynamicTypeImpl.instance; |
| 38 } |
| 39 return type; |
| 40 } |
| 41 |
| 42 /** |
| 43 * Return the variable element represented by the given expression, or `null`
if there is no |
| 44 * such element. |
| 45 * |
| 46 * @param expression the expression whose element is to be returned |
| 47 * @return the variable element represented by the expression |
| 48 */ |
| 49 static VariableElement getVariableElement(Expression expression) { |
| 50 if (expression is Identifier) { |
| 51 Element element = expression.staticElement; |
| 52 if (element is VariableElement) { |
| 53 return element; |
| 54 } |
| 55 } |
| 56 return null; |
| 57 } |
| 58 |
| 59 /** |
| 60 * The error reporter by which errors will be reported. |
| 61 */ |
| 62 final ErrorReporter _errorReporter; |
| 63 |
| 64 /** |
| 65 * The current library that is being analyzed. |
| 66 */ |
| 67 final LibraryElement _currentLibrary; |
| 68 |
| 69 /** |
| 70 * The type representing the type 'bool'. |
| 71 */ |
| 72 InterfaceType _boolType; |
| 73 |
| 74 /** |
| 75 * The type representing the type 'int'. |
| 76 */ |
| 77 InterfaceType _intType; |
| 78 |
| 79 /** |
| 80 * The object providing access to the types defined by the language. |
| 81 */ |
| 82 final TypeProvider _typeProvider; |
| 83 |
| 84 /** |
| 85 * The manager for the inheritance mappings. |
| 86 */ |
| 87 final InheritanceManager _inheritanceManager; |
| 88 |
| 89 /** |
| 90 * This is set to `true` iff the visitor is currently visiting children nodes
of a |
| 91 * [ConstructorDeclaration] and the constructor is 'const'. |
| 92 * |
| 93 * @see #visitConstructorDeclaration(ConstructorDeclaration) |
| 94 */ |
| 95 bool _isEnclosingConstructorConst = false; |
| 96 |
| 97 /** |
| 98 * A flag indicating whether we are currently within a function body marked as
being asynchronous. |
| 99 */ |
| 100 bool _inAsync = false; |
| 101 |
| 102 /** |
| 103 * A flag indicating whether we are currently within a function body marked as
being a generator. |
| 104 */ |
| 105 bool _inGenerator = false; |
| 106 |
| 107 /** |
| 108 * This is set to `true` iff the visitor is currently visiting children nodes
of a |
| 109 * [CatchClause]. |
| 110 * |
| 111 * @see #visitCatchClause(CatchClause) |
| 112 */ |
| 113 bool _isInCatchClause = false; |
| 114 |
| 115 /** |
| 116 * This is set to `true` iff the visitor is currently visiting children nodes
of an |
| 117 * [Comment]. |
| 118 */ |
| 119 bool _isInComment = false; |
| 120 |
| 121 /** |
| 122 * This is set to `true` iff the visitor is currently visiting children nodes
of an |
| 123 * [InstanceCreationExpression]. |
| 124 */ |
| 125 bool _isInConstInstanceCreation = false; |
| 126 |
| 127 /** |
| 128 * This is set to `true` iff the visitor is currently visiting children nodes
of a native |
| 129 * [ClassDeclaration]. |
| 130 */ |
| 131 bool _isInNativeClass = false; |
| 132 |
| 133 /** |
| 134 * This is set to `true` iff the visitor is currently visiting a static variab
le |
| 135 * declaration. |
| 136 */ |
| 137 bool _isInStaticVariableDeclaration = false; |
| 138 |
| 139 /** |
| 140 * This is set to `true` iff the visitor is currently visiting an instance var
iable |
| 141 * declaration. |
| 142 */ |
| 143 bool _isInInstanceVariableDeclaration = false; |
| 144 |
| 145 /** |
| 146 * This is set to `true` iff the visitor is currently visiting an instance var
iable |
| 147 * initializer. |
| 148 */ |
| 149 bool _isInInstanceVariableInitializer = false; |
| 150 |
| 151 /** |
| 152 * This is set to `true` iff the visitor is currently visiting a |
| 153 * [ConstructorInitializer]. |
| 154 */ |
| 155 bool _isInConstructorInitializer = false; |
| 156 |
| 157 /** |
| 158 * This is set to `true` iff the visitor is currently visiting a |
| 159 * [FunctionTypedFormalParameter]. |
| 160 */ |
| 161 bool _isInFunctionTypedFormalParameter = false; |
| 162 |
| 163 /** |
| 164 * This is set to `true` iff the visitor is currently visiting a static method
. By "method" |
| 165 * here getter, setter and operator declarations are also implied since they a
re all represented |
| 166 * with a [MethodDeclaration] in the AST structure. |
| 167 */ |
| 168 bool _isInStaticMethod = false; |
| 169 |
| 170 /** |
| 171 * This is set to `true` iff the visitor is currently visiting a factory const
ructor. |
| 172 */ |
| 173 bool _isInFactory = false; |
| 174 |
| 175 /** |
| 176 * This is set to `true` iff the visitor is currently visiting code in the SDK
. |
| 177 */ |
| 178 bool _isInSystemLibrary = false; |
| 179 |
| 180 /** |
| 181 * A flag indicating whether the current library contains at least one import
directive with a URI |
| 182 * that uses the "dart-ext" scheme. |
| 183 */ |
| 184 bool _hasExtUri = false; |
| 185 |
| 186 /** |
| 187 * This is set to `false` on the entry of every [BlockFunctionBody], and is re
stored |
| 188 * to the enclosing value on exit. The value is used in |
| 189 * [checkForMixedReturns] to prevent both |
| 190 * [StaticWarningCode#MIXED_RETURN_TYPES] and [StaticWarningCode#RETURN_WITHOU
T_VALUE] |
| 191 * from being generated in the same function body. |
| 192 */ |
| 193 bool _hasReturnWithoutValue = false; |
| 194 |
| 195 /** |
| 196 * The class containing the AST nodes being visited, or `null` if we are not i
n the scope of |
| 197 * a class. |
| 198 */ |
| 199 ClassElement _enclosingClass; |
| 200 |
| 201 /** |
| 202 * The method or function that we are currently visiting, or `null` if we are
not inside a |
| 203 * method or function. |
| 204 */ |
| 205 ExecutableElement _enclosingFunction; |
| 206 |
| 207 /** |
| 208 * The return statements found in the method or function that we are currently
visiting that have |
| 209 * a return value. |
| 210 */ |
| 211 List<ReturnStatement> _returnsWith = new List<ReturnStatement>(); |
| 212 |
| 213 /** |
| 214 * The return statements found in the method or function that we are currently
visiting that do |
| 215 * not have a return value. |
| 216 */ |
| 217 List<ReturnStatement> _returnsWithout = new List<ReturnStatement>(); |
| 218 |
| 219 /** |
| 220 * This map is initialized when visiting the contents of a class declaration.
If the visitor is |
| 221 * not in an enclosing class declaration, then the map is set to `null`. |
| 222 * |
| 223 * When set the map maps the set of [FieldElement]s in the class to an |
| 224 * [INIT_STATE#NOT_INIT] or [INIT_STATE#INIT_IN_DECLARATION]. <code>checkFor*<
/code> |
| 225 * methods, specifically [checkForAllFinalInitializedErrorCodes], |
| 226 * can make a copy of the map to compute error code states. <code>checkFor*</c
ode> methods should |
| 227 * only ever make a copy, or read from this map after it has been set in |
| 228 * [visitClassDeclaration]. |
| 229 * |
| 230 * @see #visitClassDeclaration(ClassDeclaration) |
| 231 * @see #checkForAllFinalInitializedErrorCodes(ConstructorDeclaration) |
| 232 */ |
| 233 HashMap<FieldElement, INIT_STATE> _initialFieldElementsMap; |
| 234 |
| 235 /** |
| 236 * A table mapping name of the library to the export directive which export th
is library. |
| 237 */ |
| 238 HashMap<String, LibraryElement> _nameToExportElement = new HashMap<String, Lib
raryElement>(); |
| 239 |
| 240 /** |
| 241 * A table mapping name of the library to the import directive which import th
is library. |
| 242 */ |
| 243 HashMap<String, LibraryElement> _nameToImportElement = new HashMap<String, Lib
raryElement>(); |
| 244 |
| 245 /** |
| 246 * A table mapping names to the exported elements. |
| 247 */ |
| 248 HashMap<String, Element> _exportedElements = new HashMap<String, Element>(); |
| 249 |
| 250 /** |
| 251 * A set of the names of the variable initializers we are visiting now. |
| 252 */ |
| 253 HashSet<String> _namesForReferenceToDeclaredVariableInInitializer = new HashSe
t<String>(); |
| 254 |
| 255 /** |
| 256 * A list of types used by the [CompileTimeErrorCode#EXTENDS_DISALLOWED_CLASS]
and |
| 257 * [CompileTimeErrorCode#IMPLEMENTS_DISALLOWED_CLASS] error codes. |
| 258 */ |
| 259 List<InterfaceType> _DISALLOWED_TYPES_TO_EXTEND_OR_IMPLEMENT; |
| 260 |
| 261 /** |
| 262 * Static final string with value `"getter "` used in the construction of the |
| 263 * [StaticWarningCode#NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE], and si
milar, error |
| 264 * code messages. |
| 265 * |
| 266 * @see #checkForNonAbstractClassInheritsAbstractMember(ClassDeclaration) |
| 267 */ |
| 268 static String _GETTER_SPACE = "getter "; |
| 269 |
| 270 /** |
| 271 * Static final string with value `"setter "` used in the construction of the |
| 272 * [StaticWarningCode#NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE], and si
milar, error |
| 273 * code messages. |
| 274 * |
| 275 * @see #checkForNonAbstractClassInheritsAbstractMember(ClassDeclaration) |
| 276 */ |
| 277 static String _SETTER_SPACE = "setter "; |
| 278 |
| 279 /** |
| 280 * Initialize the [ErrorVerifier] visitor. |
| 281 */ |
| 282 ErrorVerifier(this._errorReporter, this._currentLibrary, this._typeProvider, t
his._inheritanceManager) { |
| 283 this._isInSystemLibrary = _currentLibrary.source.isInSystemLibrary; |
| 284 this._hasExtUri = _currentLibrary.hasExtUri; |
| 285 _isEnclosingConstructorConst = false; |
| 286 _isInCatchClause = false; |
| 287 _isInStaticVariableDeclaration = false; |
| 288 _isInInstanceVariableDeclaration = false; |
| 289 _isInInstanceVariableInitializer = false; |
| 290 _isInConstructorInitializer = false; |
| 291 _isInStaticMethod = false; |
| 292 _boolType = _typeProvider.boolType; |
| 293 _intType = _typeProvider.intType; |
| 294 _DISALLOWED_TYPES_TO_EXTEND_OR_IMPLEMENT = <InterfaceType> [ |
| 295 _typeProvider.nullType, |
| 296 _typeProvider.numType, |
| 297 _intType, |
| 298 _typeProvider.doubleType, |
| 299 _boolType, |
| 300 _typeProvider.stringType]; |
| 301 } |
| 302 |
| 303 @override |
| 304 Object visitAnnotation(Annotation node) { |
| 305 _checkForInvalidAnnotationFromDeferredLibrary(node); |
| 306 return super.visitAnnotation(node); |
| 307 } |
| 308 |
| 309 @override |
| 310 Object visitArgumentList(ArgumentList node) { |
| 311 _checkForArgumentTypesNotAssignableInList(node); |
| 312 return super.visitArgumentList(node); |
| 313 } |
| 314 |
| 315 @override |
| 316 Object visitAsExpression(AsExpression node) { |
| 317 _checkForTypeAnnotationDeferredClass(node.type); |
| 318 return super.visitAsExpression(node); |
| 319 } |
| 320 |
| 321 @override |
| 322 Object visitAssertStatement(AssertStatement node) { |
| 323 _checkForNonBoolExpression(node); |
| 324 return super.visitAssertStatement(node); |
| 325 } |
| 326 |
| 327 @override |
| 328 Object visitAssignmentExpression(AssignmentExpression node) { |
| 329 sc.TokenType operatorType = node.operator.type; |
| 330 Expression lhs = node.leftHandSide; |
| 331 Expression rhs = node.rightHandSide; |
| 332 if (operatorType == sc.TokenType.EQ) { |
| 333 _checkForInvalidAssignment(lhs, rhs); |
| 334 } else { |
| 335 _checkForInvalidCompoundAssignment(node, lhs, rhs); |
| 336 _checkForArgumentTypeNotAssignableForArgument(rhs); |
| 337 } |
| 338 _checkForAssignmentToFinal(lhs); |
| 339 return super.visitAssignmentExpression(node); |
| 340 } |
| 341 |
| 342 @override |
| 343 Object visitAwaitExpression(AwaitExpression node) { |
| 344 if (!_inAsync) { |
| 345 _errorReporter.reportErrorForToken(CompileTimeErrorCode.AWAIT_IN_WRONG_CON
TEXT, node.awaitKeyword, []); |
| 346 } |
| 347 return super.visitAwaitExpression(node); |
| 348 } |
| 349 |
| 350 @override |
| 351 Object visitBinaryExpression(BinaryExpression node) { |
| 352 sc.Token operator = node.operator; |
| 353 sc.TokenType type = operator.type; |
| 354 if (type == sc.TokenType.AMPERSAND_AMPERSAND || type == sc.TokenType.BAR_BAR
) { |
| 355 String lexeme = operator.lexeme; |
| 356 _checkForAssignability(node.leftOperand, _boolType, StaticTypeWarningCode.
NON_BOOL_OPERAND, [lexeme]); |
| 357 _checkForAssignability(node.rightOperand, _boolType, StaticTypeWarningCode
.NON_BOOL_OPERAND, [lexeme]); |
| 358 } else { |
| 359 _checkForArgumentTypeNotAssignableForArgument(node.rightOperand); |
| 360 } |
| 361 return super.visitBinaryExpression(node); |
| 362 } |
| 363 |
| 364 @override |
| 365 Object visitBlockFunctionBody(BlockFunctionBody node) { |
| 366 bool wasInAsync = _inAsync; |
| 367 bool wasInGenerator = _inGenerator; |
| 368 bool previousHasReturnWithoutValue = _hasReturnWithoutValue; |
| 369 _hasReturnWithoutValue = false; |
| 370 List<ReturnStatement> previousReturnsWith = _returnsWith; |
| 371 List<ReturnStatement> previousReturnsWithout = _returnsWithout; |
| 372 try { |
| 373 _inAsync = node.isAsynchronous; |
| 374 _inGenerator = node.isGenerator; |
| 375 _returnsWith = new List<ReturnStatement>(); |
| 376 _returnsWithout = new List<ReturnStatement>(); |
| 377 super.visitBlockFunctionBody(node); |
| 378 _checkForMixedReturns(node); |
| 379 } finally { |
| 380 _inAsync = wasInAsync; |
| 381 _inGenerator = wasInGenerator; |
| 382 _returnsWith = previousReturnsWith; |
| 383 _returnsWithout = previousReturnsWithout; |
| 384 _hasReturnWithoutValue = previousHasReturnWithoutValue; |
| 385 } |
| 386 return null; |
| 387 } |
| 388 |
| 389 @override |
| 390 Object visitBreakStatement(BreakStatement node) { |
| 391 SimpleIdentifier labelNode = node.label; |
| 392 if (labelNode != null) { |
| 393 Element labelElement = labelNode.staticElement; |
| 394 if (labelElement is LabelElementImpl && labelElement.isOnSwitchMember) { |
| 395 _errorReporter.reportErrorForNode(ResolverErrorCode.BREAK_LABEL_ON_SWITC
H_MEMBER, labelNode, []); |
| 396 } |
| 397 } |
| 398 return null; |
| 399 } |
| 400 |
| 401 @override |
| 402 Object visitCatchClause(CatchClause node) { |
| 403 bool previousIsInCatchClause = _isInCatchClause; |
| 404 try { |
| 405 _isInCatchClause = true; |
| 406 _checkForTypeAnnotationDeferredClass(node.exceptionType); |
| 407 return super.visitCatchClause(node); |
| 408 } finally { |
| 409 _isInCatchClause = previousIsInCatchClause; |
| 410 } |
| 411 } |
| 412 |
| 413 @override |
| 414 Object visitClassDeclaration(ClassDeclaration node) { |
| 415 ClassElement outerClass = _enclosingClass; |
| 416 try { |
| 417 _isInNativeClass = node.nativeClause != null; |
| 418 _enclosingClass = node.element; |
| 419 ExtendsClause extendsClause = node.extendsClause; |
| 420 ImplementsClause implementsClause = node.implementsClause; |
| 421 WithClause withClause = node.withClause; |
| 422 _checkForBuiltInIdentifierAsName(node.name, CompileTimeErrorCode.BUILT_IN_
IDENTIFIER_AS_TYPE_NAME); |
| 423 _checkForMemberWithClassName(); |
| 424 _checkForNoDefaultSuperConstructorImplicit(node); |
| 425 _checkForConflictingTypeVariableErrorCodes(node); |
| 426 // Only do error checks on the clause nodes if there is a non-null clause |
| 427 if (implementsClause != null || extendsClause != null || withClause != nul
l) { |
| 428 // Only check for all of the inheritance logic around clauses if there i
sn't an error code |
| 429 // such as "Cannot extend double" already on the class. |
| 430 if (!_checkForImplementsDisallowedClass(implementsClause) && !_checkForE
xtendsDisallowedClass(extendsClause) && !_checkForAllMixinErrorCodes(withClause)
) { |
| 431 _checkForExtendsDeferredClass(extendsClause); |
| 432 _checkForImplementsDeferredClass(implementsClause); |
| 433 _checkForNonAbstractClassInheritsAbstractMember(node.name); |
| 434 _checkForInconsistentMethodInheritance(); |
| 435 _checkForRecursiveInterfaceInheritance(_enclosingClass); |
| 436 _checkForConflictingGetterAndMethod(); |
| 437 _checkForConflictingInstanceGetterAndSuperclassMember(); |
| 438 _checkImplementsSuperClass(node); |
| 439 _checkImplementsFunctionWithoutCall(node); |
| 440 } |
| 441 } |
| 442 // initialize initialFieldElementsMap |
| 443 if (_enclosingClass != null) { |
| 444 List<FieldElement> fieldElements = _enclosingClass.fields; |
| 445 _initialFieldElementsMap = new HashMap<FieldElement, INIT_STATE>(); |
| 446 for (FieldElement fieldElement in fieldElements) { |
| 447 if (!fieldElement.isSynthetic) { |
| 448 _initialFieldElementsMap[fieldElement] = fieldElement.initializer ==
null ? INIT_STATE.NOT_INIT : INIT_STATE.INIT_IN_DECLARATION; |
| 449 } |
| 450 } |
| 451 } |
| 452 _checkForFinalNotInitializedInClass(node); |
| 453 _checkForDuplicateDefinitionInheritance(); |
| 454 _checkForConflictingInstanceMethodSetter(node); |
| 455 return super.visitClassDeclaration(node); |
| 456 } finally { |
| 457 _isInNativeClass = false; |
| 458 _initialFieldElementsMap = null; |
| 459 _enclosingClass = outerClass; |
| 460 } |
| 461 } |
| 462 |
| 463 @override |
| 464 Object visitClassTypeAlias(ClassTypeAlias node) { |
| 465 _checkForBuiltInIdentifierAsName(node.name, CompileTimeErrorCode.BUILT_IN_ID
ENTIFIER_AS_TYPEDEF_NAME); |
| 466 ClassElement outerClassElement = _enclosingClass; |
| 467 try { |
| 468 _enclosingClass = node.element; |
| 469 ImplementsClause implementsClause = node.implementsClause; |
| 470 // Only check for all of the inheritance logic around clauses if there isn
't an error code |
| 471 // such as "Cannot extend double" already on the class. |
| 472 if (!_checkForExtendsDisallowedClassInTypeAlias(node) && !_checkForImpleme
ntsDisallowedClass(implementsClause) && !_checkForAllMixinErrorCodes(node.withCl
ause)) { |
| 473 _checkForExtendsDeferredClassInTypeAlias(node); |
| 474 _checkForImplementsDeferredClass(implementsClause); |
| 475 _checkForRecursiveInterfaceInheritance(_enclosingClass); |
| 476 _checkForNonAbstractClassInheritsAbstractMember(node.name); |
| 477 } |
| 478 } finally { |
| 479 _enclosingClass = outerClassElement; |
| 480 } |
| 481 return super.visitClassTypeAlias(node); |
| 482 } |
| 483 |
| 484 @override |
| 485 Object visitComment(Comment node) { |
| 486 _isInComment = true; |
| 487 try { |
| 488 return super.visitComment(node); |
| 489 } finally { |
| 490 _isInComment = false; |
| 491 } |
| 492 } |
| 493 |
| 494 @override |
| 495 Object visitCompilationUnit(CompilationUnit node) { |
| 496 _checkForDeferredPrefixCollisions(node); |
| 497 return super.visitCompilationUnit(node); |
| 498 } |
| 499 |
| 500 @override |
| 501 Object visitConditionalExpression(ConditionalExpression node) { |
| 502 _checkForNonBoolCondition(node.condition); |
| 503 return super.visitConditionalExpression(node); |
| 504 } |
| 505 |
| 506 @override |
| 507 Object visitConstructorDeclaration(ConstructorDeclaration node) { |
| 508 ExecutableElement outerFunction = _enclosingFunction; |
| 509 try { |
| 510 ConstructorElement constructorElement = node.element; |
| 511 _enclosingFunction = constructorElement; |
| 512 _isEnclosingConstructorConst = node.constKeyword != null; |
| 513 _isInFactory = node.factoryKeyword != null; |
| 514 _checkForInvalidModifierOnBody(node.body, CompileTimeErrorCode.INVALID_MOD
IFIER_ON_CONSTRUCTOR); |
| 515 _checkForConstConstructorWithNonFinalField(node, constructorElement); |
| 516 _checkForConstConstructorWithNonConstSuper(node); |
| 517 _checkForConflictingConstructorNameAndMember(node, constructorElement); |
| 518 _checkForAllFinalInitializedErrorCodes(node); |
| 519 _checkForRedirectingConstructorErrorCodes(node); |
| 520 _checkForMultipleSuperInitializers(node); |
| 521 _checkForRecursiveConstructorRedirect(node, constructorElement); |
| 522 if (!_checkForRecursiveFactoryRedirect(node, constructorElement)) { |
| 523 _checkForAllRedirectConstructorErrorCodes(node); |
| 524 } |
| 525 _checkForUndefinedConstructorInInitializerImplicit(node); |
| 526 _checkForRedirectToNonConstConstructor(node, constructorElement); |
| 527 _checkForReturnInGenerativeConstructor(node); |
| 528 return super.visitConstructorDeclaration(node); |
| 529 } finally { |
| 530 _isEnclosingConstructorConst = false; |
| 531 _isInFactory = false; |
| 532 _enclosingFunction = outerFunction; |
| 533 } |
| 534 } |
| 535 |
| 536 @override |
| 537 Object visitConstructorFieldInitializer(ConstructorFieldInitializer node) { |
| 538 _isInConstructorInitializer = true; |
| 539 try { |
| 540 SimpleIdentifier fieldName = node.fieldName; |
| 541 Element staticElement = fieldName.staticElement; |
| 542 _checkForInvalidField(node, fieldName, staticElement); |
| 543 _checkForFieldInitializerNotAssignable(node, staticElement); |
| 544 return super.visitConstructorFieldInitializer(node); |
| 545 } finally { |
| 546 _isInConstructorInitializer = false; |
| 547 } |
| 548 } |
| 549 |
| 550 @override |
| 551 Object visitContinueStatement(ContinueStatement node) { |
| 552 SimpleIdentifier labelNode = node.label; |
| 553 if (labelNode != null) { |
| 554 Element labelElement = labelNode.staticElement; |
| 555 if (labelElement is LabelElementImpl && labelElement.isOnSwitchStatement)
{ |
| 556 _errorReporter.reportErrorForNode(ResolverErrorCode.CONTINUE_LABEL_ON_SW
ITCH, labelNode, []); |
| 557 } |
| 558 } |
| 559 return null; |
| 560 } |
| 561 |
| 562 @override |
| 563 Object visitDefaultFormalParameter(DefaultFormalParameter node) { |
| 564 _checkForInvalidAssignment(node.identifier, node.defaultValue); |
| 565 _checkForDefaultValueInFunctionTypedParameter(node); |
| 566 return super.visitDefaultFormalParameter(node); |
| 567 } |
| 568 |
| 569 @override |
| 570 Object visitDoStatement(DoStatement node) { |
| 571 _checkForNonBoolCondition(node.condition); |
| 572 return super.visitDoStatement(node); |
| 573 } |
| 574 |
| 575 @override |
| 576 Object visitExportDirective(ExportDirective node) { |
| 577 ExportElement exportElement = node.element; |
| 578 if (exportElement != null) { |
| 579 LibraryElement exportedLibrary = exportElement.exportedLibrary; |
| 580 _checkForAmbiguousExport(node, exportElement, exportedLibrary); |
| 581 _checkForExportDuplicateLibraryName(node, exportElement, exportedLibrary); |
| 582 _checkForExportInternalLibrary(node, exportElement); |
| 583 } |
| 584 return super.visitExportDirective(node); |
| 585 } |
| 586 |
| 587 @override |
| 588 Object visitExpressionFunctionBody(ExpressionFunctionBody node) { |
| 589 bool wasInAsync = _inAsync; |
| 590 bool wasInGenerator = _inGenerator; |
| 591 try { |
| 592 _inAsync = node.isAsynchronous; |
| 593 _inGenerator = node.isGenerator; |
| 594 FunctionType functionType = _enclosingFunction == null ? null : _enclosing
Function.type; |
| 595 DartType expectedReturnType = functionType == null ? DynamicTypeImpl.insta
nce : functionType.returnType; |
| 596 _checkForReturnOfInvalidType(node.expression, expectedReturnType); |
| 597 return super.visitExpressionFunctionBody(node); |
| 598 } finally { |
| 599 _inAsync = wasInAsync; |
| 600 _inGenerator = wasInGenerator; |
| 601 } |
| 602 } |
| 603 |
| 604 @override |
| 605 Object visitFieldDeclaration(FieldDeclaration node) { |
| 606 _isInStaticVariableDeclaration = node.isStatic; |
| 607 _isInInstanceVariableDeclaration = !_isInStaticVariableDeclaration; |
| 608 if (_isInInstanceVariableDeclaration) { |
| 609 VariableDeclarationList variables = node.fields; |
| 610 if (variables.isConst) { |
| 611 _errorReporter.reportErrorForToken(CompileTimeErrorCode.CONST_INSTANCE_F
IELD, variables.keyword, []); |
| 612 } |
| 613 } |
| 614 try { |
| 615 _checkForAllInvalidOverrideErrorCodesForField(node); |
| 616 return super.visitFieldDeclaration(node); |
| 617 } finally { |
| 618 _isInStaticVariableDeclaration = false; |
| 619 _isInInstanceVariableDeclaration = false; |
| 620 } |
| 621 } |
| 622 |
| 623 @override |
| 624 Object visitFieldFormalParameter(FieldFormalParameter node) { |
| 625 _checkForValidField(node); |
| 626 _checkForConstFormalParameter(node); |
| 627 _checkForPrivateOptionalParameter(node); |
| 628 _checkForFieldInitializingFormalRedirectingConstructor(node); |
| 629 _checkForTypeAnnotationDeferredClass(node.type); |
| 630 return super.visitFieldFormalParameter(node); |
| 631 } |
| 632 |
| 633 @override |
| 634 Object visitFunctionDeclaration(FunctionDeclaration node) { |
| 635 ExecutableElement outerFunction = _enclosingFunction; |
| 636 try { |
| 637 SimpleIdentifier identifier = node.name; |
| 638 String methodName = ""; |
| 639 if (identifier != null) { |
| 640 methodName = identifier.name; |
| 641 } |
| 642 _enclosingFunction = node.element; |
| 643 TypeName returnType = node.returnType; |
| 644 if (node.isSetter || node.isGetter) { |
| 645 _checkForMismatchedAccessorTypes(node, methodName); |
| 646 if (node.isSetter) { |
| 647 FunctionExpression functionExpression = node.functionExpression; |
| 648 if (functionExpression != null) { |
| 649 _checkForWrongNumberOfParametersForSetter(identifier, functionExpres
sion.parameters); |
| 650 } |
| 651 _checkForNonVoidReturnTypeForSetter(returnType); |
| 652 } |
| 653 } |
| 654 if (node.isSetter) { |
| 655 _checkForInvalidModifierOnBody(node.functionExpression.body, CompileTime
ErrorCode.INVALID_MODIFIER_ON_SETTER); |
| 656 } |
| 657 _checkForTypeAnnotationDeferredClass(returnType); |
| 658 return super.visitFunctionDeclaration(node); |
| 659 } finally { |
| 660 _enclosingFunction = outerFunction; |
| 661 } |
| 662 } |
| 663 |
| 664 @override |
| 665 Object visitFunctionExpression(FunctionExpression node) { |
| 666 // If this function expression is wrapped in a function declaration, don't c
hange the |
| 667 // enclosingFunction field. |
| 668 if (node.parent is! FunctionDeclaration) { |
| 669 ExecutableElement outerFunction = _enclosingFunction; |
| 670 try { |
| 671 _enclosingFunction = node.element; |
| 672 return super.visitFunctionExpression(node); |
| 673 } finally { |
| 674 _enclosingFunction = outerFunction; |
| 675 } |
| 676 } else { |
| 677 return super.visitFunctionExpression(node); |
| 678 } |
| 679 } |
| 680 |
| 681 @override |
| 682 Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { |
| 683 Expression functionExpression = node.function; |
| 684 DartType expressionType = functionExpression.staticType; |
| 685 if (!_isFunctionType(expressionType)) { |
| 686 _errorReporter.reportErrorForNode(StaticTypeWarningCode.INVOCATION_OF_NON_
FUNCTION_EXPRESSION, functionExpression, []); |
| 687 } |
| 688 return super.visitFunctionExpressionInvocation(node); |
| 689 } |
| 690 |
| 691 @override |
| 692 Object visitFunctionTypeAlias(FunctionTypeAlias node) { |
| 693 _checkForBuiltInIdentifierAsName(node.name, CompileTimeErrorCode.BUILT_IN_ID
ENTIFIER_AS_TYPEDEF_NAME); |
| 694 _checkForDefaultValueInFunctionTypeAlias(node); |
| 695 _checkForTypeAliasCannotReferenceItself_function(node); |
| 696 return super.visitFunctionTypeAlias(node); |
| 697 } |
| 698 |
| 699 @override |
| 700 Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { |
| 701 bool old = _isInFunctionTypedFormalParameter; |
| 702 _isInFunctionTypedFormalParameter = true; |
| 703 try { |
| 704 _checkForTypeAnnotationDeferredClass(node.returnType); |
| 705 return super.visitFunctionTypedFormalParameter(node); |
| 706 } finally { |
| 707 _isInFunctionTypedFormalParameter = old; |
| 708 } |
| 709 } |
| 710 |
| 711 @override |
| 712 Object visitIfStatement(IfStatement node) { |
| 713 _checkForNonBoolCondition(node.condition); |
| 714 return super.visitIfStatement(node); |
| 715 } |
| 716 |
| 717 @override |
| 718 Object visitImportDirective(ImportDirective node) { |
| 719 ImportElement importElement = node.element; |
| 720 if (importElement != null) { |
| 721 _checkForImportDuplicateLibraryName(node, importElement); |
| 722 _checkForImportInternalLibrary(node, importElement); |
| 723 } |
| 724 return super.visitImportDirective(node); |
| 725 } |
| 726 |
| 727 @override |
| 728 Object visitIndexExpression(IndexExpression node) { |
| 729 _checkForArgumentTypeNotAssignableForArgument(node.index); |
| 730 return super.visitIndexExpression(node); |
| 731 } |
| 732 |
| 733 @override |
| 734 Object visitInstanceCreationExpression(InstanceCreationExpression node) { |
| 735 bool wasInConstInstanceCreation = _isInConstInstanceCreation; |
| 736 _isInConstInstanceCreation = node.isConst; |
| 737 try { |
| 738 ConstructorName constructorName = node.constructorName; |
| 739 TypeName typeName = constructorName.type; |
| 740 DartType type = typeName.type; |
| 741 if (type is InterfaceType) { |
| 742 InterfaceType interfaceType = type; |
| 743 _checkForConstOrNewWithAbstractClass(node, typeName, interfaceType); |
| 744 _checkForConstOrNewWithEnum(node, typeName, interfaceType); |
| 745 if (_isInConstInstanceCreation) { |
| 746 _checkForConstWithNonConst(node); |
| 747 _checkForConstWithUndefinedConstructor(node, constructorName, typeName
); |
| 748 _checkForConstWithTypeParameters(typeName); |
| 749 _checkForConstDeferredClass(node, constructorName, typeName); |
| 750 } else { |
| 751 _checkForNewWithUndefinedConstructor(node, constructorName, typeName); |
| 752 } |
| 753 } |
| 754 return super.visitInstanceCreationExpression(node); |
| 755 } finally { |
| 756 _isInConstInstanceCreation = wasInConstInstanceCreation; |
| 757 } |
| 758 } |
| 759 |
| 760 @override |
| 761 Object visitIsExpression(IsExpression node) { |
| 762 _checkForTypeAnnotationDeferredClass(node.type); |
| 763 return super.visitIsExpression(node); |
| 764 } |
| 765 |
| 766 @override |
| 767 Object visitListLiteral(ListLiteral node) { |
| 768 TypeArgumentList typeArguments = node.typeArguments; |
| 769 if (typeArguments != null) { |
| 770 if (node.constKeyword != null) { |
| 771 NodeList<TypeName> arguments = typeArguments.arguments; |
| 772 if (arguments.length != 0) { |
| 773 _checkForInvalidTypeArgumentInConstTypedLiteral(arguments, CompileTime
ErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_LIST); |
| 774 } |
| 775 } |
| 776 _checkForExpectedOneListTypeArgument(node, typeArguments); |
| 777 _checkForListElementTypeNotAssignable(node, typeArguments); |
| 778 } |
| 779 return super.visitListLiteral(node); |
| 780 } |
| 781 |
| 782 @override |
| 783 Object visitMapLiteral(MapLiteral node) { |
| 784 TypeArgumentList typeArguments = node.typeArguments; |
| 785 if (typeArguments != null) { |
| 786 NodeList<TypeName> arguments = typeArguments.arguments; |
| 787 if (arguments.length != 0) { |
| 788 if (node.constKeyword != null) { |
| 789 _checkForInvalidTypeArgumentInConstTypedLiteral(arguments, CompileTime
ErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_MAP); |
| 790 } |
| 791 } |
| 792 _checkExpectedTwoMapTypeArguments(typeArguments); |
| 793 _checkForMapTypeNotAssignable(node, typeArguments); |
| 794 } |
| 795 _checkForNonConstMapAsExpressionStatement(node); |
| 796 return super.visitMapLiteral(node); |
| 797 } |
| 798 |
| 799 @override |
| 800 Object visitMethodDeclaration(MethodDeclaration node) { |
| 801 ExecutableElement previousFunction = _enclosingFunction; |
| 802 try { |
| 803 _isInStaticMethod = node.isStatic; |
| 804 _enclosingFunction = node.element; |
| 805 SimpleIdentifier identifier = node.name; |
| 806 String methodName = ""; |
| 807 if (identifier != null) { |
| 808 methodName = identifier.name; |
| 809 } |
| 810 TypeName returnTypeName = node.returnType; |
| 811 if (node.isSetter || node.isGetter) { |
| 812 _checkForMismatchedAccessorTypes(node, methodName); |
| 813 } |
| 814 if (node.isGetter) { |
| 815 _checkForVoidReturnType(node); |
| 816 _checkForConflictingStaticGetterAndInstanceSetter(node); |
| 817 } else if (node.isSetter) { |
| 818 _checkForInvalidModifierOnBody(node.body, CompileTimeErrorCode.INVALID_M
ODIFIER_ON_SETTER); |
| 819 _checkForWrongNumberOfParametersForSetter(node.name, node.parameters); |
| 820 _checkForNonVoidReturnTypeForSetter(returnTypeName); |
| 821 _checkForConflictingStaticSetterAndInstanceMember(node); |
| 822 } else if (node.isOperator) { |
| 823 _checkForOptionalParameterInOperator(node); |
| 824 _checkForWrongNumberOfParametersForOperator(node); |
| 825 _checkForNonVoidReturnTypeForOperator(node); |
| 826 } |
| 827 _checkForConcreteClassWithAbstractMember(node); |
| 828 _checkForAllInvalidOverrideErrorCodesForMethod(node); |
| 829 _checkForTypeAnnotationDeferredClass(returnTypeName); |
| 830 return super.visitMethodDeclaration(node); |
| 831 } finally { |
| 832 _enclosingFunction = previousFunction; |
| 833 _isInStaticMethod = false; |
| 834 } |
| 835 } |
| 836 |
| 837 @override |
| 838 Object visitMethodInvocation(MethodInvocation node) { |
| 839 Expression target = node.realTarget; |
| 840 SimpleIdentifier methodName = node.methodName; |
| 841 if (target != null) { |
| 842 ClassElement typeReference = ElementResolver.getTypeReference(target); |
| 843 _checkForStaticAccessToInstanceMember(typeReference, methodName); |
| 844 _checkForInstanceAccessToStaticMember(typeReference, methodName); |
| 845 } else { |
| 846 _checkForUnqualifiedReferenceToNonLocalStaticMember(methodName); |
| 847 } |
| 848 return super.visitMethodInvocation(node); |
| 849 } |
| 850 |
| 851 @override |
| 852 Object visitNativeClause(NativeClause node) { |
| 853 // TODO(brianwilkerson) Figure out the right rule for when 'native' is allow
ed. |
| 854 if (!_isInSystemLibrary) { |
| 855 _errorReporter.reportErrorForNode(ParserErrorCode.NATIVE_CLAUSE_IN_NON_SDK
_CODE, node, []); |
| 856 } |
| 857 return super.visitNativeClause(node); |
| 858 } |
| 859 |
| 860 @override |
| 861 Object visitNativeFunctionBody(NativeFunctionBody node) { |
| 862 _checkForNativeFunctionBodyInNonSDKCode(node); |
| 863 return super.visitNativeFunctionBody(node); |
| 864 } |
| 865 |
| 866 @override |
| 867 Object visitPostfixExpression(PostfixExpression node) { |
| 868 _checkForAssignmentToFinal(node.operand); |
| 869 _checkForIntNotAssignable(node.operand); |
| 870 return super.visitPostfixExpression(node); |
| 871 } |
| 872 |
| 873 @override |
| 874 Object visitPrefixedIdentifier(PrefixedIdentifier node) { |
| 875 if (node.parent is! Annotation) { |
| 876 ClassElement typeReference = ElementResolver.getTypeReference(node.prefix)
; |
| 877 SimpleIdentifier name = node.identifier; |
| 878 _checkForStaticAccessToInstanceMember(typeReference, name); |
| 879 _checkForInstanceAccessToStaticMember(typeReference, name); |
| 880 } |
| 881 return super.visitPrefixedIdentifier(node); |
| 882 } |
| 883 |
| 884 @override |
| 885 Object visitPrefixExpression(PrefixExpression node) { |
| 886 sc.TokenType operatorType = node.operator.type; |
| 887 Expression operand = node.operand; |
| 888 if (operatorType == sc.TokenType.BANG) { |
| 889 _checkForNonBoolNegationExpression(operand); |
| 890 } else if (operatorType.isIncrementOperator) { |
| 891 _checkForAssignmentToFinal(operand); |
| 892 } |
| 893 _checkForIntNotAssignable(operand); |
| 894 return super.visitPrefixExpression(node); |
| 895 } |
| 896 |
| 897 @override |
| 898 Object visitPropertyAccess(PropertyAccess node) { |
| 899 ClassElement typeReference = ElementResolver.getTypeReference(node.realTarge
t); |
| 900 SimpleIdentifier propertyName = node.propertyName; |
| 901 _checkForStaticAccessToInstanceMember(typeReference, propertyName); |
| 902 _checkForInstanceAccessToStaticMember(typeReference, propertyName); |
| 903 return super.visitPropertyAccess(node); |
| 904 } |
| 905 |
| 906 @override |
| 907 Object visitRedirectingConstructorInvocation(RedirectingConstructorInvocation
node) { |
| 908 _isInConstructorInitializer = true; |
| 909 try { |
| 910 return super.visitRedirectingConstructorInvocation(node); |
| 911 } finally { |
| 912 _isInConstructorInitializer = false; |
| 913 } |
| 914 } |
| 915 |
| 916 @override |
| 917 Object visitRethrowExpression(RethrowExpression node) { |
| 918 _checkForRethrowOutsideCatch(node); |
| 919 return super.visitRethrowExpression(node); |
| 920 } |
| 921 |
| 922 @override |
| 923 Object visitReturnStatement(ReturnStatement node) { |
| 924 if (node.expression == null) { |
| 925 _returnsWithout.add(node); |
| 926 } else { |
| 927 _returnsWith.add(node); |
| 928 } |
| 929 _checkForAllReturnStatementErrorCodes(node); |
| 930 return super.visitReturnStatement(node); |
| 931 } |
| 932 |
| 933 @override |
| 934 Object visitSimpleFormalParameter(SimpleFormalParameter node) { |
| 935 _checkForConstFormalParameter(node); |
| 936 _checkForPrivateOptionalParameter(node); |
| 937 _checkForTypeAnnotationDeferredClass(node.type); |
| 938 return super.visitSimpleFormalParameter(node); |
| 939 } |
| 940 |
| 941 @override |
| 942 Object visitSimpleIdentifier(SimpleIdentifier node) { |
| 943 _checkForImplicitThisReferenceInInitializer(node); |
| 944 if (!_isUnqualifiedReferenceToNonLocalStaticMemberAllowed(node)) { |
| 945 _checkForUnqualifiedReferenceToNonLocalStaticMember(node); |
| 946 } |
| 947 return super.visitSimpleIdentifier(node); |
| 948 } |
| 949 |
| 950 @override |
| 951 Object visitSuperConstructorInvocation(SuperConstructorInvocation node) { |
| 952 _isInConstructorInitializer = true; |
| 953 try { |
| 954 return super.visitSuperConstructorInvocation(node); |
| 955 } finally { |
| 956 _isInConstructorInitializer = false; |
| 957 } |
| 958 } |
| 959 |
| 960 @override |
| 961 Object visitSwitchStatement(SwitchStatement node) { |
| 962 _checkForSwitchExpressionNotAssignable(node); |
| 963 _checkForCaseBlocksNotTerminated(node); |
| 964 _checkForMissingEnumConstantInSwitch(node); |
| 965 return super.visitSwitchStatement(node); |
| 966 } |
| 967 |
| 968 @override |
| 969 Object visitThisExpression(ThisExpression node) { |
| 970 _checkForInvalidReferenceToThis(node); |
| 971 return super.visitThisExpression(node); |
| 972 } |
| 973 |
| 974 @override |
| 975 Object visitThrowExpression(ThrowExpression node) { |
| 976 _checkForConstEvalThrowsException(node); |
| 977 return super.visitThrowExpression(node); |
| 978 } |
| 979 |
| 980 @override |
| 981 Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
| 982 _checkForFinalNotInitialized(node.variables); |
| 983 return super.visitTopLevelVariableDeclaration(node); |
| 984 } |
| 985 |
| 986 @override |
| 987 Object visitTypeArgumentList(TypeArgumentList node) { |
| 988 NodeList<TypeName> list = node.arguments; |
| 989 for (TypeName typeName in list) { |
| 990 _checkForTypeAnnotationDeferredClass(typeName); |
| 991 } |
| 992 return super.visitTypeArgumentList(node); |
| 993 } |
| 994 |
| 995 @override |
| 996 Object visitTypeName(TypeName node) { |
| 997 _checkForTypeArgumentNotMatchingBounds(node); |
| 998 _checkForTypeParameterReferencedByStatic(node); |
| 999 return super.visitTypeName(node); |
| 1000 } |
| 1001 |
| 1002 @override |
| 1003 Object visitTypeParameter(TypeParameter node) { |
| 1004 _checkForBuiltInIdentifierAsName(node.name, CompileTimeErrorCode.BUILT_IN_ID
ENTIFIER_AS_TYPE_PARAMETER_NAME); |
| 1005 _checkForTypeParameterSupertypeOfItsBound(node); |
| 1006 _checkForTypeAnnotationDeferredClass(node.bound); |
| 1007 return super.visitTypeParameter(node); |
| 1008 } |
| 1009 |
| 1010 @override |
| 1011 Object visitVariableDeclaration(VariableDeclaration node) { |
| 1012 SimpleIdentifier nameNode = node.name; |
| 1013 Expression initializerNode = node.initializer; |
| 1014 // do checks |
| 1015 _checkForInvalidAssignment(nameNode, initializerNode); |
| 1016 // visit name |
| 1017 nameNode.accept(this); |
| 1018 // visit initializer |
| 1019 String name = nameNode.name; |
| 1020 _namesForReferenceToDeclaredVariableInInitializer.add(name); |
| 1021 bool wasInInstanceVariableInitializer = _isInInstanceVariableInitializer; |
| 1022 _isInInstanceVariableInitializer = _isInInstanceVariableDeclaration; |
| 1023 try { |
| 1024 if (initializerNode != null) { |
| 1025 initializerNode.accept(this); |
| 1026 } |
| 1027 } finally { |
| 1028 _isInInstanceVariableInitializer = wasInInstanceVariableInitializer; |
| 1029 _namesForReferenceToDeclaredVariableInInitializer.remove(name); |
| 1030 } |
| 1031 // done |
| 1032 return null; |
| 1033 } |
| 1034 |
| 1035 @override |
| 1036 Object visitVariableDeclarationList(VariableDeclarationList node) { |
| 1037 _checkForTypeAnnotationDeferredClass(node.type); |
| 1038 return super.visitVariableDeclarationList(node); |
| 1039 } |
| 1040 |
| 1041 @override |
| 1042 Object visitVariableDeclarationStatement(VariableDeclarationStatement node) { |
| 1043 _checkForFinalNotInitialized(node.variables); |
| 1044 return super.visitVariableDeclarationStatement(node); |
| 1045 } |
| 1046 |
| 1047 @override |
| 1048 Object visitWhileStatement(WhileStatement node) { |
| 1049 _checkForNonBoolCondition(node.condition); |
| 1050 return super.visitWhileStatement(node); |
| 1051 } |
| 1052 |
| 1053 @override |
| 1054 Object visitYieldStatement(YieldStatement node) { |
| 1055 if (!_inGenerator) { |
| 1056 CompileTimeErrorCode errorCode; |
| 1057 if (node.star != null) { |
| 1058 errorCode = CompileTimeErrorCode.YIELD_EACH_IN_NON_GENERATOR; |
| 1059 } else { |
| 1060 errorCode = CompileTimeErrorCode.YIELD_IN_NON_GENERATOR; |
| 1061 } |
| 1062 _errorReporter.reportErrorForNode(errorCode, node, []); |
| 1063 } |
| 1064 return super.visitYieldStatement(node); |
| 1065 } |
| 1066 |
| 1067 /** |
| 1068 * This verifies if the passed map literal has type arguments then there is ex
actly two. |
| 1069 * |
| 1070 * @param typeArguments the type arguments, always non-`null` |
| 1071 * @return `true` if and only if an error code is generated on the passed node |
| 1072 * @see StaticTypeWarningCode#EXPECTED_TWO_MAP_TYPE_ARGUMENTS |
| 1073 */ |
| 1074 bool _checkExpectedTwoMapTypeArguments(TypeArgumentList typeArguments) { |
| 1075 // check number of type arguments |
| 1076 int num = typeArguments.arguments.length; |
| 1077 if (num == 2) { |
| 1078 return false; |
| 1079 } |
| 1080 // report problem |
| 1081 _errorReporter.reportErrorForNode(StaticTypeWarningCode.EXPECTED_TWO_MAP_TYP
E_ARGUMENTS, typeArguments, [num]); |
| 1082 return true; |
| 1083 } |
| 1084 |
| 1085 /** |
| 1086 * This verifies that the passed constructor declaration does not violate any
of the error codes |
| 1087 * relating to the initialization of fields in the enclosing class. |
| 1088 * |
| 1089 * @param node the [ConstructorDeclaration] to evaluate |
| 1090 * @return `true` if and only if an error code is generated on the passed node |
| 1091 * @see #initialFieldElementsMap |
| 1092 * @see CompileTimeErrorCode#FINAL_INITIALIZED_IN_DECLARATION_AND_CONSTRUCTOR |
| 1093 * @see CompileTimeErrorCode#FINAL_INITIALIZED_MULTIPLE_TIMES |
| 1094 */ |
| 1095 bool _checkForAllFinalInitializedErrorCodes(ConstructorDeclaration node) { |
| 1096 if (node.factoryKeyword != null || node.redirectedConstructor != null || nod
e.externalKeyword != null) { |
| 1097 return false; |
| 1098 } |
| 1099 // Ignore if native class. |
| 1100 if (_isInNativeClass) { |
| 1101 return false; |
| 1102 } |
| 1103 bool foundError = false; |
| 1104 HashMap<FieldElement, INIT_STATE> fieldElementsMap = new HashMap<FieldElemen
t, INIT_STATE>.from(_initialFieldElementsMap); |
| 1105 // Visit all of the field formal parameters |
| 1106 NodeList<FormalParameter> formalParameters = node.parameters.parameters; |
| 1107 for (FormalParameter formalParameter in formalParameters) { |
| 1108 FormalParameter parameter = formalParameter; |
| 1109 if (parameter is DefaultFormalParameter) { |
| 1110 parameter = (parameter as DefaultFormalParameter).parameter; |
| 1111 } |
| 1112 if (parameter is FieldFormalParameter) { |
| 1113 FieldElement fieldElement = (parameter.element as FieldFormalParameterEl
ementImpl).field; |
| 1114 INIT_STATE state = fieldElementsMap[fieldElement]; |
| 1115 if (state == INIT_STATE.NOT_INIT) { |
| 1116 fieldElementsMap[fieldElement] = INIT_STATE.INIT_IN_FIELD_FORMAL; |
| 1117 } else if (state == INIT_STATE.INIT_IN_DECLARATION) { |
| 1118 if (fieldElement.isFinal || fieldElement.isConst) { |
| 1119 _errorReporter.reportErrorForNode(StaticWarningCode.FINAL_INITIALIZE
D_IN_DECLARATION_AND_CONSTRUCTOR, formalParameter.identifier, [fieldElement.disp
layName]); |
| 1120 foundError = true; |
| 1121 } |
| 1122 } else if (state == INIT_STATE.INIT_IN_FIELD_FORMAL) { |
| 1123 if (fieldElement.isFinal || fieldElement.isConst) { |
| 1124 _errorReporter.reportErrorForNode(CompileTimeErrorCode.FINAL_INITIAL
IZED_MULTIPLE_TIMES, formalParameter.identifier, [fieldElement.displayName]); |
| 1125 foundError = true; |
| 1126 } |
| 1127 } |
| 1128 } |
| 1129 } |
| 1130 // Visit all of the initializers |
| 1131 NodeList<ConstructorInitializer> initializers = node.initializers; |
| 1132 for (ConstructorInitializer constructorInitializer in initializers) { |
| 1133 if (constructorInitializer is RedirectingConstructorInvocation) { |
| 1134 return false; |
| 1135 } |
| 1136 if (constructorInitializer is ConstructorFieldInitializer) { |
| 1137 ConstructorFieldInitializer constructorFieldInitializer = constructorIni
tializer; |
| 1138 SimpleIdentifier fieldName = constructorFieldInitializer.fieldName; |
| 1139 Element element = fieldName.staticElement; |
| 1140 if (element is FieldElement) { |
| 1141 FieldElement fieldElement = element; |
| 1142 INIT_STATE state = fieldElementsMap[fieldElement]; |
| 1143 if (state == INIT_STATE.NOT_INIT) { |
| 1144 fieldElementsMap[fieldElement] = INIT_STATE.INIT_IN_INITIALIZERS; |
| 1145 } else if (state == INIT_STATE.INIT_IN_DECLARATION) { |
| 1146 if (fieldElement.isFinal || fieldElement.isConst) { |
| 1147 _errorReporter.reportErrorForNode(StaticWarningCode.FIELD_INITIALI
ZED_IN_INITIALIZER_AND_DECLARATION, fieldName, []); |
| 1148 foundError = true; |
| 1149 } |
| 1150 } else if (state == INIT_STATE.INIT_IN_FIELD_FORMAL) { |
| 1151 _errorReporter.reportErrorForNode(CompileTimeErrorCode.FIELD_INITIAL
IZED_IN_PARAMETER_AND_INITIALIZER, fieldName, []); |
| 1152 foundError = true; |
| 1153 } else if (state == INIT_STATE.INIT_IN_INITIALIZERS) { |
| 1154 _errorReporter.reportErrorForNode(CompileTimeErrorCode.FIELD_INITIAL
IZED_BY_MULTIPLE_INITIALIZERS, fieldName, [fieldElement.displayName]); |
| 1155 foundError = true; |
| 1156 } |
| 1157 } |
| 1158 } |
| 1159 } |
| 1160 // Visit all of the states in the map to ensure that none were never |
| 1161 // initialized. |
| 1162 fieldElementsMap.forEach((FieldElement fieldElement, INIT_STATE state) { |
| 1163 if (state == INIT_STATE.NOT_INIT) { |
| 1164 if (fieldElement.isConst) { |
| 1165 _errorReporter.reportErrorForNode( |
| 1166 CompileTimeErrorCode.CONST_NOT_INITIALIZED, |
| 1167 node.returnType, |
| 1168 [fieldElement.name]); |
| 1169 foundError = true; |
| 1170 } else if (fieldElement.isFinal) { |
| 1171 _errorReporter.reportErrorForNode( |
| 1172 StaticWarningCode.FINAL_NOT_INITIALIZED, |
| 1173 node.returnType, |
| 1174 [fieldElement.name]); |
| 1175 foundError = true; |
| 1176 } |
| 1177 } |
| 1178 }); |
| 1179 return foundError; |
| 1180 } |
| 1181 |
| 1182 /** |
| 1183 * This checks the passed executable element against override-error codes. |
| 1184 * |
| 1185 * @param executableElement a non-null [ExecutableElement] to evaluate |
| 1186 * @param overriddenExecutable the element that the executableElement is overr
iding |
| 1187 * @param parameters the parameters of the executable element |
| 1188 * @param errorNameTarget the node to report problems on |
| 1189 * @return `true` if and only if an error code is generated on the passed node |
| 1190 * @see StaticWarningCode#INSTANCE_METHOD_NAME_COLLIDES_WITH_SUPERCLASS_STATIC |
| 1191 * @see CompileTimeErrorCode#INVALID_OVERRIDE_REQUIRED |
| 1192 * @see CompileTimeErrorCode#INVALID_OVERRIDE_POSITIONAL |
| 1193 * @see CompileTimeErrorCode#INVALID_OVERRIDE_NAMED |
| 1194 * @see StaticWarningCode#INVALID_GETTER_OVERRIDE_RETURN_TYPE |
| 1195 * @see StaticWarningCode#INVALID_METHOD_OVERRIDE_RETURN_TYPE |
| 1196 * @see StaticWarningCode#INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE |
| 1197 * @see StaticWarningCode#INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE |
| 1198 * @see StaticWarningCode#INVALID_METHOD_OVERRIDE_OPTIONAL_PARAM_TYPE |
| 1199 * @see StaticWarningCode#INVALID_METHOD_OVERRIDE_NAMED_PARAM_TYPE |
| 1200 * @see StaticWarningCode#INVALID_OVERRIDE_DIFFERENT_DEFAULT_VALUES |
| 1201 */ |
| 1202 bool _checkForAllInvalidOverrideErrorCodes(ExecutableElement executableElement
, ExecutableElement overriddenExecutable, List<ParameterElement> parameters, Lis
t<AstNode> parameterLocations, SimpleIdentifier errorNameTarget) { |
| 1203 bool isGetter = false; |
| 1204 bool isSetter = false; |
| 1205 if (executableElement is PropertyAccessorElement) { |
| 1206 PropertyAccessorElement accessorElement = executableElement; |
| 1207 isGetter = accessorElement.isGetter; |
| 1208 isSetter = accessorElement.isSetter; |
| 1209 } |
| 1210 String executableElementName = executableElement.name; |
| 1211 FunctionType overridingFT = executableElement.type; |
| 1212 FunctionType overriddenFT = overriddenExecutable.type; |
| 1213 InterfaceType enclosingType = _enclosingClass.type; |
| 1214 overriddenFT = _inheritanceManager.substituteTypeArgumentsInMemberFromInheri
tance(overriddenFT, executableElementName, enclosingType); |
| 1215 if (overridingFT == null || overriddenFT == null) { |
| 1216 return false; |
| 1217 } |
| 1218 DartType overridingFTReturnType = overridingFT.returnType; |
| 1219 DartType overriddenFTReturnType = overriddenFT.returnType; |
| 1220 List<DartType> overridingNormalPT = overridingFT.normalParameterTypes; |
| 1221 List<DartType> overriddenNormalPT = overriddenFT.normalParameterTypes; |
| 1222 List<DartType> overridingPositionalPT = overridingFT.optionalParameterTypes; |
| 1223 List<DartType> overriddenPositionalPT = overriddenFT.optionalParameterTypes; |
| 1224 Map<String, DartType> overridingNamedPT = overridingFT.namedParameterTypes; |
| 1225 Map<String, DartType> overriddenNamedPT = overriddenFT.namedParameterTypes; |
| 1226 // CTEC.INVALID_OVERRIDE_REQUIRED, CTEC.INVALID_OVERRIDE_POSITIONAL and CTEC
.INVALID_OVERRIDE_NAMED |
| 1227 if (overridingNormalPT.length > overriddenNormalPT.length) { |
| 1228 _errorReporter.reportErrorForNode(StaticWarningCode.INVALID_OVERRIDE_REQUI
RED, errorNameTarget, [ |
| 1229 overriddenNormalPT.length, |
| 1230 overriddenExecutable.enclosingElement.displayName]); |
| 1231 return true; |
| 1232 } |
| 1233 if (overridingNormalPT.length + overridingPositionalPT.length < overriddenPo
sitionalPT.length + overriddenNormalPT.length) { |
| 1234 _errorReporter.reportErrorForNode(StaticWarningCode.INVALID_OVERRIDE_POSIT
IONAL, errorNameTarget, [ |
| 1235 overriddenPositionalPT.length + overriddenNormalPT.length, |
| 1236 overriddenExecutable.enclosingElement.displayName]); |
| 1237 return true; |
| 1238 } |
| 1239 // For each named parameter in the overridden method, verify that there is |
| 1240 // the same name in the overriding method. |
| 1241 for (String overriddenParamName in overriddenNamedPT.keys) { |
| 1242 if (!overridingNamedPT.containsKey(overriddenParamName)) { |
| 1243 // The overridden method expected the overriding method to have |
| 1244 // overridingParamName, but it does not. |
| 1245 _errorReporter.reportErrorForNode( |
| 1246 StaticWarningCode.INVALID_OVERRIDE_NAMED, |
| 1247 errorNameTarget, |
| 1248 [overriddenParamName, |
| 1249 overriddenExecutable.enclosingElement.displayName]); |
| 1250 return true; |
| 1251 } |
| 1252 } |
| 1253 // SWC.INVALID_METHOD_OVERRIDE_RETURN_TYPE |
| 1254 if (overriddenFTReturnType != VoidTypeImpl.instance && !overridingFTReturnTy
pe.isAssignableTo(overriddenFTReturnType)) { |
| 1255 _errorReporter.reportTypeErrorForNode(!isGetter ? StaticWarningCode.INVALI
D_METHOD_OVERRIDE_RETURN_TYPE : StaticWarningCode.INVALID_GETTER_OVERRIDE_RETURN
_TYPE, errorNameTarget, [ |
| 1256 overridingFTReturnType, |
| 1257 overriddenFTReturnType, |
| 1258 overriddenExecutable.enclosingElement.displayName]); |
| 1259 return true; |
| 1260 } |
| 1261 // SWC.INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE |
| 1262 if (parameterLocations == null) { |
| 1263 return false; |
| 1264 } |
| 1265 int parameterIndex = 0; |
| 1266 for (int i = 0; i < overridingNormalPT.length; i++) { |
| 1267 if (!overridingNormalPT[i].isAssignableTo(overriddenNormalPT[i])) { |
| 1268 _errorReporter.reportTypeErrorForNode(!isSetter ? StaticWarningCode.INVA
LID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE : StaticWarningCode.INVALID_SETTER_OVERRID
E_NORMAL_PARAM_TYPE, parameterLocations[parameterIndex], [ |
| 1269 overridingNormalPT[i], |
| 1270 overriddenNormalPT[i], |
| 1271 overriddenExecutable.enclosingElement.displayName]); |
| 1272 return true; |
| 1273 } |
| 1274 parameterIndex++; |
| 1275 } |
| 1276 // SWC.INVALID_METHOD_OVERRIDE_OPTIONAL_PARAM_TYPE |
| 1277 for (int i = 0; i < overriddenPositionalPT.length; i++) { |
| 1278 if (!overridingPositionalPT[i].isAssignableTo(overriddenPositionalPT[i]))
{ |
| 1279 _errorReporter.reportTypeErrorForNode(StaticWarningCode.INVALID_METHOD_O
VERRIDE_OPTIONAL_PARAM_TYPE, parameterLocations[parameterIndex], [ |
| 1280 overridingPositionalPT[i], |
| 1281 overriddenPositionalPT[i], |
| 1282 overriddenExecutable.enclosingElement.displayName]); |
| 1283 return true; |
| 1284 } |
| 1285 parameterIndex++; |
| 1286 } |
| 1287 // SWC.INVALID_METHOD_OVERRIDE_NAMED_PARAM_TYPE & SWC.INVALID_OVERRIDE_DIFFE
RENT_DEFAULT_VALUES |
| 1288 for (String overriddenName in overriddenNamedPT.keys) { |
| 1289 DartType overridingType = overridingNamedPT[overriddenName]; |
| 1290 if (overridingType == null) { |
| 1291 // Error, this is never reached- INVALID_OVERRIDE_NAMED would have been |
| 1292 // created above if this could be reached. |
| 1293 continue; |
| 1294 } |
| 1295 DartType overriddenType = overriddenNamedPT[overriddenName]; |
| 1296 if (!overriddenType.isAssignableTo(overridingType)) { |
| 1297 // lookup the parameter for the error to select |
| 1298 ParameterElement parameterToSelect = null; |
| 1299 AstNode parameterLocationToSelect = null; |
| 1300 for (int i = 0; i < parameters.length; i++) { |
| 1301 ParameterElement parameter = parameters[i]; |
| 1302 if (parameter.parameterKind == ParameterKind.NAMED |
| 1303 && overriddenName == parameter.name) { |
| 1304 parameterToSelect = parameter; |
| 1305 parameterLocationToSelect = parameterLocations[i]; |
| 1306 break; |
| 1307 } |
| 1308 } |
| 1309 if (parameterToSelect != null) { |
| 1310 _errorReporter.reportTypeErrorForNode( |
| 1311 StaticWarningCode.INVALID_METHOD_OVERRIDE_NAMED_PARAM_TYPE, |
| 1312 parameterLocationToSelect, |
| 1313 [overridingType, |
| 1314 overriddenType, |
| 1315 overriddenExecutable.enclosingElement.displayName]); |
| 1316 return true; |
| 1317 } |
| 1318 } |
| 1319 } |
| 1320 // SWC.INVALID_OVERRIDE_DIFFERENT_DEFAULT_VALUES |
| 1321 // |
| 1322 // Create three arrays: an array of the optional parameter ASTs (FormalParam
eters), an array of |
| 1323 // the optional parameters elements from our method, and finally an array of
the optional |
| 1324 // parameter elements from the method we are overriding. |
| 1325 // |
| 1326 bool foundError = false; |
| 1327 List<AstNode> formalParameters = new List<AstNode>(); |
| 1328 List<ParameterElementImpl> parameterElts = new List<ParameterElementImpl>(); |
| 1329 List<ParameterElementImpl> overriddenParameterElts = new List<ParameterEleme
ntImpl>(); |
| 1330 List<ParameterElement> overriddenPEs = overriddenExecutable.parameters; |
| 1331 for (int i = 0; i < parameters.length; i++) { |
| 1332 ParameterElement parameter = parameters[i]; |
| 1333 if (parameter.parameterKind.isOptional) { |
| 1334 formalParameters.add(parameterLocations[i]); |
| 1335 parameterElts.add(parameter as ParameterElementImpl); |
| 1336 } |
| 1337 } |
| 1338 for (ParameterElement parameterElt in overriddenPEs) { |
| 1339 if (parameterElt.parameterKind.isOptional) { |
| 1340 if (parameterElt is ParameterElementImpl) { |
| 1341 overriddenParameterElts.add(parameterElt); |
| 1342 } |
| 1343 } |
| 1344 } |
| 1345 // |
| 1346 // Next compare the list of optional parameter elements to the list of overr
idden optional |
| 1347 // parameter elements. |
| 1348 // |
| 1349 if (parameterElts.length > 0) { |
| 1350 if (parameterElts[0].parameterKind == ParameterKind.NAMED) { |
| 1351 // Named parameters, consider the names when matching the parameterElts
to the overriddenParameterElts |
| 1352 for (int i = 0; i < parameterElts.length; i++) { |
| 1353 ParameterElementImpl parameterElt = parameterElts[i]; |
| 1354 EvaluationResultImpl result = parameterElt.evaluationResult; |
| 1355 // TODO (jwren) Ignore Object types, see Dart bug 11287 |
| 1356 if (_isUserDefinedObject(result)) { |
| 1357 continue; |
| 1358 } |
| 1359 String parameterName = parameterElt.name; |
| 1360 for (int j = 0; j < overriddenParameterElts.length; j++) { |
| 1361 ParameterElementImpl overriddenParameterElt = overriddenParameterElt
s[j]; |
| 1362 String overriddenParameterName = overriddenParameterElt.name; |
| 1363 if (parameterName != null && parameterName == overriddenParameterNam
e) { |
| 1364 EvaluationResultImpl overriddenResult = overriddenParameterElt.eva
luationResult; |
| 1365 if (_isUserDefinedObject(overriddenResult)) { |
| 1366 break; |
| 1367 } |
| 1368 if (!result.equalValues(_typeProvider, overriddenResult)) { |
| 1369 _errorReporter.reportErrorForNode(StaticWarningCode.INVALID_OVER
RIDE_DIFFERENT_DEFAULT_VALUES_NAMED, formalParameters[i], [ |
| 1370 overriddenExecutable.enclosingElement.displayName, |
| 1371 overriddenExecutable.displayName, |
| 1372 parameterName]); |
| 1373 foundError = true; |
| 1374 } |
| 1375 } |
| 1376 } |
| 1377 } |
| 1378 } else { |
| 1379 // Positional parameters, consider the positions when matching the param
eterElts to the overriddenParameterElts |
| 1380 for (int i = 0; i < parameterElts.length && i < overriddenParameterElts.
length; i++) { |
| 1381 ParameterElementImpl parameterElt = parameterElts[i]; |
| 1382 EvaluationResultImpl result = parameterElt.evaluationResult; |
| 1383 // TODO (jwren) Ignore Object types, see Dart bug 11287 |
| 1384 if (_isUserDefinedObject(result)) { |
| 1385 continue; |
| 1386 } |
| 1387 ParameterElementImpl overriddenParameterElt = overriddenParameterElts[
i]; |
| 1388 EvaluationResultImpl overriddenResult = overriddenParameterElt.evaluat
ionResult; |
| 1389 if (_isUserDefinedObject(overriddenResult)) { |
| 1390 continue; |
| 1391 } |
| 1392 if (!result.equalValues(_typeProvider, overriddenResult)) { |
| 1393 _errorReporter.reportErrorForNode(StaticWarningCode.INVALID_OVERRIDE
_DIFFERENT_DEFAULT_VALUES_POSITIONAL, formalParameters[i], [ |
| 1394 overriddenExecutable.enclosingElement.displayName, |
| 1395 overriddenExecutable.displayName]); |
| 1396 foundError = true; |
| 1397 } |
| 1398 } |
| 1399 } |
| 1400 } |
| 1401 return foundError; |
| 1402 } |
| 1403 |
| 1404 /** |
| 1405 * This checks the passed executable element against override-error codes. Thi
s method computes |
| 1406 * the passed executableElement is overriding and calls |
| 1407 * [checkForAllInvalidOverrideErrorCodes] |
| 1408 * when the [InheritanceManager] returns a [MultiplyInheritedExecutableElement
], this |
| 1409 * method loops through the array in the [MultiplyInheritedExecutableElement]. |
| 1410 * |
| 1411 * @param executableElement a non-null [ExecutableElement] to evaluate |
| 1412 * @param parameters the parameters of the executable element |
| 1413 * @param errorNameTarget the node to report problems on |
| 1414 * @return `true` if and only if an error code is generated on the passed node |
| 1415 */ |
| 1416 bool _checkForAllInvalidOverrideErrorCodesForExecutable(ExecutableElement exec
utableElement, List<ParameterElement> parameters, List<AstNode> parameterLocatio
ns, SimpleIdentifier errorNameTarget) { |
| 1417 // |
| 1418 // Compute the overridden executable from the InheritanceManager |
| 1419 // |
| 1420 List<ExecutableElement> overriddenExecutables = _inheritanceManager.lookupOv
errides(_enclosingClass, executableElement.name); |
| 1421 if (overriddenExecutables.isEmpty) { |
| 1422 // Nothing is overridden, so we just have to check if the new name collide
s |
| 1423 // with a static defined in the superclass. |
| 1424 // TODO(paulberry): currently we don't do this check if the new element |
| 1425 // overrides a method in an interface (see issue 18947). |
| 1426 return _checkForInstanceMethodNameCollidesWithSuperclassStatic(executableE
lement, errorNameTarget); |
| 1427 } |
| 1428 for (ExecutableElement overriddenElement in overriddenExecutables) { |
| 1429 if (_checkForAllInvalidOverrideErrorCodes(executableElement, overriddenEle
ment, parameters, parameterLocations, errorNameTarget)) { |
| 1430 return true; |
| 1431 } |
| 1432 } |
| 1433 return false; |
| 1434 } |
| 1435 |
| 1436 /** |
| 1437 * This checks the passed field declaration against override-error codes. |
| 1438 * |
| 1439 * @param node the [MethodDeclaration] to evaluate |
| 1440 * @return `true` if and only if an error code is generated on the passed node |
| 1441 * @see #checkForAllInvalidOverrideErrorCodes(ExecutableElement) |
| 1442 */ |
| 1443 bool _checkForAllInvalidOverrideErrorCodesForField(FieldDeclaration node) { |
| 1444 if (_enclosingClass == null || node.isStatic) { |
| 1445 return false; |
| 1446 } |
| 1447 bool hasProblems = false; |
| 1448 VariableDeclarationList fields = node.fields; |
| 1449 for (VariableDeclaration field in fields.variables) { |
| 1450 FieldElement element = field.element as FieldElement; |
| 1451 if (element == null) { |
| 1452 continue; |
| 1453 } |
| 1454 PropertyAccessorElement getter = element.getter; |
| 1455 PropertyAccessorElement setter = element.setter; |
| 1456 SimpleIdentifier fieldName = field.name; |
| 1457 if (getter != null) { |
| 1458 if (_checkForAllInvalidOverrideErrorCodesForExecutable( |
| 1459 getter, |
| 1460 ParameterElementImpl.EMPTY_ARRAY, |
| 1461 AstNode.EMPTY_ARRAY, |
| 1462 fieldName)) { |
| 1463 hasProblems = true; |
| 1464 } |
| 1465 } |
| 1466 if (setter != null) { |
| 1467 if (_checkForAllInvalidOverrideErrorCodesForExecutable( |
| 1468 setter, |
| 1469 setter.parameters, |
| 1470 <AstNode> [fieldName], |
| 1471 fieldName)) { |
| 1472 hasProblems = true; |
| 1473 } |
| 1474 } |
| 1475 } |
| 1476 return hasProblems; |
| 1477 } |
| 1478 |
| 1479 /** |
| 1480 * This checks the passed method declaration against override-error codes. |
| 1481 * |
| 1482 * @param node the [MethodDeclaration] to evaluate |
| 1483 * @return `true` if and only if an error code is generated on the passed node |
| 1484 * @see #checkForAllInvalidOverrideErrorCodes(ExecutableElement) |
| 1485 */ |
| 1486 bool _checkForAllInvalidOverrideErrorCodesForMethod(MethodDeclaration node) { |
| 1487 if (_enclosingClass == null || node.isStatic || node.body is NativeFunctionB
ody) { |
| 1488 return false; |
| 1489 } |
| 1490 ExecutableElement executableElement = node.element; |
| 1491 if (executableElement == null) { |
| 1492 return false; |
| 1493 } |
| 1494 SimpleIdentifier methodName = node.name; |
| 1495 if (methodName.isSynthetic) { |
| 1496 return false; |
| 1497 } |
| 1498 FormalParameterList formalParameterList = node.parameters; |
| 1499 NodeList<FormalParameter> parameterList = formalParameterList != null ? form
alParameterList.parameters : null; |
| 1500 List<AstNode> parameters = parameterList != null ? new List.from(parameterLi
st) : null; |
| 1501 return _checkForAllInvalidOverrideErrorCodesForExecutable(executableElement,
executableElement.parameters, parameters, methodName); |
| 1502 } |
| 1503 |
| 1504 /** |
| 1505 * This verifies that all classes of the passed 'with' clause are valid. |
| 1506 * |
| 1507 * @param node the 'with' clause to evaluate |
| 1508 * @return `true` if and only if an error code is generated on the passed node |
| 1509 * @see CompileTimeErrorCode#MIXIN_DECLARES_CONSTRUCTOR |
| 1510 * @see CompileTimeErrorCode#MIXIN_INHERITS_FROM_NOT_OBJECT |
| 1511 * @see CompileTimeErrorCode#MIXIN_REFERENCES_SUPER |
| 1512 */ |
| 1513 bool _checkForAllMixinErrorCodes(WithClause withClause) { |
| 1514 if (withClause == null) { |
| 1515 return false; |
| 1516 } |
| 1517 bool problemReported = false; |
| 1518 for (TypeName mixinName in withClause.mixinTypes) { |
| 1519 DartType mixinType = mixinName.type; |
| 1520 if (mixinType is! InterfaceType) { |
| 1521 continue; |
| 1522 } |
| 1523 if (_checkForExtendsOrImplementsDisallowedClass( |
| 1524 mixinName, |
| 1525 CompileTimeErrorCode.MIXIN_OF_DISALLOWED_CLASS)) { |
| 1526 problemReported = true; |
| 1527 } else { |
| 1528 ClassElement mixinElement = (mixinType as InterfaceType).element; |
| 1529 if (_checkForExtendsOrImplementsDeferredClass( |
| 1530 mixinName, |
| 1531 CompileTimeErrorCode.MIXIN_DEFERRED_CLASS)) { |
| 1532 problemReported = true; |
| 1533 } |
| 1534 if (_checkForMixinDeclaresConstructor(mixinName, mixinElement)) { |
| 1535 problemReported = true; |
| 1536 } |
| 1537 if (_checkForMixinInheritsNotFromObject(mixinName, mixinElement)) { |
| 1538 problemReported = true; |
| 1539 } |
| 1540 if (_checkForMixinReferencesSuper(mixinName, mixinElement)) { |
| 1541 problemReported = true; |
| 1542 } |
| 1543 } |
| 1544 } |
| 1545 return problemReported; |
| 1546 } |
| 1547 |
| 1548 /** |
| 1549 * This checks error related to the redirected constructors. |
| 1550 * |
| 1551 * @param node the constructor declaration to evaluate |
| 1552 * @return `true` if and only if an error code is generated on the passed node |
| 1553 * @see StaticWarningCode#REDIRECT_TO_INVALID_RETURN_TYPE |
| 1554 * @see StaticWarningCode#REDIRECT_TO_INVALID_FUNCTION_TYPE |
| 1555 * @see StaticWarningCode#REDIRECT_TO_MISSING_CONSTRUCTOR |
| 1556 */ |
| 1557 bool _checkForAllRedirectConstructorErrorCodes(ConstructorDeclaration node) { |
| 1558 // |
| 1559 // Prepare redirected constructor node |
| 1560 // |
| 1561 ConstructorName redirectedConstructor = node.redirectedConstructor; |
| 1562 if (redirectedConstructor == null) { |
| 1563 return false; |
| 1564 } |
| 1565 // |
| 1566 // Prepare redirected constructor type |
| 1567 // |
| 1568 ConstructorElement redirectedElement = redirectedConstructor.staticElement; |
| 1569 if (redirectedElement == null) { |
| 1570 // |
| 1571 // If the element is null, we check for the REDIRECT_TO_MISSING_CONSTRUCTO
R case |
| 1572 // |
| 1573 TypeName constructorTypeName = redirectedConstructor.type; |
| 1574 DartType redirectedType = constructorTypeName.type; |
| 1575 if (redirectedType != null && redirectedType.element != null && !redirecte
dType.isDynamic) { |
| 1576 // |
| 1577 // Prepare the constructor name |
| 1578 // |
| 1579 String constructorStrName = constructorTypeName.name.name; |
| 1580 if (redirectedConstructor.name != null) { |
| 1581 constructorStrName += ".${redirectedConstructor.name.name}"; |
| 1582 } |
| 1583 ErrorCode errorCode = (node.constKeyword != null ? CompileTimeErrorCode.
REDIRECT_TO_MISSING_CONSTRUCTOR : StaticWarningCode.REDIRECT_TO_MISSING_CONSTRUC
TOR); |
| 1584 _errorReporter.reportErrorForNode(errorCode, redirectedConstructor, [con
structorStrName, redirectedType.displayName]); |
| 1585 return true; |
| 1586 } |
| 1587 return false; |
| 1588 } |
| 1589 FunctionType redirectedType = redirectedElement.type; |
| 1590 DartType redirectedReturnType = redirectedType.returnType; |
| 1591 // |
| 1592 // Report specific problem when return type is incompatible |
| 1593 // |
| 1594 FunctionType constructorType = node.element.type; |
| 1595 DartType constructorReturnType = constructorType.returnType; |
| 1596 if (!redirectedReturnType.isAssignableTo(constructorReturnType)) { |
| 1597 _errorReporter.reportErrorForNode(StaticWarningCode.REDIRECT_TO_INVALID_RE
TURN_TYPE, redirectedConstructor, [redirectedReturnType, constructorReturnType])
; |
| 1598 return true; |
| 1599 } |
| 1600 // |
| 1601 // Check parameters |
| 1602 // |
| 1603 if (!redirectedType.isSubtypeOf(constructorType)) { |
| 1604 _errorReporter.reportErrorForNode(StaticWarningCode.REDIRECT_TO_INVALID_FU
NCTION_TYPE, redirectedConstructor, [redirectedType, constructorType]); |
| 1605 return true; |
| 1606 } |
| 1607 return false; |
| 1608 } |
| 1609 |
| 1610 /** |
| 1611 * This checks that the return statement of the form <i>return e;</i> is not i
n a generative |
| 1612 * constructor. |
| 1613 * |
| 1614 * This checks that return statements without expressions are not in a generat
ive constructor and |
| 1615 * the return type is not assignable to `null`; that is, we don't have `return
;` if |
| 1616 * the enclosing method has a return type. |
| 1617 * |
| 1618 * This checks that the return type matches the type of the declared return ty
pe in the enclosing |
| 1619 * method or function. |
| 1620 * |
| 1621 * @param node the return statement to evaluate |
| 1622 * @return `true` if and only if an error code is generated on the passed node |
| 1623 * @see CompileTimeErrorCode#RETURN_IN_GENERATIVE_CONSTRUCTOR |
| 1624 * @see StaticWarningCode#RETURN_WITHOUT_VALUE |
| 1625 * @see StaticTypeWarningCode#RETURN_OF_INVALID_TYPE |
| 1626 */ |
| 1627 bool _checkForAllReturnStatementErrorCodes(ReturnStatement node) { |
| 1628 FunctionType functionType = _enclosingFunction == null ? null : _enclosingFu
nction.type; |
| 1629 DartType expectedReturnType = functionType == null ? DynamicTypeImpl.instanc
e : functionType.returnType; |
| 1630 Expression returnExpression = node.expression; |
| 1631 // RETURN_IN_GENERATIVE_CONSTRUCTOR |
| 1632 bool isGenerativeConstructor = _enclosingFunction is ConstructorElement && !
(_enclosingFunction as ConstructorElement).isFactory; |
| 1633 if (isGenerativeConstructor) { |
| 1634 if (returnExpression == null) { |
| 1635 return false; |
| 1636 } |
| 1637 _errorReporter.reportErrorForNode(CompileTimeErrorCode.RETURN_IN_GENERATIV
E_CONSTRUCTOR, returnExpression, []); |
| 1638 return true; |
| 1639 } |
| 1640 // RETURN_WITHOUT_VALUE |
| 1641 if (returnExpression == null) { |
| 1642 if (VoidTypeImpl.instance.isAssignableTo(expectedReturnType)) { |
| 1643 return false; |
| 1644 } |
| 1645 _hasReturnWithoutValue = true; |
| 1646 _errorReporter.reportErrorForNode(StaticWarningCode.RETURN_WITHOUT_VALUE,
node, []); |
| 1647 return true; |
| 1648 } else if (_inGenerator) { |
| 1649 // RETURN_IN_GENERATOR |
| 1650 _errorReporter.reportErrorForNode(CompileTimeErrorCode.RETURN_IN_GENERATOR
, node, []); |
| 1651 } |
| 1652 // RETURN_OF_INVALID_TYPE |
| 1653 return _checkForReturnOfInvalidType(returnExpression, expectedReturnType); |
| 1654 } |
| 1655 |
| 1656 /** |
| 1657 * This verifies that the export namespace of the passed export directive does
not export any name |
| 1658 * already exported by other export directive. |
| 1659 * |
| 1660 * @param node the export directive node to report problem on |
| 1661 * @param exportElement the [ExportElement] retrieved from the node, if the el
ement in the |
| 1662 * node was `null`, then this method is not called |
| 1663 * @param exportedLibrary the library element containing the exported element |
| 1664 * @return `true` if and only if an error code is generated on the passed node |
| 1665 * @see CompileTimeErrorCode#AMBIGUOUS_EXPORT |
| 1666 */ |
| 1667 bool _checkForAmbiguousExport(ExportDirective node, ExportElement exportElemen
t, LibraryElement exportedLibrary) { |
| 1668 if (exportedLibrary == null) { |
| 1669 return false; |
| 1670 } |
| 1671 // check exported names |
| 1672 Namespace namespace = new NamespaceBuilder().createExportNamespaceForDirecti
ve(exportElement); |
| 1673 Map<String, Element> definedNames = namespace.definedNames; |
| 1674 for (String name in definedNames.keys) { |
| 1675 Element element = definedNames[name]; |
| 1676 Element prevElement = _exportedElements[name]; |
| 1677 if (element != null && prevElement != null && prevElement != element) { |
| 1678 _errorReporter.reportErrorForNode(CompileTimeErrorCode.AMBIGUOUS_EXPORT,
node, [ |
| 1679 name, |
| 1680 prevElement.library.definingCompilationUnit.displayName, |
| 1681 element.library.definingCompilationUnit.displayName]); |
| 1682 return true; |
| 1683 } else { |
| 1684 _exportedElements[name] = element; |
| 1685 } |
| 1686 } |
| 1687 return false; |
| 1688 } |
| 1689 |
| 1690 /** |
| 1691 * This verifies that the passed expression can be assigned to its correspondi
ng parameters. |
| 1692 * |
| 1693 * This method corresponds to BestPracticesVerifier.checkForArgumentTypeNotAss
ignable. |
| 1694 * |
| 1695 * @param expression the expression to evaluate |
| 1696 * @param expectedStaticType the expected static type of the parameter |
| 1697 * @param actualStaticType the actual static type of the argument |
| 1698 * @param expectedPropagatedType the expected propagated type of the parameter
, may be |
| 1699 * `null` |
| 1700 * @param actualPropagatedType the expected propagated type of the parameter,
may be `null` |
| 1701 * @return `true` if and only if an error code is generated on the passed node |
| 1702 * @see StaticWarningCode#ARGUMENT_TYPE_NOT_ASSIGNABLE |
| 1703 * @see CompileTimeErrorCode#LIST_ELEMENT_TYPE_NOT_ASSIGNABLE |
| 1704 * @see StaticWarningCode#LIST_ELEMENT_TYPE_NOT_ASSIGNABLE |
| 1705 * @see CompileTimeErrorCode#MAP_KEY_TYPE_NOT_ASSIGNABLE |
| 1706 * @see CompileTimeErrorCode#MAP_VALUE_TYPE_NOT_ASSIGNABLE |
| 1707 * @see StaticWarningCode#MAP_KEY_TYPE_NOT_ASSIGNABLE |
| 1708 * @see StaticWarningCode#MAP_VALUE_TYPE_NOT_ASSIGNABLE |
| 1709 */ |
| 1710 bool _checkForArgumentTypeNotAssignable(Expression expression, DartType expect
edStaticType, DartType actualStaticType, ErrorCode errorCode) { |
| 1711 // |
| 1712 // Warning case: test static type information |
| 1713 // |
| 1714 if (actualStaticType != null && expectedStaticType != null) { |
| 1715 if (!actualStaticType.isAssignableTo(expectedStaticType)) { |
| 1716 _errorReporter.reportTypeErrorForNode(errorCode, expression, [actualStat
icType, expectedStaticType]); |
| 1717 return true; |
| 1718 } |
| 1719 } |
| 1720 return false; |
| 1721 } |
| 1722 |
| 1723 /** |
| 1724 * This verifies that the passed argument can be assigned to its corresponding
parameter. |
| 1725 * |
| 1726 * This method corresponds to BestPracticesVerifier.checkForArgumentTypeNotAss
ignableForArgument. |
| 1727 * |
| 1728 * @param argument the argument to evaluate |
| 1729 * @return `true` if and only if an error code is generated on the passed node |
| 1730 * @see StaticWarningCode#ARGUMENT_TYPE_NOT_ASSIGNABLE |
| 1731 */ |
| 1732 bool _checkForArgumentTypeNotAssignableForArgument(Expression argument) { |
| 1733 if (argument == null) { |
| 1734 return false; |
| 1735 } |
| 1736 ParameterElement staticParameterElement = argument.staticParameterElement; |
| 1737 DartType staticParameterType = staticParameterElement == null ? null : stati
cParameterElement.type; |
| 1738 return _checkForArgumentTypeNotAssignableWithExpectedTypes(argument, staticP
arameterType, StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE); |
| 1739 } |
| 1740 |
| 1741 /** |
| 1742 * This verifies that the passed expression can be assigned to its correspondi
ng parameters. |
| 1743 * |
| 1744 * This method corresponds to |
| 1745 * BestPracticesVerifier.checkForArgumentTypeNotAssignableWithExpectedTypes. |
| 1746 * |
| 1747 * @param expression the expression to evaluate |
| 1748 * @param expectedStaticType the expected static type |
| 1749 * @param expectedPropagatedType the expected propagated type, may be `null` |
| 1750 * @return `true` if and only if an error code is generated on the passed node |
| 1751 * @see StaticWarningCode#ARGUMENT_TYPE_NOT_ASSIGNABLE |
| 1752 * @see CompileTimeErrorCode#LIST_ELEMENT_TYPE_NOT_ASSIGNABLE |
| 1753 * @see StaticWarningCode#LIST_ELEMENT_TYPE_NOT_ASSIGNABLE |
| 1754 * @see CompileTimeErrorCode#MAP_KEY_TYPE_NOT_ASSIGNABLE |
| 1755 * @see CompileTimeErrorCode#MAP_VALUE_TYPE_NOT_ASSIGNABLE |
| 1756 * @see StaticWarningCode#MAP_KEY_TYPE_NOT_ASSIGNABLE |
| 1757 * @see StaticWarningCode#MAP_VALUE_TYPE_NOT_ASSIGNABLE |
| 1758 */ |
| 1759 bool _checkForArgumentTypeNotAssignableWithExpectedTypes(Expression expression
, DartType expectedStaticType, ErrorCode errorCode) => _checkForArgumentTypeNotA
ssignable(expression, expectedStaticType, getStaticType(expression), errorCode); |
| 1760 |
| 1761 /** |
| 1762 * This verifies that the passed arguments can be assigned to their correspond
ing parameters. |
| 1763 * |
| 1764 * This method corresponds to BestPracticesVerifier.checkForArgumentTypesNotAs
signableInList. |
| 1765 * |
| 1766 * @param node the arguments to evaluate |
| 1767 * @return `true` if and only if an error code is generated on the passed node |
| 1768 * @see StaticWarningCode#ARGUMENT_TYPE_NOT_ASSIGNABLE |
| 1769 */ |
| 1770 bool _checkForArgumentTypesNotAssignableInList(ArgumentList argumentList) { |
| 1771 if (argumentList == null) { |
| 1772 return false; |
| 1773 } |
| 1774 bool problemReported = false; |
| 1775 for (Expression argument in argumentList.arguments) { |
| 1776 if (_checkForArgumentTypeNotAssignableForArgument(argument)) { |
| 1777 problemReported = true; |
| 1778 } |
| 1779 } |
| 1780 return problemReported; |
| 1781 } |
| 1782 |
| 1783 /** |
| 1784 * Check that the static type of the given expression is assignable to the giv
en type. If it |
| 1785 * isn't, report an error with the given error code. |
| 1786 * |
| 1787 * @param expression the expression being tested |
| 1788 * @param type the type that the expression must be assignable to |
| 1789 * @param errorCode the error code to be reported |
| 1790 * @param arguments the arguments to pass in when creating the error |
| 1791 * @return `true` if an error was reported |
| 1792 */ |
| 1793 bool _checkForAssignability(Expression expression, InterfaceType type, ErrorCo
de errorCode, List<Object> arguments) { |
| 1794 if (expression == null) { |
| 1795 return false; |
| 1796 } |
| 1797 DartType expressionType = expression.staticType; |
| 1798 if (expressionType == null) { |
| 1799 return false; |
| 1800 } |
| 1801 if (expressionType.isAssignableTo(type)) { |
| 1802 return false; |
| 1803 } |
| 1804 _errorReporter.reportErrorForNode(errorCode, expression, arguments); |
| 1805 return true; |
| 1806 } |
| 1807 |
| 1808 /** |
| 1809 * This verifies that the passed expression is not final. |
| 1810 * |
| 1811 * @param node the expression to evaluate |
| 1812 * @return `true` if and only if an error code is generated on the passed node |
| 1813 * @see StaticWarningCode#ASSIGNMENT_TO_CONST |
| 1814 * @see StaticWarningCode#ASSIGNMENT_TO_FINAL |
| 1815 * @see StaticWarningCode#ASSIGNMENT_TO_METHOD |
| 1816 */ |
| 1817 bool _checkForAssignmentToFinal(Expression expression) { |
| 1818 // prepare element |
| 1819 Element element = null; |
| 1820 AstNode highlightedNode = expression; |
| 1821 if (expression is Identifier) { |
| 1822 element = expression.staticElement; |
| 1823 if (expression is PrefixedIdentifier) { |
| 1824 highlightedNode = expression.identifier; |
| 1825 } |
| 1826 } else if (expression is PropertyAccess) { |
| 1827 PropertyAccess propertyAccess = expression; |
| 1828 element = propertyAccess.propertyName.staticElement; |
| 1829 highlightedNode = propertyAccess.propertyName; |
| 1830 } |
| 1831 // check if element is assignable |
| 1832 if (element is PropertyAccessorElement) { |
| 1833 PropertyAccessorElement accessor = element as PropertyAccessorElement; |
| 1834 element = accessor.variable; |
| 1835 } |
| 1836 if (element is VariableElement) { |
| 1837 VariableElement variable = element as VariableElement; |
| 1838 if (variable.isConst) { |
| 1839 _errorReporter.reportErrorForNode(StaticWarningCode.ASSIGNMENT_TO_CONST,
expression, []); |
| 1840 return true; |
| 1841 } |
| 1842 if (variable.isFinal) { |
| 1843 if (variable is FieldElementImpl && variable.setter == null && variable.
isSynthetic) { |
| 1844 _errorReporter.reportErrorForNode(StaticWarningCode.ASSIGNMENT_TO_FINA
L_NO_SETTER, highlightedNode, [variable.name, variable.enclosingElement.displayN
ame]); |
| 1845 return true; |
| 1846 } |
| 1847 _errorReporter.reportErrorForNode(StaticWarningCode.ASSIGNMENT_TO_FINAL,
highlightedNode, [variable.name]); |
| 1848 return true; |
| 1849 } |
| 1850 return false; |
| 1851 } |
| 1852 if (element is FunctionElement) { |
| 1853 _errorReporter.reportErrorForNode(StaticWarningCode.ASSIGNMENT_TO_FUNCTION
, expression, []); |
| 1854 return true; |
| 1855 } |
| 1856 if (element is MethodElement) { |
| 1857 _errorReporter.reportErrorForNode(StaticWarningCode.ASSIGNMENT_TO_METHOD,
expression, []); |
| 1858 return true; |
| 1859 } |
| 1860 return false; |
| 1861 } |
| 1862 |
| 1863 /** |
| 1864 * This verifies that the passed identifier is not a keyword, and generates th
e passed error code |
| 1865 * on the identifier if it is a keyword. |
| 1866 * |
| 1867 * @param identifier the identifier to check to ensure that it is not a keywor
d |
| 1868 * @param errorCode if the passed identifier is a keyword then this error code
is created on the |
| 1869 * identifier, the error code will be one of |
| 1870 * [CompileTimeErrorCode#BUILT_IN_IDENTIFIER_AS_TYPE_NAME], |
| 1871 * [CompileTimeErrorCode#BUILT_IN_IDENTIFIER_AS_TYPE_PARAMETER_NAME]
or |
| 1872 * [CompileTimeErrorCode#BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME] |
| 1873 * @return `true` if and only if an error code is generated on the passed node |
| 1874 * @see CompileTimeErrorCode#BUILT_IN_IDENTIFIER_AS_TYPE_NAME |
| 1875 * @see CompileTimeErrorCode#BUILT_IN_IDENTIFIER_AS_TYPE_PARAMETER_NAME |
| 1876 * @see CompileTimeErrorCode#BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME |
| 1877 */ |
| 1878 bool _checkForBuiltInIdentifierAsName(SimpleIdentifier identifier, ErrorCode e
rrorCode) { |
| 1879 sc.Token token = identifier.token; |
| 1880 if (token.type == sc.TokenType.KEYWORD) { |
| 1881 _errorReporter.reportErrorForNode(errorCode, identifier, [identifier.name]
); |
| 1882 return true; |
| 1883 } |
| 1884 return false; |
| 1885 } |
| 1886 |
| 1887 /** |
| 1888 * This verifies that the given switch case is terminated with 'break', 'conti
nue', 'return' or |
| 1889 * 'throw'. |
| 1890 * |
| 1891 * @param node the switch case to evaluate |
| 1892 * @return `true` if and only if an error code is generated on the passed node |
| 1893 * @see StaticWarningCode#CASE_BLOCK_NOT_TERMINATED |
| 1894 */ |
| 1895 bool _checkForCaseBlockNotTerminated(SwitchCase node) { |
| 1896 NodeList<Statement> statements = node.statements; |
| 1897 if (statements.isEmpty) { |
| 1898 // fall-through without statements at all |
| 1899 AstNode parent = node.parent; |
| 1900 if (parent is SwitchStatement) { |
| 1901 SwitchStatement switchStatement = parent; |
| 1902 NodeList<SwitchMember> members = switchStatement.members; |
| 1903 int index = members.indexOf(node); |
| 1904 if (index != -1 && index < members.length - 1) { |
| 1905 return false; |
| 1906 } |
| 1907 } |
| 1908 // no other switch member after this one |
| 1909 } else { |
| 1910 Statement statement = statements[statements.length - 1]; |
| 1911 // terminated with statement |
| 1912 if (statement is BreakStatement || statement is ContinueStatement || state
ment is ReturnStatement) { |
| 1913 return false; |
| 1914 } |
| 1915 // terminated with 'throw' expression |
| 1916 if (statement is ExpressionStatement) { |
| 1917 Expression expression = statement.expression; |
| 1918 if (expression is ThrowExpression) { |
| 1919 return false; |
| 1920 } |
| 1921 } |
| 1922 } |
| 1923 // report error |
| 1924 _errorReporter.reportErrorForToken(StaticWarningCode.CASE_BLOCK_NOT_TERMINAT
ED, node.keyword, []); |
| 1925 return true; |
| 1926 } |
| 1927 |
| 1928 /** |
| 1929 * This verifies that the switch cases in the given switch statement is termin
ated with 'break', |
| 1930 * 'continue', 'return' or 'throw'. |
| 1931 * |
| 1932 * @param node the switch statement containing the cases to be checked |
| 1933 * @return `true` if and only if an error code is generated on the passed node |
| 1934 * @see StaticWarningCode#CASE_BLOCK_NOT_TERMINATED |
| 1935 */ |
| 1936 bool _checkForCaseBlocksNotTerminated(SwitchStatement node) { |
| 1937 bool foundError = false; |
| 1938 NodeList<SwitchMember> members = node.members; |
| 1939 int lastMember = members.length - 1; |
| 1940 for (int i = 0; i < lastMember; i++) { |
| 1941 SwitchMember member = members[i]; |
| 1942 if (member is SwitchCase && _checkForCaseBlockNotTerminated(member)) { |
| 1943 foundError = true; |
| 1944 } |
| 1945 } |
| 1946 return foundError; |
| 1947 } |
| 1948 |
| 1949 /** |
| 1950 * This verifies that the passed method declaration is abstract only if the en
closing class is |
| 1951 * also abstract. |
| 1952 * |
| 1953 * @param node the method declaration to evaluate |
| 1954 * @return `true` if and only if an error code is generated on the passed node |
| 1955 * @see StaticWarningCode#CONCRETE_CLASS_WITH_ABSTRACT_MEMBER |
| 1956 */ |
| 1957 bool _checkForConcreteClassWithAbstractMember(MethodDeclaration node) { |
| 1958 if (node.isAbstract && _enclosingClass != null && !_enclosingClass.isAbstrac
t) { |
| 1959 SimpleIdentifier nameNode = node.name; |
| 1960 String memberName = nameNode.name; |
| 1961 ExecutableElement overriddenMember; |
| 1962 if (node.isGetter) { |
| 1963 overriddenMember = _enclosingClass.lookUpInheritedConcreteGetter(memberN
ame, _currentLibrary); |
| 1964 } else if (node.isSetter) { |
| 1965 overriddenMember = _enclosingClass.lookUpInheritedConcreteSetter(memberN
ame, _currentLibrary); |
| 1966 } else { |
| 1967 overriddenMember = _enclosingClass.lookUpInheritedConcreteMethod(memberN
ame, _currentLibrary); |
| 1968 } |
| 1969 if (overriddenMember == null) { |
| 1970 _errorReporter.reportErrorForNode(StaticWarningCode.CONCRETE_CLASS_WITH_
ABSTRACT_MEMBER, nameNode, [memberName, _enclosingClass.displayName]); |
| 1971 return true; |
| 1972 } |
| 1973 } |
| 1974 return false; |
| 1975 } |
| 1976 |
| 1977 /** |
| 1978 * This verifies all possible conflicts of the constructor name with other con
structors and |
| 1979 * members of the same class. |
| 1980 * |
| 1981 * @param node the constructor declaration to evaluate |
| 1982 * @param constructorElement the constructor element |
| 1983 * @return `true` if and only if an error code is generated on the passed node |
| 1984 * @see CompileTimeErrorCode#DUPLICATE_CONSTRUCTOR_DEFAULT |
| 1985 * @see CompileTimeErrorCode#DUPLICATE_CONSTRUCTOR_NAME |
| 1986 * @see CompileTimeErrorCode#CONFLICTING_CONSTRUCTOR_NAME_AND_FIELD |
| 1987 * @see CompileTimeErrorCode#CONFLICTING_CONSTRUCTOR_NAME_AND_METHOD |
| 1988 */ |
| 1989 bool _checkForConflictingConstructorNameAndMember(ConstructorDeclaration node,
ConstructorElement constructorElement) { |
| 1990 SimpleIdentifier constructorName = node.name; |
| 1991 String name = constructorElement.name; |
| 1992 ClassElement classElement = constructorElement.enclosingElement; |
| 1993 // constructors |
| 1994 List<ConstructorElement> constructors = classElement.constructors; |
| 1995 for (ConstructorElement otherConstructor in constructors) { |
| 1996 if (identical(otherConstructor, constructorElement)) { |
| 1997 continue; |
| 1998 } |
| 1999 if (name == otherConstructor.name) { |
| 2000 if (name == null || name.length == 0) { |
| 2001 _errorReporter.reportErrorForNode(CompileTimeErrorCode.DUPLICATE_CONST
RUCTOR_DEFAULT, node, []); |
| 2002 } else { |
| 2003 _errorReporter.reportErrorForNode(CompileTimeErrorCode.DUPLICATE_CONST
RUCTOR_NAME, node, [name]); |
| 2004 } |
| 2005 return true; |
| 2006 } |
| 2007 } |
| 2008 // conflict with class member |
| 2009 if (constructorName != null && constructorElement != null && !constructorNam
e.isSynthetic) { |
| 2010 // fields |
| 2011 FieldElement field = classElement.getField(name); |
| 2012 if (field != null) { |
| 2013 _errorReporter.reportErrorForNode(CompileTimeErrorCode.CONFLICTING_CONST
RUCTOR_NAME_AND_FIELD, node, [name]); |
| 2014 return true; |
| 2015 } |
| 2016 // methods |
| 2017 MethodElement method = classElement.getMethod(name); |
| 2018 if (method != null) { |
| 2019 _errorReporter.reportErrorForNode(CompileTimeErrorCode.CONFLICTING_CONST
RUCTOR_NAME_AND_METHOD, node, [name]); |
| 2020 return true; |
| 2021 } |
| 2022 } |
| 2023 return false; |
| 2024 } |
| 2025 |
| 2026 /** |
| 2027 * This verifies that the [enclosingClass] does not have a method and getter p
air with the |
| 2028 * same name on, via inheritance. |
| 2029 * |
| 2030 * @return `true` if and only if an error code is generated on the passed node |
| 2031 * @see CompileTimeErrorCode#CONFLICTING_GETTER_AND_METHOD |
| 2032 * @see CompileTimeErrorCode#CONFLICTING_METHOD_AND_GETTER |
| 2033 */ |
| 2034 bool _checkForConflictingGetterAndMethod() { |
| 2035 if (_enclosingClass == null) { |
| 2036 return false; |
| 2037 } |
| 2038 bool hasProblem = false; |
| 2039 // method declared in the enclosing class vs. inherited getter |
| 2040 for (MethodElement method in _enclosingClass.methods) { |
| 2041 String name = method.name; |
| 2042 // find inherited property accessor (and can be only getter) |
| 2043 ExecutableElement inherited = _inheritanceManager.lookupInheritance(_enclo
singClass, name); |
| 2044 if (inherited is! PropertyAccessorElement) { |
| 2045 continue; |
| 2046 } |
| 2047 // report problem |
| 2048 hasProblem = true; |
| 2049 _errorReporter.reportErrorForOffset(CompileTimeErrorCode.CONFLICTING_GETTE
R_AND_METHOD, method.nameOffset, name.length, [ |
| 2050 _enclosingClass.displayName, |
| 2051 inherited.enclosingElement.displayName, |
| 2052 name]); |
| 2053 } |
| 2054 // getter declared in the enclosing class vs. inherited method |
| 2055 for (PropertyAccessorElement accessor in _enclosingClass.accessors) { |
| 2056 if (!accessor.isGetter) { |
| 2057 continue; |
| 2058 } |
| 2059 String name = accessor.name; |
| 2060 // find inherited method |
| 2061 ExecutableElement inherited = _inheritanceManager.lookupInheritance(_enclo
singClass, name); |
| 2062 if (inherited is! MethodElement) { |
| 2063 continue; |
| 2064 } |
| 2065 // report problem |
| 2066 hasProblem = true; |
| 2067 _errorReporter.reportErrorForOffset(CompileTimeErrorCode.CONFLICTING_METHO
D_AND_GETTER, accessor.nameOffset, name.length, [ |
| 2068 _enclosingClass.displayName, |
| 2069 inherited.enclosingElement.displayName, |
| 2070 name]); |
| 2071 } |
| 2072 // done |
| 2073 return hasProblem; |
| 2074 } |
| 2075 |
| 2076 /** |
| 2077 * This verifies that the superclass of the [enclosingClass] does not declare
accessible |
| 2078 * static members with the same name as the instance getters/setters declared
in |
| 2079 * [enclosingClass]. |
| 2080 * |
| 2081 * @param node the method declaration to evaluate |
| 2082 * @return `true` if and only if an error code is generated on the passed node |
| 2083 * @see StaticWarningCode#CONFLICTING_INSTANCE_GETTER_AND_SUPERCLASS_MEMBER |
| 2084 * @see StaticWarningCode#CONFLICTING_INSTANCE_SETTER_AND_SUPERCLASS_MEMBER |
| 2085 */ |
| 2086 bool _checkForConflictingInstanceGetterAndSuperclassMember() { |
| 2087 if (_enclosingClass == null) { |
| 2088 return false; |
| 2089 } |
| 2090 InterfaceType enclosingType = _enclosingClass.type; |
| 2091 // check every accessor |
| 2092 bool hasProblem = false; |
| 2093 for (PropertyAccessorElement accessor in _enclosingClass.accessors) { |
| 2094 // we analyze instance accessors here |
| 2095 if (accessor.isStatic) { |
| 2096 continue; |
| 2097 } |
| 2098 // prepare accessor properties |
| 2099 String name = accessor.displayName; |
| 2100 bool getter = accessor.isGetter; |
| 2101 // if non-final variable, ignore setter - we alreay reported problem for g
etter |
| 2102 if (accessor.isSetter && accessor.isSynthetic) { |
| 2103 continue; |
| 2104 } |
| 2105 // try to find super element |
| 2106 ExecutableElement superElement; |
| 2107 superElement = enclosingType.lookUpGetterInSuperclass(name, _currentLibrar
y); |
| 2108 if (superElement == null) { |
| 2109 superElement = enclosingType.lookUpSetterInSuperclass(name, _currentLibr
ary); |
| 2110 } |
| 2111 if (superElement == null) { |
| 2112 superElement = enclosingType.lookUpMethodInSuperclass(name, _currentLibr
ary); |
| 2113 } |
| 2114 if (superElement == null) { |
| 2115 continue; |
| 2116 } |
| 2117 // OK, not static |
| 2118 if (!superElement.isStatic) { |
| 2119 continue; |
| 2120 } |
| 2121 // prepare "super" type to report its name |
| 2122 ClassElement superElementClass = superElement.enclosingElement as ClassEle
ment; |
| 2123 InterfaceType superElementType = superElementClass.type; |
| 2124 // report problem |
| 2125 hasProblem = true; |
| 2126 if (getter) { |
| 2127 _errorReporter.reportErrorForElement(StaticWarningCode.CONFLICTING_INSTA
NCE_GETTER_AND_SUPERCLASS_MEMBER, accessor, [superElementType.displayName]); |
| 2128 } else { |
| 2129 _errorReporter.reportErrorForElement(StaticWarningCode.CONFLICTING_INSTA
NCE_SETTER_AND_SUPERCLASS_MEMBER, accessor, [superElementType.displayName]); |
| 2130 } |
| 2131 } |
| 2132 // done |
| 2133 return hasProblem; |
| 2134 } |
| 2135 |
| 2136 /** |
| 2137 * This verifies that the enclosing class does not have a setter with the same
name as the passed |
| 2138 * instance method declaration. |
| 2139 * |
| 2140 * TODO(jwren) add other "conflicting" error codes into algorithm/ data struct
ure |
| 2141 * |
| 2142 * @param node the method declaration to evaluate |
| 2143 * @return `true` if and only if an error code is generated on the passed node |
| 2144 * @see StaticWarningCode#CONFLICTING_INSTANCE_METHOD_SETTER |
| 2145 */ |
| 2146 bool _checkForConflictingInstanceMethodSetter(ClassDeclaration node) { |
| 2147 // Reference all of the class members in this class. |
| 2148 NodeList<ClassMember> classMembers = node.members; |
| 2149 if (classMembers.isEmpty) { |
| 2150 return false; |
| 2151 } |
| 2152 // Create a HashMap to track conflicting members, and then loop through memb
ers in the class to |
| 2153 // construct the HashMap, at the same time, look for violations. Don't add
members if they are |
| 2154 // part of a conflict, this prevents multiple warnings for one issue. |
| 2155 bool foundError = false; |
| 2156 HashMap<String, ClassMember> memberHashMap = new HashMap<String, ClassMember
>(); |
| 2157 for (ClassMember classMember in classMembers) { |
| 2158 if (classMember is MethodDeclaration) { |
| 2159 MethodDeclaration method = classMember; |
| 2160 if (method.isStatic) { |
| 2161 continue; |
| 2162 } |
| 2163 // prepare name |
| 2164 SimpleIdentifier name = method.name; |
| 2165 if (name == null) { |
| 2166 continue; |
| 2167 } |
| 2168 bool addThisMemberToTheMap = true; |
| 2169 bool isGetter = method.isGetter; |
| 2170 bool isSetter = method.isSetter; |
| 2171 bool isOperator = method.isOperator; |
| 2172 bool isMethod = !isGetter && !isSetter && !isOperator; |
| 2173 // Do lookups in the enclosing class (and the inherited member) if the m
ember is a method or |
| 2174 // a setter for StaticWarningCode.CONFLICTING_INSTANCE_METHOD_SETTER war
ning. |
| 2175 if (isMethod) { |
| 2176 String setterName = "${name.name}="; |
| 2177 Element enclosingElementOfSetter = null; |
| 2178 ClassMember conflictingSetter = memberHashMap[setterName]; |
| 2179 if (conflictingSetter != null) { |
| 2180 enclosingElementOfSetter = conflictingSetter.element.enclosingElemen
t; |
| 2181 } else { |
| 2182 ExecutableElement elementFromInheritance = _inheritanceManager.looku
pInheritance(_enclosingClass, setterName); |
| 2183 if (elementFromInheritance != null) { |
| 2184 enclosingElementOfSetter = elementFromInheritance.enclosingElement
; |
| 2185 } |
| 2186 } |
| 2187 if (enclosingElementOfSetter != null) { |
| 2188 // report problem |
| 2189 _errorReporter.reportErrorForNode(StaticWarningCode.CONFLICTING_INST
ANCE_METHOD_SETTER, name, [ |
| 2190 _enclosingClass.displayName, |
| 2191 name.name, |
| 2192 enclosingElementOfSetter.displayName]); |
| 2193 foundError = true; |
| 2194 addThisMemberToTheMap = false; |
| 2195 } |
| 2196 } else if (isSetter) { |
| 2197 String methodName = name.name; |
| 2198 ClassMember conflictingMethod = memberHashMap[methodName]; |
| 2199 if (conflictingMethod != null && conflictingMethod is MethodDeclaratio
n && !conflictingMethod.isGetter) { |
| 2200 // report problem |
| 2201 _errorReporter.reportErrorForNode(StaticWarningCode.CONFLICTING_INST
ANCE_METHOD_SETTER2, name, [_enclosingClass.displayName, name.name]); |
| 2202 foundError = true; |
| 2203 addThisMemberToTheMap = false; |
| 2204 } |
| 2205 } |
| 2206 // Finally, add this member into the HashMap. |
| 2207 if (addThisMemberToTheMap) { |
| 2208 if (method.isSetter) { |
| 2209 memberHashMap["${name.name}="] = method; |
| 2210 } else { |
| 2211 memberHashMap[name.name] = method; |
| 2212 } |
| 2213 } |
| 2214 } |
| 2215 } |
| 2216 return foundError; |
| 2217 } |
| 2218 |
| 2219 /** |
| 2220 * This verifies that the enclosing class does not have an instance member wit
h the same name as |
| 2221 * the passed static getter method declaration. |
| 2222 * |
| 2223 * @param node the method declaration to evaluate |
| 2224 * @return `true` if and only if an error code is generated on the passed node |
| 2225 * @see StaticWarningCode#CONFLICTING_STATIC_GETTER_AND_INSTANCE_SETTER |
| 2226 */ |
| 2227 bool _checkForConflictingStaticGetterAndInstanceSetter(MethodDeclaration node)
{ |
| 2228 if (!node.isStatic) { |
| 2229 return false; |
| 2230 } |
| 2231 // prepare name |
| 2232 SimpleIdentifier nameNode = node.name; |
| 2233 if (nameNode == null) { |
| 2234 return false; |
| 2235 } |
| 2236 String name = nameNode.name; |
| 2237 // prepare enclosing type |
| 2238 if (_enclosingClass == null) { |
| 2239 return false; |
| 2240 } |
| 2241 InterfaceType enclosingType = _enclosingClass.type; |
| 2242 // try to find setter |
| 2243 ExecutableElement setter = enclosingType.lookUpSetter(name, _currentLibrary)
; |
| 2244 if (setter == null) { |
| 2245 return false; |
| 2246 } |
| 2247 // OK, also static |
| 2248 if (setter.isStatic) { |
| 2249 return false; |
| 2250 } |
| 2251 // prepare "setter" type to report its name |
| 2252 ClassElement setterClass = setter.enclosingElement as ClassElement; |
| 2253 InterfaceType setterType = setterClass.type; |
| 2254 // report problem |
| 2255 _errorReporter.reportErrorForNode(StaticWarningCode.CONFLICTING_STATIC_GETTE
R_AND_INSTANCE_SETTER, nameNode, [setterType.displayName]); |
| 2256 return true; |
| 2257 } |
| 2258 |
| 2259 /** |
| 2260 * This verifies that the enclosing class does not have an instance member wit
h the same name as |
| 2261 * the passed static getter method declaration. |
| 2262 * |
| 2263 * @param node the method declaration to evaluate |
| 2264 * @return `true` if and only if an error code is generated on the passed node |
| 2265 * @see StaticWarningCode#CONFLICTING_STATIC_SETTER_AND_INSTANCE_MEMBER |
| 2266 */ |
| 2267 bool _checkForConflictingStaticSetterAndInstanceMember(MethodDeclaration node)
{ |
| 2268 if (!node.isStatic) { |
| 2269 return false; |
| 2270 } |
| 2271 // prepare name |
| 2272 SimpleIdentifier nameNode = node.name; |
| 2273 if (nameNode == null) { |
| 2274 return false; |
| 2275 } |
| 2276 String name = nameNode.name; |
| 2277 // prepare enclosing type |
| 2278 if (_enclosingClass == null) { |
| 2279 return false; |
| 2280 } |
| 2281 InterfaceType enclosingType = _enclosingClass.type; |
| 2282 // try to find member |
| 2283 ExecutableElement member; |
| 2284 member = enclosingType.lookUpMethod(name, _currentLibrary); |
| 2285 if (member == null) { |
| 2286 member = enclosingType.lookUpGetter(name, _currentLibrary); |
| 2287 } |
| 2288 if (member == null) { |
| 2289 member = enclosingType.lookUpSetter(name, _currentLibrary); |
| 2290 } |
| 2291 if (member == null) { |
| 2292 return false; |
| 2293 } |
| 2294 // OK, also static |
| 2295 if (member.isStatic) { |
| 2296 return false; |
| 2297 } |
| 2298 // prepare "member" type to report its name |
| 2299 ClassElement memberClass = member.enclosingElement as ClassElement; |
| 2300 InterfaceType memberType = memberClass.type; |
| 2301 // report problem |
| 2302 _errorReporter.reportErrorForNode(StaticWarningCode.CONFLICTING_STATIC_SETTE
R_AND_INSTANCE_MEMBER, nameNode, [memberType.displayName]); |
| 2303 return true; |
| 2304 } |
| 2305 |
| 2306 /** |
| 2307 * This verifies all conflicts between type variable and enclosing class. TODO
(scheglov) |
| 2308 * |
| 2309 * @param node the class declaration to evaluate |
| 2310 * @return `true` if and only if an error code is generated on the passed node |
| 2311 * @see CompileTimeErrorCode#CONFLICTING_TYPE_VARIABLE_AND_CLASS |
| 2312 * @see CompileTimeErrorCode#CONFLICTING_TYPE_VARIABLE_AND_MEMBER |
| 2313 */ |
| 2314 bool _checkForConflictingTypeVariableErrorCodes(ClassDeclaration node) { |
| 2315 bool problemReported = false; |
| 2316 for (TypeParameterElement typeParameter in _enclosingClass.typeParameters) { |
| 2317 String name = typeParameter.name; |
| 2318 // name is same as the name of the enclosing class |
| 2319 if (_enclosingClass.name == name) { |
| 2320 _errorReporter.reportErrorForOffset(CompileTimeErrorCode.CONFLICTING_TYP
E_VARIABLE_AND_CLASS, typeParameter.nameOffset, name.length, [name]); |
| 2321 problemReported = true; |
| 2322 } |
| 2323 // check members |
| 2324 if (_enclosingClass.getMethod(name) != null || _enclosingClass.getGetter(n
ame) != null || _enclosingClass.getSetter(name) != null) { |
| 2325 _errorReporter.reportErrorForOffset(CompileTimeErrorCode.CONFLICTING_TYP
E_VARIABLE_AND_MEMBER, typeParameter.nameOffset, name.length, [name]); |
| 2326 problemReported = true; |
| 2327 } |
| 2328 } |
| 2329 return problemReported; |
| 2330 } |
| 2331 |
| 2332 /** |
| 2333 * This verifies that if the passed constructor declaration is 'const' then th
ere are no |
| 2334 * invocations of non-'const' super constructors. |
| 2335 * |
| 2336 * @param node the constructor declaration to evaluate |
| 2337 * @return `true` if and only if an error code is generated on the passed node |
| 2338 * @see CompileTimeErrorCode#CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER |
| 2339 */ |
| 2340 bool _checkForConstConstructorWithNonConstSuper(ConstructorDeclaration node) { |
| 2341 if (!_isEnclosingConstructorConst) { |
| 2342 return false; |
| 2343 } |
| 2344 // OK, const factory, checked elsewhere |
| 2345 if (node.factoryKeyword != null) { |
| 2346 return false; |
| 2347 } |
| 2348 // check for mixins |
| 2349 if (_enclosingClass.mixins.length != 0) { |
| 2350 _errorReporter.reportErrorForNode(CompileTimeErrorCode.CONST_CONSTRUCTOR_W
ITH_MIXIN, node.returnType, []); |
| 2351 return true; |
| 2352 } |
| 2353 // try to find and check super constructor invocation |
| 2354 for (ConstructorInitializer initializer in node.initializers) { |
| 2355 if (initializer is SuperConstructorInvocation) { |
| 2356 SuperConstructorInvocation superInvocation = initializer; |
| 2357 ConstructorElement element = superInvocation.staticElement; |
| 2358 if (element == null || element.isConst) { |
| 2359 return false; |
| 2360 } |
| 2361 _errorReporter.reportErrorForNode(CompileTimeErrorCode.CONST_CONSTRUCTOR
_WITH_NON_CONST_SUPER, superInvocation, [element.enclosingElement.displayName]); |
| 2362 return true; |
| 2363 } |
| 2364 } |
| 2365 // no explicit super constructor invocation, check default constructor |
| 2366 InterfaceType supertype = _enclosingClass.supertype; |
| 2367 if (supertype == null) { |
| 2368 return false; |
| 2369 } |
| 2370 if (supertype.isObject) { |
| 2371 return false; |
| 2372 } |
| 2373 ConstructorElement unnamedConstructor = supertype.element.unnamedConstructor
; |
| 2374 if (unnamedConstructor == null) { |
| 2375 return false; |
| 2376 } |
| 2377 if (unnamedConstructor.isConst) { |
| 2378 return false; |
| 2379 } |
| 2380 // default constructor is not 'const', report problem |
| 2381 _errorReporter.reportErrorForNode(CompileTimeErrorCode.CONST_CONSTRUCTOR_WIT
H_NON_CONST_SUPER, node.returnType, [supertype.displayName]); |
| 2382 return true; |
| 2383 } |
| 2384 |
| 2385 /** |
| 2386 * This verifies that if the passed constructor declaration is 'const' then th
ere are no non-final |
| 2387 * instance variable. |
| 2388 * |
| 2389 * @param node the constructor declaration to evaluate |
| 2390 * @param constructorElement the constructor element |
| 2391 * @return `true` if and only if an error code is generated on the passed node |
| 2392 * @see CompileTimeErrorCode#CONST_CONSTRUCTOR_WITH_NON_FINAL_FIELD |
| 2393 */ |
| 2394 bool _checkForConstConstructorWithNonFinalField(ConstructorDeclaration node, C
onstructorElement constructorElement) { |
| 2395 if (!_isEnclosingConstructorConst) { |
| 2396 return false; |
| 2397 } |
| 2398 // check if there is non-final field |
| 2399 ClassElement classElement = constructorElement.enclosingElement; |
| 2400 if (!classElement.hasNonFinalField) { |
| 2401 return false; |
| 2402 } |
| 2403 // report problem |
| 2404 _errorReporter.reportErrorForNode(CompileTimeErrorCode.CONST_CONSTRUCTOR_WIT
H_NON_FINAL_FIELD, node, []); |
| 2405 return true; |
| 2406 } |
| 2407 |
| 2408 /** |
| 2409 * This verifies that the passed 'const' instance creation expression is not c
reating a deferred |
| 2410 * type. |
| 2411 * |
| 2412 * @param node the instance creation expression to evaluate |
| 2413 * @param constructorName the constructor name, always non-`null` |
| 2414 * @param typeName the name of the type defining the constructor, always non-`
null` |
| 2415 * @return `true` if and only if an error code is generated on the passed node |
| 2416 * @see CompileTimeErrorCode#CONST_DEFERRED_CLASS |
| 2417 */ |
| 2418 bool _checkForConstDeferredClass(InstanceCreationExpression node, ConstructorN
ame constructorName, TypeName typeName) { |
| 2419 if (typeName.isDeferred) { |
| 2420 _errorReporter.reportErrorForNode(CompileTimeErrorCode.CONST_DEFERRED_CLAS
S, constructorName, [typeName.name.name]); |
| 2421 return true; |
| 2422 } |
| 2423 return false; |
| 2424 } |
| 2425 |
| 2426 /** |
| 2427 * This verifies that the passed throw expression is not enclosed in a 'const'
constructor |
| 2428 * declaration. |
| 2429 * |
| 2430 * @param node the throw expression expression to evaluate |
| 2431 * @return `true` if and only if an error code is generated on the passed node |
| 2432 * @see CompileTimeErrorCode#CONST_CONSTRUCTOR_THROWS_EXCEPTION |
| 2433 */ |
| 2434 bool _checkForConstEvalThrowsException(ThrowExpression node) { |
| 2435 if (_isEnclosingConstructorConst) { |
| 2436 _errorReporter.reportErrorForNode(CompileTimeErrorCode.CONST_CONSTRUCTOR_T
HROWS_EXCEPTION, node, []); |
| 2437 return true; |
| 2438 } |
| 2439 return false; |
| 2440 } |
| 2441 |
| 2442 /** |
| 2443 * This verifies that the passed normal formal parameter is not 'const'. |
| 2444 * |
| 2445 * @param node the normal formal parameter to evaluate |
| 2446 * @return `true` if and only if an error code is generated on the passed node |
| 2447 * @see CompileTimeErrorCode#CONST_FORMAL_PARAMETER |
| 2448 */ |
| 2449 bool _checkForConstFormalParameter(NormalFormalParameter node) { |
| 2450 if (node.isConst) { |
| 2451 _errorReporter.reportErrorForNode(CompileTimeErrorCode.CONST_FORMAL_PARAME
TER, node, []); |
| 2452 return true; |
| 2453 } |
| 2454 return false; |
| 2455 } |
| 2456 |
| 2457 /** |
| 2458 * This verifies that the passed instance creation expression is not being inv
oked on an abstract |
| 2459 * class. |
| 2460 * |
| 2461 * @param node the instance creation expression to evaluate |
| 2462 * @param typeName the [TypeName] of the [ConstructorName] from the |
| 2463 * [InstanceCreationExpression], this is the AST node that the error
is attached to |
| 2464 * @param type the type being constructed with this [InstanceCreationExpressio
n] |
| 2465 * @return `true` if and only if an error code is generated on the passed node |
| 2466 * @see StaticWarningCode#CONST_WITH_ABSTRACT_CLASS |
| 2467 * @see StaticWarningCode#NEW_WITH_ABSTRACT_CLASS |
| 2468 */ |
| 2469 bool _checkForConstOrNewWithAbstractClass(InstanceCreationExpression node, Typ
eName typeName, InterfaceType type) { |
| 2470 if (type.element.isAbstract) { |
| 2471 ConstructorElement element = node.staticElement; |
| 2472 if (element != null && !element.isFactory) { |
| 2473 if ((node.keyword as sc.KeywordToken).keyword == sc.Keyword.CONST) { |
| 2474 _errorReporter.reportErrorForNode(StaticWarningCode.CONST_WITH_ABSTRAC
T_CLASS, typeName, []); |
| 2475 } else { |
| 2476 _errorReporter.reportErrorForNode(StaticWarningCode.NEW_WITH_ABSTRACT_
CLASS, typeName, []); |
| 2477 } |
| 2478 return true; |
| 2479 } |
| 2480 } |
| 2481 return false; |
| 2482 } |
| 2483 |
| 2484 /** |
| 2485 * This verifies that the passed instance creation expression is not being inv
oked on an enum. |
| 2486 * |
| 2487 * @param node the instance creation expression to verify |
| 2488 * @param typeName the [TypeName] of the [ConstructorName] from the |
| 2489 * [InstanceCreationExpression], this is the AST node that the error
is attached to |
| 2490 * @param type the type being constructed with this [InstanceCreationExpressio
n] |
| 2491 * @return `true` if and only if an error code is generated on the passed node |
| 2492 * @see CompileTimeErrorCode#INSTANTIATE_ENUM |
| 2493 */ |
| 2494 bool _checkForConstOrNewWithEnum(InstanceCreationExpression node, TypeName typ
eName, InterfaceType type) { |
| 2495 if (type.element.isEnum) { |
| 2496 _errorReporter.reportErrorForNode(CompileTimeErrorCode.INSTANTIATE_ENUM, t
ypeName, []); |
| 2497 return true; |
| 2498 } |
| 2499 return false; |
| 2500 } |
| 2501 |
| 2502 /** |
| 2503 * This verifies that the passed 'const' instance creation expression is not b
eing invoked on a |
| 2504 * constructor that is not 'const'. |
| 2505 * |
| 2506 * This method assumes that the instance creation was tested to be 'const' bef
ore being called. |
| 2507 * |
| 2508 * @param node the instance creation expression to verify |
| 2509 * @return `true` if and only if an error code is generated on the passed node |
| 2510 * @see CompileTimeErrorCode#CONST_WITH_NON_CONST |
| 2511 */ |
| 2512 bool _checkForConstWithNonConst(InstanceCreationExpression node) { |
| 2513 ConstructorElement constructorElement = node.staticElement; |
| 2514 if (constructorElement != null && !constructorElement.isConst) { |
| 2515 _errorReporter.reportErrorForNode(CompileTimeErrorCode.CONST_WITH_NON_CONS
T, node, []); |
| 2516 return true; |
| 2517 } |
| 2518 return false; |
| 2519 } |
| 2520 |
| 2521 /** |
| 2522 * This verifies that the passed type name does not reference any type paramet
ers. |
| 2523 * |
| 2524 * @param typeName the type name to evaluate |
| 2525 * @return `true` if and only if an error code is generated on the passed node |
| 2526 * @see CompileTimeErrorCode#CONST_WITH_TYPE_PARAMETERS |
| 2527 */ |
| 2528 bool _checkForConstWithTypeParameters(TypeName typeName) { |
| 2529 // something wrong with AST |
| 2530 if (typeName == null) { |
| 2531 return false; |
| 2532 } |
| 2533 Identifier name = typeName.name; |
| 2534 if (name == null) { |
| 2535 return false; |
| 2536 } |
| 2537 // should not be a type parameter |
| 2538 if (name.staticElement is TypeParameterElement) { |
| 2539 _errorReporter.reportErrorForNode(CompileTimeErrorCode.CONST_WITH_TYPE_PAR
AMETERS, name, []); |
| 2540 } |
| 2541 // check type arguments |
| 2542 TypeArgumentList typeArguments = typeName.typeArguments; |
| 2543 if (typeArguments != null) { |
| 2544 bool hasError = false; |
| 2545 for (TypeName argument in typeArguments.arguments) { |
| 2546 if (_checkForConstWithTypeParameters(argument)) { |
| 2547 hasError = true; |
| 2548 } |
| 2549 } |
| 2550 return hasError; |
| 2551 } |
| 2552 // OK |
| 2553 return false; |
| 2554 } |
| 2555 |
| 2556 /** |
| 2557 * This verifies that if the passed 'const' instance creation expression is be
ing invoked on the |
| 2558 * resolved constructor. |
| 2559 * |
| 2560 * This method assumes that the instance creation was tested to be 'const' bef
ore being called. |
| 2561 * |
| 2562 * @param node the instance creation expression to evaluate |
| 2563 * @param constructorName the constructor name, always non-`null` |
| 2564 * @param typeName the name of the type defining the constructor, always non-`
null` |
| 2565 * @return `true` if and only if an error code is generated on the passed node |
| 2566 * @see CompileTimeErrorCode#CONST_WITH_UNDEFINED_CONSTRUCTOR |
| 2567 * @see CompileTimeErrorCode#CONST_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT |
| 2568 */ |
| 2569 bool _checkForConstWithUndefinedConstructor(InstanceCreationExpression node, C
onstructorName constructorName, TypeName typeName) { |
| 2570 // OK if resolved |
| 2571 if (node.staticElement != null) { |
| 2572 return false; |
| 2573 } |
| 2574 DartType type = typeName.type; |
| 2575 if (type is InterfaceType) { |
| 2576 ClassElement element = type.element; |
| 2577 if (element != null && element.isEnum) { |
| 2578 // We have already reported the error. |
| 2579 return false; |
| 2580 } |
| 2581 } |
| 2582 Identifier className = typeName.name; |
| 2583 // report as named or default constructor absence |
| 2584 SimpleIdentifier name = constructorName.name; |
| 2585 if (name != null) { |
| 2586 _errorReporter.reportErrorForNode(CompileTimeErrorCode.CONST_WITH_UNDEFINE
D_CONSTRUCTOR, name, [className, name]); |
| 2587 } else { |
| 2588 _errorReporter.reportErrorForNode(CompileTimeErrorCode.CONST_WITH_UNDEFINE
D_CONSTRUCTOR_DEFAULT, constructorName, [className]); |
| 2589 } |
| 2590 return true; |
| 2591 } |
| 2592 |
| 2593 /** |
| 2594 * This verifies that there are no default parameters in the passed function t
ype alias. |
| 2595 * |
| 2596 * @param node the function type alias to evaluate |
| 2597 * @return `true` if and only if an error code is generated on the passed node |
| 2598 * @see CompileTimeErrorCode#DEFAULT_VALUE_IN_FUNCTION_TYPE_ALIAS |
| 2599 */ |
| 2600 bool _checkForDefaultValueInFunctionTypeAlias(FunctionTypeAlias node) { |
| 2601 bool result = false; |
| 2602 FormalParameterList formalParameterList = node.parameters; |
| 2603 NodeList<FormalParameter> parameters = formalParameterList.parameters; |
| 2604 for (FormalParameter formalParameter in parameters) { |
| 2605 if (formalParameter is DefaultFormalParameter) { |
| 2606 DefaultFormalParameter defaultFormalParameter = formalParameter; |
| 2607 if (defaultFormalParameter.defaultValue != null) { |
| 2608 _errorReporter.reportErrorForNode(CompileTimeErrorCode.DEFAULT_VALUE_I
N_FUNCTION_TYPE_ALIAS, node, []); |
| 2609 result = true; |
| 2610 } |
| 2611 } |
| 2612 } |
| 2613 return result; |
| 2614 } |
| 2615 |
| 2616 /** |
| 2617 * This verifies that the given default formal parameter is not part of a func
tion typed |
| 2618 * parameter. |
| 2619 * |
| 2620 * @param node the default formal parameter to evaluate |
| 2621 * @return `true` if and only if an error code is generated on the passed node |
| 2622 * @see CompileTimeErrorCode#DEFAULT_VALUE_IN_FUNCTION_TYPED_PARAMETER |
| 2623 */ |
| 2624 bool _checkForDefaultValueInFunctionTypedParameter(DefaultFormalParameter node
) { |
| 2625 // OK, not in a function typed parameter. |
| 2626 if (!_isInFunctionTypedFormalParameter) { |
| 2627 return false; |
| 2628 } |
| 2629 // OK, no default value. |
| 2630 if (node.defaultValue == null) { |
| 2631 return false; |
| 2632 } |
| 2633 // Report problem. |
| 2634 _errorReporter.reportErrorForNode(CompileTimeErrorCode.DEFAULT_VALUE_IN_FUNC
TION_TYPED_PARAMETER, node, []); |
| 2635 return true; |
| 2636 } |
| 2637 |
| 2638 /** |
| 2639 * This verifies that any deferred imports in the given compilation unit have
a unique prefix. |
| 2640 * |
| 2641 * @param node the compilation unit containing the imports to be checked |
| 2642 * @return `true` if an error was generated |
| 2643 * @see CompileTimeErrorCode#SHARED_DEFERRED_PREFIX |
| 2644 */ |
| 2645 bool _checkForDeferredPrefixCollisions(CompilationUnit node) { |
| 2646 bool foundError = false; |
| 2647 NodeList<Directive> directives = node.directives; |
| 2648 int count = directives.length; |
| 2649 if (count > 0) { |
| 2650 HashMap<PrefixElement, List<ImportDirective>> prefixToDirectivesMap = new
HashMap<PrefixElement, List<ImportDirective>>(); |
| 2651 for (int i = 0; i < count; i++) { |
| 2652 Directive directive = directives[i]; |
| 2653 if (directive is ImportDirective) { |
| 2654 ImportDirective importDirective = directive; |
| 2655 SimpleIdentifier prefix = importDirective.prefix; |
| 2656 if (prefix != null) { |
| 2657 Element element = prefix.staticElement; |
| 2658 if (element is PrefixElement) { |
| 2659 PrefixElement prefixElement = element; |
| 2660 List<ImportDirective> elements = prefixToDirectivesMap[prefixEleme
nt]; |
| 2661 if (elements == null) { |
| 2662 elements = new List<ImportDirective>(); |
| 2663 prefixToDirectivesMap[prefixElement] = elements; |
| 2664 } |
| 2665 elements.add(importDirective); |
| 2666 } |
| 2667 } |
| 2668 } |
| 2669 } |
| 2670 for (List<ImportDirective> imports in prefixToDirectivesMap.values) { |
| 2671 if (_hasDeferredPrefixCollision(imports)) { |
| 2672 foundError = true; |
| 2673 } |
| 2674 } |
| 2675 } |
| 2676 return foundError; |
| 2677 } |
| 2678 |
| 2679 /** |
| 2680 * This verifies that the enclosing class does not have an instance member wit
h the given name of |
| 2681 * the static member. |
| 2682 * |
| 2683 * @return `true` if and only if an error code is generated on the passed node |
| 2684 * @see CompileTimeErrorCode#DUPLICATE_DEFINITION_INHERITANCE |
| 2685 */ |
| 2686 bool _checkForDuplicateDefinitionInheritance() { |
| 2687 if (_enclosingClass == null) { |
| 2688 return false; |
| 2689 } |
| 2690 bool hasProblem = false; |
| 2691 for (ExecutableElement member in _enclosingClass.methods) { |
| 2692 if (member.isStatic && _checkForDuplicateDefinitionOfMember(member)) { |
| 2693 hasProblem = true; |
| 2694 } |
| 2695 } |
| 2696 for (ExecutableElement member in _enclosingClass.accessors) { |
| 2697 if (member.isStatic && _checkForDuplicateDefinitionOfMember(member)) { |
| 2698 hasProblem = true; |
| 2699 } |
| 2700 } |
| 2701 return hasProblem; |
| 2702 } |
| 2703 |
| 2704 /** |
| 2705 * This verifies that the enclosing class does not have an instance member wit
h the given name of |
| 2706 * the static member. |
| 2707 * |
| 2708 * @param staticMember the static member to check conflict for |
| 2709 * @return `true` if and only if an error code is generated on the passed node |
| 2710 * @see CompileTimeErrorCode#DUPLICATE_DEFINITION_INHERITANCE |
| 2711 */ |
| 2712 bool _checkForDuplicateDefinitionOfMember(ExecutableElement staticMember) { |
| 2713 // prepare name |
| 2714 String name = staticMember.name; |
| 2715 if (name == null) { |
| 2716 return false; |
| 2717 } |
| 2718 // try to find member |
| 2719 ExecutableElement inheritedMember = _inheritanceManager.lookupInheritance(_e
nclosingClass, name); |
| 2720 if (inheritedMember == null) { |
| 2721 return false; |
| 2722 } |
| 2723 // OK, also static |
| 2724 if (inheritedMember.isStatic) { |
| 2725 return false; |
| 2726 } |
| 2727 // determine the display name, use the extended display name if the enclosin
g class of the |
| 2728 // inherited member is in a different source |
| 2729 String displayName; |
| 2730 Element enclosingElement = inheritedMember.enclosingElement; |
| 2731 if (enclosingElement.source == _enclosingClass.source) { |
| 2732 displayName = enclosingElement.displayName; |
| 2733 } else { |
| 2734 displayName = enclosingElement.getExtendedDisplayName(null); |
| 2735 } |
| 2736 // report problem |
| 2737 _errorReporter.reportErrorForOffset(CompileTimeErrorCode.DUPLICATE_DEFINITIO
N_INHERITANCE, staticMember.nameOffset, name.length, [name, displayName]); |
| 2738 return true; |
| 2739 } |
| 2740 |
| 2741 /** |
| 2742 * This verifies if the passed list literal has type arguments then there is e
xactly one. |
| 2743 * |
| 2744 * @param node the list literal to evaluate |
| 2745 * @param typeArguments the type arguments, always non-`null` |
| 2746 * @return `true` if and only if an error code is generated on the passed node |
| 2747 * @see StaticTypeWarningCode#EXPECTED_ONE_LIST_TYPE_ARGUMENTS |
| 2748 */ |
| 2749 bool _checkForExpectedOneListTypeArgument(ListLiteral node, TypeArgumentList t
ypeArguments) { |
| 2750 // check number of type arguments |
| 2751 int num = typeArguments.arguments.length; |
| 2752 if (num == 1) { |
| 2753 return false; |
| 2754 } |
| 2755 // report problem |
| 2756 _errorReporter.reportErrorForNode(StaticTypeWarningCode.EXPECTED_ONE_LIST_TY
PE_ARGUMENTS, typeArguments, [num]); |
| 2757 return true; |
| 2758 } |
| 2759 |
| 2760 /** |
| 2761 * This verifies the passed import has unique name among other exported librar
ies. |
| 2762 * |
| 2763 * @param node the export directive to evaluate |
| 2764 * @param exportElement the [ExportElement] retrieved from the node, if the el
ement in the |
| 2765 * node was `null`, then this method is not called |
| 2766 * @param exportedLibrary the library element containing the exported element |
| 2767 * @return `true` if and only if an error code is generated on the passed node |
| 2768 * @see CompileTimeErrorCode#EXPORT_DUPLICATED_LIBRARY_NAME |
| 2769 */ |
| 2770 bool _checkForExportDuplicateLibraryName(ExportDirective node, ExportElement e
xportElement, LibraryElement exportedLibrary) { |
| 2771 if (exportedLibrary == null) { |
| 2772 return false; |
| 2773 } |
| 2774 String name = exportedLibrary.name; |
| 2775 // check if there is other exported library with the same name |
| 2776 LibraryElement prevLibrary = _nameToExportElement[name]; |
| 2777 if (prevLibrary != null) { |
| 2778 if (prevLibrary != exportedLibrary) { |
| 2779 _errorReporter.reportErrorForNode(StaticWarningCode.EXPORT_DUPLICATED_LI
BRARY_NAME, node, [ |
| 2780 prevLibrary.definingCompilationUnit.displayName, |
| 2781 exportedLibrary.definingCompilationUnit.displayName, |
| 2782 name]); |
| 2783 return true; |
| 2784 } |
| 2785 } else { |
| 2786 _nameToExportElement[name] = exportedLibrary; |
| 2787 } |
| 2788 // OK |
| 2789 return false; |
| 2790 } |
| 2791 |
| 2792 /** |
| 2793 * Check that if the visiting library is not system, then any passed library s
hould not be SDK |
| 2794 * internal library. |
| 2795 * |
| 2796 * @param node the export directive to evaluate |
| 2797 * @param exportElement the [ExportElement] retrieved from the node, if the el
ement in the |
| 2798 * node was `null`, then this method is not called |
| 2799 * @return `true` if and only if an error code is generated on the passed node |
| 2800 * @see CompileTimeErrorCode#EXPORT_INTERNAL_LIBRARY |
| 2801 */ |
| 2802 bool _checkForExportInternalLibrary(ExportDirective node, ExportElement export
Element) { |
| 2803 if (_isInSystemLibrary) { |
| 2804 return false; |
| 2805 } |
| 2806 // should be private |
| 2807 DartSdk sdk = _currentLibrary.context.sourceFactory.dartSdk; |
| 2808 String uri = exportElement.uri; |
| 2809 SdkLibrary sdkLibrary = sdk.getSdkLibrary(uri); |
| 2810 if (sdkLibrary == null) { |
| 2811 return false; |
| 2812 } |
| 2813 if (!sdkLibrary.isInternal) { |
| 2814 return false; |
| 2815 } |
| 2816 // report problem |
| 2817 _errorReporter.reportErrorForNode(CompileTimeErrorCode.EXPORT_INTERNAL_LIBRA
RY, node, [node.uri]); |
| 2818 return true; |
| 2819 } |
| 2820 |
| 2821 /** |
| 2822 * This verifies that the passed extends clause does not extend a deferred cla
ss. |
| 2823 * |
| 2824 * @param node the extends clause to test |
| 2825 * @return `true` if and only if an error code is generated on the passed node |
| 2826 * @see CompileTimeErrorCode#EXTENDS_DEFERRED_CLASS |
| 2827 */ |
| 2828 bool _checkForExtendsDeferredClass(ExtendsClause node) { |
| 2829 if (node == null) { |
| 2830 return false; |
| 2831 } |
| 2832 return _checkForExtendsOrImplementsDeferredClass(node.superclass, CompileTim
eErrorCode.EXTENDS_DEFERRED_CLASS); |
| 2833 } |
| 2834 |
| 2835 /** |
| 2836 * This verifies that the passed type alias does not extend a deferred class. |
| 2837 * |
| 2838 * @param node the extends clause to test |
| 2839 * @return `true` if and only if an error code is generated on the passed node |
| 2840 * @see CompileTimeErrorCode#EXTENDS_DISALLOWED_CLASS |
| 2841 */ |
| 2842 bool _checkForExtendsDeferredClassInTypeAlias(ClassTypeAlias node) { |
| 2843 if (node == null) { |
| 2844 return false; |
| 2845 } |
| 2846 return _checkForExtendsOrImplementsDeferredClass(node.superclass, CompileTim
eErrorCode.EXTENDS_DEFERRED_CLASS); |
| 2847 } |
| 2848 |
| 2849 /** |
| 2850 * This verifies that the passed extends clause does not extend classes such a
s num or String. |
| 2851 * |
| 2852 * @param node the extends clause to test |
| 2853 * @return `true` if and only if an error code is generated on the passed node |
| 2854 * @see CompileTimeErrorCode#EXTENDS_DISALLOWED_CLASS |
| 2855 */ |
| 2856 bool _checkForExtendsDisallowedClass(ExtendsClause node) { |
| 2857 if (node == null) { |
| 2858 return false; |
| 2859 } |
| 2860 return _checkForExtendsOrImplementsDisallowedClass(node.superclass, CompileT
imeErrorCode.EXTENDS_DISALLOWED_CLASS); |
| 2861 } |
| 2862 |
| 2863 /** |
| 2864 * This verifies that the passed type alias does not extend classes such as nu
m or String. |
| 2865 * |
| 2866 * @param node the extends clause to test |
| 2867 * @return `true` if and only if an error code is generated on the passed node |
| 2868 * @see CompileTimeErrorCode#EXTENDS_DISALLOWED_CLASS |
| 2869 */ |
| 2870 bool _checkForExtendsDisallowedClassInTypeAlias(ClassTypeAlias node) { |
| 2871 if (node == null) { |
| 2872 return false; |
| 2873 } |
| 2874 return _checkForExtendsOrImplementsDisallowedClass(node.superclass, CompileT
imeErrorCode.EXTENDS_DISALLOWED_CLASS); |
| 2875 } |
| 2876 |
| 2877 /** |
| 2878 * This verifies that the passed type name does not extend, implement or mixin
classes that are |
| 2879 * deferred. |
| 2880 * |
| 2881 * @param node the type name to test |
| 2882 * @return `true` if and only if an error code is generated on the passed node |
| 2883 * @see #checkForExtendsDeferredClass(ExtendsClause) |
| 2884 * @see #checkForExtendsDeferredClassInTypeAlias(ClassTypeAlias) |
| 2885 * @see #checkForImplementsDeferredClass(ImplementsClause) |
| 2886 * @see #checkForAllMixinErrorCodes(WithClause) |
| 2887 * @see CompileTimeErrorCode#EXTENDS_DEFERRED_CLASS |
| 2888 * @see CompileTimeErrorCode#IMPLEMENTS_DEFERRED_CLASS |
| 2889 * @see CompileTimeErrorCode#MIXIN_DEFERRED_CLASS |
| 2890 */ |
| 2891 bool _checkForExtendsOrImplementsDeferredClass(TypeName typeName, ErrorCode er
rorCode) { |
| 2892 if (typeName.isSynthetic) { |
| 2893 return false; |
| 2894 } |
| 2895 if (typeName.isDeferred) { |
| 2896 _errorReporter.reportErrorForNode(errorCode, typeName, [typeName.name.name
]); |
| 2897 return true; |
| 2898 } |
| 2899 return false; |
| 2900 } |
| 2901 |
| 2902 /** |
| 2903 * This verifies that the passed type name does not extend, implement or mixin
classes such as |
| 2904 * 'num' or 'String'. |
| 2905 * |
| 2906 * @param node the type name to test |
| 2907 * @return `true` if and only if an error code is generated on the passed node |
| 2908 * @see #checkForExtendsDisallowedClass(ExtendsClause) |
| 2909 * @see #checkForExtendsDisallowedClassInTypeAlias(ClassTypeAlias) |
| 2910 * @see #checkForImplementsDisallowedClass(ImplementsClause) |
| 2911 * @see #checkForAllMixinErrorCodes(WithClause) |
| 2912 * @see CompileTimeErrorCode#EXTENDS_DISALLOWED_CLASS |
| 2913 * @see CompileTimeErrorCode#IMPLEMENTS_DISALLOWED_CLASS |
| 2914 * @see CompileTimeErrorCode#MIXIN_OF_DISALLOWED_CLASS |
| 2915 */ |
| 2916 bool _checkForExtendsOrImplementsDisallowedClass(TypeName typeName, ErrorCode
errorCode) { |
| 2917 if (typeName.isSynthetic) { |
| 2918 return false; |
| 2919 } |
| 2920 DartType superType = typeName.type; |
| 2921 for (InterfaceType disallowedType in _DISALLOWED_TYPES_TO_EXTEND_OR_IMPLEMEN
T) { |
| 2922 if (superType != null && superType == disallowedType) { |
| 2923 // if the violating type happens to be 'num', we need to rule out the ca
se where the |
| 2924 // enclosing class is 'int' or 'double' |
| 2925 if (superType == _typeProvider.numType) { |
| 2926 AstNode grandParent = typeName.parent.parent; |
| 2927 // Note: this is a corner case that won't happen often, so adding a fi
eld currentClass |
| 2928 // (see currentFunction) to ErrorVerifier isn't worth if for this case
, but if the field |
| 2929 // currentClass is added, then this message should become a todo to no
t lookup the |
| 2930 // grandparent node |
| 2931 if (grandParent is ClassDeclaration) { |
| 2932 ClassElement classElement = grandParent.element; |
| 2933 DartType classType = classElement.type; |
| 2934 if (classType != null && (classType == _intType || classType == _typ
eProvider.doubleType)) { |
| 2935 return false; |
| 2936 } |
| 2937 } |
| 2938 } |
| 2939 // otherwise, report the error |
| 2940 _errorReporter.reportErrorForNode(errorCode, typeName, [disallowedType.d
isplayName]); |
| 2941 return true; |
| 2942 } |
| 2943 } |
| 2944 return false; |
| 2945 } |
| 2946 |
| 2947 /** |
| 2948 * This verifies that the passed constructor field initializer has compatible
field and |
| 2949 * initializer expression types. |
| 2950 * |
| 2951 * @param node the constructor field initializer to test |
| 2952 * @param staticElement the static element from the name in the |
| 2953 * [ConstructorFieldInitializer] |
| 2954 * @return `true` if and only if an error code is generated on the passed node |
| 2955 * @see CompileTimeErrorCode#CONST_FIELD_INITIALIZER_NOT_ASSIGNABLE |
| 2956 * @see StaticWarningCode#FIELD_INITIALIZER_NOT_ASSIGNABLE |
| 2957 */ |
| 2958 bool _checkForFieldInitializerNotAssignable(ConstructorFieldInitializer node,
Element staticElement) { |
| 2959 // prepare field element |
| 2960 if (staticElement is! FieldElement) { |
| 2961 return false; |
| 2962 } |
| 2963 FieldElement fieldElement = staticElement as FieldElement; |
| 2964 // prepare field type |
| 2965 DartType fieldType = fieldElement.type; |
| 2966 // prepare expression type |
| 2967 Expression expression = node.expression; |
| 2968 if (expression == null) { |
| 2969 return false; |
| 2970 } |
| 2971 // test the static type of the expression |
| 2972 DartType staticType = getStaticType(expression); |
| 2973 if (staticType == null) { |
| 2974 return false; |
| 2975 } |
| 2976 if (staticType.isAssignableTo(fieldType)) { |
| 2977 return false; |
| 2978 } |
| 2979 // report problem |
| 2980 if (_isEnclosingConstructorConst) { |
| 2981 // TODO(paulberry): this error should be based on the actual type of the c
onstant, not the |
| 2982 // static type. See dartbug.com/21119. |
| 2983 _errorReporter.reportTypeErrorForNode(CheckedModeCompileTimeErrorCode.CONS
T_FIELD_INITIALIZER_NOT_ASSIGNABLE, expression, [staticType, fieldType]); |
| 2984 } |
| 2985 _errorReporter.reportTypeErrorForNode(StaticWarningCode.FIELD_INITIALIZER_NO
T_ASSIGNABLE, expression, [staticType, fieldType]); |
| 2986 return true; |
| 2987 // TODO(brianwilkerson) Define a hint corresponding to these errors and repo
rt it if appropriate. |
| 2988 // // test the propagated type of the expression |
| 2989 // Type propagatedType = expression.getPropagatedType(); |
| 2990 // if (propagatedType != null && propagatedType.isAssignableTo(fieldType)
) { |
| 2991 // return false; |
| 2992 // } |
| 2993 // // report problem |
| 2994 // if (isEnclosingConstructorConst) { |
| 2995 // errorReporter.reportTypeErrorForNode( |
| 2996 // CompileTimeErrorCode.CONST_FIELD_INITIALIZER_NOT_ASSIGNABLE, |
| 2997 // expression, |
| 2998 // propagatedType == null ? staticType : propagatedType, |
| 2999 // fieldType); |
| 3000 // } else { |
| 3001 // errorReporter.reportTypeErrorForNode( |
| 3002 // StaticWarningCode.FIELD_INITIALIZER_NOT_ASSIGNABLE, |
| 3003 // expression, |
| 3004 // propagatedType == null ? staticType : propagatedType, |
| 3005 // fieldType); |
| 3006 // } |
| 3007 // return true; |
| 3008 } |
| 3009 |
| 3010 /** |
| 3011 * This verifies that the passed field formal parameter is in a constructor de
claration. |
| 3012 * |
| 3013 * @param node the field formal parameter to test |
| 3014 * @return `true` if and only if an error code is generated on the passed node |
| 3015 * @see CompileTimeErrorCode#FIELD_INITIALIZER_OUTSIDE_CONSTRUCTOR |
| 3016 */ |
| 3017 bool _checkForFieldInitializingFormalRedirectingConstructor(FieldFormalParamet
er node) { |
| 3018 ConstructorDeclaration constructor = node.getAncestor((node) => node is Cons
tructorDeclaration); |
| 3019 if (constructor == null) { |
| 3020 _errorReporter.reportErrorForNode(CompileTimeErrorCode.FIELD_INITIALIZER_O
UTSIDE_CONSTRUCTOR, node, []); |
| 3021 return true; |
| 3022 } |
| 3023 // constructor cannot be a factory |
| 3024 if (constructor.factoryKeyword != null) { |
| 3025 _errorReporter.reportErrorForNode(CompileTimeErrorCode.FIELD_INITIALIZER_F
ACTORY_CONSTRUCTOR, node, []); |
| 3026 return true; |
| 3027 } |
| 3028 // constructor cannot have a redirection |
| 3029 for (ConstructorInitializer initializer in constructor.initializers) { |
| 3030 if (initializer is RedirectingConstructorInvocation) { |
| 3031 _errorReporter.reportErrorForNode(CompileTimeErrorCode.FIELD_INITIALIZER
_REDIRECTING_CONSTRUCTOR, node, []); |
| 3032 return true; |
| 3033 } |
| 3034 } |
| 3035 // OK |
| 3036 return false; |
| 3037 } |
| 3038 |
| 3039 /** |
| 3040 * This verifies that the passed variable declaration list has only initialize
d variables if the |
| 3041 * list is final or const. This method is called by |
| 3042 * [checkForFinalNotInitializedInClass], |
| 3043 * [visitTopLevelVariableDeclaration] and |
| 3044 * [visitVariableDeclarationStatement]. |
| 3045 * |
| 3046 * @param node the class declaration to test |
| 3047 * @return `true` if and only if an error code is generated on the passed node |
| 3048 * @see CompileTimeErrorCode#CONST_NOT_INITIALIZED |
| 3049 * @see StaticWarningCode#FINAL_NOT_INITIALIZED |
| 3050 */ |
| 3051 bool _checkForFinalNotInitialized(VariableDeclarationList node) { |
| 3052 if (_isInNativeClass) { |
| 3053 return false; |
| 3054 } |
| 3055 bool foundError = false; |
| 3056 if (!node.isSynthetic) { |
| 3057 NodeList<VariableDeclaration> variables = node.variables; |
| 3058 for (VariableDeclaration variable in variables) { |
| 3059 if (variable.initializer == null) { |
| 3060 if (node.isConst) { |
| 3061 _errorReporter.reportErrorForNode(CompileTimeErrorCode.CONST_NOT_INI
TIALIZED, variable.name, [variable.name.name]); |
| 3062 } else if (node.isFinal) { |
| 3063 _errorReporter.reportErrorForNode(StaticWarningCode.FINAL_NOT_INITIA
LIZED, variable.name, [variable.name.name]); |
| 3064 } |
| 3065 foundError = true; |
| 3066 } |
| 3067 } |
| 3068 } |
| 3069 return foundError; |
| 3070 } |
| 3071 |
| 3072 /** |
| 3073 * This verifies that final fields that are declared, without any constructors
in the enclosing |
| 3074 * class, are initialized. Cases in which there is at least one constructor ar
e handled at the end |
| 3075 * of [checkForAllFinalInitializedErrorCodes]. |
| 3076 * |
| 3077 * @param node the class declaration to test |
| 3078 * @return `true` if and only if an error code is generated on the passed node |
| 3079 * @see CompileTimeErrorCode#CONST_NOT_INITIALIZED |
| 3080 * @see StaticWarningCode#FINAL_NOT_INITIALIZED |
| 3081 */ |
| 3082 bool _checkForFinalNotInitializedInClass(ClassDeclaration node) { |
| 3083 NodeList<ClassMember> classMembers = node.members; |
| 3084 for (ClassMember classMember in classMembers) { |
| 3085 if (classMember is ConstructorDeclaration) { |
| 3086 return false; |
| 3087 } |
| 3088 } |
| 3089 bool foundError = false; |
| 3090 for (ClassMember classMember in classMembers) { |
| 3091 if (classMember is FieldDeclaration |
| 3092 && _checkForFinalNotInitialized(classMember.fields)) { |
| 3093 foundError = true; |
| 3094 } |
| 3095 } |
| 3096 return foundError; |
| 3097 } |
| 3098 |
| 3099 /** |
| 3100 * This verifies that the passed implements clause does not implement classes
that are deferred. |
| 3101 * |
| 3102 * @param node the implements clause to test |
| 3103 * @return `true` if and only if an error code is generated on the passed node |
| 3104 * @see CompileTimeErrorCode#IMPLEMENTS_DEFERRED_CLASS |
| 3105 */ |
| 3106 bool _checkForImplementsDeferredClass(ImplementsClause node) { |
| 3107 if (node == null) { |
| 3108 return false; |
| 3109 } |
| 3110 bool foundError = false; |
| 3111 for (TypeName type in node.interfaces) { |
| 3112 if (_checkForExtendsOrImplementsDeferredClass( |
| 3113 type, |
| 3114 CompileTimeErrorCode.IMPLEMENTS_DEFERRED_CLASS)) { |
| 3115 foundError = true; |
| 3116 } |
| 3117 } |
| 3118 return foundError; |
| 3119 } |
| 3120 |
| 3121 /** |
| 3122 * This verifies that the passed implements clause does not implement classes
such as 'num' or |
| 3123 * 'String'. |
| 3124 * |
| 3125 * @param node the implements clause to test |
| 3126 * @return `true` if and only if an error code is generated on the passed node |
| 3127 * @see CompileTimeErrorCode#IMPLEMENTS_DISALLOWED_CLASS |
| 3128 */ |
| 3129 bool _checkForImplementsDisallowedClass(ImplementsClause node) { |
| 3130 if (node == null) { |
| 3131 return false; |
| 3132 } |
| 3133 bool foundError = false; |
| 3134 for (TypeName type in node.interfaces) { |
| 3135 if (_checkForExtendsOrImplementsDisallowedClass( |
| 3136 type, |
| 3137 CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS)) { |
| 3138 foundError = true; |
| 3139 } |
| 3140 } |
| 3141 return foundError; |
| 3142 } |
| 3143 |
| 3144 /** |
| 3145 * This verifies that if the passed identifier is part of constructor initiali
zer, then it does |
| 3146 * not reference implicitly 'this' expression. |
| 3147 * |
| 3148 * @param node the simple identifier to test |
| 3149 * @return `true` if and only if an error code is generated on the passed node |
| 3150 * @see CompileTimeErrorCode#IMPLICIT_THIS_REFERENCE_IN_INITIALIZER |
| 3151 * @see CompileTimeErrorCode#INSTANCE_MEMBER_ACCESS_FROM_STATIC TODO(scheglov)
rename thid method |
| 3152 */ |
| 3153 bool _checkForImplicitThisReferenceInInitializer(SimpleIdentifier node) { |
| 3154 if (!_isInConstructorInitializer && !_isInStaticMethod && !_isInFactory && !
_isInInstanceVariableInitializer && !_isInStaticVariableDeclaration) { |
| 3155 return false; |
| 3156 } |
| 3157 // prepare element |
| 3158 Element element = node.staticElement; |
| 3159 if (!(element is MethodElement || element is PropertyAccessorElement)) { |
| 3160 return false; |
| 3161 } |
| 3162 // static element |
| 3163 ExecutableElement executableElement = element as ExecutableElement; |
| 3164 if (executableElement.isStatic) { |
| 3165 return false; |
| 3166 } |
| 3167 // not a class member |
| 3168 Element enclosingElement = element.enclosingElement; |
| 3169 if (enclosingElement is! ClassElement) { |
| 3170 return false; |
| 3171 } |
| 3172 // comment |
| 3173 AstNode parent = node.parent; |
| 3174 if (parent is CommentReference) { |
| 3175 return false; |
| 3176 } |
| 3177 // qualified method invocation |
| 3178 if (parent is MethodInvocation) { |
| 3179 MethodInvocation invocation = parent; |
| 3180 if (identical(invocation.methodName, node) && invocation.realTarget != nul
l) { |
| 3181 return false; |
| 3182 } |
| 3183 } |
| 3184 // qualified property access |
| 3185 if (parent is PropertyAccess) { |
| 3186 PropertyAccess access = parent; |
| 3187 if (identical(access.propertyName, node) && access.realTarget != null) { |
| 3188 return false; |
| 3189 } |
| 3190 } |
| 3191 if (parent is PrefixedIdentifier) { |
| 3192 PrefixedIdentifier prefixed = parent; |
| 3193 if (identical(prefixed.identifier, node)) { |
| 3194 return false; |
| 3195 } |
| 3196 } |
| 3197 // report problem |
| 3198 if (_isInStaticMethod) { |
| 3199 _errorReporter.reportErrorForNode(CompileTimeErrorCode.INSTANCE_MEMBER_ACC
ESS_FROM_STATIC, node, []); |
| 3200 } else if (_isInFactory) { |
| 3201 _errorReporter.reportErrorForNode(CompileTimeErrorCode.INSTANCE_MEMBER_ACC
ESS_FROM_FACTORY, node, []); |
| 3202 } else { |
| 3203 _errorReporter.reportErrorForNode(CompileTimeErrorCode.IMPLICIT_THIS_REFER
ENCE_IN_INITIALIZER, node, []); |
| 3204 } |
| 3205 return true; |
| 3206 } |
| 3207 |
| 3208 /** |
| 3209 * This verifies the passed import has unique name among other imported librar
ies. |
| 3210 * |
| 3211 * @param node the import directive to evaluate |
| 3212 * @param importElement the [ImportElement] retrieved from the node, if the el
ement in the |
| 3213 * node was `null`, then this method is not called |
| 3214 * @return `true` if and only if an error code is generated on the passed node |
| 3215 * @see CompileTimeErrorCode#IMPORT_DUPLICATED_LIBRARY_NAME |
| 3216 */ |
| 3217 bool _checkForImportDuplicateLibraryName(ImportDirective node, ImportElement i
mportElement) { |
| 3218 // prepare imported library |
| 3219 LibraryElement nodeLibrary = importElement.importedLibrary; |
| 3220 if (nodeLibrary == null) { |
| 3221 return false; |
| 3222 } |
| 3223 String name = nodeLibrary.name; |
| 3224 // check if there is other imported library with the same name |
| 3225 LibraryElement prevLibrary = _nameToImportElement[name]; |
| 3226 if (prevLibrary != null) { |
| 3227 if (prevLibrary != nodeLibrary) { |
| 3228 _errorReporter.reportErrorForNode(StaticWarningCode.IMPORT_DUPLICATED_LI
BRARY_NAME, node, [ |
| 3229 prevLibrary.definingCompilationUnit.displayName, |
| 3230 nodeLibrary.definingCompilationUnit.displayName, |
| 3231 name]); |
| 3232 return true; |
| 3233 } |
| 3234 } else { |
| 3235 _nameToImportElement[name] = nodeLibrary; |
| 3236 } |
| 3237 // OK |
| 3238 return false; |
| 3239 } |
| 3240 |
| 3241 /** |
| 3242 * Check that if the visiting library is not system, then any passed library s
hould not be SDK |
| 3243 * internal library. |
| 3244 * |
| 3245 * @param node the import directive to evaluate |
| 3246 * @param importElement the [ImportElement] retrieved from the node, if the el
ement in the |
| 3247 * node was `null`, then this method is not called |
| 3248 * @return `true` if and only if an error code is generated on the passed node |
| 3249 * @see CompileTimeErrorCode#IMPORT_INTERNAL_LIBRARY |
| 3250 */ |
| 3251 bool _checkForImportInternalLibrary(ImportDirective node, ImportElement import
Element) { |
| 3252 if (_isInSystemLibrary) { |
| 3253 return false; |
| 3254 } |
| 3255 // should be private |
| 3256 DartSdk sdk = _currentLibrary.context.sourceFactory.dartSdk; |
| 3257 String uri = importElement.uri; |
| 3258 SdkLibrary sdkLibrary = sdk.getSdkLibrary(uri); |
| 3259 if (sdkLibrary == null) { |
| 3260 return false; |
| 3261 } |
| 3262 if (!sdkLibrary.isInternal) { |
| 3263 return false; |
| 3264 } |
| 3265 // report problem |
| 3266 _errorReporter.reportErrorForNode(CompileTimeErrorCode.IMPORT_INTERNAL_LIBRA
RY, node, [node.uri]); |
| 3267 return true; |
| 3268 } |
| 3269 |
| 3270 /** |
| 3271 * For each class declaration, this method is called which verifies that all i
nherited members are |
| 3272 * inherited consistently. |
| 3273 * |
| 3274 * @return `true` if and only if an error code is generated on the passed node |
| 3275 * @see StaticTypeWarningCode#INCONSISTENT_METHOD_INHERITANCE |
| 3276 */ |
| 3277 bool _checkForInconsistentMethodInheritance() { |
| 3278 // Ensure that the inheritance manager has a chance to generate all errors w
e may care about, |
| 3279 // note that we ensure that the interfaces data since there are no errors. |
| 3280 _inheritanceManager.getMapOfMembersInheritedFromInterfaces(_enclosingClass); |
| 3281 HashSet<AnalysisError> errors = _inheritanceManager.getErrors(_enclosingClas
s); |
| 3282 if (errors == null || errors.isEmpty) { |
| 3283 return false; |
| 3284 } |
| 3285 for (AnalysisError error in errors) { |
| 3286 _errorReporter.reportError(error); |
| 3287 } |
| 3288 return true; |
| 3289 } |
| 3290 |
| 3291 /** |
| 3292 * This checks the given "typeReference" is not a type reference and that then
the "name" is |
| 3293 * reference to an instance member. |
| 3294 * |
| 3295 * @param typeReference the resolved [ClassElement] of the left hand side of t
he expression, |
| 3296 * or `null`, aka, the class element of 'C' in 'C.x', see |
| 3297 * [getTypeReference] |
| 3298 * @param name the accessed name to evaluate |
| 3299 * @return `true` if and only if an error code is generated on the passed node |
| 3300 * @see StaticTypeWarningCode#INSTANCE_ACCESS_TO_STATIC_MEMBER |
| 3301 */ |
| 3302 bool _checkForInstanceAccessToStaticMember(ClassElement typeReference, SimpleI
dentifier name) { |
| 3303 // OK, in comment |
| 3304 if (_isInComment) { |
| 3305 return false; |
| 3306 } |
| 3307 // OK, target is a type |
| 3308 if (typeReference != null) { |
| 3309 return false; |
| 3310 } |
| 3311 // prepare member Element |
| 3312 Element element = name.staticElement; |
| 3313 if (element is! ExecutableElement) { |
| 3314 return false; |
| 3315 } |
| 3316 ExecutableElement executableElement = element as ExecutableElement; |
| 3317 // OK, top-level element |
| 3318 if (executableElement.enclosingElement is! ClassElement) { |
| 3319 return false; |
| 3320 } |
| 3321 // OK, instance member |
| 3322 if (!executableElement.isStatic) { |
| 3323 return false; |
| 3324 } |
| 3325 // report problem |
| 3326 _errorReporter.reportErrorForNode(StaticTypeWarningCode.INSTANCE_ACCESS_TO_S
TATIC_MEMBER, name, [name.name]); |
| 3327 return true; |
| 3328 } |
| 3329 |
| 3330 /** |
| 3331 * This checks whether the given [executableElement] collides with the name of
a static |
| 3332 * method in one of its superclasses, and reports the appropriate warning if i
t does. |
| 3333 * |
| 3334 * @param executableElement the method to check. |
| 3335 * @param errorNameTarget the node to report problems on. |
| 3336 * @return `true` if and only if a warning was generated. |
| 3337 * @see StaticTypeWarningCode#INSTANCE_METHOD_NAME_COLLIDES_WITH_SUPERCLASS_ST
ATIC |
| 3338 */ |
| 3339 bool _checkForInstanceMethodNameCollidesWithSuperclassStatic(ExecutableElement
executableElement, SimpleIdentifier errorNameTarget) { |
| 3340 String executableElementName = executableElement.name; |
| 3341 if (executableElement is! PropertyAccessorElement && !executableElement.isOp
erator) { |
| 3342 HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>(); |
| 3343 InterfaceType superclassType = _enclosingClass.supertype; |
| 3344 ClassElement superclassElement = superclassType == null ? null : superclas
sType.element; |
| 3345 bool executableElementPrivate = Identifier.isPrivateName(executableElement
Name); |
| 3346 while (superclassElement != null && !visitedClasses.contains(superclassEle
ment)) { |
| 3347 visitedClasses.add(superclassElement); |
| 3348 LibraryElement superclassLibrary = superclassElement.library; |
| 3349 // Check fields. |
| 3350 List<FieldElement> fieldElts = superclassElement.fields; |
| 3351 for (FieldElement fieldElt in fieldElts) { |
| 3352 // We need the same name. |
| 3353 if (fieldElt.name != executableElementName) { |
| 3354 continue; |
| 3355 } |
| 3356 // Ignore if private in a different library - cannot collide. |
| 3357 if (executableElementPrivate && _currentLibrary != superclassLibrary)
{ |
| 3358 continue; |
| 3359 } |
| 3360 // instance vs. static |
| 3361 if (fieldElt.isStatic) { |
| 3362 _errorReporter.reportErrorForNode(StaticWarningCode.INSTANCE_METHOD_
NAME_COLLIDES_WITH_SUPERCLASS_STATIC, errorNameTarget, [ |
| 3363 executableElementName, |
| 3364 fieldElt.enclosingElement.displayName]); |
| 3365 return true; |
| 3366 } |
| 3367 } |
| 3368 // Check methods. |
| 3369 List<MethodElement> methodElements = superclassElement.methods; |
| 3370 for (MethodElement methodElement in methodElements) { |
| 3371 // We need the same name. |
| 3372 if (methodElement.name != executableElementName) { |
| 3373 continue; |
| 3374 } |
| 3375 // Ignore if private in a different library - cannot collide. |
| 3376 if (executableElementPrivate && _currentLibrary != superclassLibrary)
{ |
| 3377 continue; |
| 3378 } |
| 3379 // instance vs. static |
| 3380 if (methodElement.isStatic) { |
| 3381 _errorReporter.reportErrorForNode(StaticWarningCode.INSTANCE_METHOD_
NAME_COLLIDES_WITH_SUPERCLASS_STATIC, errorNameTarget, [ |
| 3382 executableElementName, |
| 3383 methodElement.enclosingElement.displayName]); |
| 3384 return true; |
| 3385 } |
| 3386 } |
| 3387 superclassType = superclassElement.supertype; |
| 3388 superclassElement = superclassType == null ? null : superclassType.eleme
nt; |
| 3389 } |
| 3390 } |
| 3391 return false; |
| 3392 } |
| 3393 |
| 3394 /** |
| 3395 * This verifies that an 'int' can be assigned to the parameter corresponding
to the given |
| 3396 * expression. This is used for prefix and postfix expressions where the argum
ent value is |
| 3397 * implicit. |
| 3398 * |
| 3399 * @param argument the expression to which the operator is being applied |
| 3400 * @return `true` if and only if an error code is generated on the passed node |
| 3401 * @see StaticWarningCode#ARGUMENT_TYPE_NOT_ASSIGNABLE |
| 3402 */ |
| 3403 bool _checkForIntNotAssignable(Expression argument) { |
| 3404 if (argument == null) { |
| 3405 return false; |
| 3406 } |
| 3407 ParameterElement staticParameterElement = argument.staticParameterElement; |
| 3408 DartType staticParameterType = staticParameterElement == null ? null : stati
cParameterElement.type; |
| 3409 return _checkForArgumentTypeNotAssignable(argument, staticParameterType, _in
tType, StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE); |
| 3410 } |
| 3411 |
| 3412 /** |
| 3413 * This verifies that the passed [Annotation] isn't defined in a deferred libr
ary. |
| 3414 * |
| 3415 * @param node the [Annotation] |
| 3416 * @return `true` if and only if an error code is generated on the passed node |
| 3417 * @see CompileTimeErrorCode.INVALID_ANNOTATION_FROM_DEFERRED_LIBRARY |
| 3418 */ |
| 3419 bool _checkForInvalidAnnotationFromDeferredLibrary(Annotation node) { |
| 3420 Identifier nameIdentifier = node.name; |
| 3421 if (nameIdentifier is PrefixedIdentifier) { |
| 3422 if (nameIdentifier.isDeferred) { |
| 3423 _errorReporter.reportErrorForNode(CompileTimeErrorCode.INVALID_ANNOTATIO
N_FROM_DEFERRED_LIBRARY, node.name, []); |
| 3424 return true; |
| 3425 } |
| 3426 } |
| 3427 return false; |
| 3428 } |
| 3429 |
| 3430 /** |
| 3431 * This verifies that the passed left hand side and right hand side represent
a valid assignment. |
| 3432 * |
| 3433 * @param lhs the left hand side expression |
| 3434 * @param rhs the right hand side expression |
| 3435 * @return `true` if and only if an error code is generated on the passed node |
| 3436 * @see StaticTypeWarningCode#INVALID_ASSIGNMENT |
| 3437 */ |
| 3438 bool _checkForInvalidAssignment(Expression lhs, Expression rhs) { |
| 3439 if (lhs == null || rhs == null) { |
| 3440 return false; |
| 3441 } |
| 3442 VariableElement leftVariableElement = getVariableElement(lhs); |
| 3443 DartType leftType = (leftVariableElement == null) ? getStaticType(lhs) : lef
tVariableElement.type; |
| 3444 DartType staticRightType = getStaticType(rhs); |
| 3445 if (!staticRightType.isAssignableTo(leftType)) { |
| 3446 _errorReporter.reportTypeErrorForNode(StaticTypeWarningCode.INVALID_ASSIGN
MENT, rhs, [staticRightType, leftType]); |
| 3447 return true; |
| 3448 } |
| 3449 return false; |
| 3450 } |
| 3451 |
| 3452 /** |
| 3453 * Given an assignment using a compound assignment operator, this verifies tha
t the given |
| 3454 * assignment is valid. |
| 3455 * |
| 3456 * @param node the assignment expression being tested |
| 3457 * @param lhs the left hand side expression |
| 3458 * @param rhs the right hand side expression |
| 3459 * @return `true` if and only if an error code is generated on the passed node |
| 3460 * @see StaticTypeWarningCode#INVALID_ASSIGNMENT |
| 3461 */ |
| 3462 bool _checkForInvalidCompoundAssignment(AssignmentExpression node, Expression
lhs, Expression rhs) { |
| 3463 if (lhs == null) { |
| 3464 return false; |
| 3465 } |
| 3466 VariableElement leftVariableElement = getVariableElement(lhs); |
| 3467 DartType leftType = (leftVariableElement == null) ? getStaticType(lhs) : lef
tVariableElement.type; |
| 3468 MethodElement invokedMethod = node.staticElement; |
| 3469 if (invokedMethod == null) { |
| 3470 return false; |
| 3471 } |
| 3472 DartType rightType = invokedMethod.type.returnType; |
| 3473 if (leftType == null || rightType == null) { |
| 3474 return false; |
| 3475 } |
| 3476 if (!rightType.isAssignableTo(leftType)) { |
| 3477 _errorReporter.reportTypeErrorForNode(StaticTypeWarningCode.INVALID_ASSIGN
MENT, rhs, [rightType, leftType]); |
| 3478 return true; |
| 3479 } |
| 3480 return false; |
| 3481 } |
| 3482 |
| 3483 /** |
| 3484 * Check the given initializer to ensure that the field being initialized is a
valid field. |
| 3485 * |
| 3486 * @param node the field initializer being checked |
| 3487 * @param fieldName the field name from the [ConstructorFieldInitializer] |
| 3488 * @param staticElement the static element from the name in the |
| 3489 * [ConstructorFieldInitializer] |
| 3490 */ |
| 3491 void _checkForInvalidField(ConstructorFieldInitializer node, SimpleIdentifier
fieldName, Element staticElement) { |
| 3492 if (staticElement is FieldElement) { |
| 3493 FieldElement fieldElement = staticElement; |
| 3494 if (fieldElement.isSynthetic) { |
| 3495 _errorReporter.reportErrorForNode(CompileTimeErrorCode.INITIALIZER_FOR_N
ON_EXISTENT_FIELD, node, [fieldName]); |
| 3496 } else if (fieldElement.isStatic) { |
| 3497 _errorReporter.reportErrorForNode(CompileTimeErrorCode.INITIALIZER_FOR_S
TATIC_FIELD, node, [fieldName]); |
| 3498 } |
| 3499 } else { |
| 3500 _errorReporter.reportErrorForNode(CompileTimeErrorCode.INITIALIZER_FOR_NON
_EXISTENT_FIELD, node, [fieldName]); |
| 3501 return; |
| 3502 } |
| 3503 } |
| 3504 |
| 3505 /** |
| 3506 * Check to see whether the given function body has a modifier associated with
it, and report it |
| 3507 * as an error if it does. |
| 3508 * |
| 3509 * @param body the function body being checked |
| 3510 * @param errorCode the error code to be reported if a modifier is found |
| 3511 * @return `true` if an error was reported |
| 3512 */ |
| 3513 bool _checkForInvalidModifierOnBody(FunctionBody body, CompileTimeErrorCode er
rorCode) { |
| 3514 sc.Token keyword = body.keyword; |
| 3515 if (keyword != null) { |
| 3516 _errorReporter.reportErrorForToken(errorCode, keyword, [keyword.lexeme]); |
| 3517 return true; |
| 3518 } |
| 3519 return false; |
| 3520 } |
| 3521 |
| 3522 /** |
| 3523 * This verifies that the usage of the passed 'this' is valid. |
| 3524 * |
| 3525 * @param node the 'this' expression to evaluate |
| 3526 * @return `true` if and only if an error code is generated on the passed node |
| 3527 * @see CompileTimeErrorCode#INVALID_REFERENCE_TO_THIS |
| 3528 */ |
| 3529 bool _checkForInvalidReferenceToThis(ThisExpression node) { |
| 3530 if (!_isThisInValidContext(node)) { |
| 3531 _errorReporter.reportErrorForNode(CompileTimeErrorCode.INVALID_REFERENCE_T
O_THIS, node, []); |
| 3532 return true; |
| 3533 } |
| 3534 return false; |
| 3535 } |
| 3536 |
| 3537 /** |
| 3538 * Checks to ensure that the passed [ListLiteral] or [MapLiteral] does not hav
e a type |
| 3539 * parameter as a type argument. |
| 3540 * |
| 3541 * @param arguments a non-`null`, non-empty [TypeName] node list from the resp
ective |
| 3542 * [ListLiteral] or [MapLiteral] |
| 3543 * @param errorCode either [CompileTimeErrorCode#INVALID_TYPE_ARGUMENT_IN_CONS
T_LIST] or |
| 3544 * [CompileTimeErrorCode#INVALID_TYPE_ARGUMENT_IN_CONST_MAP] |
| 3545 * @return `true` if and only if an error code is generated on the passed node |
| 3546 */ |
| 3547 bool _checkForInvalidTypeArgumentInConstTypedLiteral(NodeList<TypeName> argume
nts, ErrorCode errorCode) { |
| 3548 bool foundError = false; |
| 3549 for (TypeName typeName in arguments) { |
| 3550 if (typeName.type is TypeParameterType) { |
| 3551 _errorReporter.reportErrorForNode(errorCode, typeName, [typeName.name]); |
| 3552 foundError = true; |
| 3553 } |
| 3554 } |
| 3555 return foundError; |
| 3556 } |
| 3557 |
| 3558 /** |
| 3559 * This verifies that the elements given [ListLiteral] are subtypes of the spe
cified element |
| 3560 * type. |
| 3561 * |
| 3562 * @param node the list literal to evaluate |
| 3563 * @param typeArguments the type arguments, always non-`null` |
| 3564 * @return `true` if and only if an error code is generated on the passed node |
| 3565 * @see CompileTimeErrorCode#LIST_ELEMENT_TYPE_NOT_ASSIGNABLE |
| 3566 * @see StaticWarningCode#LIST_ELEMENT_TYPE_NOT_ASSIGNABLE |
| 3567 */ |
| 3568 bool _checkForListElementTypeNotAssignable(ListLiteral node, TypeArgumentList
typeArguments) { |
| 3569 NodeList<TypeName> typeNames = typeArguments.arguments; |
| 3570 if (typeNames.length < 1) { |
| 3571 return false; |
| 3572 } |
| 3573 DartType listElementType = typeNames[0].type; |
| 3574 // Check every list element. |
| 3575 bool hasProblems = false; |
| 3576 for (Expression element in node.elements) { |
| 3577 if (node.constKeyword != null) { |
| 3578 // TODO(paulberry): this error should be based on the actual type of the |
| 3579 // list element, not the static type. See dartbug.com/21119. |
| 3580 if (_checkForArgumentTypeNotAssignableWithExpectedTypes( |
| 3581 element, |
| 3582 listElementType, |
| 3583 CheckedModeCompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE)) { |
| 3584 hasProblems = true; |
| 3585 } |
| 3586 } |
| 3587 if (_checkForArgumentTypeNotAssignableWithExpectedTypes( |
| 3588 element, |
| 3589 listElementType, |
| 3590 StaticWarningCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE)) { |
| 3591 hasProblems = true; |
| 3592 } |
| 3593 } |
| 3594 return hasProblems; |
| 3595 } |
| 3596 |
| 3597 /** |
| 3598 * This verifies that the key/value of entries of the given [MapLiteral] are s
ubtypes of the |
| 3599 * key/value types specified in the type arguments. |
| 3600 * |
| 3601 * @param node the map literal to evaluate |
| 3602 * @param typeArguments the type arguments, always non-`null` |
| 3603 * @return `true` if and only if an error code is generated on the passed node |
| 3604 * @see CompileTimeErrorCode#MAP_KEY_TYPE_NOT_ASSIGNABLE |
| 3605 * @see CompileTimeErrorCode#MAP_VALUE_TYPE_NOT_ASSIGNABLE |
| 3606 * @see StaticWarningCode#MAP_KEY_TYPE_NOT_ASSIGNABLE |
| 3607 * @see StaticWarningCode#MAP_VALUE_TYPE_NOT_ASSIGNABLE |
| 3608 */ |
| 3609 bool _checkForMapTypeNotAssignable(MapLiteral node, TypeArgumentList typeArgum
ents) { |
| 3610 // Prepare maps key/value types. |
| 3611 NodeList<TypeName> typeNames = typeArguments.arguments; |
| 3612 if (typeNames.length < 2) { |
| 3613 return false; |
| 3614 } |
| 3615 DartType keyType = typeNames[0].type; |
| 3616 DartType valueType = typeNames[1].type; |
| 3617 // Check every map entry. |
| 3618 bool hasProblems = false; |
| 3619 NodeList<MapLiteralEntry> entries = node.entries; |
| 3620 for (MapLiteralEntry entry in entries) { |
| 3621 Expression key = entry.key; |
| 3622 Expression value = entry.value; |
| 3623 if (node.constKeyword != null) { |
| 3624 // TODO(paulberry): this error should be based on the actual type of the |
| 3625 // list element, not the static type. See dartbug.com/21119. |
| 3626 if (_checkForArgumentTypeNotAssignableWithExpectedTypes( |
| 3627 key, |
| 3628 keyType, |
| 3629 CheckedModeCompileTimeErrorCode.MAP_KEY_TYPE_NOT_ASSIGNABLE)) { |
| 3630 hasProblems = true; |
| 3631 } |
| 3632 if (_checkForArgumentTypeNotAssignableWithExpectedTypes( |
| 3633 value, |
| 3634 valueType, |
| 3635 CheckedModeCompileTimeErrorCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE)) { |
| 3636 hasProblems = true; |
| 3637 } |
| 3638 } |
| 3639 if (_checkForArgumentTypeNotAssignableWithExpectedTypes( |
| 3640 key, |
| 3641 keyType, |
| 3642 StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE)) { |
| 3643 hasProblems = true; |
| 3644 } |
| 3645 if (_checkForArgumentTypeNotAssignableWithExpectedTypes( |
| 3646 value, |
| 3647 valueType, |
| 3648 StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE)) { |
| 3649 hasProblems = true; |
| 3650 } |
| 3651 } |
| 3652 return hasProblems; |
| 3653 } |
| 3654 |
| 3655 /** |
| 3656 * This verifies that the [enclosingClass] does not define members with the sa
me name as |
| 3657 * the enclosing class. |
| 3658 * |
| 3659 * @return `true` if and only if an error code is generated on the passed node |
| 3660 * @see CompileTimeErrorCode#MEMBER_WITH_CLASS_NAME |
| 3661 */ |
| 3662 bool _checkForMemberWithClassName() { |
| 3663 if (_enclosingClass == null) { |
| 3664 return false; |
| 3665 } |
| 3666 String className = _enclosingClass.name; |
| 3667 if (className == null) { |
| 3668 return false; |
| 3669 } |
| 3670 bool problemReported = false; |
| 3671 // check accessors |
| 3672 for (PropertyAccessorElement accessor in _enclosingClass.accessors) { |
| 3673 if (className == accessor.name) { |
| 3674 _errorReporter.reportErrorForOffset(CompileTimeErrorCode.MEMBER_WITH_CLA
SS_NAME, accessor.nameOffset, className.length, []); |
| 3675 problemReported = true; |
| 3676 } |
| 3677 } |
| 3678 // don't check methods, they would be constructors |
| 3679 // done |
| 3680 return problemReported; |
| 3681 } |
| 3682 |
| 3683 /** |
| 3684 * Check to make sure that all similarly typed accessors are of the same type
(including inherited |
| 3685 * accessors). |
| 3686 * |
| 3687 * @param node the accessor currently being visited |
| 3688 * @return `true` if and only if an error code is generated on the passed node |
| 3689 * @see StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES |
| 3690 * @see StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE |
| 3691 */ |
| 3692 bool _checkForMismatchedAccessorTypes(Declaration accessorDeclaration, String
accessorTextName) { |
| 3693 ExecutableElement accessorElement = accessorDeclaration.element as Executabl
eElement; |
| 3694 if (accessorElement is! PropertyAccessorElement) { |
| 3695 return false; |
| 3696 } |
| 3697 PropertyAccessorElement propertyAccessorElement = accessorElement as Propert
yAccessorElement; |
| 3698 PropertyAccessorElement counterpartAccessor = null; |
| 3699 ClassElement enclosingClassForCounterpart = null; |
| 3700 if (propertyAccessorElement.isGetter) { |
| 3701 counterpartAccessor = propertyAccessorElement.correspondingSetter; |
| 3702 } else { |
| 3703 counterpartAccessor = propertyAccessorElement.correspondingGetter; |
| 3704 // If the setter and getter are in the same enclosing element, return, thi
s prevents having |
| 3705 // MISMATCHED_GETTER_AND_SETTER_TYPES reported twice. |
| 3706 if (counterpartAccessor != null && identical(counterpartAccessor.enclosing
Element, propertyAccessorElement.enclosingElement)) { |
| 3707 return false; |
| 3708 } |
| 3709 } |
| 3710 if (counterpartAccessor == null) { |
| 3711 // If the accessor is declared in a class, check the superclasses. |
| 3712 if (_enclosingClass != null) { |
| 3713 // Figure out the correct identifier to lookup in the inheritance graph,
if 'x', then 'x=', |
| 3714 // or if 'x=', then 'x'. |
| 3715 String lookupIdentifier = propertyAccessorElement.name; |
| 3716 if (StringUtilities.endsWithChar(lookupIdentifier, 0x3D)) { |
| 3717 lookupIdentifier = lookupIdentifier.substring(0, lookupIdentifier.leng
th - 1); |
| 3718 } else { |
| 3719 lookupIdentifier += "="; |
| 3720 } |
| 3721 // lookup with the identifier. |
| 3722 ExecutableElement elementFromInheritance = _inheritanceManager.lookupInh
eritance(_enclosingClass, lookupIdentifier); |
| 3723 // Verify that we found something, and that it is an accessor |
| 3724 if (elementFromInheritance != null && elementFromInheritance is Property
AccessorElement) { |
| 3725 enclosingClassForCounterpart = elementFromInheritance.enclosingElement
as ClassElement; |
| 3726 counterpartAccessor = elementFromInheritance; |
| 3727 } |
| 3728 } |
| 3729 if (counterpartAccessor == null) { |
| 3730 return false; |
| 3731 } |
| 3732 } |
| 3733 // Default of null == no accessor or no type (dynamic) |
| 3734 DartType getterType = null; |
| 3735 DartType setterType = null; |
| 3736 // Get an existing counterpart accessor if any. |
| 3737 if (propertyAccessorElement.isGetter) { |
| 3738 getterType = _getGetterType(propertyAccessorElement); |
| 3739 setterType = _getSetterType(counterpartAccessor); |
| 3740 } else if (propertyAccessorElement.isSetter) { |
| 3741 setterType = _getSetterType(propertyAccessorElement); |
| 3742 getterType = _getGetterType(counterpartAccessor); |
| 3743 } |
| 3744 // If either types are not assignable to each other, report an error (if the
getter is null, |
| 3745 // it is dynamic which is assignable to everything). |
| 3746 if (setterType != null && getterType != null && !getterType.isAssignableTo(s
etterType)) { |
| 3747 if (enclosingClassForCounterpart == null) { |
| 3748 _errorReporter.reportTypeErrorForNode(StaticWarningCode.MISMATCHED_GETTE
R_AND_SETTER_TYPES, accessorDeclaration, [accessorTextName, setterType, getterTy
pe]); |
| 3749 return true; |
| 3750 } else { |
| 3751 _errorReporter.reportTypeErrorForNode(StaticWarningCode.MISMATCHED_GETTE
R_AND_SETTER_TYPES_FROM_SUPERTYPE, accessorDeclaration, [ |
| 3752 accessorTextName, |
| 3753 setterType, |
| 3754 getterType, |
| 3755 enclosingClassForCounterpart.displayName]); |
| 3756 } |
| 3757 } |
| 3758 return false; |
| 3759 } |
| 3760 |
| 3761 /** |
| 3762 * Check to make sure that switch statements whose static type is an enum type
either have a |
| 3763 * default case or include all of the enum constants. |
| 3764 * |
| 3765 * @param statement the switch statement to check |
| 3766 * @return `true` if and only if an error code is generated on the passed node |
| 3767 */ |
| 3768 bool _checkForMissingEnumConstantInSwitch(SwitchStatement statement) { |
| 3769 // TODO(brianwilkerson) This needs to be checked after constant values have
been computed. |
| 3770 Expression expression = statement.expression; |
| 3771 DartType expressionType = getStaticType(expression); |
| 3772 if (expressionType == null) { |
| 3773 return false; |
| 3774 } |
| 3775 Element expressionElement = expressionType.element; |
| 3776 if (expressionElement is! ClassElement) { |
| 3777 return false; |
| 3778 } |
| 3779 ClassElement classElement = expressionElement as ClassElement; |
| 3780 if (!classElement.isEnum) { |
| 3781 return false; |
| 3782 } |
| 3783 List<String> constantNames = new List<String>(); |
| 3784 List<FieldElement> fields = classElement.fields; |
| 3785 int fieldCount = fields.length; |
| 3786 for (int i = 0; i < fieldCount; i++) { |
| 3787 FieldElement field = fields[i]; |
| 3788 if (field.isStatic && !field.isSynthetic) { |
| 3789 constantNames.add(field.name); |
| 3790 } |
| 3791 } |
| 3792 NodeList<SwitchMember> members = statement.members; |
| 3793 int memberCount = members.length; |
| 3794 for (int i = 0; i < memberCount; i++) { |
| 3795 SwitchMember member = members[i]; |
| 3796 if (member is SwitchDefault) { |
| 3797 return false; |
| 3798 } |
| 3799 String constantName = _getConstantName((member as SwitchCase).expression); |
| 3800 if (constantName != null) { |
| 3801 constantNames.remove(constantName); |
| 3802 } |
| 3803 } |
| 3804 int nameCount = constantNames.length; |
| 3805 if (nameCount == 0) { |
| 3806 return false; |
| 3807 } |
| 3808 for (int i = 0; i < nameCount; i++) { |
| 3809 _errorReporter.reportErrorForNode(CompileTimeErrorCode.MISSING_ENUM_CONSTA
NT_IN_SWITCH, statement, [constantNames[i]]); |
| 3810 } |
| 3811 return true; |
| 3812 } |
| 3813 |
| 3814 /** |
| 3815 * This verifies that the given function body does not contain return statemen
ts that both have |
| 3816 * and do not have return values. |
| 3817 * |
| 3818 * @param node the function body being tested |
| 3819 * @return `true` if and only if an error code is generated on the passed node |
| 3820 * @see StaticWarningCode#MIXED_RETURN_TYPES |
| 3821 */ |
| 3822 bool _checkForMixedReturns(BlockFunctionBody node) { |
| 3823 if (_hasReturnWithoutValue) { |
| 3824 return false; |
| 3825 } |
| 3826 int withCount = _returnsWith.length; |
| 3827 int withoutCount = _returnsWithout.length; |
| 3828 if (withCount > 0 && withoutCount > 0) { |
| 3829 for (int i = 0; i < withCount; i++) { |
| 3830 _errorReporter.reportErrorForToken(StaticWarningCode.MIXED_RETURN_TYPES,
_returnsWith[i].keyword, []); |
| 3831 } |
| 3832 for (int i = 0; i < withoutCount; i++) { |
| 3833 _errorReporter.reportErrorForToken(StaticWarningCode.MIXED_RETURN_TYPES,
_returnsWithout[i].keyword, []); |
| 3834 } |
| 3835 return true; |
| 3836 } |
| 3837 return false; |
| 3838 } |
| 3839 |
| 3840 /** |
| 3841 * This verifies that the passed mixin does not have an explicitly declared co
nstructor. |
| 3842 * |
| 3843 * @param mixinName the node to report problem on |
| 3844 * @param mixinElement the mixing to evaluate |
| 3845 * @return `true` if and only if an error code is generated on the passed node |
| 3846 * @see CompileTimeErrorCode#MIXIN_DECLARES_CONSTRUCTOR |
| 3847 */ |
| 3848 bool _checkForMixinDeclaresConstructor(TypeName mixinName, ClassElement mixinE
lement) { |
| 3849 for (ConstructorElement constructor in mixinElement.constructors) { |
| 3850 if (!constructor.isSynthetic && !constructor.isFactory) { |
| 3851 _errorReporter.reportErrorForNode(CompileTimeErrorCode.MIXIN_DECLARES_CO
NSTRUCTOR, mixinName, [mixinElement.name]); |
| 3852 return true; |
| 3853 } |
| 3854 } |
| 3855 return false; |
| 3856 } |
| 3857 |
| 3858 /** |
| 3859 * This verifies that the passed mixin has the 'Object' superclass. |
| 3860 * |
| 3861 * @param mixinName the node to report problem on |
| 3862 * @param mixinElement the mixing to evaluate |
| 3863 * @return `true` if and only if an error code is generated on the passed node |
| 3864 * @see CompileTimeErrorCode#MIXIN_INHERITS_FROM_NOT_OBJECT |
| 3865 */ |
| 3866 bool _checkForMixinInheritsNotFromObject(TypeName mixinName, ClassElement mixi
nElement) { |
| 3867 InterfaceType mixinSupertype = mixinElement.supertype; |
| 3868 if (mixinSupertype != null) { |
| 3869 if (!mixinSupertype.isObject || !mixinElement.isTypedef && mixinElement.mi
xins.length != 0) { |
| 3870 _errorReporter.reportErrorForNode(CompileTimeErrorCode.MIXIN_INHERITS_FR
OM_NOT_OBJECT, mixinName, [mixinElement.name]); |
| 3871 return true; |
| 3872 } |
| 3873 } |
| 3874 return false; |
| 3875 } |
| 3876 |
| 3877 /** |
| 3878 * This verifies that the passed mixin does not reference 'super'. |
| 3879 * |
| 3880 * @param mixinName the node to report problem on |
| 3881 * @param mixinElement the mixing to evaluate |
| 3882 * @return `true` if and only if an error code is generated on the passed node |
| 3883 * @see CompileTimeErrorCode#MIXIN_REFERENCES_SUPER |
| 3884 */ |
| 3885 bool _checkForMixinReferencesSuper(TypeName mixinName, ClassElement mixinEleme
nt) { |
| 3886 if (mixinElement.hasReferenceToSuper) { |
| 3887 _errorReporter.reportErrorForNode(CompileTimeErrorCode.MIXIN_REFERENCES_SU
PER, mixinName, [mixinElement.name]); |
| 3888 } |
| 3889 return false; |
| 3890 } |
| 3891 |
| 3892 /** |
| 3893 * This verifies that the passed constructor has at most one 'super' initializ
er. |
| 3894 * |
| 3895 * @param node the constructor declaration to evaluate |
| 3896 * @return `true` if and only if an error code is generated on the passed node |
| 3897 * @see CompileTimeErrorCode#MULTIPLE_SUPER_INITIALIZERS |
| 3898 */ |
| 3899 bool _checkForMultipleSuperInitializers(ConstructorDeclaration node) { |
| 3900 int numSuperInitializers = 0; |
| 3901 for (ConstructorInitializer initializer in node.initializers) { |
| 3902 if (initializer is SuperConstructorInvocation) { |
| 3903 numSuperInitializers++; |
| 3904 if (numSuperInitializers > 1) { |
| 3905 _errorReporter.reportErrorForNode(CompileTimeErrorCode.MULTIPLE_SUPER_
INITIALIZERS, initializer, []); |
| 3906 } |
| 3907 } |
| 3908 } |
| 3909 return numSuperInitializers > 0; |
| 3910 } |
| 3911 |
| 3912 /** |
| 3913 * Checks to ensure that native function bodies can only in SDK code. |
| 3914 * |
| 3915 * @param node the native function body to test |
| 3916 * @return `true` if and only if an error code is generated on the passed node |
| 3917 * @see ParserErrorCode#NATIVE_FUNCTION_BODY_IN_NON_SDK_CODE |
| 3918 */ |
| 3919 bool _checkForNativeFunctionBodyInNonSDKCode(NativeFunctionBody node) { |
| 3920 if (!_isInSystemLibrary && !_hasExtUri) { |
| 3921 _errorReporter.reportErrorForNode(ParserErrorCode.NATIVE_FUNCTION_BODY_IN_
NON_SDK_CODE, node, []); |
| 3922 return true; |
| 3923 } |
| 3924 return false; |
| 3925 } |
| 3926 |
| 3927 /** |
| 3928 * This verifies that the passed 'new' instance creation expression invokes ex
isting constructor. |
| 3929 * |
| 3930 * This method assumes that the instance creation was tested to be 'new' befor
e being called. |
| 3931 * |
| 3932 * @param node the instance creation expression to evaluate |
| 3933 * @param constructorName the constructor name, always non-`null` |
| 3934 * @param typeName the name of the type defining the constructor, always non-`
null` |
| 3935 * @return `true` if and only if an error code is generated on the passed node |
| 3936 * @see StaticWarningCode#NEW_WITH_UNDEFINED_CONSTRUCTOR |
| 3937 */ |
| 3938 bool _checkForNewWithUndefinedConstructor(InstanceCreationExpression node, Con
structorName constructorName, TypeName typeName) { |
| 3939 // OK if resolved |
| 3940 if (node.staticElement != null) { |
| 3941 return false; |
| 3942 } |
| 3943 DartType type = typeName.type; |
| 3944 if (type is InterfaceType) { |
| 3945 ClassElement element = type.element; |
| 3946 if (element != null && element.isEnum) { |
| 3947 // We have already reported the error. |
| 3948 return false; |
| 3949 } |
| 3950 } |
| 3951 // prepare class name |
| 3952 Identifier className = typeName.name; |
| 3953 // report as named or default constructor absence |
| 3954 SimpleIdentifier name = constructorName.name; |
| 3955 if (name != null) { |
| 3956 _errorReporter.reportErrorForNode(StaticWarningCode.NEW_WITH_UNDEFINED_CON
STRUCTOR, name, [className, name]); |
| 3957 } else { |
| 3958 _errorReporter.reportErrorForNode(StaticWarningCode.NEW_WITH_UNDEFINED_CON
STRUCTOR_DEFAULT, constructorName, [className]); |
| 3959 } |
| 3960 return true; |
| 3961 } |
| 3962 |
| 3963 /** |
| 3964 * This checks that if the passed class declaration implicitly calls default c
onstructor of its |
| 3965 * superclass, there should be such default constructor - implicit or explicit
. |
| 3966 * |
| 3967 * @param node the [ClassDeclaration] to evaluate |
| 3968 * @return `true` if and only if an error code is generated on the passed node |
| 3969 * @see CompileTimeErrorCode#NO_DEFAULT_SUPER_CONSTRUCTOR_IMPLICIT |
| 3970 */ |
| 3971 bool _checkForNoDefaultSuperConstructorImplicit(ClassDeclaration node) { |
| 3972 // do nothing if there is explicit constructor |
| 3973 List<ConstructorElement> constructors = _enclosingClass.constructors; |
| 3974 if (!constructors[0].isSynthetic) { |
| 3975 return false; |
| 3976 } |
| 3977 // prepare super |
| 3978 InterfaceType superType = _enclosingClass.supertype; |
| 3979 if (superType == null) { |
| 3980 return false; |
| 3981 } |
| 3982 ClassElement superElement = superType.element; |
| 3983 // try to find default generative super constructor |
| 3984 ConstructorElement superUnnamedConstructor = superElement.unnamedConstructor
; |
| 3985 if (superUnnamedConstructor != null) { |
| 3986 if (superUnnamedConstructor.isFactory) { |
| 3987 _errorReporter.reportErrorForNode(CompileTimeErrorCode.NON_GENERATIVE_CO
NSTRUCTOR, node.name, [superUnnamedConstructor]); |
| 3988 return true; |
| 3989 } |
| 3990 if (superUnnamedConstructor.isDefaultConstructor) { |
| 3991 return true; |
| 3992 } |
| 3993 } |
| 3994 // report problem |
| 3995 _errorReporter.reportErrorForNode(CompileTimeErrorCode.NO_DEFAULT_SUPER_CONS
TRUCTOR_IMPLICIT, node.name, [superType.displayName]); |
| 3996 return true; |
| 3997 } |
| 3998 |
| 3999 /** |
| 4000 * This checks that passed class declaration overrides all members required by
its superclasses |
| 4001 * and interfaces. |
| 4002 * |
| 4003 * @param classNameNode the [SimpleIdentifier] to be used if there is a violat
ion, this is |
| 4004 * either the named from the [ClassDeclaration] or from the [ClassTyp
eAlias]. |
| 4005 * @return `true` if and only if an error code is generated on the passed node |
| 4006 * @see StaticWarningCode#NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE |
| 4007 * @see StaticWarningCode#NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_TWO |
| 4008 * @see StaticWarningCode#NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_THREE |
| 4009 * @see StaticWarningCode#NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FOUR |
| 4010 * @see StaticWarningCode#NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FIVE_PLU
S |
| 4011 */ |
| 4012 bool _checkForNonAbstractClassInheritsAbstractMember(SimpleIdentifier classNam
eNode) { |
| 4013 if (_enclosingClass.isAbstract) { |
| 4014 return false; |
| 4015 } |
| 4016 // |
| 4017 // Store in local sets the set of all method and accessor names |
| 4018 // |
| 4019 List<MethodElement> methods = _enclosingClass.methods; |
| 4020 for (MethodElement method in methods) { |
| 4021 String methodName = method.name; |
| 4022 // If the enclosing class declares the method noSuchMethod(), then return. |
| 4023 // From Spec: It is a static warning if a concrete class does not have an
implementation for |
| 4024 // a method in any of its superinterfaces unless it declares its own noSuc
hMethod |
| 4025 // method (7.10). |
| 4026 if (methodName == FunctionElement.NO_SUCH_METHOD_METHOD_NAME) { |
| 4027 return false; |
| 4028 } |
| 4029 } |
| 4030 HashSet<ExecutableElement> missingOverrides = new HashSet<ExecutableElement>
(); |
| 4031 // |
| 4032 // Loop through the set of all executable elements declared in the implicit
interface. |
| 4033 // |
| 4034 MemberMap membersInheritedFromInterfaces = _inheritanceManager.getMapOfMembe
rsInheritedFromInterfaces(_enclosingClass); |
| 4035 MemberMap membersInheritedFromSuperclasses = _inheritanceManager.getMapOfMem
bersInheritedFromClasses(_enclosingClass); |
| 4036 for (int i = 0; i < membersInheritedFromInterfaces.size; i++) { |
| 4037 String memberName = membersInheritedFromInterfaces.getKey(i); |
| 4038 ExecutableElement executableElt = membersInheritedFromInterfaces.getValue(
i); |
| 4039 if (memberName == null) { |
| 4040 break; |
| 4041 } |
| 4042 // If the element is not synthetic and can be determined to be defined in
Object, skip it. |
| 4043 if (executableElt.enclosingElement != null && (executableElt.enclosingElem
ent as ClassElement).type.isObject) { |
| 4044 continue; |
| 4045 } |
| 4046 // Check to see if some element is in local enclosing class that matches t
he name of the |
| 4047 // required member. |
| 4048 if (_isMemberInClassOrMixin(executableElt, _enclosingClass)) { |
| 4049 // We do not have to verify that this implementation of the found method
matches the |
| 4050 // required function type: the set of StaticWarningCode.INVALID_METHOD_O
VERRIDE_* warnings |
| 4051 // break out the different specific situations. |
| 4052 continue; |
| 4053 } |
| 4054 // First check to see if this element was declared in the superclass chain
, in which case |
| 4055 // there is already a concrete implementation. |
| 4056 ExecutableElement elt = membersInheritedFromSuperclasses.get(memberName); |
| 4057 // Check to see if an element was found in the superclass chain with the c
orrect name. |
| 4058 if (elt != null) { |
| 4059 // Reference the types, if any are null then continue. |
| 4060 InterfaceType enclosingType = _enclosingClass.type; |
| 4061 FunctionType concreteType = elt.type; |
| 4062 FunctionType requiredMemberType = executableElt.type; |
| 4063 if (enclosingType == null || concreteType == null || requiredMemberType
== null) { |
| 4064 continue; |
| 4065 } |
| 4066 // Some element was found in the superclass chain that matches the name
of the required |
| 4067 // member. |
| 4068 // If it is not abstract and it is the correct one (types match- the ver
sion of this method |
| 4069 // that we have has the correct number of parameters, etc), then this cl
ass has a valid |
| 4070 // implementation of this method, so skip it. |
| 4071 if ((elt is MethodElement && !elt.isAbstract) || (elt is PropertyAccesso
rElement && !elt.isAbstract)) { |
| 4072 // Since we are comparing two function types, we need to do the approp
riate type |
| 4073 // substitutions first (). |
| 4074 FunctionType foundConcreteFT = _inheritanceManager.substituteTypeArgum
entsInMemberFromInheritance(concreteType, memberName, enclosingType); |
| 4075 FunctionType requiredMemberFT = _inheritanceManager.substituteTypeArgu
mentsInMemberFromInheritance(requiredMemberType, memberName, enclosingType); |
| 4076 if (foundConcreteFT.isSubtypeOf(requiredMemberFT)) { |
| 4077 continue; |
| 4078 } |
| 4079 } |
| 4080 } |
| 4081 // The not qualifying concrete executable element was found, add it to the
list. |
| 4082 missingOverrides.add(executableElt); |
| 4083 } |
| 4084 // Now that we have the set of missing overrides, generate a warning on this
class |
| 4085 int missingOverridesSize = missingOverrides.length; |
| 4086 if (missingOverridesSize == 0) { |
| 4087 return false; |
| 4088 } |
| 4089 List<ExecutableElement> missingOverridesArray = new List.from(missingOverrid
es); |
| 4090 List<String> stringMembersArrayListSet = new List<String>(); |
| 4091 for (int i = 0; i < missingOverridesArray.length; i++) { |
| 4092 String newStrMember; |
| 4093 Element enclosingElement = missingOverridesArray[i].enclosingElement; |
| 4094 String prefix = StringUtilities.EMPTY; |
| 4095 if (missingOverridesArray[i] is PropertyAccessorElement) { |
| 4096 PropertyAccessorElement propertyAccessorElement = missingOverridesArray[
i] as PropertyAccessorElement; |
| 4097 if (propertyAccessorElement.isGetter) { |
| 4098 prefix = _GETTER_SPACE; |
| 4099 // "getter " |
| 4100 } else { |
| 4101 prefix = _SETTER_SPACE; |
| 4102 // "setter " |
| 4103 } |
| 4104 } |
| 4105 if (enclosingElement != null) { |
| 4106 newStrMember = "$prefix'${enclosingElement.displayName}.${missingOverrid
esArray[i].displayName}'"; |
| 4107 } else { |
| 4108 newStrMember = "$prefix'${missingOverridesArray[i].displayName}'"; |
| 4109 } |
| 4110 stringMembersArrayListSet.add(newStrMember); |
| 4111 } |
| 4112 List<String> stringMembersArray = new List.from(stringMembersArrayListSet); |
| 4113 AnalysisErrorWithProperties analysisError; |
| 4114 if (stringMembersArray.length == 1) { |
| 4115 analysisError = _errorReporter.newErrorWithProperties(StaticWarningCode.NO
N_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE, classNameNode, [stringMembersArra
y[0]]); |
| 4116 } else if (stringMembersArray.length == 2) { |
| 4117 analysisError = _errorReporter.newErrorWithProperties(StaticWarningCode.NO
N_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_TWO, classNameNode, [stringMembersArra
y[0], stringMembersArray[1]]); |
| 4118 } else if (stringMembersArray.length == 3) { |
| 4119 analysisError = _errorReporter.newErrorWithProperties(StaticWarningCode.NO
N_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_THREE, classNameNode, [ |
| 4120 stringMembersArray[0], |
| 4121 stringMembersArray[1], |
| 4122 stringMembersArray[2]]); |
| 4123 } else if (stringMembersArray.length == 4) { |
| 4124 analysisError = _errorReporter.newErrorWithProperties(StaticWarningCode.NO
N_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FOUR, classNameNode, [ |
| 4125 stringMembersArray[0], |
| 4126 stringMembersArray[1], |
| 4127 stringMembersArray[2], |
| 4128 stringMembersArray[3]]); |
| 4129 } else { |
| 4130 analysisError = _errorReporter.newErrorWithProperties(StaticWarningCode.NO
N_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FIVE_PLUS, classNameNode, [ |
| 4131 stringMembersArray[0], |
| 4132 stringMembersArray[1], |
| 4133 stringMembersArray[2], |
| 4134 stringMembersArray[3], |
| 4135 stringMembersArray.length - 4]); |
| 4136 } |
| 4137 analysisError.setProperty(ErrorProperty.UNIMPLEMENTED_METHODS, missingOverri
desArray); |
| 4138 _errorReporter.reportError(analysisError); |
| 4139 return true; |
| 4140 } |
| 4141 |
| 4142 /** |
| 4143 * Checks to ensure that the expressions that need to be of type bool, are. Ot
herwise an error is |
| 4144 * reported on the expression. |
| 4145 * |
| 4146 * @param condition the conditional expression to test |
| 4147 * @return `true` if and only if an error code is generated on the passed node |
| 4148 * @see StaticTypeWarningCode#NON_BOOL_CONDITION |
| 4149 */ |
| 4150 bool _checkForNonBoolCondition(Expression condition) { |
| 4151 DartType conditionType = getStaticType(condition); |
| 4152 if (conditionType != null && !conditionType.isAssignableTo(_boolType)) { |
| 4153 _errorReporter.reportErrorForNode(StaticTypeWarningCode.NON_BOOL_CONDITION
, condition, []); |
| 4154 return true; |
| 4155 } |
| 4156 return false; |
| 4157 } |
| 4158 |
| 4159 /** |
| 4160 * This verifies that the passed assert statement has either a 'bool' or '() -
> bool' input. |
| 4161 * |
| 4162 * @param node the assert statement to evaluate |
| 4163 * @return `true` if and only if an error code is generated on the passed node |
| 4164 * @see StaticTypeWarningCode#NON_BOOL_EXPRESSION |
| 4165 */ |
| 4166 bool _checkForNonBoolExpression(AssertStatement node) { |
| 4167 Expression expression = node.condition; |
| 4168 DartType type = getStaticType(expression); |
| 4169 if (type is InterfaceType) { |
| 4170 if (!type.isAssignableTo(_boolType)) { |
| 4171 _errorReporter.reportErrorForNode(StaticTypeWarningCode.NON_BOOL_EXPRESS
ION, expression, []); |
| 4172 return true; |
| 4173 } |
| 4174 } else if (type is FunctionType) { |
| 4175 FunctionType functionType = type; |
| 4176 if (functionType.typeArguments.length == 0 && !functionType.returnType.isA
ssignableTo(_boolType)) { |
| 4177 _errorReporter.reportErrorForNode(StaticTypeWarningCode.NON_BOOL_EXPRESS
ION, expression, []); |
| 4178 return true; |
| 4179 } |
| 4180 } |
| 4181 return false; |
| 4182 } |
| 4183 |
| 4184 /** |
| 4185 * Checks to ensure that the given expression is assignable to bool. |
| 4186 * |
| 4187 * @param expression the expression expression to test |
| 4188 * @return `true` if and only if an error code is generated on the passed node |
| 4189 * @see StaticTypeWarningCode#NON_BOOL_NEGATION_EXPRESSION |
| 4190 */ |
| 4191 bool _checkForNonBoolNegationExpression(Expression expression) { |
| 4192 DartType conditionType = getStaticType(expression); |
| 4193 if (conditionType != null && !conditionType.isAssignableTo(_boolType)) { |
| 4194 _errorReporter.reportErrorForNode(StaticTypeWarningCode.NON_BOOL_NEGATION_
EXPRESSION, expression, []); |
| 4195 return true; |
| 4196 } |
| 4197 return false; |
| 4198 } |
| 4199 |
| 4200 /** |
| 4201 * This verifies the passed map literal either: |
| 4202 * * has `const modifier` |
| 4203 * * has explicit type arguments |
| 4204 * * is not start of the statement |
| 4205 * |
| 4206 * @param node the map literal to evaluate |
| 4207 * @return `true` if and only if an error code is generated on the passed node |
| 4208 * @see CompileTimeErrorCode#NON_CONST_MAP_AS_EXPRESSION_STATEMENT |
| 4209 */ |
| 4210 bool _checkForNonConstMapAsExpressionStatement(MapLiteral node) { |
| 4211 // "const" |
| 4212 if (node.constKeyword != null) { |
| 4213 return false; |
| 4214 } |
| 4215 // has type arguments |
| 4216 if (node.typeArguments != null) { |
| 4217 return false; |
| 4218 } |
| 4219 // prepare statement |
| 4220 Statement statement = node.getAncestor((node) => node is ExpressionStatement
); |
| 4221 if (statement == null) { |
| 4222 return false; |
| 4223 } |
| 4224 // OK, statement does not start with map |
| 4225 if (!identical(statement.beginToken, node.beginToken)) { |
| 4226 return false; |
| 4227 } |
| 4228 // report problem |
| 4229 _errorReporter.reportErrorForNode(CompileTimeErrorCode.NON_CONST_MAP_AS_EXPR
ESSION_STATEMENT, node, []); |
| 4230 return true; |
| 4231 } |
| 4232 |
| 4233 /** |
| 4234 * This verifies the passed method declaration of operator `[]=`, has `void` r
eturn |
| 4235 * type. |
| 4236 * |
| 4237 * @param node the method declaration to evaluate |
| 4238 * @return `true` if and only if an error code is generated on the passed node |
| 4239 * @see StaticWarningCode#NON_VOID_RETURN_FOR_OPERATOR |
| 4240 */ |
| 4241 bool _checkForNonVoidReturnTypeForOperator(MethodDeclaration node) { |
| 4242 // check that []= operator |
| 4243 SimpleIdentifier name = node.name; |
| 4244 if (name.name != "[]=") { |
| 4245 return false; |
| 4246 } |
| 4247 // check return type |
| 4248 TypeName typeName = node.returnType; |
| 4249 if (typeName != null) { |
| 4250 DartType type = typeName.type; |
| 4251 if (type != null && !type.isVoid) { |
| 4252 _errorReporter.reportErrorForNode(StaticWarningCode.NON_VOID_RETURN_FOR_
OPERATOR, typeName, []); |
| 4253 } |
| 4254 } |
| 4255 // no warning |
| 4256 return false; |
| 4257 } |
| 4258 |
| 4259 /** |
| 4260 * This verifies the passed setter has no return type or the `void` return typ
e. |
| 4261 * |
| 4262 * @param typeName the type name to evaluate |
| 4263 * @return `true` if and only if an error code is generated on the passed node |
| 4264 * @see StaticWarningCode#NON_VOID_RETURN_FOR_SETTER |
| 4265 */ |
| 4266 bool _checkForNonVoidReturnTypeForSetter(TypeName typeName) { |
| 4267 if (typeName != null) { |
| 4268 DartType type = typeName.type; |
| 4269 if (type != null && !type.isVoid) { |
| 4270 _errorReporter.reportErrorForNode(StaticWarningCode.NON_VOID_RETURN_FOR_
SETTER, typeName, []); |
| 4271 } |
| 4272 } |
| 4273 return false; |
| 4274 } |
| 4275 |
| 4276 /** |
| 4277 * This verifies the passed operator-method declaration, does not have an opti
onal parameter. |
| 4278 * |
| 4279 * This method assumes that the method declaration was tested to be an operato
r declaration before |
| 4280 * being called. |
| 4281 * |
| 4282 * @param node the method declaration to evaluate |
| 4283 * @return `true` if and only if an error code is generated on the passed node |
| 4284 * @see CompileTimeErrorCode#OPTIONAL_PARAMETER_IN_OPERATOR |
| 4285 */ |
| 4286 bool _checkForOptionalParameterInOperator(MethodDeclaration node) { |
| 4287 FormalParameterList parameterList = node.parameters; |
| 4288 if (parameterList == null) { |
| 4289 return false; |
| 4290 } |
| 4291 bool foundError = false; |
| 4292 NodeList<FormalParameter> formalParameters = parameterList.parameters; |
| 4293 for (FormalParameter formalParameter in formalParameters) { |
| 4294 if (formalParameter.kind.isOptional) { |
| 4295 _errorReporter.reportErrorForNode(CompileTimeErrorCode.OPTIONAL_PARAMETE
R_IN_OPERATOR, formalParameter, []); |
| 4296 foundError = true; |
| 4297 } |
| 4298 } |
| 4299 return foundError; |
| 4300 } |
| 4301 |
| 4302 /** |
| 4303 * This checks for named optional parameters that begin with '_'. |
| 4304 * |
| 4305 * @param node the default formal parameter to evaluate |
| 4306 * @return `true` if and only if an error code is generated on the passed node |
| 4307 * @see CompileTimeErrorCode#PRIVATE_OPTIONAL_PARAMETER |
| 4308 */ |
| 4309 bool _checkForPrivateOptionalParameter(FormalParameter node) { |
| 4310 // should be named parameter |
| 4311 if (node.kind != ParameterKind.NAMED) { |
| 4312 return false; |
| 4313 } |
| 4314 // name should start with '_' |
| 4315 SimpleIdentifier name = node.identifier; |
| 4316 if (name.isSynthetic || !StringUtilities.startsWithChar(name.name, 0x5F)) { |
| 4317 return false; |
| 4318 } |
| 4319 // report problem |
| 4320 _errorReporter.reportErrorForNode(CompileTimeErrorCode.PRIVATE_OPTIONAL_PARA
METER, node, []); |
| 4321 return true; |
| 4322 } |
| 4323 |
| 4324 /** |
| 4325 * This checks if the passed constructor declaration is the redirecting genera
tive constructor and |
| 4326 * references itself directly or indirectly. |
| 4327 * |
| 4328 * @param node the constructor declaration to evaluate |
| 4329 * @param constructorElement the constructor element |
| 4330 * @return `true` if and only if an error code is generated on the passed node |
| 4331 * @see CompileTimeErrorCode#RECURSIVE_CONSTRUCTOR_REDIRECT |
| 4332 */ |
| 4333 bool _checkForRecursiveConstructorRedirect(ConstructorDeclaration node, Constr
uctorElement constructorElement) { |
| 4334 // we check generative constructor here |
| 4335 if (node.factoryKeyword != null) { |
| 4336 return false; |
| 4337 } |
| 4338 // try to find redirecting constructor invocation and analyzer it for recurs
ion |
| 4339 for (ConstructorInitializer initializer in node.initializers) { |
| 4340 if (initializer is RedirectingConstructorInvocation) { |
| 4341 // OK if no cycle |
| 4342 if (!_hasRedirectingFactoryConstructorCycle(constructorElement)) { |
| 4343 return false; |
| 4344 } |
| 4345 // report error |
| 4346 _errorReporter.reportErrorForNode(CompileTimeErrorCode.RECURSIVE_CONSTRU
CTOR_REDIRECT, initializer, []); |
| 4347 return true; |
| 4348 } |
| 4349 } |
| 4350 // OK, no redirecting constructor invocation |
| 4351 return false; |
| 4352 } |
| 4353 |
| 4354 /** |
| 4355 * This checks if the passed constructor declaration has redirected constructo
r and references |
| 4356 * itself directly or indirectly. |
| 4357 * |
| 4358 * @param node the constructor declaration to evaluate |
| 4359 * @param constructorElement the constructor element |
| 4360 * @return `true` if and only if an error code is generated on the passed node |
| 4361 * @see CompileTimeErrorCode#RECURSIVE_FACTORY_REDIRECT |
| 4362 */ |
| 4363 bool _checkForRecursiveFactoryRedirect(ConstructorDeclaration node, Constructo
rElement constructorElement) { |
| 4364 // prepare redirected constructor |
| 4365 ConstructorName redirectedConstructorNode = node.redirectedConstructor; |
| 4366 if (redirectedConstructorNode == null) { |
| 4367 return false; |
| 4368 } |
| 4369 // OK if no cycle |
| 4370 if (!_hasRedirectingFactoryConstructorCycle(constructorElement)) { |
| 4371 return false; |
| 4372 } |
| 4373 // report error |
| 4374 _errorReporter.reportErrorForNode(CompileTimeErrorCode.RECURSIVE_FACTORY_RED
IRECT, redirectedConstructorNode, []); |
| 4375 return true; |
| 4376 } |
| 4377 |
| 4378 /** |
| 4379 * This checks the class declaration is not a superinterface to itself. |
| 4380 * |
| 4381 * @param classElt the class element to test |
| 4382 * @return `true` if and only if an error code is generated on the passed elem
ent |
| 4383 * @see CompileTimeErrorCode#RECURSIVE_INTERFACE_INHERITANCE |
| 4384 * @see CompileTimeErrorCode#RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_EXTENDS |
| 4385 * @see CompileTimeErrorCode#RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_IMPLEME
NTS |
| 4386 */ |
| 4387 bool _checkForRecursiveInterfaceInheritance(ClassElement classElt) { |
| 4388 if (classElt == null) { |
| 4389 return false; |
| 4390 } |
| 4391 return _safeCheckForRecursiveInterfaceInheritance(classElt, new List<ClassEl
ement>()); |
| 4392 } |
| 4393 |
| 4394 /** |
| 4395 * This checks the passed constructor declaration has a valid combination of r
edirected |
| 4396 * constructor invocation(s), super constructor invocations and field initiali
zers. |
| 4397 * |
| 4398 * @param node the constructor declaration to evaluate |
| 4399 * @return `true` if and only if an error code is generated on the passed node |
| 4400 * @see CompileTimeErrorCode#DEFAULT_VALUE_IN_REDIRECTING_FACTORY_CONSTRUCTOR |
| 4401 * @see CompileTimeErrorCode#FIELD_INITIALIZER_REDIRECTING_CONSTRUCTOR |
| 4402 * @see CompileTimeErrorCode#MULTIPLE_REDIRECTING_CONSTRUCTOR_INVOCATIONS |
| 4403 * @see CompileTimeErrorCode#SUPER_IN_REDIRECTING_CONSTRUCTOR |
| 4404 * @see CompileTimeErrorCode#REDIRECT_GENERATIVE_TO_NON_GENERATIVE_CONSTRUCTOR |
| 4405 */ |
| 4406 bool _checkForRedirectingConstructorErrorCodes(ConstructorDeclaration node) { |
| 4407 bool errorReported = false; |
| 4408 // |
| 4409 // Check for default values in the parameters |
| 4410 // |
| 4411 ConstructorName redirectedConstructor = node.redirectedConstructor; |
| 4412 if (redirectedConstructor != null) { |
| 4413 for (FormalParameter parameter in node.parameters.parameters) { |
| 4414 if (parameter is DefaultFormalParameter && parameter.defaultValue != nul
l) { |
| 4415 _errorReporter.reportErrorForNode(CompileTimeErrorCode.DEFAULT_VALUE_I
N_REDIRECTING_FACTORY_CONSTRUCTOR, parameter.identifier, []); |
| 4416 errorReported = true; |
| 4417 } |
| 4418 } |
| 4419 } |
| 4420 // check if there are redirected invocations |
| 4421 int numRedirections = 0; |
| 4422 for (ConstructorInitializer initializer in node.initializers) { |
| 4423 if (initializer is RedirectingConstructorInvocation) { |
| 4424 if (numRedirections > 0) { |
| 4425 _errorReporter.reportErrorForNode(CompileTimeErrorCode.MULTIPLE_REDIRE
CTING_CONSTRUCTOR_INVOCATIONS, initializer, []); |
| 4426 errorReported = true; |
| 4427 } |
| 4428 if (node.factoryKeyword == null) { |
| 4429 RedirectingConstructorInvocation invocation = initializer; |
| 4430 ConstructorElement redirectingElement = invocation.staticElement; |
| 4431 if (redirectingElement == null) { |
| 4432 String enclosingTypeName = _enclosingClass.displayName; |
| 4433 String constructorStrName = enclosingTypeName; |
| 4434 if (invocation.constructorName != null) { |
| 4435 constructorStrName += ".${invocation.constructorName.name}"; |
| 4436 } |
| 4437 _errorReporter.reportErrorForNode(CompileTimeErrorCode.REDIRECT_GENE
RATIVE_TO_MISSING_CONSTRUCTOR, invocation, [constructorStrName, enclosingTypeNam
e]); |
| 4438 } else { |
| 4439 if (redirectingElement.isFactory) { |
| 4440 _errorReporter.reportErrorForNode(CompileTimeErrorCode.REDIRECT_GE
NERATIVE_TO_NON_GENERATIVE_CONSTRUCTOR, initializer, []); |
| 4441 } |
| 4442 } |
| 4443 } |
| 4444 numRedirections++; |
| 4445 } |
| 4446 } |
| 4447 // check for other initializers |
| 4448 if (numRedirections > 0) { |
| 4449 for (ConstructorInitializer initializer in node.initializers) { |
| 4450 if (initializer is SuperConstructorInvocation) { |
| 4451 _errorReporter.reportErrorForNode(CompileTimeErrorCode.SUPER_IN_REDIRE
CTING_CONSTRUCTOR, initializer, []); |
| 4452 errorReported = true; |
| 4453 } |
| 4454 if (initializer is ConstructorFieldInitializer) { |
| 4455 _errorReporter.reportErrorForNode(CompileTimeErrorCode.FIELD_INITIALIZ
ER_REDIRECTING_CONSTRUCTOR, initializer, []); |
| 4456 errorReported = true; |
| 4457 } |
| 4458 } |
| 4459 } |
| 4460 // done |
| 4461 return errorReported; |
| 4462 } |
| 4463 |
| 4464 /** |
| 4465 * This checks if the passed constructor declaration has redirected constructo
r and references |
| 4466 * itself directly or indirectly. |
| 4467 * |
| 4468 * @param node the constructor declaration to evaluate |
| 4469 * @param constructorElement the constructor element |
| 4470 * @return `true` if and only if an error code is generated on the passed node |
| 4471 * @see CompileTimeErrorCode#REDIRECT_TO_NON_CONST_CONSTRUCTOR |
| 4472 */ |
| 4473 bool _checkForRedirectToNonConstConstructor(ConstructorDeclaration node, Const
ructorElement constructorElement) { |
| 4474 // prepare redirected constructor |
| 4475 ConstructorName redirectedConstructorNode = node.redirectedConstructor; |
| 4476 if (redirectedConstructorNode == null) { |
| 4477 return false; |
| 4478 } |
| 4479 // prepare element |
| 4480 if (constructorElement == null) { |
| 4481 return false; |
| 4482 } |
| 4483 // OK, it is not 'const' |
| 4484 if (!constructorElement.isConst) { |
| 4485 return false; |
| 4486 } |
| 4487 // prepare redirected constructor |
| 4488 ConstructorElement redirectedConstructor = constructorElement.redirectedCons
tructor; |
| 4489 if (redirectedConstructor == null) { |
| 4490 return false; |
| 4491 } |
| 4492 // OK, it is also 'const' |
| 4493 if (redirectedConstructor.isConst) { |
| 4494 return false; |
| 4495 } |
| 4496 // report error |
| 4497 _errorReporter.reportErrorForNode(CompileTimeErrorCode.REDIRECT_TO_NON_CONST
_CONSTRUCTOR, redirectedConstructorNode, []); |
| 4498 return true; |
| 4499 } |
| 4500 |
| 4501 /** |
| 4502 * This checks that the rethrow is inside of a catch clause. |
| 4503 * |
| 4504 * @param node the rethrow expression to evaluate |
| 4505 * @return `true` if and only if an error code is generated on the passed node |
| 4506 * @see CompileTimeErrorCode#RETHROW_OUTSIDE_CATCH |
| 4507 */ |
| 4508 bool _checkForRethrowOutsideCatch(RethrowExpression node) { |
| 4509 if (!_isInCatchClause) { |
| 4510 _errorReporter.reportErrorForNode(CompileTimeErrorCode.RETHROW_OUTSIDE_CAT
CH, node, []); |
| 4511 return true; |
| 4512 } |
| 4513 return false; |
| 4514 } |
| 4515 |
| 4516 /** |
| 4517 * This checks that if the the given constructor declaration is generative, th
en it does not have |
| 4518 * an expression function body. |
| 4519 * |
| 4520 * @param node the constructor to evaluate |
| 4521 * @return `true` if and only if an error code is generated on the passed node |
| 4522 * @see CompileTimeErrorCode#RETURN_IN_GENERATIVE_CONSTRUCTOR |
| 4523 */ |
| 4524 bool _checkForReturnInGenerativeConstructor(ConstructorDeclaration node) { |
| 4525 // ignore factory |
| 4526 if (node.factoryKeyword != null) { |
| 4527 return false; |
| 4528 } |
| 4529 // block body (with possible return statement) is checked elsewhere |
| 4530 FunctionBody body = node.body; |
| 4531 if (body is! ExpressionFunctionBody) { |
| 4532 return false; |
| 4533 } |
| 4534 // report error |
| 4535 _errorReporter.reportErrorForNode(CompileTimeErrorCode.RETURN_IN_GENERATIVE_
CONSTRUCTOR, body, []); |
| 4536 return true; |
| 4537 } |
| 4538 |
| 4539 /** |
| 4540 * This checks that a type mis-match between the return type and the expressed
return type by the |
| 4541 * enclosing method or function. |
| 4542 * |
| 4543 * This method is called both by [checkForAllReturnStatementErrorCodes] |
| 4544 * and [visitExpressionFunctionBody]. |
| 4545 * |
| 4546 * @param returnExpression the returned expression to evaluate |
| 4547 * @param expectedReturnType the expressed return type by the enclosing method
or function |
| 4548 * @return `true` if and only if an error code is generated on the passed node |
| 4549 * @see StaticTypeWarningCode#RETURN_OF_INVALID_TYPE |
| 4550 */ |
| 4551 bool _checkForReturnOfInvalidType(Expression returnExpression, DartType expect
edReturnType) { |
| 4552 if (_enclosingFunction == null) { |
| 4553 return false; |
| 4554 } |
| 4555 DartType staticReturnType = getStaticType(returnExpression); |
| 4556 if (expectedReturnType.isVoid) { |
| 4557 if (staticReturnType.isVoid || staticReturnType.isDynamic || staticReturnT
ype.isBottom) { |
| 4558 return false; |
| 4559 } |
| 4560 _errorReporter.reportTypeErrorForNode(StaticTypeWarningCode.RETURN_OF_INVA
LID_TYPE, returnExpression, [ |
| 4561 staticReturnType, |
| 4562 expectedReturnType, |
| 4563 _enclosingFunction.displayName]); |
| 4564 return true; |
| 4565 } |
| 4566 if (_enclosingFunction.isAsynchronous && !_enclosingFunction.isGenerator) { |
| 4567 // TODO(brianwilkerson) Figure out how to get the type "Future" so that we
can build the type |
| 4568 // we need to test against. |
| 4569 // InterfaceType impliedType = "Future<" + flatten(staticReturnType)
+ ">" |
| 4570 // if (impliedType.isAssignableTo(expectedReturnType)) { |
| 4571 // return false; |
| 4572 // } |
| 4573 // errorReporter.reportTypeErrorForNode( |
| 4574 // StaticTypeWarningCode.RETURN_OF_INVALID_TYPE, |
| 4575 // returnExpression, |
| 4576 // impliedType, |
| 4577 // expectedReturnType.getDisplayName(), |
| 4578 // enclosingFunction.getDisplayName()); |
| 4579 // return true; |
| 4580 return false; |
| 4581 } |
| 4582 if (staticReturnType.isAssignableTo(expectedReturnType)) { |
| 4583 return false; |
| 4584 } |
| 4585 _errorReporter.reportTypeErrorForNode(StaticTypeWarningCode.RETURN_OF_INVALI
D_TYPE, returnExpression, [ |
| 4586 staticReturnType, |
| 4587 expectedReturnType, |
| 4588 _enclosingFunction.displayName]); |
| 4589 return true; |
| 4590 // TODO(brianwilkerson) Define a hint corresponding to the warning and repor
t it if appropriate. |
| 4591 // Type propagatedReturnType = returnExpression.getPropagatedType(); |
| 4592 // boolean isPropagatedAssignable = propagatedReturnType.isAssignableTo(e
xpectedReturnType); |
| 4593 // if (isStaticAssignable || isPropagatedAssignable) { |
| 4594 // return false; |
| 4595 // } |
| 4596 // errorReporter.reportTypeErrorForNode( |
| 4597 // StaticTypeWarningCode.RETURN_OF_INVALID_TYPE, |
| 4598 // returnExpression, |
| 4599 // staticReturnType, |
| 4600 // expectedReturnType, |
| 4601 // enclosingFunction.getDisplayName()); |
| 4602 // return true; |
| 4603 } |
| 4604 |
| 4605 /** |
| 4606 * This checks the given "typeReference" and that the "name" is not the refere
nce to an instance |
| 4607 * member. |
| 4608 * |
| 4609 * @param typeReference the resolved [ClassElement] of the left hand side of t
he expression, |
| 4610 * or `null`, aka, the class element of 'C' in 'C.x', see |
| 4611 * [getTypeReference] |
| 4612 * @param name the accessed name to evaluate |
| 4613 * @return `true` if and only if an error code is generated on the passed node |
| 4614 * @see StaticWarningCode#STATIC_ACCESS_TO_INSTANCE_MEMBER |
| 4615 */ |
| 4616 bool _checkForStaticAccessToInstanceMember(ClassElement typeReference, SimpleI
dentifier name) { |
| 4617 // OK, target is not a type |
| 4618 if (typeReference == null) { |
| 4619 return false; |
| 4620 } |
| 4621 // prepare member Element |
| 4622 Element element = name.staticElement; |
| 4623 if (element is! ExecutableElement) { |
| 4624 return false; |
| 4625 } |
| 4626 ExecutableElement memberElement = element as ExecutableElement; |
| 4627 // OK, static |
| 4628 if (memberElement.isStatic) { |
| 4629 return false; |
| 4630 } |
| 4631 // report problem |
| 4632 _errorReporter.reportErrorForNode(StaticWarningCode.STATIC_ACCESS_TO_INSTANC
E_MEMBER, name, [name.name]); |
| 4633 return true; |
| 4634 } |
| 4635 |
| 4636 /** |
| 4637 * This checks that the type of the passed 'switch' expression is assignable t
o the type of the |
| 4638 * 'case' members. |
| 4639 * |
| 4640 * @param node the 'switch' statement to evaluate |
| 4641 * @return `true` if and only if an error code is generated on the passed node |
| 4642 * @see StaticWarningCode#SWITCH_EXPRESSION_NOT_ASSIGNABLE |
| 4643 */ |
| 4644 bool _checkForSwitchExpressionNotAssignable(SwitchStatement node) { |
| 4645 // prepare 'switch' expression type |
| 4646 Expression expression = node.expression; |
| 4647 DartType expressionType = getStaticType(expression); |
| 4648 if (expressionType == null) { |
| 4649 return false; |
| 4650 } |
| 4651 // compare with type of the first 'case' |
| 4652 NodeList<SwitchMember> members = node.members; |
| 4653 for (SwitchMember switchMember in members) { |
| 4654 if (switchMember is! SwitchCase) { |
| 4655 continue; |
| 4656 } |
| 4657 SwitchCase switchCase = switchMember as SwitchCase; |
| 4658 // prepare 'case' type |
| 4659 Expression caseExpression = switchCase.expression; |
| 4660 DartType caseType = getStaticType(caseExpression); |
| 4661 // check types |
| 4662 if (expressionType.isAssignableTo(caseType)) { |
| 4663 return false; |
| 4664 } |
| 4665 // report problem |
| 4666 _errorReporter.reportErrorForNode(StaticWarningCode.SWITCH_EXPRESSION_NOT_
ASSIGNABLE, expression, [expressionType, caseType]); |
| 4667 return true; |
| 4668 } |
| 4669 return false; |
| 4670 } |
| 4671 |
| 4672 /** |
| 4673 * This verifies that the passed function type alias does not reference itself
directly. |
| 4674 * |
| 4675 * @param node the function type alias to evaluate |
| 4676 * @return `true` if and only if an error code is generated on the passed node |
| 4677 * @see CompileTimeErrorCode#TYPE_ALIAS_CANNOT_REFERENCE_ITSELF |
| 4678 */ |
| 4679 bool _checkForTypeAliasCannotReferenceItself_function(FunctionTypeAlias node)
{ |
| 4680 FunctionTypeAliasElement element = node.element; |
| 4681 if (!_hasTypedefSelfReference(element)) { |
| 4682 return false; |
| 4683 } |
| 4684 _errorReporter.reportErrorForNode(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REF
ERENCE_ITSELF, node, []); |
| 4685 return true; |
| 4686 } |
| 4687 |
| 4688 /** |
| 4689 * This verifies that the passed type name is not a deferred type. |
| 4690 * |
| 4691 * @param expression the expression to evaluate |
| 4692 * @return `true` if and only if an error code is generated on the passed node |
| 4693 * @see StaticWarningCode#TYPE_ANNOTATION_DEFERRED_CLASS |
| 4694 */ |
| 4695 bool _checkForTypeAnnotationDeferredClass(TypeName node) { |
| 4696 if (node != null && node.isDeferred) { |
| 4697 _errorReporter.reportErrorForNode(StaticWarningCode.TYPE_ANNOTATION_DEFERR
ED_CLASS, node, [node.name]); |
| 4698 } |
| 4699 return false; |
| 4700 } |
| 4701 |
| 4702 /** |
| 4703 * This verifies that the type arguments in the passed type name are all withi
n their bounds. |
| 4704 * |
| 4705 * @param node the [TypeName] to evaluate |
| 4706 * @return `true` if and only if an error code is generated on the passed node |
| 4707 * @see StaticTypeWarningCode#TYPE_ARGUMENT_NOT_MATCHING_BOUNDS |
| 4708 */ |
| 4709 bool _checkForTypeArgumentNotMatchingBounds(TypeName node) { |
| 4710 if (node.typeArguments == null) { |
| 4711 return false; |
| 4712 } |
| 4713 // prepare Type |
| 4714 DartType type = node.type; |
| 4715 if (type == null) { |
| 4716 return false; |
| 4717 } |
| 4718 // prepare ClassElement |
| 4719 Element element = type.element; |
| 4720 if (element is! ClassElement) { |
| 4721 return false; |
| 4722 } |
| 4723 ClassElement classElement = element as ClassElement; |
| 4724 // prepare type parameters |
| 4725 List<DartType> typeParameters = classElement.type.typeArguments; |
| 4726 List<TypeParameterElement> boundingElts = classElement.typeParameters; |
| 4727 // iterate over each bounded type parameter and corresponding argument |
| 4728 NodeList<TypeName> typeNameArgList = node.typeArguments.arguments; |
| 4729 List<DartType> typeArguments = (type as InterfaceType).typeArguments; |
| 4730 int loopThroughIndex = math.min(typeNameArgList.length, boundingElts.length)
; |
| 4731 bool foundError = false; |
| 4732 for (int i = 0; i < loopThroughIndex; i++) { |
| 4733 TypeName argTypeName = typeNameArgList[i]; |
| 4734 DartType argType = argTypeName.type; |
| 4735 DartType boundType = boundingElts[i].bound; |
| 4736 if (argType != null && boundType != null) { |
| 4737 if (typeArguments.length != 0 && typeArguments.length == typeParameters.
length) { |
| 4738 boundType = boundType.substitute2(typeArguments, typeParameters); |
| 4739 } |
| 4740 if (!argType.isSubtypeOf(boundType)) { |
| 4741 ErrorCode errorCode; |
| 4742 if (_isInConstInstanceCreation) { |
| 4743 errorCode = CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS; |
| 4744 } else { |
| 4745 errorCode = StaticTypeWarningCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS; |
| 4746 } |
| 4747 _errorReporter.reportTypeErrorForNode(errorCode, argTypeName, [argType
, boundType]); |
| 4748 foundError = true; |
| 4749 } |
| 4750 } |
| 4751 } |
| 4752 return foundError; |
| 4753 } |
| 4754 |
| 4755 /** |
| 4756 * This checks that if the passed type name is a type parameter being used to
define a static |
| 4757 * member. |
| 4758 * |
| 4759 * @param node the type name to evaluate |
| 4760 * @return `true` if and only if an error code is generated on the passed node |
| 4761 * @see StaticWarningCode#TYPE_PARAMETER_REFERENCED_BY_STATIC |
| 4762 */ |
| 4763 bool _checkForTypeParameterReferencedByStatic(TypeName node) { |
| 4764 if (_isInStaticMethod || _isInStaticVariableDeclaration) { |
| 4765 DartType type = node.type; |
| 4766 if (type is TypeParameterType) { |
| 4767 _errorReporter.reportErrorForNode(StaticWarningCode.TYPE_PARAMETER_REFER
ENCED_BY_STATIC, node, []); |
| 4768 return true; |
| 4769 } |
| 4770 } |
| 4771 return false; |
| 4772 } |
| 4773 |
| 4774 /** |
| 4775 * This checks that if the passed type parameter is a supertype of its bound. |
| 4776 * |
| 4777 * @param node the type parameter to evaluate |
| 4778 * @return `true` if and only if an error code is generated on the passed node |
| 4779 * @see StaticTypeWarningCode#TYPE_PARAMETER_SUPERTYPE_OF_ITS_BOUND |
| 4780 */ |
| 4781 bool _checkForTypeParameterSupertypeOfItsBound(TypeParameter node) { |
| 4782 TypeParameterElement element = node.element; |
| 4783 // prepare bound |
| 4784 DartType bound = element.bound; |
| 4785 if (bound == null) { |
| 4786 return false; |
| 4787 } |
| 4788 // OK, type parameter is not supertype of its bound |
| 4789 if (!bound.isMoreSpecificThan(element.type)) { |
| 4790 return false; |
| 4791 } |
| 4792 // report problem |
| 4793 _errorReporter.reportErrorForNode(StaticTypeWarningCode.TYPE_PARAMETER_SUPER
TYPE_OF_ITS_BOUND, node, [element.displayName]); |
| 4794 return true; |
| 4795 } |
| 4796 |
| 4797 /** |
| 4798 * This checks that if the passed generative constructor has neither an explic
it super constructor |
| 4799 * invocation nor a redirecting constructor invocation, that the superclass ha
s a default |
| 4800 * generative constructor. |
| 4801 * |
| 4802 * @param node the constructor declaration to evaluate |
| 4803 * @return `true` if and only if an error code is generated on the passed node |
| 4804 * @see CompileTimeErrorCode#UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT |
| 4805 * @see CompileTimeErrorCode#NON_GENERATIVE_CONSTRUCTOR |
| 4806 * @see StaticWarningCode#NO_DEFAULT_SUPER_CONSTRUCTOR_EXPLICIT |
| 4807 */ |
| 4808 bool _checkForUndefinedConstructorInInitializerImplicit(ConstructorDeclaration
node) { |
| 4809 // |
| 4810 // Ignore if the constructor is not generative. |
| 4811 // |
| 4812 if (node.factoryKeyword != null) { |
| 4813 return false; |
| 4814 } |
| 4815 // |
| 4816 // Ignore if the constructor has either an implicit super constructor invoca
tion or a |
| 4817 // redirecting constructor invocation. |
| 4818 // |
| 4819 for (ConstructorInitializer constructorInitializer in node.initializers) { |
| 4820 if (constructorInitializer is SuperConstructorInvocation || constructorIni
tializer is RedirectingConstructorInvocation) { |
| 4821 return false; |
| 4822 } |
| 4823 } |
| 4824 // |
| 4825 // Check to see whether the superclass has a non-factory unnamed constructor
. |
| 4826 // |
| 4827 if (_enclosingClass == null) { |
| 4828 return false; |
| 4829 } |
| 4830 InterfaceType superType = _enclosingClass.supertype; |
| 4831 if (superType == null) { |
| 4832 return false; |
| 4833 } |
| 4834 ClassElement superElement = superType.element; |
| 4835 ConstructorElement superUnnamedConstructor = superElement.unnamedConstructor
; |
| 4836 if (superUnnamedConstructor != null) { |
| 4837 if (superUnnamedConstructor.isFactory) { |
| 4838 _errorReporter.reportErrorForNode(CompileTimeErrorCode.NON_GENERATIVE_CO
NSTRUCTOR, node.returnType, [superUnnamedConstructor]); |
| 4839 return true; |
| 4840 } |
| 4841 if (!superUnnamedConstructor.isDefaultConstructor) { |
| 4842 int offset; |
| 4843 int length; |
| 4844 { |
| 4845 Identifier returnType = node.returnType; |
| 4846 SimpleIdentifier name = node.name; |
| 4847 offset = returnType.offset; |
| 4848 length = (name != null ? name.end : returnType.end) - offset; |
| 4849 } |
| 4850 _errorReporter.reportErrorForOffset(CompileTimeErrorCode.NO_DEFAULT_SUPE
R_CONSTRUCTOR_EXPLICIT, offset, length, [superType.displayName]); |
| 4851 } |
| 4852 return false; |
| 4853 } |
| 4854 _errorReporter.reportErrorForNode(CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR
_IN_INITIALIZER_DEFAULT, node.returnType, [superElement.name]); |
| 4855 return true; |
| 4856 } |
| 4857 |
| 4858 /** |
| 4859 * This checks that if the given name is a reference to a static member it is
defined in the |
| 4860 * enclosing class rather than in a superclass. |
| 4861 * |
| 4862 * @param name the name to be evaluated |
| 4863 * @return `true` if and only if an error code is generated on the passed node |
| 4864 * @see StaticTypeWarningCode#UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER |
| 4865 */ |
| 4866 bool _checkForUnqualifiedReferenceToNonLocalStaticMember(SimpleIdentifier name
) { |
| 4867 Element element = name.staticElement; |
| 4868 if (element == null || element is TypeParameterElement) { |
| 4869 return false; |
| 4870 } |
| 4871 Element enclosingElement = element.enclosingElement; |
| 4872 if (enclosingElement is! ClassElement) { |
| 4873 return false; |
| 4874 } |
| 4875 if ((element is MethodElement && !element.isStatic) || (element is PropertyA
ccessorElement && !element.isStatic)) { |
| 4876 return false; |
| 4877 } |
| 4878 if (identical(enclosingElement, _enclosingClass)) { |
| 4879 return false; |
| 4880 } |
| 4881 _errorReporter.reportErrorForNode(StaticTypeWarningCode.UNQUALIFIED_REFERENC
E_TO_NON_LOCAL_STATIC_MEMBER, name, [name.name]); |
| 4882 return true; |
| 4883 } |
| 4884 |
| 4885 void _checkForValidField(FieldFormalParameter node) { |
| 4886 ParameterElement element = node.element; |
| 4887 if (element is FieldFormalParameterElement) { |
| 4888 FieldElement fieldElement = element.field; |
| 4889 if (fieldElement == null || fieldElement.isSynthetic) { |
| 4890 _errorReporter.reportErrorForNode(CompileTimeErrorCode.INITIALIZING_FORM
AL_FOR_NON_EXISTENT_FIELD, node, [node.identifier.name]); |
| 4891 } else { |
| 4892 ParameterElement parameterElement = node.element; |
| 4893 if (parameterElement is FieldFormalParameterElementImpl) { |
| 4894 FieldFormalParameterElementImpl fieldFormal = parameterElement; |
| 4895 DartType declaredType = fieldFormal.type; |
| 4896 DartType fieldType = fieldElement.type; |
| 4897 if (fieldElement.isSynthetic) { |
| 4898 _errorReporter.reportErrorForNode(CompileTimeErrorCode.INITIALIZING_
FORMAL_FOR_NON_EXISTENT_FIELD, node, [node.identifier.name]); |
| 4899 } else if (fieldElement.isStatic) { |
| 4900 _errorReporter.reportErrorForNode(CompileTimeErrorCode.INITIALIZING_
FORMAL_FOR_STATIC_FIELD, node, [node.identifier.name]); |
| 4901 } else if (declaredType != null && fieldType != null && !declaredType.
isAssignableTo(fieldType)) { |
| 4902 _errorReporter.reportTypeErrorForNode(StaticWarningCode.FIELD_INITIA
LIZING_FORMAL_NOT_ASSIGNABLE, node, [declaredType, fieldType]); |
| 4903 } |
| 4904 } else { |
| 4905 if (fieldElement.isSynthetic) { |
| 4906 _errorReporter.reportErrorForNode(CompileTimeErrorCode.INITIALIZING_
FORMAL_FOR_NON_EXISTENT_FIELD, node, [node.identifier.name]); |
| 4907 } else if (fieldElement.isStatic) { |
| 4908 _errorReporter.reportErrorForNode(CompileTimeErrorCode.INITIALIZING_
FORMAL_FOR_STATIC_FIELD, node, [node.identifier.name]); |
| 4909 } |
| 4910 } |
| 4911 } |
| 4912 } |
| 4913 // else { |
| 4914 // // TODO(jwren) Report error, constructor initializer variable is a top
level element |
| 4915 // // (Either here or in ErrorVerifier#checkForAllFinalInitializedErrorCo
des) |
| 4916 // } |
| 4917 } |
| 4918 |
| 4919 /** |
| 4920 * This verifies that the given getter does not have a return type of 'void'. |
| 4921 * |
| 4922 * @param node the method declaration to evaluate |
| 4923 * @return `true` if and only if an error code is generated on the passed node |
| 4924 * @see StaticWarningCode#VOID_RETURN_FOR_GETTER |
| 4925 */ |
| 4926 bool _checkForVoidReturnType(MethodDeclaration node) { |
| 4927 TypeName returnType = node.returnType; |
| 4928 if (returnType == null || returnType.name.name != "void") { |
| 4929 return false; |
| 4930 } |
| 4931 _errorReporter.reportErrorForNode(StaticWarningCode.VOID_RETURN_FOR_GETTER,
returnType, []); |
| 4932 return true; |
| 4933 } |
| 4934 |
| 4935 /** |
| 4936 * This verifies the passed operator-method declaration, has correct number of
parameters. |
| 4937 * |
| 4938 * This method assumes that the method declaration was tested to be an operato
r declaration before |
| 4939 * being called. |
| 4940 * |
| 4941 * @param node the method declaration to evaluate |
| 4942 * @return `true` if and only if an error code is generated on the passed node |
| 4943 * @see CompileTimeErrorCode#WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR |
| 4944 */ |
| 4945 bool _checkForWrongNumberOfParametersForOperator(MethodDeclaration node) { |
| 4946 // prepare number of parameters |
| 4947 FormalParameterList parameterList = node.parameters; |
| 4948 if (parameterList == null) { |
| 4949 return false; |
| 4950 } |
| 4951 int numParameters = parameterList.parameters.length; |
| 4952 // prepare operator name |
| 4953 SimpleIdentifier nameNode = node.name; |
| 4954 if (nameNode == null) { |
| 4955 return false; |
| 4956 } |
| 4957 String name = nameNode.name; |
| 4958 // check for exact number of parameters |
| 4959 int expected = -1; |
| 4960 if ("[]=" == name) { |
| 4961 expected = 2; |
| 4962 } else if ("<" == name || ">" == name || "<=" == name || ">=" == name || "==
" == name || "+" == name || "/" == name || "~/" == name || "*" == name || "%" ==
name || "|" == name || "^" == name || "&" == name || "<<" == name || ">>" == na
me || "[]" == name) { |
| 4963 expected = 1; |
| 4964 } else if ("~" == name) { |
| 4965 expected = 0; |
| 4966 } |
| 4967 if (expected != -1 && numParameters != expected) { |
| 4968 _errorReporter.reportErrorForNode(CompileTimeErrorCode.WRONG_NUMBER_OF_PAR
AMETERS_FOR_OPERATOR, nameNode, [name, expected, numParameters]); |
| 4969 return true; |
| 4970 } |
| 4971 // check for operator "-" |
| 4972 if ("-" == name && numParameters > 1) { |
| 4973 _errorReporter.reportErrorForNode(CompileTimeErrorCode.WRONG_NUMBER_OF_PAR
AMETERS_FOR_OPERATOR_MINUS, nameNode, [numParameters]); |
| 4974 return true; |
| 4975 } |
| 4976 // OK |
| 4977 return false; |
| 4978 } |
| 4979 |
| 4980 /** |
| 4981 * This verifies if the passed setter parameter list have only one required pa
rameter. |
| 4982 * |
| 4983 * This method assumes that the method declaration was tested to be a setter b
efore being called. |
| 4984 * |
| 4985 * @param setterName the name of the setter to report problems on |
| 4986 * @param parameterList the parameter list to evaluate |
| 4987 * @return `true` if and only if an error code is generated on the passed node |
| 4988 * @see CompileTimeErrorCode#WRONG_NUMBER_OF_PARAMETERS_FOR_SETTER |
| 4989 */ |
| 4990 bool _checkForWrongNumberOfParametersForSetter(SimpleIdentifier setterName, Fo
rmalParameterList parameterList) { |
| 4991 if (setterName == null) { |
| 4992 return false; |
| 4993 } |
| 4994 if (parameterList == null) { |
| 4995 return false; |
| 4996 } |
| 4997 NodeList<FormalParameter> parameters = parameterList.parameters; |
| 4998 if (parameters.length != 1 || parameters[0].kind != ParameterKind.REQUIRED)
{ |
| 4999 _errorReporter.reportErrorForNode(CompileTimeErrorCode.WRONG_NUMBER_OF_PAR
AMETERS_FOR_SETTER, setterName, []); |
| 5000 return true; |
| 5001 } |
| 5002 return false; |
| 5003 } |
| 5004 |
| 5005 /** |
| 5006 * This verifies that if the given class declaration implements the class Func
tion that it has a |
| 5007 * concrete implementation of the call method. |
| 5008 * |
| 5009 * @return `true` if and only if an error code is generated on the passed node |
| 5010 * @see StaticWarningCode#FUNCTION_WITHOUT_CALL |
| 5011 */ |
| 5012 bool _checkImplementsFunctionWithoutCall(ClassDeclaration node) { |
| 5013 if (node.isAbstract) { |
| 5014 return false; |
| 5015 } |
| 5016 ClassElement classElement = node.element; |
| 5017 if (classElement == null) { |
| 5018 return false; |
| 5019 } |
| 5020 if (!classElement.type.isSubtypeOf(_typeProvider.functionType)) { |
| 5021 return false; |
| 5022 } |
| 5023 // If there is a noSuchMethod method, then don't report the warning, see dar
tbug.com/16078 |
| 5024 if (classElement.getMethod(FunctionElement.NO_SUCH_METHOD_METHOD_NAME) != nu
ll) { |
| 5025 return false; |
| 5026 } |
| 5027 ExecutableElement callMethod = _inheritanceManager.lookupMember(classElement
, "call"); |
| 5028 if (callMethod == null || callMethod is! MethodElement || (callMethod as Met
hodElement).isAbstract) { |
| 5029 _errorReporter.reportErrorForNode(StaticWarningCode.FUNCTION_WITHOUT_CALL,
node.name, []); |
| 5030 return true; |
| 5031 } |
| 5032 return false; |
| 5033 } |
| 5034 |
| 5035 /** |
| 5036 * This verifies that the given class declaration does not have the same class
in the 'extends' |
| 5037 * and 'implements' clauses. |
| 5038 * |
| 5039 * @return `true` if and only if an error code is generated on the passed node |
| 5040 * @see CompileTimeErrorCode#IMPLEMENTS_SUPER_CLASS |
| 5041 */ |
| 5042 bool _checkImplementsSuperClass(ClassDeclaration node) { |
| 5043 // prepare super type |
| 5044 InterfaceType superType = _enclosingClass.supertype; |
| 5045 if (superType == null) { |
| 5046 return false; |
| 5047 } |
| 5048 // prepare interfaces |
| 5049 ImplementsClause implementsClause = node.implementsClause; |
| 5050 if (implementsClause == null) { |
| 5051 return false; |
| 5052 } |
| 5053 // check interfaces |
| 5054 bool hasProblem = false; |
| 5055 for (TypeName interfaceNode in implementsClause.interfaces) { |
| 5056 if (interfaceNode.type == superType) { |
| 5057 hasProblem = true; |
| 5058 _errorReporter.reportErrorForNode(CompileTimeErrorCode.IMPLEMENTS_SUPER_
CLASS, interfaceNode, [superType.displayName]); |
| 5059 } |
| 5060 } |
| 5061 // done |
| 5062 return hasProblem; |
| 5063 } |
| 5064 |
| 5065 /** |
| 5066 * Return the error code that should be used when the given class references i
tself directly. |
| 5067 * |
| 5068 * @param classElt the class that references itself |
| 5069 * @return the error code that should be used |
| 5070 */ |
| 5071 ErrorCode _getBaseCaseErrorCode(ClassElement classElt) { |
| 5072 InterfaceType supertype = classElt.supertype; |
| 5073 if (supertype != null && _enclosingClass == supertype.element) { |
| 5074 return CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_EXTE
NDS; |
| 5075 } |
| 5076 List<InterfaceType> mixins = classElt.mixins; |
| 5077 for (int i = 0; i < mixins.length; i++) { |
| 5078 if (_enclosingClass == mixins[i].element) { |
| 5079 return CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_WI
TH; |
| 5080 } |
| 5081 } |
| 5082 return CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_IMPLEM
ENTS; |
| 5083 } |
| 5084 |
| 5085 /** |
| 5086 * Given an expression in a switch case whose value is expected to be an enum
constant, return the |
| 5087 * name of the constant. |
| 5088 * |
| 5089 * @param expression the expression from the switch case |
| 5090 * @return the name of the constant referenced by the expression |
| 5091 */ |
| 5092 String _getConstantName(Expression expression) { |
| 5093 // TODO(brianwilkerson) Convert this to return the element representing the
constant. |
| 5094 if (expression is SimpleIdentifier) { |
| 5095 return expression.name; |
| 5096 } else if (expression is PrefixedIdentifier) { |
| 5097 return expression.identifier.name; |
| 5098 } else if (expression is PropertyAccess) { |
| 5099 return expression.propertyName.name; |
| 5100 } |
| 5101 return null; |
| 5102 } |
| 5103 |
| 5104 /** |
| 5105 * Returns the Type (return type) for a given getter. |
| 5106 * |
| 5107 * @param propertyAccessorElement |
| 5108 * @return The type of the given getter. |
| 5109 */ |
| 5110 DartType _getGetterType(PropertyAccessorElement propertyAccessorElement) { |
| 5111 FunctionType functionType = propertyAccessorElement.type; |
| 5112 if (functionType != null) { |
| 5113 return functionType.returnType; |
| 5114 } else { |
| 5115 return null; |
| 5116 } |
| 5117 } |
| 5118 |
| 5119 /** |
| 5120 * Returns the Type (first and only parameter) for a given setter. |
| 5121 * |
| 5122 * @param propertyAccessorElement |
| 5123 * @return The type of the given setter. |
| 5124 */ |
| 5125 DartType _getSetterType(PropertyAccessorElement propertyAccessorElement) { |
| 5126 // Get the parameters for MethodDeclaration or FunctionDeclaration |
| 5127 List<ParameterElement> setterParameters = propertyAccessorElement.parameters
; |
| 5128 // If there are no setter parameters, return no type. |
| 5129 if (setterParameters.length == 0) { |
| 5130 return null; |
| 5131 } |
| 5132 return setterParameters[0].type; |
| 5133 } |
| 5134 |
| 5135 /** |
| 5136 * Given a list of directives that have the same prefix, generate an error if
there is more than |
| 5137 * one import and any of those imports is deferred. |
| 5138 * |
| 5139 * @param directives the list of directives that have the same prefix |
| 5140 * @return `true` if an error was generated |
| 5141 * @see CompileTimeErrorCode#SHARED_DEFERRED_PREFIX |
| 5142 */ |
| 5143 bool _hasDeferredPrefixCollision(List<ImportDirective> directives) { |
| 5144 bool foundError = false; |
| 5145 int count = directives.length; |
| 5146 if (count > 1) { |
| 5147 for (int i = 0; i < count; i++) { |
| 5148 sc.Token deferredToken = directives[i].deferredToken; |
| 5149 if (deferredToken != null) { |
| 5150 _errorReporter.reportErrorForToken(CompileTimeErrorCode.SHARED_DEFERRE
D_PREFIX, deferredToken, []); |
| 5151 foundError = true; |
| 5152 } |
| 5153 } |
| 5154 } |
| 5155 return foundError; |
| 5156 } |
| 5157 |
| 5158 /** |
| 5159 * @return `true` if the given constructor redirects to itself, directly or in
directly |
| 5160 */ |
| 5161 bool _hasRedirectingFactoryConstructorCycle(ConstructorElement element) { |
| 5162 Set<ConstructorElement> constructors = new HashSet<ConstructorElement>(); |
| 5163 ConstructorElement current = element; |
| 5164 while (current != null) { |
| 5165 if (constructors.contains(current)) { |
| 5166 return identical(current, element); |
| 5167 } |
| 5168 constructors.add(current); |
| 5169 current = current.redirectedConstructor; |
| 5170 if (current is ConstructorMember) { |
| 5171 current = (current as ConstructorMember).baseElement; |
| 5172 } |
| 5173 } |
| 5174 return false; |
| 5175 } |
| 5176 |
| 5177 /** |
| 5178 * @return <code>true</code> if given [Element] has direct or indirect referen
ce to itself |
| 5179 * from anywhere except [ClassElement] or type parameter bounds. |
| 5180 */ |
| 5181 bool _hasTypedefSelfReference(Element target) { |
| 5182 Set<Element> checked = new HashSet<Element>(); |
| 5183 List<Element> toCheck = new List<Element>(); |
| 5184 toCheck.add(target); |
| 5185 bool firstIteration = true; |
| 5186 while (true) { |
| 5187 Element current; |
| 5188 // get next element |
| 5189 while (true) { |
| 5190 // may be no more elements to check |
| 5191 if (toCheck.isEmpty) { |
| 5192 return false; |
| 5193 } |
| 5194 // try to get next element |
| 5195 current = toCheck.removeAt(toCheck.length - 1); |
| 5196 if (target == current) { |
| 5197 if (firstIteration) { |
| 5198 firstIteration = false; |
| 5199 break; |
| 5200 } else { |
| 5201 return true; |
| 5202 } |
| 5203 } |
| 5204 if (current != null && !checked.contains(current)) { |
| 5205 break; |
| 5206 } |
| 5207 } |
| 5208 // check current element |
| 5209 current.accept(new GeneralizingElementVisitor_ErrorVerifier_hasTypedefSelf
Reference(target, toCheck)); |
| 5210 checked.add(current); |
| 5211 } |
| 5212 } |
| 5213 |
| 5214 bool _isFunctionType(DartType type) { |
| 5215 if (type.isDynamic || type.isBottom) { |
| 5216 return true; |
| 5217 } else if (type is FunctionType || type.isDartCoreFunction) { |
| 5218 return true; |
| 5219 } else if (type is InterfaceType) { |
| 5220 MethodElement callMethod = type.lookUpMethod(FunctionElement.CALL_METHOD_N
AME, _currentLibrary); |
| 5221 return callMethod != null; |
| 5222 } |
| 5223 return false; |
| 5224 } |
| 5225 |
| 5226 /** |
| 5227 * Return `true` if the given type represents the class `Future` from the |
| 5228 * `dart:async` library. |
| 5229 * |
| 5230 * @param type the type to be tested |
| 5231 * @return `true` if the given type represents the class `Future` from the |
| 5232 * `dart:async` library |
| 5233 */ |
| 5234 bool _isFuture(DartType type) { |
| 5235 if (type is InterfaceType) { |
| 5236 InterfaceType interfaceType = type; |
| 5237 if (interfaceType.name == "Future") { |
| 5238 ClassElement element = interfaceType.element; |
| 5239 if (element != null) { |
| 5240 LibraryElement library = element.library; |
| 5241 if (library.name == "dart.async") { |
| 5242 return true; |
| 5243 } |
| 5244 } |
| 5245 } |
| 5246 } |
| 5247 return false; |
| 5248 } |
| 5249 |
| 5250 /** |
| 5251 * Return `true` iff the passed [ClassElement] has a method, getter or setter
that |
| 5252 * matches the name of the passed [ExecutableElement] in either the class itse
lf, or one of |
| 5253 * its' mixins that is concrete. |
| 5254 * |
| 5255 * By "match", only the name of the member is tested to match, it does not hav
e to equal or be a |
| 5256 * subtype of the passed executable element, this is due to the specific use w
here this method is |
| 5257 * used in [checkForNonAbstractClassInheritsAbstractMember]. |
| 5258 * |
| 5259 * @param executableElt the executable to search for in the passed class eleme
nt |
| 5260 * @param classElt the class method to search through the members of |
| 5261 * @return `true` iff the passed member is found in the passed class element |
| 5262 */ |
| 5263 bool _isMemberInClassOrMixin(ExecutableElement executableElt, ClassElement cla
ssElt) { |
| 5264 ExecutableElement foundElt = null; |
| 5265 String executableName = executableElt.name; |
| 5266 if (executableElt is MethodElement) { |
| 5267 foundElt = classElt.getMethod(executableName); |
| 5268 if (foundElt != null && !(foundElt as MethodElement).isAbstract) { |
| 5269 return true; |
| 5270 } |
| 5271 List<InterfaceType> mixins = classElt.mixins; |
| 5272 for (int i = 0; i < mixins.length && foundElt == null; i++) { |
| 5273 foundElt = mixins[i].getMethod(executableName); |
| 5274 } |
| 5275 if (foundElt != null && !(foundElt as MethodElement).isAbstract) { |
| 5276 return true; |
| 5277 } |
| 5278 } else if (executableElt is PropertyAccessorElement) { |
| 5279 PropertyAccessorElement propertyAccessorElement = executableElt; |
| 5280 if (propertyAccessorElement.isGetter) { |
| 5281 foundElt = classElt.getGetter(executableName); |
| 5282 } |
| 5283 if (foundElt == null && propertyAccessorElement.isSetter) { |
| 5284 foundElt = classElt.getSetter(executableName); |
| 5285 } |
| 5286 if (foundElt != null && !(foundElt as PropertyAccessorElement).isAbstract)
{ |
| 5287 return true; |
| 5288 } |
| 5289 List<InterfaceType> mixins = classElt.mixins; |
| 5290 for (int i = 0; i < mixins.length && foundElt == null; i++) { |
| 5291 foundElt = mixins[i].getGetter(executableName); |
| 5292 if (foundElt == null) { |
| 5293 foundElt = mixins[i].getSetter(executableName); |
| 5294 } |
| 5295 } |
| 5296 if (foundElt != null && !(foundElt as PropertyAccessorElement).isAbstract)
{ |
| 5297 return true; |
| 5298 } |
| 5299 } |
| 5300 return false; |
| 5301 } |
| 5302 |
| 5303 /** |
| 5304 * @param node the 'this' expression to analyze |
| 5305 * @return `true` if the given 'this' expression is in the valid context |
| 5306 */ |
| 5307 bool _isThisInValidContext(ThisExpression node) { |
| 5308 for (AstNode n = node; n != null; n = n.parent) { |
| 5309 if (n is CompilationUnit) { |
| 5310 return false; |
| 5311 } |
| 5312 if (n is ConstructorDeclaration) { |
| 5313 ConstructorDeclaration constructor = n as ConstructorDeclaration; |
| 5314 return constructor.factoryKeyword == null; |
| 5315 } |
| 5316 if (n is ConstructorInitializer) { |
| 5317 return false; |
| 5318 } |
| 5319 if (n is MethodDeclaration) { |
| 5320 MethodDeclaration method = n as MethodDeclaration; |
| 5321 return !method.isStatic; |
| 5322 } |
| 5323 } |
| 5324 return false; |
| 5325 } |
| 5326 |
| 5327 /** |
| 5328 * Return `true` if the given identifier is in a location where it is allowed
to resolve to |
| 5329 * a static member of a supertype. |
| 5330 * |
| 5331 * @param node the node being tested |
| 5332 * @return `true` if the given identifier is in a location where it is allowed
to resolve to |
| 5333 * a static member of a supertype |
| 5334 */ |
| 5335 bool _isUnqualifiedReferenceToNonLocalStaticMemberAllowed(SimpleIdentifier nod
e) { |
| 5336 if (node.inDeclarationContext()) { |
| 5337 return true; |
| 5338 } |
| 5339 AstNode parent = node.parent; |
| 5340 if (parent is ConstructorName || parent is MethodInvocation || parent is Pro
pertyAccess || parent is SuperConstructorInvocation) { |
| 5341 return true; |
| 5342 } |
| 5343 if (parent is PrefixedIdentifier && identical(parent.identifier, node)) { |
| 5344 return true; |
| 5345 } |
| 5346 if (parent is Annotation && identical(parent.constructorName, node)) { |
| 5347 return true; |
| 5348 } |
| 5349 if (parent is CommentReference) { |
| 5350 CommentReference commentReference = parent; |
| 5351 if (commentReference.newKeyword != null) { |
| 5352 return true; |
| 5353 } |
| 5354 } |
| 5355 return false; |
| 5356 } |
| 5357 |
| 5358 bool _isUserDefinedObject(EvaluationResultImpl result) => result == null || (r
esult.value != null && result.value.isUserDefinedObject); |
| 5359 |
| 5360 /** |
| 5361 * This checks the class declaration is not a superinterface to itself. |
| 5362 * |
| 5363 * @param classElt the class element to test |
| 5364 * @param path a list containing the potentially cyclic implements path |
| 5365 * @return `true` if and only if an error code is generated on the passed elem
ent |
| 5366 * @see CompileTimeErrorCode#RECURSIVE_INTERFACE_INHERITANCE |
| 5367 * @see CompileTimeErrorCode#RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_EXTENDS |
| 5368 * @see CompileTimeErrorCode#RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_IMPLEME
NTS |
| 5369 * @see CompileTimeErrorCode#RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_WITH |
| 5370 */ |
| 5371 bool _safeCheckForRecursiveInterfaceInheritance(ClassElement classElt, List<Cl
assElement> path) { |
| 5372 // Detect error condition. |
| 5373 int size = path.length; |
| 5374 // If this is not the base case (size > 0), and the enclosing class is the p
assed class |
| 5375 // element then an error an error. |
| 5376 if (size > 0 && _enclosingClass == classElt) { |
| 5377 String enclosingClassName = _enclosingClass.displayName; |
| 5378 if (size > 1) { |
| 5379 // Construct a string showing the cyclic implements path: "A, B, C, D, A
" |
| 5380 String separator = ", "; |
| 5381 StringBuffer buffer = new StringBuffer(); |
| 5382 for (int i = 0; i < size; i++) { |
| 5383 buffer.write(path[i].displayName); |
| 5384 buffer.write(separator); |
| 5385 } |
| 5386 buffer.write(classElt.displayName); |
| 5387 _errorReporter.reportErrorForOffset(CompileTimeErrorCode.RECURSIVE_INTER
FACE_INHERITANCE, _enclosingClass.nameOffset, enclosingClassName.length, [enclos
ingClassName, buffer.toString()]); |
| 5388 return true; |
| 5389 } else { |
| 5390 // RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_EXTENDS or |
| 5391 // RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_IMPLEMENTS or |
| 5392 // RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_WITH |
| 5393 _errorReporter.reportErrorForOffset(_getBaseCaseErrorCode(classElt), _en
closingClass.nameOffset, enclosingClassName.length, [enclosingClassName]); |
| 5394 return true; |
| 5395 } |
| 5396 } |
| 5397 if (path.indexOf(classElt) > 0) { |
| 5398 return false; |
| 5399 } |
| 5400 path.add(classElt); |
| 5401 // n-case |
| 5402 InterfaceType supertype = classElt.supertype; |
| 5403 if (supertype != null && _safeCheckForRecursiveInterfaceInheritance(supertyp
e.element, path)) { |
| 5404 return true; |
| 5405 } |
| 5406 List<InterfaceType> interfaceTypes = classElt.interfaces; |
| 5407 for (InterfaceType interfaceType in interfaceTypes) { |
| 5408 if (_safeCheckForRecursiveInterfaceInheritance(interfaceType.element, path
)) { |
| 5409 return true; |
| 5410 } |
| 5411 } |
| 5412 List<InterfaceType> mixinTypes = classElt.mixins; |
| 5413 for (InterfaceType mixinType in mixinTypes) { |
| 5414 if (_safeCheckForRecursiveInterfaceInheritance(mixinType.element, path)) { |
| 5415 return true; |
| 5416 } |
| 5417 } |
| 5418 path.removeAt(path.length - 1); |
| 5419 return false; |
| 5420 } |
| 5421 } |
| 5422 |
| 5423 class GeneralizingElementVisitor_ErrorVerifier_hasTypedefSelfReference extends G
eneralizingElementVisitor<Object> { |
| 5424 Element target; |
| 5425 |
| 5426 List<Element> toCheck; |
| 5427 |
| 5428 GeneralizingElementVisitor_ErrorVerifier_hasTypedefSelfReference(this.target,
this.toCheck) : super(); |
| 5429 |
| 5430 bool _inClass = false; |
| 5431 |
| 5432 @override |
| 5433 Object visitClassElement(ClassElement element) { |
| 5434 _addTypeToCheck(element.supertype); |
| 5435 for (InterfaceType mixin in element.mixins) { |
| 5436 _addTypeToCheck(mixin); |
| 5437 } |
| 5438 _inClass = !element.isTypedef; |
| 5439 try { |
| 5440 return super.visitClassElement(element); |
| 5441 } finally { |
| 5442 _inClass = false; |
| 5443 } |
| 5444 } |
| 5445 |
| 5446 @override |
| 5447 Object visitExecutableElement(ExecutableElement element) { |
| 5448 if (element.isSynthetic) { |
| 5449 return null; |
| 5450 } |
| 5451 _addTypeToCheck(element.returnType); |
| 5452 return super.visitExecutableElement(element); |
| 5453 } |
| 5454 |
| 5455 @override |
| 5456 Object visitFunctionTypeAliasElement(FunctionTypeAliasElement element) { |
| 5457 _addTypeToCheck(element.returnType); |
| 5458 return super.visitFunctionTypeAliasElement(element); |
| 5459 } |
| 5460 |
| 5461 @override |
| 5462 Object visitParameterElement(ParameterElement element) { |
| 5463 _addTypeToCheck(element.type); |
| 5464 return super.visitParameterElement(element); |
| 5465 } |
| 5466 |
| 5467 @override |
| 5468 Object visitTypeParameterElement(TypeParameterElement element) { |
| 5469 _addTypeToCheck(element.bound); |
| 5470 return super.visitTypeParameterElement(element); |
| 5471 } |
| 5472 |
| 5473 @override |
| 5474 Object visitVariableElement(VariableElement element) { |
| 5475 _addTypeToCheck(element.type); |
| 5476 return super.visitVariableElement(element); |
| 5477 } |
| 5478 |
| 5479 void _addTypeToCheck(DartType type) { |
| 5480 if (type == null) { |
| 5481 return; |
| 5482 } |
| 5483 Element element = type.element; |
| 5484 // it is OK to reference target from class |
| 5485 if (_inClass && target == element) { |
| 5486 return; |
| 5487 } |
| 5488 // schedule for checking |
| 5489 toCheck.add(element); |
| 5490 // type arguments |
| 5491 if (type is InterfaceType) { |
| 5492 InterfaceType interfaceType = type; |
| 5493 for (DartType typeArgument in interfaceType.typeArguments) { |
| 5494 _addTypeToCheck(typeArgument); |
| 5495 } |
| 5496 } |
| 5497 } |
| 5498 } |
| OLD | NEW |