Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(21)

Side by Side Diff: pkg/analyzer/lib/src/task/strong/checker.dart

Issue 1780783002: Don't report redundant type errors in strong mode. (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Stop type propagation in test. Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « pkg/analyzer/lib/src/generated/type_system.dart ('k') | pkg/analyzer/lib/src/task/strong/info.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698