| 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'; |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 138 DartType expectedType = _elementType(element); | 138 DartType expectedType = _elementType(element); |
| 139 if (expectedType == null) expectedType = DynamicTypeImpl.instance; | 139 if (expectedType == null) expectedType = DynamicTypeImpl.instance; |
| 140 checkArgument(arg, expectedType); | 140 checkArgument(arg, expectedType); |
| 141 } | 141 } |
| 142 } | 142 } |
| 143 | 143 |
| 144 void checkAssignment(Expression expr, DartType type) { | 144 void checkAssignment(Expression expr, DartType type) { |
| 145 if (expr is ParenthesizedExpression) { | 145 if (expr is ParenthesizedExpression) { |
| 146 checkAssignment(expr.expression, type); | 146 checkAssignment(expr.expression, type); |
| 147 } else { | 147 } else { |
| 148 _recordMessage(_checkAssignment(expr, type)); | 148 _checkDowncast(expr, type); |
| 149 } | 149 } |
| 150 } | 150 } |
| 151 | 151 |
| 152 /// Analyzer checks boolean conversions, but we need to check too, because | 152 /// Analyzer checks boolean conversions, but we need to check too, because |
| 153 /// it uses the default assignability rules that allow `dynamic` and `Object` | 153 /// it uses the default assignability rules that allow `dynamic` and `Object` |
| 154 /// to be assigned to bool with no message. | 154 /// to be assigned to bool with no message. |
| 155 void checkBoolean(Expression expr) => | 155 void checkBoolean(Expression expr) => |
| 156 checkAssignment(expr, typeProvider.boolType); | 156 checkAssignment(expr, typeProvider.boolType); |
| 157 | 157 |
| 158 void checkFunctionApplication( | 158 void checkFunctionApplication( |
| (...skipping 399 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 558 checkBoolean(node.condition); | 558 checkBoolean(node.condition); |
| 559 node.visitChildren(this); | 559 node.visitChildren(this); |
| 560 } | 560 } |
| 561 | 561 |
| 562 @override | 562 @override |
| 563 void visitYieldStatement(YieldStatement node) { | 563 void visitYieldStatement(YieldStatement node) { |
| 564 _checkReturnOrYield(node.expression, node, yieldStar: node.star != null); | 564 _checkReturnOrYield(node.expression, node, yieldStar: node.star != null); |
| 565 node.visitChildren(this); | 565 node.visitChildren(this); |
| 566 } | 566 } |
| 567 | 567 |
| 568 StaticInfo _checkAssignment(Expression expr, DartType toT) { | |
| 569 final fromT = _getStaticType(expr); | |
| 570 final Coercion c = _coerceTo(fromT, toT); | |
| 571 if (c is Identity) return null; | |
| 572 if (c is CoercionError) return new StaticTypeError(rules, expr, toT); | |
| 573 if (c is Cast) return DownCast.create(rules, expr, c); | |
| 574 assert(false); | |
| 575 return null; | |
| 576 } | |
| 577 | |
| 578 void _checkCompoundAssignment(AssignmentExpression expr) { | 568 void _checkCompoundAssignment(AssignmentExpression expr) { |
| 579 var op = expr.operator.type; | 569 var op = expr.operator.type; |
| 580 assert(op.isAssignmentOperator && op != TokenType.EQ); | 570 assert(op.isAssignmentOperator && op != TokenType.EQ); |
| 581 var methodElement = expr.staticElement; | 571 var methodElement = expr.staticElement; |
| 582 if (methodElement == null) { | 572 if (methodElement == null) { |
| 583 // Dynamic invocation | 573 // Dynamic invocation. |
| 584 _recordDynamicInvoke(expr, expr.leftHandSide); | 574 _recordDynamicInvoke(expr, expr.leftHandSide); |
| 585 } else { | 575 } else { |
| 586 // Sanity check the operator | 576 // Sanity check the operator. |
| 587 assert(methodElement.isOperator); | 577 assert(methodElement.isOperator); |
| 588 var functionType = methodElement.type; | 578 var functionType = methodElement.type; |
| 589 var paramTypes = functionType.normalParameterTypes; | 579 var paramTypes = functionType.normalParameterTypes; |
| 590 assert(paramTypes.length == 1); | 580 assert(paramTypes.length == 1); |
| 591 assert(functionType.namedParameterTypes.isEmpty); | 581 assert(functionType.namedParameterTypes.isEmpty); |
| 592 assert(functionType.optionalParameterTypes.isEmpty); | 582 assert(functionType.optionalParameterTypes.isEmpty); |
| 593 | 583 |
| 594 // Check the lhs type | 584 // Check the LHS type. |
| 595 var staticInfo; | 585 var staticInfo; |
| 596 var rhsType = _getStaticType(expr.rightHandSide); | 586 var rhsType = _getStaticType(expr.rightHandSide); |
| 597 var lhsType = _getStaticType(expr.leftHandSide); | 587 var lhsType = _getStaticType(expr.leftHandSide); |
| 598 var returnType = _specializedBinaryReturnType( | 588 var returnType = _specializedBinaryReturnType( |
| 599 op, lhsType, rhsType, functionType.returnType); | 589 op, lhsType, rhsType, functionType.returnType); |
| 600 | 590 |
| 601 if (!rules.isSubtypeOf(returnType, lhsType)) { | 591 if (!rules.isSubtypeOf(returnType, lhsType)) { |
| 602 final numType = typeProvider.numType; | 592 final numType = typeProvider.numType; |
| 603 // Try to fix up the numerical case if possible. | 593 // Try to fix up the numerical case if possible. |
| 604 if (rules.isSubtypeOf(lhsType, numType) && | 594 if (rules.isSubtypeOf(lhsType, numType) && |
| 605 rules.isSubtypeOf(lhsType, rhsType)) { | 595 rules.isSubtypeOf(lhsType, rhsType)) { |
| 606 // This is also slightly different from spec, but allows us to keep | 596 // This is also slightly different from spec, but allows us to keep |
| 607 // compound operators in the int += num and num += dynamic cases. | 597 // compound operators in the int += num and num += dynamic cases. |
| 608 staticInfo = DownCast.create( | 598 staticInfo = DownCast.create( |
| 609 rules, expr.rightHandSide, Coercion.cast(rhsType, lhsType)); | 599 rules, expr.rightHandSide, new Cast(rhsType, lhsType)); |
| 610 rhsType = lhsType; | 600 rhsType = lhsType; |
| 611 } else { | 601 } else { |
| 612 // Static type error | |
| 613 staticInfo = new StaticTypeError(rules, expr, lhsType); | 602 staticInfo = new StaticTypeError(rules, expr, lhsType); |
| 614 } | 603 } |
| 615 _recordMessage(staticInfo); | 604 _recordMessage(staticInfo); |
| 616 } | 605 } |
| 617 | 606 |
| 618 // Check the rhs type | 607 // Check the rhs type |
| 619 if (staticInfo is! CoercionInfo) { | 608 if (staticInfo is! CoercionInfo) { |
| 620 var paramType = paramTypes.first; | 609 var paramType = paramTypes.first; |
| 621 staticInfo = _checkAssignment(expr.rightHandSide, paramType); | 610 _checkDowncast(expr.rightHandSide, paramType); |
| 622 _recordMessage(staticInfo); | |
| 623 } | 611 } |
| 624 } | 612 } |
| 625 } | 613 } |
| 626 | 614 |
| 627 void _checkFieldAccess(AstNode node, AstNode target, SimpleIdentifier field) { | 615 void _checkFieldAccess(AstNode node, AstNode target, SimpleIdentifier field) { |
| 628 if ((_isDynamicTarget(target) || field.staticElement == null) && | 616 if ((_isDynamicTarget(target) || field.staticElement == null) && |
| 629 !_isObjectProperty(target, field)) { | 617 !_isObjectProperty(target, field)) { |
| 630 _recordDynamicInvoke(node, target); | 618 _recordDynamicInvoke(node, target); |
| 631 } | 619 } |
| 632 node.visitChildren(this); | 620 node.visitChildren(this); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 668 op.type == TokenType.MINUS_MINUS) { | 656 op.type == TokenType.MINUS_MINUS) { |
| 669 if (_isDynamicTarget(node.operand)) { | 657 if (_isDynamicTarget(node.operand)) { |
| 670 _recordDynamicInvoke(node, node.operand); | 658 _recordDynamicInvoke(node, node.operand); |
| 671 } | 659 } |
| 672 // For ++ and --, even if it is not dynamic, we still need to check | 660 // For ++ and --, even if it is not dynamic, we still need to check |
| 673 // that the user defined method accepts an `int` as the RHS. | 661 // that the user defined method accepts an `int` as the RHS. |
| 674 // We assume Analyzer has done this already. | 662 // We assume Analyzer has done this already. |
| 675 } | 663 } |
| 676 } | 664 } |
| 677 | 665 |
| 678 Coercion _coerceTo(DartType fromT, DartType toT) { | 666 /// Records a [DownCast] of [expr] to [toT], if there is one. |
| 679 // We can use anything as void | 667 /// |
| 680 if (toT.isVoid) return Coercion.identity(toT); | 668 /// If [expr] does not require a downcast because it is not related to [toT] |
| 669 /// or is already a subtype of it, does nothing. |
| 670 void _checkDowncast(Expression expr, DartType toT) { |
| 671 DartType fromT = _getStaticType(expr); |
| 681 | 672 |
| 682 // fromT <: toT, no coercion needed | 673 // We can use anything as void. |
| 683 if (rules.isSubtypeOf(fromT, toT)) return Coercion.identity(toT); | 674 if (toT.isVoid) return; |
| 675 |
| 676 // fromT <: toT, no coercion needed. |
| 677 if (rules.isSubtypeOf(fromT, toT)) return; |
| 684 | 678 |
| 685 // TODO(vsm): We can get rid of the second clause if we disallow | 679 // TODO(vsm): We can get rid of the second clause if we disallow |
| 686 // all sideways casts - see TODO below. | 680 // all sideways casts - see TODO below. |
| 687 // ------- | 681 // ------- |
| 688 // Note: a function type is never assignable to a class per the Dart | 682 // Note: a function type is never assignable to a class per the Dart |
| 689 // spec - even if it has a compatible call method. We disallow as | 683 // spec - even if it has a compatible call method. We disallow as |
| 690 // well for consistency. | 684 // well for consistency. |
| 691 if ((fromT is FunctionType && rules.getCallMethodType(toT) != null) || | 685 if ((fromT is FunctionType && rules.getCallMethodType(toT) != null) || |
| 692 (toT is FunctionType && rules.getCallMethodType(fromT) != null)) { | 686 (toT is FunctionType && rules.getCallMethodType(fromT) != null)) { |
| 693 return Coercion.error(); | 687 return; |
| 694 } | 688 } |
| 695 | 689 |
| 696 // Downcast if toT <: fromT | 690 // Downcast if toT <: fromT |
| 697 if (rules.isSubtypeOf(toT, fromT)) return Coercion.cast(fromT, toT); | 691 if (rules.isSubtypeOf(toT, fromT)) { |
| 692 _recordMessage(DownCast.create(rules, expr, new Cast(fromT, toT))); |
| 693 return; |
| 694 } |
| 698 | 695 |
| 699 // TODO(vsm): Once we have generic methods, we should delete this | 696 // TODO(vsm): Once we have generic methods, we should delete this |
| 700 // workaround. These sideways casts are always ones we warn about | 697 // workaround. These sideways casts are always ones we warn about |
| 701 // - i.e., we think they are likely to fail at runtime. | 698 // - i.e., we think they are likely to fail at runtime. |
| 702 // ------- | 699 // ------- |
| 703 // Downcast if toT <===> fromT | 700 // Downcast if toT <===> fromT |
| 704 // The intention here is to allow casts that are sideways in the restricted | 701 // The intention here is to allow casts that are sideways in the restricted |
| 705 // type system, but allowed in the regular dart type system, since these | 702 // type system, but allowed in the regular dart type system, since these |
| 706 // are likely to succeed. The canonical example is List<dynamic> and | 703 // are likely to succeed. The canonical example is List<dynamic> and |
| 707 // Iterable<T> for some concrete T (e.g. Object). These are unrelated | 704 // Iterable<T> for some concrete T (e.g. Object). These are unrelated |
| 708 // in the restricted system, but List<dynamic> <: Iterable<T> in dart. | 705 // in the restricted system, but List<dynamic> <: Iterable<T> in dart. |
| 709 if (fromT.isAssignableTo(toT)) { | 706 if (fromT.isAssignableTo(toT)) { |
| 710 return Coercion.cast(fromT, toT); | 707 _recordMessage(DownCast.create(rules, expr, new Cast(fromT, toT))); |
| 711 } | 708 } |
| 712 | |
| 713 return Coercion.error(); | |
| 714 } | 709 } |
| 715 | 710 |
| 716 // Produce a coercion which coerces something of type fromT | 711 // Produce a coercion which coerces something of type fromT |
| 717 // to something of type toT. | 712 // to something of type toT. |
| 718 // Returns the error coercion if the types cannot be coerced | 713 // Returns the error coercion if the types cannot be coerced |
| 719 // according to our current criteria. | 714 // according to our current criteria. |
| 720 /// Gets the expected return type of the given function [body], either from | 715 /// Gets the expected return type of the given function [body], either from |
| 721 /// a normal return/yield, or from a yield*. | 716 /// a normal return/yield, or from a yield*. |
| 722 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) { | 717 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) { |
| 723 FunctionType functionType; | 718 FunctionType functionType; |
| (...skipping 552 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1276 } while (!current.isObject && !visited.contains(current)); | 1271 } while (!current.isObject && !visited.contains(current)); |
| 1277 } | 1272 } |
| 1278 | 1273 |
| 1279 void _recordMessage(StaticInfo info) { | 1274 void _recordMessage(StaticInfo info) { |
| 1280 if (info == null) return; | 1275 if (info == null) return; |
| 1281 var error = info.toAnalysisError(); | 1276 var error = info.toAnalysisError(); |
| 1282 if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true; | 1277 if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true; |
| 1283 _reporter.onError(error); | 1278 _reporter.onError(error); |
| 1284 } | 1279 } |
| 1285 } | 1280 } |
| OLD | NEW |