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 |