| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 // TODO(jmesserly): this was ported from package:dev_compiler, and needs to be | 5 // TODO(jmesserly): this was ported from package:dev_compiler, and needs to be |
| 6 // refactored to fit into analyzer. | 6 // refactored to fit into analyzer. |
| 7 library analyzer.src.task.strong.checker; | 7 library analyzer.src.task.strong.checker; |
| 8 | 8 |
| 9 import 'package:analyzer/analyzer.dart'; | 9 import 'package:analyzer/analyzer.dart'; |
| 10 import 'package:analyzer/dart/ast/ast.dart'; | 10 import 'package:analyzer/dart/ast/ast.dart'; |
| 11 import 'package:analyzer/dart/ast/token.dart' show TokenType; | 11 import 'package:analyzer/dart/ast/token.dart' show TokenType; |
| 12 import 'package:analyzer/dart/ast/visitor.dart'; | 12 import 'package:analyzer/dart/ast/visitor.dart'; |
| 13 import 'package:analyzer/dart/element/element.dart'; | 13 import 'package:analyzer/dart/element/element.dart'; |
| 14 import 'package:analyzer/dart/element/type.dart'; | 14 import 'package:analyzer/dart/element/type.dart'; |
| 15 import 'package:analyzer/src/dart/element/type.dart'; | 15 import 'package:analyzer/src/dart/element/type.dart'; |
| 16 import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl; | 16 import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl; |
| 17 import 'package:analyzer/src/generated/error.dart' show StrongModeCode; |
| 17 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; | 18 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; |
| 18 import 'package:analyzer/src/generated/type_system.dart'; | 19 import 'package:analyzer/src/generated/type_system.dart'; |
| 19 | 20 |
| 20 import 'info.dart'; | 21 import 'ast_properties.dart'; |
| 21 | 22 |
| 22 DartType _elementType(Element e) { | 23 DartType _elementType(Element e) { |
| 23 if (e == null) { | 24 if (e == null) { |
| 24 // Malformed code - just return dynamic. | 25 // Malformed code - just return dynamic. |
| 25 return DynamicTypeImpl.instance; | 26 return DynamicTypeImpl.instance; |
| 26 } | 27 } |
| 27 return (e as dynamic).type; | 28 return (e as dynamic).type; |
| 28 } | 29 } |
| 29 | 30 |
| 30 PropertyInducingElement _getMemberField( | 31 PropertyInducingElement _getMemberField( |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 94 return f; | 95 return f; |
| 95 } | 96 } |
| 96 | 97 |
| 97 typedef FunctionType _MemberTypeGetter(InterfaceType type); | 98 typedef FunctionType _MemberTypeGetter(InterfaceType type); |
| 98 | 99 |
| 99 /// Checks the body of functions and properties. | 100 /// Checks the body of functions and properties. |
| 100 class CodeChecker extends RecursiveAstVisitor { | 101 class CodeChecker extends RecursiveAstVisitor { |
| 101 final StrongTypeSystemImpl rules; | 102 final StrongTypeSystemImpl rules; |
| 102 final TypeProvider typeProvider; | 103 final TypeProvider typeProvider; |
| 103 final AnalysisErrorListener reporter; | 104 final AnalysisErrorListener reporter; |
| 104 final _OverrideChecker _overrideChecker; | |
| 105 final AnalysisOptionsImpl _options; | 105 final AnalysisOptionsImpl _options; |
| 106 _OverrideChecker _overrideChecker; |
| 106 | 107 |
| 107 bool _failure = false; | 108 bool _failure = false; |
| 108 | 109 |
| 109 CodeChecker(TypeProvider typeProvider, StrongTypeSystemImpl rules, | 110 CodeChecker(TypeProvider typeProvider, StrongTypeSystemImpl rules, |
| 110 AnalysisErrorListener reporter, this._options) | 111 AnalysisErrorListener reporter, this._options) |
| 111 : typeProvider = typeProvider, | 112 : typeProvider = typeProvider, |
| 112 rules = rules, | 113 rules = rules, |
| 113 reporter = reporter, | 114 reporter = reporter { |
| 114 _overrideChecker = new _OverrideChecker(typeProvider, rules, reporter); | 115 _overrideChecker = new _OverrideChecker(this); |
| 116 } |
| 115 | 117 |
| 116 bool get failure => _failure || _overrideChecker._failure; | 118 bool get failure => _failure; |
| 117 | 119 |
| 118 void checkArgument(Expression arg, DartType expectedType) { | 120 void checkArgument(Expression arg, DartType expectedType) { |
| 119 // Preserve named argument structure, so their immediate parent is the | 121 // Preserve named argument structure, so their immediate parent is the |
| 120 // method invocation. | 122 // method invocation. |
| 121 Expression baseExpression = arg is NamedExpression ? arg.expression : arg; | 123 Expression baseExpression = arg is NamedExpression ? arg.expression : arg; |
| 122 checkAssignment(baseExpression, expectedType); | 124 checkAssignment(baseExpression, expectedType); |
| 123 } | 125 } |
| 124 | 126 |
| 125 void checkArgumentList(ArgumentList node, FunctionType type) { | 127 void checkArgumentList(ArgumentList node, FunctionType type) { |
| 126 NodeList<Expression> list = node.arguments; | 128 NodeList<Expression> list = node.arguments; |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 163 checkArgumentList(list, _getTypeAsCaller(f)); | 165 checkArgumentList(list, _getTypeAsCaller(f)); |
| 164 } | 166 } |
| 165 } | 167 } |
| 166 | 168 |
| 167 DartType getType(TypeName name) { | 169 DartType getType(TypeName name) { |
| 168 return (name == null) ? DynamicTypeImpl.instance : name.type; | 170 return (name == null) ? DynamicTypeImpl.instance : name.type; |
| 169 } | 171 } |
| 170 | 172 |
| 171 void reset() { | 173 void reset() { |
| 172 _failure = false; | 174 _failure = false; |
| 173 _overrideChecker._failure = false; | |
| 174 } | 175 } |
| 175 | 176 |
| 176 @override | 177 @override |
| 177 void visitAsExpression(AsExpression node) { | 178 void visitAsExpression(AsExpression node) { |
| 178 // We could do the same check as the IsExpression below, but that is | 179 // We could do the same check as the IsExpression below, but that is |
| 179 // potentially too conservative. Instead, at runtime, we must fail hard | 180 // potentially too conservative. Instead, at runtime, we must fail hard |
| 180 // if the Dart as and the DDC as would return different values. | 181 // if the Dart as and the DDC as would return different values. |
| 181 node.visitChildren(this); | 182 node.visitChildren(this); |
| 182 } | 183 } |
| 183 | 184 |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 257 | 258 |
| 258 /// Check constructor declaration to ensure correct super call placement. | 259 /// Check constructor declaration to ensure correct super call placement. |
| 259 @override | 260 @override |
| 260 void visitConstructorDeclaration(ConstructorDeclaration node) { | 261 void visitConstructorDeclaration(ConstructorDeclaration node) { |
| 261 node.visitChildren(this); | 262 node.visitChildren(this); |
| 262 | 263 |
| 263 final init = node.initializers; | 264 final init = node.initializers; |
| 264 for (int i = 0, last = init.length - 1; i < last; i++) { | 265 for (int i = 0, last = init.length - 1; i < last; i++) { |
| 265 final node = init[i]; | 266 final node = init[i]; |
| 266 if (node is SuperConstructorInvocation) { | 267 if (node is SuperConstructorInvocation) { |
| 267 _recordMessage(new InvalidSuperInvocation(node)); | 268 _recordMessage(node, StrongModeCode.INVALID_SUPER_INVOCATION, [node]); |
| 268 } | 269 } |
| 269 } | 270 } |
| 270 } | 271 } |
| 271 | 272 |
| 272 // Check invocations | 273 // Check invocations |
| 273 @override | 274 @override |
| 274 void visitConstructorFieldInitializer(ConstructorFieldInitializer node) { | 275 void visitConstructorFieldInitializer(ConstructorFieldInitializer node) { |
| 275 var field = node.fieldName; | 276 var field = node.fieldName; |
| 276 var element = field.staticElement; | 277 var element = field.staticElement; |
| 277 DartType staticType = _elementType(element); | 278 DartType staticType = _elementType(element); |
| (...skipping 30 matching lines...) Expand all Loading... |
| 308 @override | 309 @override |
| 309 void visitFieldFormalParameter(FieldFormalParameter node) { | 310 void visitFieldFormalParameter(FieldFormalParameter node) { |
| 310 var element = node.element; | 311 var element = node.element; |
| 311 var typeName = node.type; | 312 var typeName = node.type; |
| 312 if (typeName != null) { | 313 if (typeName != null) { |
| 313 var type = _elementType(element); | 314 var type = _elementType(element); |
| 314 var fieldElement = | 315 var fieldElement = |
| 315 node.identifier.staticElement as FieldFormalParameterElement; | 316 node.identifier.staticElement as FieldFormalParameterElement; |
| 316 var fieldType = _elementType(fieldElement.field); | 317 var fieldType = _elementType(fieldElement.field); |
| 317 if (!rules.isSubtypeOf(type, fieldType)) { | 318 if (!rules.isSubtypeOf(type, fieldType)) { |
| 318 var staticInfo = | 319 _recordMessage(node, StrongModeCode.INVALID_PARAMETER_DECLARATION, |
| 319 new InvalidParameterDeclaration(rules, node, fieldType); | 320 [node, fieldType]); |
| 320 _recordMessage(staticInfo); | |
| 321 } | 321 } |
| 322 } | 322 } |
| 323 node.visitChildren(this); | 323 node.visitChildren(this); |
| 324 } | 324 } |
| 325 | 325 |
| 326 @override | 326 @override |
| 327 void visitForEachStatement(ForEachStatement node) { | 327 void visitForEachStatement(ForEachStatement node) { |
| 328 var loopVariable = node.identifier ?? node.loopVariable?.identifier; | 328 var loopVariable = node.identifier ?? node.loopVariable?.identifier; |
| 329 | 329 |
| 330 // Safely handle malformed statements. | 330 // Safely handle malformed statements. |
| 331 if (loopVariable != null) { | 331 if (loopVariable != null) { |
| 332 // Find the element type of the sequence. | 332 // Find the element type of the sequence. |
| 333 var sequenceInterface = node.awaitKeyword != null | 333 var sequenceInterface = node.awaitKeyword != null |
| 334 ? typeProvider.streamType | 334 ? typeProvider.streamType |
| 335 : typeProvider.iterableType; | 335 : typeProvider.iterableType; |
| 336 var iterableType = _getStaticType(node.iterable); | 336 var iterableType = _getStaticType(node.iterable); |
| 337 var elementType = | 337 var elementType = |
| 338 rules.mostSpecificTypeArgument(iterableType, sequenceInterface); | 338 rules.mostSpecificTypeArgument(iterableType, sequenceInterface); |
| 339 | 339 |
| 340 // If the sequence is not an Iterable (or Stream for await for) but is a | 340 // If the sequence is not an Iterable (or Stream for await for) but is a |
| 341 // supertype of it, do an implicit downcast to Iterable<dynamic>. Then | 341 // supertype of it, do an implicit downcast to Iterable<dynamic>. Then |
| 342 // we'll do a separate cast of the dynamic element to the variable's type. | 342 // we'll do a separate cast of the dynamic element to the variable's type. |
| 343 if (elementType == null) { | 343 if (elementType == null) { |
| 344 var sequenceType = | 344 var sequenceType = |
| 345 sequenceInterface.instantiate([DynamicTypeImpl.instance]); | 345 sequenceInterface.instantiate([DynamicTypeImpl.instance]); |
| 346 | 346 |
| 347 if (rules.isSubtypeOf(sequenceType, iterableType)) { | 347 if (rules.isSubtypeOf(sequenceType, iterableType)) { |
| 348 _recordMessage(DownCast.create( | 348 _recordImplicitCast(node.iterable, iterableType, sequenceType); |
| 349 rules, node.iterable, iterableType, sequenceType, _options)); | |
| 350 elementType = DynamicTypeImpl.instance; | 349 elementType = DynamicTypeImpl.instance; |
| 351 } | 350 } |
| 352 } | 351 } |
| 353 | 352 |
| 354 // If the sequence doesn't implement the interface at all, [ErrorVerifier] | 353 // If the sequence doesn't implement the interface at all, [ErrorVerifier] |
| 355 // will report the error, so ignore it here. | 354 // will report the error, so ignore it here. |
| 356 if (elementType != null) { | 355 if (elementType != null) { |
| 357 // Insert a cast from the sequence's element type to the loop variable's | 356 // Insert a cast from the sequence's element type to the loop variable's |
| 358 // if needed. | 357 // if needed. |
| 359 _checkDowncast(loopVariable, _getStaticType(loopVariable), | 358 _checkDowncast(loopVariable, _getStaticType(loopVariable), |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 493 // dynamic d; | 492 // dynamic d; |
| 494 // d.someMethod(...); // the whole method call must be a dynamic send. | 493 // d.someMethod(...); // the whole method call must be a dynamic send. |
| 495 // | 494 // |
| 496 // ... from case like: | 495 // ... from case like: |
| 497 // | 496 // |
| 498 // SomeType s; | 497 // SomeType s; |
| 499 // s.someDynamicField(...); // static get, followed by dynamic call. | 498 // s.someDynamicField(...); // static get, followed by dynamic call. |
| 500 // | 499 // |
| 501 // The first case is handled here, the second case is handled below when | 500 // The first case is handled here, the second case is handled below when |
| 502 // we call [checkFunctionApplication]. | 501 // we call [checkFunctionApplication]. |
| 503 DynamicInvoke.set(node.methodName, true); | 502 setIsDynamicInvoke(node.methodName, true); |
| 504 } else { | 503 } else { |
| 505 checkFunctionApplication(node, node.methodName, node.argumentList); | 504 checkFunctionApplication(node, node.methodName, node.argumentList); |
| 506 } | 505 } |
| 507 node.visitChildren(this); | 506 node.visitChildren(this); |
| 508 } | 507 } |
| 509 | 508 |
| 510 @override | 509 @override |
| 511 void visitPostfixExpression(PostfixExpression node) { | 510 void visitPostfixExpression(PostfixExpression node) { |
| 512 _checkUnary(node); | 511 _checkUnary(node); |
| 513 node.visitChildren(this); | 512 node.visitChildren(this); |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 612 } else { | 611 } else { |
| 613 // Sanity check the operator. | 612 // Sanity check the operator. |
| 614 assert(methodElement.isOperator); | 613 assert(methodElement.isOperator); |
| 615 var functionType = methodElement.type; | 614 var functionType = methodElement.type; |
| 616 var paramTypes = functionType.normalParameterTypes; | 615 var paramTypes = functionType.normalParameterTypes; |
| 617 assert(paramTypes.length == 1); | 616 assert(paramTypes.length == 1); |
| 618 assert(functionType.namedParameterTypes.isEmpty); | 617 assert(functionType.namedParameterTypes.isEmpty); |
| 619 assert(functionType.optionalParameterTypes.isEmpty); | 618 assert(functionType.optionalParameterTypes.isEmpty); |
| 620 | 619 |
| 621 // Check the LHS type. | 620 // Check the LHS type. |
| 622 var staticInfo; | |
| 623 var rhsType = _getStaticType(expr.rightHandSide); | 621 var rhsType = _getStaticType(expr.rightHandSide); |
| 624 var lhsType = _getStaticType(expr.leftHandSide); | 622 var lhsType = _getStaticType(expr.leftHandSide); |
| 625 var returnType = rules.refineBinaryExpressionType( | 623 var returnType = rules.refineBinaryExpressionType( |
| 626 typeProvider, lhsType, op, rhsType, functionType.returnType); | 624 typeProvider, lhsType, op, rhsType, functionType.returnType); |
| 627 | 625 |
| 628 if (!rules.isSubtypeOf(returnType, lhsType)) { | 626 if (!rules.isSubtypeOf(returnType, lhsType)) { |
| 629 final numType = typeProvider.numType; | 627 final numType = typeProvider.numType; |
| 630 // Try to fix up the numerical case if possible. | 628 // Try to fix up the numerical case if possible. |
| 631 if (rules.isSubtypeOf(lhsType, numType) && | 629 if (rules.isSubtypeOf(lhsType, numType) && |
| 632 rules.isSubtypeOf(lhsType, rhsType)) { | 630 rules.isSubtypeOf(lhsType, rhsType)) { |
| 633 // This is also slightly different from spec, but allows us to keep | 631 // This is also slightly different from spec, but allows us to keep |
| 634 // compound operators in the int += num and num += dynamic cases. | 632 // compound operators in the int += num and num += dynamic cases. |
| 635 staticInfo = DownCast.create( | 633 _recordImplicitCast(expr.rightHandSide, rhsType, lhsType); |
| 636 rules, expr.rightHandSide, rhsType, lhsType, _options); | |
| 637 rhsType = lhsType; | |
| 638 } else { | 634 } else { |
| 639 staticInfo = new StaticTypeError(expr, lhsType); | 635 _recordMessage(expr, StrongModeCode.STATIC_TYPE_ERROR, |
| 636 [expr, returnType, lhsType]); |
| 640 } | 637 } |
| 641 _recordMessage(staticInfo); | 638 } else { |
| 642 } | 639 // Check the RHS type. |
| 643 | 640 // |
| 644 // Check the rhs type | 641 // This is only needed if we didn't already need a cast, and avoids |
| 645 if (staticInfo is! CoercionInfo) { | 642 // emitting two messages for the same expression. |
| 646 var paramType = paramTypes.first; | 643 _checkDowncast(expr.rightHandSide, paramTypes.first); |
| 647 _checkDowncast(expr.rightHandSide, paramType); | |
| 648 } | 644 } |
| 649 } | 645 } |
| 650 } | 646 } |
| 651 | 647 |
| 652 /// Records a [DownCast] of [expr] from [from] to [to], if there is one. | 648 /// Records a [DownCast] of [expr] from [from] to [to], if there is one. |
| 653 /// | 649 /// |
| 654 /// If [from] is omitted, uses the static type of [expr]. | 650 /// If [from] is omitted, uses the static type of [expr]. |
| 655 /// | 651 /// |
| 656 /// If [expr] does not require a downcast because it is not related to [to] | 652 /// If [expr] does not require a downcast because it is not related to [to] |
| 657 /// or is already a subtype of it, does nothing. | 653 /// or is already a subtype of it, does nothing. |
| (...skipping 14 matching lines...) Expand all Loading... |
| 672 // Note: a function type is never assignable to a class per the Dart | 668 // Note: a function type is never assignable to a class per the Dart |
| 673 // spec - even if it has a compatible call method. We disallow as | 669 // spec - even if it has a compatible call method. We disallow as |
| 674 // well for consistency. | 670 // well for consistency. |
| 675 if ((from is FunctionType && rules.getCallMethodType(to) != null) || | 671 if ((from is FunctionType && rules.getCallMethodType(to) != null) || |
| 676 (to is FunctionType && rules.getCallMethodType(from) != null)) { | 672 (to is FunctionType && rules.getCallMethodType(from) != null)) { |
| 677 return; | 673 return; |
| 678 } | 674 } |
| 679 | 675 |
| 680 // Downcast if toT <: fromT | 676 // Downcast if toT <: fromT |
| 681 if (rules.isSubtypeOf(to, from)) { | 677 if (rules.isSubtypeOf(to, from)) { |
| 682 _recordMessage(DownCast.create(rules, expr, from, to, _options)); | 678 _recordImplicitCast(expr, from, to); |
| 683 return; | 679 return; |
| 684 } | 680 } |
| 685 | 681 |
| 686 // TODO(vsm): Once we have generic methods, we should delete this | 682 // TODO(vsm): Once we have generic methods, we should delete this |
| 687 // workaround. These sideways casts are always ones we warn about | 683 // workaround. These sideways casts are always ones we warn about |
| 688 // - i.e., we think they are likely to fail at runtime. | 684 // - i.e., we think they are likely to fail at runtime. |
| 689 // ------- | 685 // ------- |
| 690 // Downcast if toT <===> fromT | 686 // Downcast if toT <===> fromT |
| 691 // The intention here is to allow casts that are sideways in the restricted | 687 // The intention here is to allow casts that are sideways in the restricted |
| 692 // type system, but allowed in the regular dart type system, since these | 688 // type system, but allowed in the regular dart type system, since these |
| 693 // are likely to succeed. The canonical example is List<dynamic> and | 689 // are likely to succeed. The canonical example is List<dynamic> and |
| 694 // Iterable<T> for some concrete T (e.g. Object). These are unrelated | 690 // Iterable<T> for some concrete T (e.g. Object). These are unrelated |
| 695 // in the restricted system, but List<dynamic> <: Iterable<T> in dart. | 691 // in the restricted system, but List<dynamic> <: Iterable<T> in dart. |
| 696 if (from.isAssignableTo(to)) { | 692 if (from.isAssignableTo(to)) { |
| 697 _recordMessage(DownCast.create(rules, expr, from, to, _options)); | 693 _recordImplicitCast(expr, from, to); |
| 698 } | 694 } |
| 699 } | 695 } |
| 700 | 696 |
| 701 void _checkFieldAccess(AstNode node, AstNode target, SimpleIdentifier field) { | 697 void _checkFieldAccess(AstNode node, AstNode target, SimpleIdentifier field) { |
| 702 if ((_isDynamicTarget(target) || field.staticElement == null) && | 698 if ((_isDynamicTarget(target) || field.staticElement == null) && |
| 703 !_isObjectProperty(target, field)) { | 699 !_isObjectProperty(target, field)) { |
| 704 _recordDynamicInvoke(node, target); | 700 _recordDynamicInvoke(node, target); |
| 705 } | 701 } |
| 706 node.visitChildren(this); | 702 node.visitChildren(this); |
| 707 } | 703 } |
| (...skipping 16 matching lines...) Expand all Loading... |
| 724 actualType.element == futureType.element) { | 720 actualType.element == futureType.element) { |
| 725 type = futureType.instantiate([type]); | 721 type = futureType.instantiate([type]); |
| 726 } | 722 } |
| 727 // TODO(vsm): Enforce void or dynamic (to void?) when expression is null. | 723 // TODO(vsm): Enforce void or dynamic (to void?) when expression is null. |
| 728 if (expression != null) checkAssignment(expression, type); | 724 if (expression != null) checkAssignment(expression, type); |
| 729 } | 725 } |
| 730 | 726 |
| 731 void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) { | 727 void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) { |
| 732 var type = getType(typeName); | 728 var type = getType(typeName); |
| 733 if (!rules.isGroundType(type)) { | 729 if (!rules.isGroundType(type)) { |
| 734 _recordMessage(new NonGroundTypeCheckInfo(node, type)); | 730 _recordMessage(node, StrongModeCode.NON_GROUND_TYPE_CHECK_INFO, [type]); |
| 735 } | 731 } |
| 736 } | 732 } |
| 737 | 733 |
| 738 void _checkUnary(/*PrefixExpression|PostfixExpression*/ node) { | 734 void _checkUnary(/*PrefixExpression|PostfixExpression*/ node) { |
| 739 var op = node.operator; | 735 var op = node.operator; |
| 740 if (op.isUserDefinableOperator || | 736 if (op.isUserDefinableOperator || |
| 741 op.type == TokenType.PLUS_PLUS || | 737 op.type == TokenType.PLUS_PLUS || |
| 742 op.type == TokenType.MINUS_MINUS) { | 738 op.type == TokenType.MINUS_MINUS) { |
| 743 if (_isDynamicTarget(node.operand)) { | 739 if (_isDynamicTarget(node.operand)) { |
| 744 _recordDynamicInvoke(node, node.operand); | 740 _recordDynamicInvoke(node, node.operand); |
| 745 } | 741 } |
| 746 // For ++ and --, even if it is not dynamic, we still need to check | 742 // For ++ and --, even if it is not dynamic, we still need to check |
| 747 // that the user defined method accepts an `int` as the RHS. | 743 // that the user defined method accepts an `int` as the RHS. |
| 748 // We assume Analyzer has done this already. | 744 // We assume Analyzer has done this already. |
| 749 } | 745 } |
| 750 } | 746 } |
| 751 | 747 |
| 748 /// Records an implicit cast for the [expression] from [fromType] to [toType]. |
| 749 /// |
| 750 /// This will emit the appropriate error/warning/hint message as well as mark |
| 751 /// the AST node. |
| 752 void _recordImplicitCast( |
| 753 Expression expression, DartType fromType, DartType toType) { |
| 754 // toT <:_R fromT => to <: fromT |
| 755 // NB: classes with call methods are subtypes of function |
| 756 // types, but the function type is not assignable to the class |
| 757 assert(toType.isSubtypeOf(fromType) || fromType.isAssignableTo(toType)); |
| 758 |
| 759 // Inference "casts": |
| 760 if (expression is Literal || expression is FunctionExpression) { |
| 761 // fromT should be an exact type - this will almost certainly fail at |
| 762 // runtime. |
| 763 _recordMessage(expression, StrongModeCode.STATIC_TYPE_ERROR, |
| 764 [expression, fromType, toType]); |
| 765 return; |
| 766 } |
| 767 |
| 768 if (expression is InstanceCreationExpression) { |
| 769 ConstructorElement e = expression.staticElement; |
| 770 if (e == null || !e.isFactory) { |
| 771 // fromT should be an exact type - this will almost certainly fail at |
| 772 // runtime. |
| 773 |
| 774 _recordMessage(expression, StrongModeCode.STATIC_TYPE_ERROR, |
| 775 [expression, fromType, toType]); |
| 776 return; |
| 777 } |
| 778 } |
| 779 |
| 780 if (isKnownFunction(expression)) { |
| 781 _recordMessage(expression, StrongModeCode.STATIC_TYPE_ERROR, |
| 782 [expression, fromType, toType]); |
| 783 return; |
| 784 } |
| 785 |
| 786 // TODO(vsm): Change this to an assert when we have generic methods and |
| 787 // fix TypeRules._coerceTo to disallow implicit sideways casts. |
| 788 bool downCastComposite = false; |
| 789 if (!rules.isSubtypeOf(toType, fromType)) { |
| 790 assert(toType.isSubtypeOf(fromType) || fromType.isAssignableTo(toType)); |
| 791 downCastComposite = true; |
| 792 } |
| 793 |
| 794 // Composite cast: these are more likely to fail. |
| 795 if (!rules.isGroundType(toType)) { |
| 796 // This cast is (probably) due to our different treatment of dynamic. |
| 797 // It may be more likely to fail at runtime. |
| 798 if (fromType is InterfaceType) { |
| 799 // For class types, we'd like to allow non-generic down casts, e.g., |
| 800 // Iterable<T> to List<T>. The intuition here is that raw (generic) |
| 801 // casts are problematic, and we should complain about those. |
| 802 var typeArgs = fromType.typeArguments; |
| 803 downCastComposite = |
| 804 typeArgs.isEmpty || typeArgs.any((t) => t.isDynamic); |
| 805 } else { |
| 806 downCastComposite = true; |
| 807 } |
| 808 } |
| 809 |
| 810 var parent = expression.parent; |
| 811 ErrorCode errorCode; |
| 812 if (downCastComposite) { |
| 813 errorCode = StrongModeCode.DOWN_CAST_COMPOSITE; |
| 814 } else if (fromType.isDynamic) { |
| 815 errorCode = StrongModeCode.DYNAMIC_CAST; |
| 816 } else if (parent is VariableDeclaration && |
| 817 parent.initializer == expression) { |
| 818 errorCode = StrongModeCode.ASSIGNMENT_CAST; |
| 819 } else { |
| 820 errorCode = StrongModeCode.DOWN_CAST_IMPLICIT; |
| 821 } |
| 822 |
| 823 _recordMessage(expression, errorCode, [fromType, toType]); |
| 824 setImplicitCast(expression, toType); |
| 825 } |
| 826 |
| 752 // Produce a coercion which coerces something of type fromT | 827 // Produce a coercion which coerces something of type fromT |
| 753 // to something of type toT. | 828 // to something of type toT. |
| 754 // Returns the error coercion if the types cannot be coerced | 829 // Returns the error coercion if the types cannot be coerced |
| 755 // according to our current criteria. | 830 // according to our current criteria. |
| 756 /// Gets the expected return type of the given function [body], either from | 831 /// Gets the expected return type of the given function [body], either from |
| 757 /// a normal return/yield, or from a yield*. | 832 /// a normal return/yield, or from a yield*. |
| 758 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) { | 833 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) { |
| 759 FunctionType functionType; | 834 FunctionType functionType; |
| 760 var parent = body.parent; | 835 var parent = body.parent; |
| 761 if (parent is Declaration) { | 836 if (parent is Declaration) { |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 804 } else { | 879 } else { |
| 805 // Malformed type - fallback on analyzer error. | 880 // Malformed type - fallback on analyzer error. |
| 806 return null; | 881 return null; |
| 807 } | 882 } |
| 808 } | 883 } |
| 809 | 884 |
| 810 DartType _getStaticType(Expression expr) { | 885 DartType _getStaticType(Expression expr) { |
| 811 DartType t = expr.staticType ?? DynamicTypeImpl.instance; | 886 DartType t = expr.staticType ?? DynamicTypeImpl.instance; |
| 812 | 887 |
| 813 // Remove fuzzy arrow if possible. | 888 // Remove fuzzy arrow if possible. |
| 814 if (t is FunctionType && StaticInfo.isKnownFunction(expr)) { | 889 if (t is FunctionType && isKnownFunction(expr)) { |
| 815 t = rules.functionTypeToConcreteType(typeProvider, t); | 890 t = rules.functionTypeToConcreteType(typeProvider, t); |
| 816 } | 891 } |
| 817 | 892 |
| 818 return t; | 893 return t; |
| 819 } | 894 } |
| 820 | 895 |
| 821 /// Given an expression, return its type assuming it is | 896 /// Given an expression, return its type assuming it is |
| 822 /// in the caller position of a call (that is, accounting | 897 /// in the caller position of a call (that is, accounting |
| 823 /// for the possibility of a call method). Returns null | 898 /// for the possibility of a call method). Returns null |
| 824 /// if expression is not statically callable. | 899 /// if expression is not statically callable. |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 886 | 961 |
| 887 bool _isObjectMethod(Expression target, SimpleIdentifier id) { | 962 bool _isObjectMethod(Expression target, SimpleIdentifier id) { |
| 888 MethodElement element = typeProvider.objectType.element.getMethod(id.name); | 963 MethodElement element = typeProvider.objectType.element.getMethod(id.name); |
| 889 return (element != null && !element.isStatic); | 964 return (element != null && !element.isStatic); |
| 890 } | 965 } |
| 891 | 966 |
| 892 bool _isObjectProperty(Expression target, SimpleIdentifier id) { | 967 bool _isObjectProperty(Expression target, SimpleIdentifier id) { |
| 893 return _isObjectGetter(target, id) || _isObjectMethod(target, id); | 968 return _isObjectGetter(target, id) || _isObjectMethod(target, id); |
| 894 } | 969 } |
| 895 | 970 |
| 896 void _recordDynamicInvoke(AstNode node, AstNode target) { | 971 void _recordDynamicInvoke(AstNode node, Expression target) { |
| 897 if (_options.strongModeHints) { | 972 _recordMessage(node, StrongModeCode.DYNAMIC_INVOKE, [node]); |
| 898 reporter.onError(new DynamicInvoke(node).toAnalysisError()); | |
| 899 } | |
| 900 // TODO(jmesserly): we may eventually want to record if the whole operation | 973 // TODO(jmesserly): we may eventually want to record if the whole operation |
| 901 // (node) was dynamic, rather than the target, but this is an easier fit | 974 // (node) was dynamic, rather than the target, but this is an easier fit |
| 902 // with what we used to do. | 975 // with what we used to do. |
| 903 DynamicInvoke.set(target, true); | 976 setIsDynamicInvoke(target, true); |
| 904 } | 977 } |
| 905 | 978 |
| 906 void _recordMessage(StaticInfo info) { | 979 void _recordMessage(AstNode node, ErrorCode errorCode, List arguments) { |
| 907 if (info == null) return; | 980 var severity = errorCode.errorSeverity; |
| 908 var error = info.toAnalysisError(); | |
| 909 var severity = error.errorCode.errorSeverity; | |
| 910 if (severity == ErrorSeverity.ERROR) _failure = true; | 981 if (severity == ErrorSeverity.ERROR) _failure = true; |
| 911 if (severity != ErrorSeverity.INFO || _options.strongModeHints) { | 982 if (severity != ErrorSeverity.INFO || _options.strongModeHints) { |
| 983 int begin = node is AnnotatedNode |
| 984 ? node.firstTokenAfterCommentAndMetadata.offset |
| 985 : node.offset; |
| 986 int length = node.end - begin; |
| 987 var source = (node.root as CompilationUnit).element.source; |
| 988 var error = |
| 989 new AnalysisError(source, begin, length, errorCode, arguments); |
| 912 reporter.onError(error); | 990 reporter.onError(error); |
| 913 } | 991 } |
| 992 } |
| 993 } |
| 914 | 994 |
| 915 if (info is CoercionInfo) { | 995 bool isKnownFunction(Expression expression) { |
| 916 // TODO(jmesserly): if we're run again on the same AST, we'll produce the | 996 Element element = null; |
| 917 // same annotations. This should be harmless. This might go away once | 997 if (expression is FunctionExpression) { |
| 918 // CodeChecker is integrated better with analyzer, as it will know that | 998 return true; |
| 919 // checking has already been performed. | 999 } else if (expression is PropertyAccess) { |
| 920 // assert(CoercionInfo.get(info.node) == null); | 1000 element = expression.propertyName.staticElement; |
| 921 CoercionInfo.set(info.node, info); | 1001 } else if (expression is Identifier) { |
| 922 } | 1002 element = expression.staticElement; |
| 923 } | 1003 } |
| 1004 // First class functions and static methods, where we know the original |
| 1005 // declaration, will have an exact type, so we know a downcast will fail. |
| 1006 return element is FunctionElement || |
| 1007 element is MethodElement && element.isStatic; |
| 924 } | 1008 } |
| 925 | 1009 |
| 926 /// Checks for overriding declarations of fields and methods. This is used to | 1010 /// Checks for overriding declarations of fields and methods. This is used to |
| 927 /// check overrides between classes and superclasses, interfaces, and mixin | 1011 /// check overrides between classes and superclasses, interfaces, and mixin |
| 928 /// applications. | 1012 /// applications. |
| 929 class _OverrideChecker { | 1013 class _OverrideChecker { |
| 930 bool _failure = false; | |
| 931 final StrongTypeSystemImpl rules; | 1014 final StrongTypeSystemImpl rules; |
| 932 final TypeProvider _typeProvider; | 1015 final TypeProvider _typeProvider; |
| 933 final AnalysisErrorListener _reporter; | 1016 final CodeChecker _checker; |
| 934 | 1017 |
| 935 _OverrideChecker(this._typeProvider, this.rules, this._reporter); | 1018 _OverrideChecker(CodeChecker checker) |
| 1019 : _checker = checker, |
| 1020 rules = checker.rules, |
| 1021 _typeProvider = checker.typeProvider; |
| 936 | 1022 |
| 937 void check(ClassDeclaration node) { | 1023 void check(ClassDeclaration node) { |
| 938 if (node.element.type.isObject) return; | 1024 if (node.element.type.isObject) return; |
| 939 _checkSuperOverrides(node); | 1025 _checkSuperOverrides(node); |
| 940 _checkMixinApplicationOverrides(node); | 1026 _checkMixinApplicationOverrides(node); |
| 941 _checkAllInterfaceOverrides(node); | 1027 _checkAllInterfaceOverrides(node); |
| 942 } | 1028 } |
| 943 | 1029 |
| 944 /// Checks that implementations correctly override all reachable interfaces. | 1030 /// Checks that implementations correctly override all reachable interfaces. |
| 945 /// In particular, we need to check these overrides for the definitions in | 1031 /// In particular, we need to check these overrides for the definitions in |
| (...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1189 FunctionType subType = _elementType(element); | 1275 FunctionType subType = _elementType(element); |
| 1190 // TODO(vsm): Test for generic | 1276 // TODO(vsm): Test for generic |
| 1191 FunctionType baseType = _getMemberType(type, element); | 1277 FunctionType baseType = _getMemberType(type, element); |
| 1192 if (baseType == null) return false; | 1278 if (baseType == null) return false; |
| 1193 | 1279 |
| 1194 if (isSubclass && element is PropertyAccessorElement) { | 1280 if (isSubclass && element is PropertyAccessorElement) { |
| 1195 // Disallow any overriding if the base class defines this member | 1281 // Disallow any overriding if the base class defines this member |
| 1196 // as a field. We effectively treat fields as final / non-virtual. | 1282 // as a field. We effectively treat fields as final / non-virtual. |
| 1197 PropertyInducingElement field = _getMemberField(type, element); | 1283 PropertyInducingElement field = _getMemberField(type, element); |
| 1198 if (field != null) { | 1284 if (field != null) { |
| 1199 _recordMessage(new InvalidFieldOverride( | 1285 _checker._recordMessage( |
| 1200 errorLocation, element, type, subType, baseType)); | 1286 errorLocation, StrongModeCode.INVALID_FIELD_OVERRIDE, [ |
| 1287 element.enclosingElement.name, |
| 1288 element.name, |
| 1289 subType, |
| 1290 type, |
| 1291 baseType |
| 1292 ]); |
| 1201 } | 1293 } |
| 1202 } | 1294 } |
| 1203 FunctionType concreteSubType = subType; | 1295 FunctionType concreteSubType = subType; |
| 1204 FunctionType concreteBaseType = baseType; | 1296 FunctionType concreteBaseType = baseType; |
| 1205 if (element is MethodElement) { | 1297 if (element is MethodElement) { |
| 1206 if (concreteSubType.typeFormals.isNotEmpty) { | 1298 if (concreteSubType.typeFormals.isNotEmpty) { |
| 1207 if (concreteBaseType.typeFormals.isEmpty) { | 1299 if (concreteBaseType.typeFormals.isEmpty) { |
| 1208 concreteSubType = rules.instantiateToBounds(concreteSubType); | 1300 concreteSubType = rules.instantiateToBounds(concreteSubType); |
| 1209 } | 1301 } |
| 1210 } | 1302 } |
| 1211 concreteSubType = | 1303 concreteSubType = |
| 1212 rules.typeToConcreteType(_typeProvider, concreteSubType); | 1304 rules.typeToConcreteType(_typeProvider, concreteSubType); |
| 1213 concreteBaseType = | 1305 concreteBaseType = |
| 1214 rules.typeToConcreteType(_typeProvider, concreteBaseType); | 1306 rules.typeToConcreteType(_typeProvider, concreteBaseType); |
| 1215 } | 1307 } |
| 1216 if (!rules.isSubtypeOf(concreteSubType, concreteBaseType)) { | 1308 if (!rules.isSubtypeOf(concreteSubType, concreteBaseType)) { |
| 1217 // See whether non-subtype cases fit one of our common patterns: | 1309 // See whether non-subtype cases fit one of our common patterns: |
| 1218 // | 1310 // |
| 1219 // Common pattern 1: Inferable return type (on getters and methods) | 1311 // Common pattern 1: Inferable return type (on getters and methods) |
| 1220 // class A { | 1312 // class A { |
| 1221 // int get foo => ...; | 1313 // int get foo => ...; |
| 1222 // String toString() { ... } | 1314 // String toString() { ... } |
| 1223 // } | 1315 // } |
| 1224 // class B extends A { | 1316 // class B extends A { |
| 1225 // get foo => e; // no type specified. | 1317 // get foo => e; // no type specified. |
| 1226 // toString() { ... } // no return type specified. | 1318 // toString() { ... } // no return type specified. |
| 1227 // } | 1319 // } |
| 1228 _recordMessage(new InvalidMethodOverride( | 1320 |
| 1229 errorLocation, element, type, subType, baseType)); | 1321 ErrorCode errorCode; |
| 1322 if (errorLocation is ExtendsClause) { |
| 1323 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE_FROM_BASE; |
| 1324 } else if (errorLocation.parent is WithClause) { |
| 1325 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE_FROM_MIXIN; |
| 1326 } else { |
| 1327 errorCode = StrongModeCode.INVALID_METHOD_OVERRIDE; |
| 1328 } |
| 1329 |
| 1330 _checker._recordMessage(errorLocation, errorCode, [ |
| 1331 element.enclosingElement.name, |
| 1332 element.name, |
| 1333 subType, |
| 1334 type, |
| 1335 baseType |
| 1336 ]); |
| 1230 } | 1337 } |
| 1231 return true; | 1338 return true; |
| 1232 } | 1339 } |
| 1233 | 1340 |
| 1234 /// Check overrides between a class and its superclasses and mixins. For | 1341 /// Check overrides between a class and its superclasses and mixins. For |
| 1235 /// example, in: | 1342 /// example, in: |
| 1236 /// | 1343 /// |
| 1237 /// A extends B with E, F | 1344 /// A extends B with E, F |
| 1238 /// | 1345 /// |
| 1239 /// we check A against B, B super classes, E, and F. | 1346 /// we check A against B, B super classes, E, and F. |
| (...skipping 18 matching lines...) Expand all Loading... |
| 1258 var current = node.element.type; | 1365 var current = node.element.type; |
| 1259 var visited = new Set<InterfaceType>(); | 1366 var visited = new Set<InterfaceType>(); |
| 1260 do { | 1367 do { |
| 1261 visited.add(current); | 1368 visited.add(current); |
| 1262 current.mixins.reversed.forEach( | 1369 current.mixins.reversed.forEach( |
| 1263 (m) => _checkIndividualOverridesFromClass(node, m, seen, true)); | 1370 (m) => _checkIndividualOverridesFromClass(node, m, seen, true)); |
| 1264 _checkIndividualOverridesFromClass(node, current.superclass, seen, true); | 1371 _checkIndividualOverridesFromClass(node, current.superclass, seen, true); |
| 1265 current = current.superclass; | 1372 current = current.superclass; |
| 1266 } while (!current.isObject && !visited.contains(current)); | 1373 } while (!current.isObject && !visited.contains(current)); |
| 1267 } | 1374 } |
| 1268 | |
| 1269 void _recordMessage(StaticInfo info) { | |
| 1270 if (info == null) return; | |
| 1271 var error = info.toAnalysisError(); | |
| 1272 if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true; | |
| 1273 _reporter.onError(error); | |
| 1274 } | |
| 1275 } | 1375 } |
| OLD | NEW |