| 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 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 179 NodeList<Expression> list = node.arguments; | 179 NodeList<Expression> list = node.arguments; |
| 180 int len = list.length; | 180 int len = list.length; |
| 181 for (int i = 0; i < len; ++i) { | 181 for (int i = 0; i < len; ++i) { |
| 182 Expression arg = list[i]; | 182 Expression arg = list[i]; |
| 183 ParameterElement element = arg.staticParameterElement; | 183 ParameterElement element = arg.staticParameterElement; |
| 184 if (element == null) { | 184 if (element == null) { |
| 185 // We found an argument mismatch, the analyzer will report this too, | 185 // We found an argument mismatch, the analyzer will report this too, |
| 186 // so no need to insert an error for this here. | 186 // so no need to insert an error for this here. |
| 187 continue; | 187 continue; |
| 188 } | 188 } |
| 189 DartType expectedType = _elementType(element); | 189 checkArgument(arg, _elementType(element)); |
| 190 if (expectedType == null) expectedType = DynamicTypeImpl.instance; | |
| 191 checkArgument(arg, expectedType); | |
| 192 } | 190 } |
| 193 } | 191 } |
| 194 | 192 |
| 195 void checkAssignment(Expression expr, DartType type) { | 193 void checkAssignment(Expression expr, DartType type) { |
| 196 if (expr is ParenthesizedExpression) { | 194 if (expr is ParenthesizedExpression) { |
| 197 checkAssignment(expr.expression, type); | 195 checkAssignment(expr.expression, type); |
| 198 } else { | 196 } else { |
| 199 _checkDowncast(expr, type); | 197 _checkImplicitCast(expr, type); |
| 200 } | 198 } |
| 201 } | 199 } |
| 202 | 200 |
| 203 /// Analyzer checks boolean conversions, but we need to check too, because | 201 /// Analyzer checks boolean conversions, but we need to check too, because |
| 204 /// it uses the default assignability rules that allow `dynamic` and `Object` | 202 /// it uses the default assignability rules that allow `dynamic` and `Object` |
| 205 /// to be assigned to bool with no message. | 203 /// to be assigned to bool with no message. |
| 206 void checkBoolean(Expression expr) => | 204 void checkBoolean(Expression expr) => |
| 207 checkAssignment(expr, typeProvider.boolType); | 205 checkAssignment(expr, typeProvider.boolType); |
| 208 | 206 |
| 209 void checkFunctionApplication(InvocationExpression node) { | 207 void checkFunctionApplication(InvocationExpression node) { |
| (...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 402 rules.mostSpecificTypeArgument(iterableType, sequenceInterface); | 400 rules.mostSpecificTypeArgument(iterableType, sequenceInterface); |
| 403 | 401 |
| 404 // If the sequence is not an Iterable (or Stream for await for) but is a | 402 // If the sequence is not an Iterable (or Stream for await for) but is a |
| 405 // supertype of it, do an implicit downcast to Iterable<dynamic>. Then | 403 // supertype of it, do an implicit downcast to Iterable<dynamic>. Then |
| 406 // we'll do a separate cast of the dynamic element to the variable's type. | 404 // we'll do a separate cast of the dynamic element to the variable's type. |
| 407 if (elementType == null) { | 405 if (elementType == null) { |
| 408 var sequenceType = | 406 var sequenceType = |
| 409 sequenceInterface.instantiate([DynamicTypeImpl.instance]); | 407 sequenceInterface.instantiate([DynamicTypeImpl.instance]); |
| 410 | 408 |
| 411 if (rules.isSubtypeOf(sequenceType, iterableType)) { | 409 if (rules.isSubtypeOf(sequenceType, iterableType)) { |
| 412 _recordImplicitCast(node.iterable, iterableType, sequenceType); | 410 _recordImplicitCast(node.iterable, sequenceType, from: iterableType); |
| 413 elementType = DynamicTypeImpl.instance; | 411 elementType = DynamicTypeImpl.instance; |
| 414 } | 412 } |
| 415 } | 413 } |
| 416 | 414 |
| 417 // If the sequence doesn't implement the interface at all, [ErrorVerifier] | 415 // If the sequence doesn't implement the interface at all, [ErrorVerifier] |
| 418 // will report the error, so ignore it here. | 416 // will report the error, so ignore it here. |
| 419 if (elementType != null) { | 417 if (elementType != null) { |
| 420 // Insert a cast from the sequence's element type to the loop variable's | 418 // Insert a cast from the sequence's element type to the loop variable's |
| 421 // if needed. | 419 // if needed. |
| 422 _checkDowncast(loopVariable, _getDefiniteType(loopVariable), | 420 _checkImplicitCast(loopVariable, _getDefiniteType(loopVariable), |
| 423 from: elementType); | 421 from: elementType); |
| 424 } | 422 } |
| 425 } | 423 } |
| 426 | 424 |
| 427 node.visitChildren(this); | 425 node.visitChildren(this); |
| 428 } | 426 } |
| 429 | 427 |
| 430 @override | 428 @override |
| 431 void visitForStatement(ForStatement node) { | 429 void visitForStatement(ForStatement node) { |
| 432 if (node.condition != null) { | 430 if (node.condition != null) { |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 570 // we call [checkFunctionApplication]. | 568 // we call [checkFunctionApplication]. |
| 571 setIsDynamicInvoke(node.methodName, true); | 569 setIsDynamicInvoke(node.methodName, true); |
| 572 } else { | 570 } else { |
| 573 checkFunctionApplication(node); | 571 checkFunctionApplication(node); |
| 574 } | 572 } |
| 575 node.visitChildren(this); | 573 node.visitChildren(this); |
| 576 } | 574 } |
| 577 | 575 |
| 578 @override | 576 @override |
| 579 void visitPostfixExpression(PostfixExpression node) { | 577 void visitPostfixExpression(PostfixExpression node) { |
| 580 _checkUnary(node, node.staticElement); | 578 _checkUnary(node.operand, node.operator, node.staticElement); |
| 581 node.visitChildren(this); | 579 node.visitChildren(this); |
| 582 } | 580 } |
| 583 | 581 |
| 584 @override | 582 @override |
| 585 void visitPrefixedIdentifier(PrefixedIdentifier node) { | 583 void visitPrefixedIdentifier(PrefixedIdentifier node) { |
| 586 _checkFieldAccess(node, node.prefix, node.identifier); | 584 _checkFieldAccess(node, node.prefix, node.identifier); |
| 587 } | 585 } |
| 588 | 586 |
| 589 @override | 587 @override |
| 590 void visitPrefixExpression(PrefixExpression node) { | 588 void visitPrefixExpression(PrefixExpression node) { |
| 591 if (node.operator.type == TokenType.BANG) { | 589 if (node.operator.type == TokenType.BANG) { |
| 592 checkBoolean(node.operand); | 590 checkBoolean(node.operand); |
| 593 } else { | 591 } else { |
| 594 _checkUnary(node, node.staticElement); | 592 _checkUnary(node.operand, node.operator, node.staticElement); |
| 595 } | 593 } |
| 596 node.visitChildren(this); | 594 node.visitChildren(this); |
| 597 } | 595 } |
| 598 | 596 |
| 599 @override | 597 @override |
| 600 void visitPropertyAccess(PropertyAccess node) { | 598 void visitPropertyAccess(PropertyAccess node) { |
| 601 _checkFieldAccess(node, node.realTarget, node.propertyName); | 599 _checkFieldAccess(node, node.realTarget, node.propertyName); |
| 602 } | 600 } |
| 603 | 601 |
| 604 @override | 602 @override |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 692 _recordDynamicInvoke(expr, expr.leftHandSide); | 690 _recordDynamicInvoke(expr, expr.leftHandSide); |
| 693 } else { | 691 } else { |
| 694 // Sanity check the operator. | 692 // Sanity check the operator. |
| 695 assert(methodElement.isOperator); | 693 assert(methodElement.isOperator); |
| 696 var functionType = methodElement.type; | 694 var functionType = methodElement.type; |
| 697 var paramTypes = functionType.normalParameterTypes; | 695 var paramTypes = functionType.normalParameterTypes; |
| 698 assert(paramTypes.length == 1); | 696 assert(paramTypes.length == 1); |
| 699 assert(functionType.namedParameterTypes.isEmpty); | 697 assert(functionType.namedParameterTypes.isEmpty); |
| 700 assert(functionType.optionalParameterTypes.isEmpty); | 698 assert(functionType.optionalParameterTypes.isEmpty); |
| 701 | 699 |
| 702 // Check the LHS type. | 700 // Refine the return type. |
| 703 var rhsType = _getDefiniteType(expr.rightHandSide); | 701 var rhsType = _getDefiniteType(expr.rightHandSide); |
| 704 var lhsType = _getDefiniteType(expr.leftHandSide); | 702 var lhsType = _getDefiniteType(expr.leftHandSide); |
| 705 var returnType = rules.refineBinaryExpressionType( | 703 var returnType = rules.refineBinaryExpressionType( |
| 706 typeProvider, lhsType, op, rhsType, functionType.returnType); | 704 typeProvider, lhsType, op, rhsType, functionType.returnType); |
| 707 | 705 |
| 708 if (!rules.isSubtypeOf(returnType, lhsType)) { | 706 // Check the argument for an implicit cast. |
| 709 final numType = typeProvider.numType; | 707 _checkImplicitCast(expr.rightHandSide, paramTypes[0], from: rhsType); |
| 710 // TODO(jmesserly): this seems to duplicate logic in StaticTypeAnalyzer. | 708 |
| 711 // Try to fix up the numerical case if possible. | 709 // Check the return type for an implicit cast. |
| 712 if (rules.isSubtypeOf(lhsType, numType) && | 710 // |
| 713 rules.isSubtypeOf(lhsType, rhsType)) { | 711 // If needed, mark the assignment to indicate a down cast when we assign |
| 714 // This is also slightly different from spec, but allows us to keep | 712 // back to it. So these two implicit casts are equivalent: |
| 715 // compound operators in the int += num and num += dynamic cases. | 713 // |
| 716 _recordImplicitCast(expr.rightHandSide, rhsType, lhsType); | 714 // y = /*implicit cast*/(y + 42); |
| 717 } else { | 715 // /*implicit assignment cast*/y += 42; |
| 718 // TODO(jmesserly): this results in a duplicate error, because | 716 // |
| 719 // ErrorVerifier also reports it. | 717 _checkImplicitCast(expr.leftHandSide, lhsType, |
| 720 _recordMessage(expr, StrongModeCode.STATIC_TYPE_ERROR, | 718 from: returnType, opAssign: true); |
| 721 [expr, returnType, lhsType]); | |
| 722 } | |
| 723 } else { | |
| 724 // Check the RHS type. | |
| 725 // | |
| 726 // This is only needed if we didn't already need a cast, and avoids | |
| 727 // emitting two messages for the same expression. | |
| 728 _checkDowncast(expr.rightHandSide, paramTypes.first); | |
| 729 } | |
| 730 } | 719 } |
| 731 } | 720 } |
| 732 | 721 |
| 733 /// Records a [DownCast] of [expr] from [from] to [to], if there is one. | 722 /// Returns true if we need an implicit cast of [expr] from [from] type to |
| 723 /// [to] type, otherwise returns false. |
| 734 /// | 724 /// |
| 735 /// If [from] is omitted, uses the static type of [expr]. | 725 /// If [from] is omitted, uses the static type of [expr]. |
| 736 /// | 726 bool _needsImplicitCast(Expression expr, DartType to, {DartType from}) { |
| 737 /// If [expr] does not require a downcast because it is not related to [to] | 727 from ??= _getDefiniteType(expr); |
| 738 /// or is already a subtype of it, does nothing. | |
| 739 void _checkDowncast(Expression expr, DartType to, {DartType from}) { | |
| 740 if (from == null) { | |
| 741 from = _getDefiniteType(expr); | |
| 742 } | |
| 743 | 728 |
| 744 if (!_checkNonNullAssignment(expr, to, from)) return; | 729 if (!_checkNonNullAssignment(expr, to, from)) return false; |
| 745 | 730 |
| 746 // We can use anything as void. | 731 // We can use anything as void. |
| 747 if (to.isVoid) return; | 732 if (to.isVoid) return false; |
| 748 | 733 |
| 749 // fromT <: toT, no coercion needed. | 734 // fromT <: toT, no coercion needed. |
| 750 if (rules.isSubtypeOf(from, to)) return; | 735 if (rules.isSubtypeOf(from, to)) return false; |
| 751 | 736 |
| 752 // Note: a function type is never assignable to a class per the Dart | 737 // Note: a function type is never assignable to a class per the Dart |
| 753 // spec - even if it has a compatible call method. We disallow as | 738 // spec - even if it has a compatible call method. We disallow as |
| 754 // well for consistency. | 739 // well for consistency. |
| 755 if (from is FunctionType && rules.getCallMethodType(to) != null) { | 740 if (from is FunctionType && rules.getCallMethodType(to) != null) { |
| 756 return; | 741 return false; |
| 757 } | 742 } |
| 758 | 743 |
| 759 // Downcast if toT <: fromT | 744 // Downcast if toT <: fromT |
| 760 if (rules.isSubtypeOf(to, from)) { | 745 if (rules.isSubtypeOf(to, from)) { |
| 761 _recordImplicitCast(expr, from, to); | 746 return true; |
| 762 return; | |
| 763 } | 747 } |
| 764 | 748 |
| 765 // Anything else is an illegal sideways cast. | 749 // Anything else is an illegal sideways cast. |
| 766 // However, these will have been reported already in error_verifier, so we | 750 // However, these will have been reported already in error_verifier, so we |
| 767 // don't need to report them again. | 751 // don't need to report them again. |
| 752 return false; |
| 753 } |
| 754 |
| 755 /// Checks if an implicit cast of [expr] from [from] type to [to] type is |
| 756 /// needed, and if so records it. |
| 757 /// |
| 758 /// If [from] is omitted, uses the static type of [expr]. |
| 759 /// |
| 760 /// If [expr] does not require an implicit cast because it is not related to |
| 761 /// [to] or is already a subtype of it, does nothing. |
| 762 void _checkImplicitCast(Expression expr, DartType to, |
| 763 {DartType from, bool opAssign: false}) { |
| 764 from ??= _getDefiniteType(expr); |
| 765 |
| 766 if (_needsImplicitCast(expr, to, from: from)) { |
| 767 _recordImplicitCast(expr, to, from: from, opAssign: opAssign); |
| 768 } |
| 768 } | 769 } |
| 769 | 770 |
| 770 void _checkFieldAccess(AstNode node, AstNode target, SimpleIdentifier field) { | 771 void _checkFieldAccess(AstNode node, AstNode target, SimpleIdentifier field) { |
| 771 if (field.staticElement == null && | 772 if (field.staticElement == null && |
| 772 !typeProvider.isObjectMember(field.name)) { | 773 !typeProvider.isObjectMember(field.name)) { |
| 773 _recordDynamicInvoke(node, target); | 774 _recordDynamicInvoke(node, target); |
| 774 } | 775 } |
| 775 node.visitChildren(this); | 776 node.visitChildren(this); |
| 776 } | 777 } |
| 777 | 778 |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 901 if (expression != null) checkAssignment(expression, type); | 902 if (expression != null) checkAssignment(expression, type); |
| 902 } | 903 } |
| 903 | 904 |
| 904 void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) { | 905 void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) { |
| 905 var type = getType(typeName); | 906 var type = getType(typeName); |
| 906 if (!rules.isGroundType(type)) { | 907 if (!rules.isGroundType(type)) { |
| 907 _recordMessage(node, StrongModeCode.NON_GROUND_TYPE_CHECK_INFO, [type]); | 908 _recordMessage(node, StrongModeCode.NON_GROUND_TYPE_CHECK_INFO, [type]); |
| 908 } | 909 } |
| 909 } | 910 } |
| 910 | 911 |
| 911 void _checkUnary( | 912 void _checkUnary(Expression operand, Token op, MethodElement element) { |
| 912 /*PrefixExpression|PostfixExpression*/ node, | 913 bool isIncrementAssign = |
| 913 Element element) { | 914 op.type == TokenType.PLUS_PLUS || op.type == TokenType.MINUS_MINUS; |
| 914 var op = node.operator; | 915 if (op.isUserDefinableOperator || isIncrementAssign) { |
| 915 if (op.isUserDefinableOperator || | |
| 916 op.type == TokenType.PLUS_PLUS || | |
| 917 op.type == TokenType.MINUS_MINUS) { | |
| 918 if (element == null) { | 916 if (element == null) { |
| 919 _recordDynamicInvoke(node, node.operand); | 917 _recordDynamicInvoke(operand.parent, operand); |
| 918 } else if (isIncrementAssign) { |
| 919 // For ++ and --, even if it is not dynamic, we still need to check |
| 920 // that the user defined method accepts an `int` as the RHS. |
| 921 // |
| 922 // We assume Analyzer has done this already (in ErrorVerifier). |
| 923 // |
| 924 // However, we also need to check the return type. |
| 925 |
| 926 // Refine the return type. |
| 927 var functionType = element.type; |
| 928 var rhsType = typeProvider.intType; |
| 929 var lhsType = _getDefiniteType(operand); |
| 930 var returnType = rules.refineBinaryExpressionType(typeProvider, lhsType, |
| 931 TokenType.PLUS, rhsType, functionType.returnType); |
| 932 |
| 933 // Skip the argument check - `int` cannot be downcast. |
| 934 // |
| 935 // Check the return type for an implicit cast. |
| 936 // |
| 937 // If needed, mark the assignment to indicate a down cast when we assign |
| 938 // back to it. So these two implicit casts are equivalent: |
| 939 // |
| 940 // y = /*implicit cast*/(y + 1); |
| 941 // /*implicit assignment cast*/y++; |
| 942 // |
| 943 _checkImplicitCast(operand, lhsType, from: returnType, opAssign: true); |
| 920 } | 944 } |
| 921 // For ++ and --, even if it is not dynamic, we still need to check | |
| 922 // that the user defined method accepts an `int` as the RHS. | |
| 923 // We assume Analyzer has done this already. | |
| 924 } | 945 } |
| 925 } | 946 } |
| 926 | 947 |
| 927 DartType _getDefiniteType(Expression expr) => | 948 DartType _getDefiniteType(Expression expr) => |
| 928 getDefiniteType(expr, rules, typeProvider); | 949 getDefiniteType(expr, rules, typeProvider); |
| 929 | 950 |
| 930 /// Gets the expected return type of the given function [body], either from | 951 /// Gets the expected return type of the given function [body], either from |
| 931 /// a normal return/yield, or from a yield*. | 952 /// a normal return/yield, or from a yield*. |
| 932 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) { | 953 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) { |
| 933 FunctionType functionType; | 954 FunctionType functionType; |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1014 } | 1035 } |
| 1015 | 1036 |
| 1016 void _recordDynamicInvoke(AstNode node, Expression target) { | 1037 void _recordDynamicInvoke(AstNode node, Expression target) { |
| 1017 _recordMessage(node, StrongModeCode.DYNAMIC_INVOKE, [node]); | 1038 _recordMessage(node, StrongModeCode.DYNAMIC_INVOKE, [node]); |
| 1018 // TODO(jmesserly): we may eventually want to record if the whole operation | 1039 // TODO(jmesserly): we may eventually want to record if the whole operation |
| 1019 // (node) was dynamic, rather than the target, but this is an easier fit | 1040 // (node) was dynamic, rather than the target, but this is an easier fit |
| 1020 // with what we used to do. | 1041 // with what we used to do. |
| 1021 if (target != null) setIsDynamicInvoke(target, true); | 1042 if (target != null) setIsDynamicInvoke(target, true); |
| 1022 } | 1043 } |
| 1023 | 1044 |
| 1024 /// Records an implicit cast for the [expression] from [fromType] to [toType]. | 1045 /// Records an implicit cast for the [expr] from [from] to [to]. |
| 1025 /// | 1046 /// |
| 1026 /// This will emit the appropriate error/warning/hint message as well as mark | 1047 /// This will emit the appropriate error/warning/hint message as well as mark |
| 1027 /// the AST node. | 1048 /// the AST node. |
| 1028 void _recordImplicitCast( | 1049 void _recordImplicitCast(Expression expr, DartType to, |
| 1029 Expression expression, DartType fromType, DartType toType) { | 1050 {DartType from, bool opAssign: false}) { |
| 1030 // toT <:_R fromT => to <: fromT | 1051 assert(rules.isSubtypeOf(to, from)); |
| 1031 // NB: classes with call methods are subtypes of function | |
| 1032 // types, but the function type is not assignable to the class | |
| 1033 assert(toType.isSubtypeOf(fromType)); | |
| 1034 | 1052 |
| 1035 // Inference "casts": | 1053 // Inference "casts": |
| 1036 if (expression is Literal || expression is FunctionExpression) { | 1054 if (expr is Literal || expr is FunctionExpression) { |
| 1037 // fromT should be an exact type - this will almost certainly fail at | 1055 // fromT should be an exact type - this will almost certainly fail at |
| 1038 // runtime. | 1056 // runtime. |
| 1039 _recordMessage(expression, StrongModeCode.STATIC_TYPE_ERROR, | 1057 _recordMessage(expr, StrongModeCode.STATIC_TYPE_ERROR, [expr, from, to]); |
| 1040 [expression, fromType, toType]); | |
| 1041 return; | 1058 return; |
| 1042 } | 1059 } |
| 1043 | 1060 |
| 1044 if (expression is InstanceCreationExpression) { | 1061 if (expr is InstanceCreationExpression) { |
| 1045 ConstructorElement e = expression.staticElement; | 1062 ConstructorElement e = expr.staticElement; |
| 1046 if (e == null || !e.isFactory) { | 1063 if (e == null || !e.isFactory) { |
| 1047 // fromT should be an exact type - this will almost certainly fail at | 1064 // fromT should be an exact type - this will almost certainly fail at |
| 1048 // runtime. | 1065 // runtime. |
| 1049 | 1066 |
| 1050 _recordMessage(expression, StrongModeCode.STATIC_TYPE_ERROR, | 1067 _recordMessage( |
| 1051 [expression, fromType, toType]); | 1068 expr, StrongModeCode.STATIC_TYPE_ERROR, [expr, from, to]); |
| 1052 return; | 1069 return; |
| 1053 } | 1070 } |
| 1054 } | 1071 } |
| 1055 | 1072 |
| 1056 if (isKnownFunction(expression)) { | 1073 if (isKnownFunction(expr)) { |
| 1057 _recordMessage(expression, StrongModeCode.STATIC_TYPE_ERROR, | 1074 _recordMessage(expr, StrongModeCode.STATIC_TYPE_ERROR, [expr, from, to]); |
| 1058 [expression, fromType, toType]); | |
| 1059 return; | 1075 return; |
| 1060 } | 1076 } |
| 1061 | 1077 |
| 1062 // TODO(vsm): Change this to an assert when we have generic methods and | 1078 // Composite cast: these are more likely to fail. |
| 1063 // fix TypeRules._coerceTo to disallow implicit sideways casts. | |
| 1064 bool downCastComposite = false; | 1079 bool downCastComposite = false; |
| 1065 if (!rules.isSubtypeOf(toType, fromType)) { | 1080 if (!rules.isGroundType(to)) { |
| 1066 assert(toType.isSubtypeOf(fromType) || fromType.isAssignableTo(toType)); | |
| 1067 downCastComposite = true; | |
| 1068 } | |
| 1069 | |
| 1070 // Composite cast: these are more likely to fail. | |
| 1071 if (!rules.isGroundType(toType)) { | |
| 1072 // This cast is (probably) due to our different treatment of dynamic. | 1081 // This cast is (probably) due to our different treatment of dynamic. |
| 1073 // It may be more likely to fail at runtime. | 1082 // It may be more likely to fail at runtime. |
| 1074 if (fromType is InterfaceType) { | 1083 if (from is InterfaceType) { |
| 1075 // For class types, we'd like to allow non-generic down casts, e.g., | 1084 // For class types, we'd like to allow non-generic down casts, e.g., |
| 1076 // Iterable<T> to List<T>. The intuition here is that raw (generic) | 1085 // Iterable<T> to List<T>. The intuition here is that raw (generic) |
| 1077 // casts are problematic, and we should complain about those. | 1086 // casts are problematic, and we should complain about those. |
| 1078 var typeArgs = fromType.typeArguments; | 1087 var typeArgs = from.typeArguments; |
| 1079 downCastComposite = | 1088 downCastComposite = |
| 1080 typeArgs.isEmpty || typeArgs.any((t) => t.isDynamic); | 1089 typeArgs.isEmpty || typeArgs.any((t) => t.isDynamic); |
| 1081 } else { | 1090 } else { |
| 1082 downCastComposite = true; | 1091 downCastComposite = true; |
| 1083 } | 1092 } |
| 1084 } | 1093 } |
| 1085 | 1094 |
| 1086 var parent = expression.parent; | 1095 var parent = expr.parent; |
| 1087 ErrorCode errorCode; | 1096 ErrorCode errorCode; |
| 1088 if (downCastComposite) { | 1097 if (downCastComposite) { |
| 1089 errorCode = StrongModeCode.DOWN_CAST_COMPOSITE; | 1098 errorCode = StrongModeCode.DOWN_CAST_COMPOSITE; |
| 1090 } else if (fromType.isDynamic) { | 1099 } else if (from.isDynamic) { |
| 1091 errorCode = StrongModeCode.DYNAMIC_CAST; | 1100 errorCode = StrongModeCode.DYNAMIC_CAST; |
| 1092 } else if (parent is VariableDeclaration && | 1101 } else if (parent is VariableDeclaration && parent.initializer == expr) { |
| 1093 parent.initializer == expression) { | |
| 1094 errorCode = StrongModeCode.ASSIGNMENT_CAST; | 1102 errorCode = StrongModeCode.ASSIGNMENT_CAST; |
| 1095 } else { | 1103 } else { |
| 1096 errorCode = StrongModeCode.DOWN_CAST_IMPLICIT; | 1104 errorCode = opAssign |
| 1105 ? StrongModeCode.DOWN_CAST_IMPLICIT_ASSIGN |
| 1106 : StrongModeCode.DOWN_CAST_IMPLICIT; |
| 1097 } | 1107 } |
| 1098 | 1108 _recordMessage(expr, errorCode, [from, to]); |
| 1099 _recordMessage(expression, errorCode, [fromType, toType]); | 1109 if (opAssign) { |
| 1100 setImplicitCast(expression, toType); | 1110 setImplicitAssignmentCast(expr, to); |
| 1111 } else { |
| 1112 setImplicitCast(expr, to); |
| 1113 } |
| 1101 _hasImplicitCasts = true; | 1114 _hasImplicitCasts = true; |
| 1102 } | 1115 } |
| 1103 | 1116 |
| 1104 void _recordMessage(AstNode node, ErrorCode errorCode, List arguments) { | 1117 void _recordMessage(AstNode node, ErrorCode errorCode, List arguments) { |
| 1105 var severity = errorCode.errorSeverity; | 1118 var severity = errorCode.errorSeverity; |
| 1106 if (severity == ErrorSeverity.ERROR) _failure = true; | 1119 if (severity == ErrorSeverity.ERROR) _failure = true; |
| 1107 if (severity != ErrorSeverity.INFO || _options.strongModeHints) { | 1120 if (severity != ErrorSeverity.INFO || _options.strongModeHints) { |
| 1108 int begin = node is AnnotatedNode | 1121 int begin = node is AnnotatedNode |
| 1109 ? node.firstTokenAfterCommentAndMetadata.offset | 1122 ? node.firstTokenAfterCommentAndMetadata.offset |
| 1110 : node.offset; | 1123 : node.offset; |
| (...skipping 353 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1464 var visited = new Set<InterfaceType>(); | 1477 var visited = new Set<InterfaceType>(); |
| 1465 do { | 1478 do { |
| 1466 visited.add(current); | 1479 visited.add(current); |
| 1467 current.mixins.reversed.forEach( | 1480 current.mixins.reversed.forEach( |
| 1468 (m) => _checkIndividualOverridesFromClass(node, m, seen, true)); | 1481 (m) => _checkIndividualOverridesFromClass(node, m, seen, true)); |
| 1469 _checkIndividualOverridesFromClass(node, current.superclass, seen, true); | 1482 _checkIndividualOverridesFromClass(node, current.superclass, seen, true); |
| 1470 current = current.superclass; | 1483 current = current.superclass; |
| 1471 } while (!current.isObject && !visited.contains(current)); | 1484 } while (!current.isObject && !visited.contains(current)); |
| 1472 } | 1485 } |
| 1473 } | 1486 } |
| OLD | NEW |