Chromium Code Reviews| 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 309 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 320 var staticInfo = | 320 var staticInfo = |
| 321 new InvalidParameterDeclaration(rules, node, fieldType); | 321 new InvalidParameterDeclaration(rules, node, fieldType); |
| 322 _recordMessage(staticInfo); | 322 _recordMessage(staticInfo); |
| 323 } | 323 } |
| 324 } | 324 } |
| 325 node.visitChildren(this); | 325 node.visitChildren(this); |
| 326 } | 326 } |
| 327 | 327 |
| 328 @override | 328 @override |
| 329 void visitForEachStatement(ForEachStatement node) { | 329 void visitForEachStatement(ForEachStatement node) { |
| 330 // Check that the expression is an Iterable. | |
| 331 var expr = node.iterable; | |
| 332 var iterableType = node.awaitKeyword != null | |
| 333 ? typeProvider.streamType | |
| 334 : typeProvider.iterableType; | |
| 335 var loopVariable = node.identifier != null | 330 var loopVariable = node.identifier != null |
| 336 ? node.identifier | 331 ? node.identifier |
| 337 : node.loopVariable?.identifier; | 332 : node.loopVariable?.identifier; |
| 333 | |
| 334 // Safely handle malformed statements. | |
| 338 if (loopVariable != null) { | 335 if (loopVariable != null) { |
| 339 var iteratorType = loopVariable.staticType; | 336 // Find up the element type of the sequence. |
|
vsm
2016/03/21 21:25:20
s/up//
Bob Nystrom
2016/03/21 22:44:55
Done.
| |
| 340 var checkedType = iterableType.instantiate([iteratorType]); | 337 var sequenceInterface = node.awaitKeyword != null |
| 341 checkAssignment(expr, checkedType); | 338 ? typeProvider.streamType |
| 339 : typeProvider.iterableType; | |
| 340 var iterableType = _getStaticType(node.iterable); | |
| 341 var elementType = | |
| 342 rules.mostSpecificTypeArgument(iterableType, sequenceInterface); | |
| 343 | |
| 344 // If the sequence is dynamic, cast it to Iterable<dynamic> or | |
| 345 // Stream<dynamic>. Then we'll do a separate cast of the dynamic element | |
| 346 // to the variable's type. | |
| 347 if (iterableType.isDynamic) { | |
|
vsm
2016/03/21 21:25:20
Should we just do checkAssignment(node.iterable, s
Bob Nystrom
2016/03/21 22:44:55
I don't think we want that because we don't want a
vsm
2016/03/21 22:50:35
It'd be a cast to Iterable, not Iterable<int> - i.
Bob Nystrom
2016/03/21 23:00:44
OK, then we're thinking the same thing. That's wha
| |
| 348 _recordMessage(DownCast.create( | |
| 349 rules, node.iterable, iterableType, sequenceInterface)); | |
| 350 elementType = DynamicTypeImpl.instance; | |
| 351 } | |
| 352 | |
| 353 // If the sequence doesn't implement the interface at all, [ErrorVerifier] | |
| 354 // will report the error, so ignore it here. | |
| 355 if (elementType != null) { | |
| 356 // Insert a cast from the sequence's element type to the loop variable's | |
| 357 // if needed. | |
| 358 _checkDowncast(loopVariable, _getStaticType(loopVariable), | |
| 359 from: elementType); | |
| 360 } | |
| 342 } | 361 } |
| 362 | |
| 343 node.visitChildren(this); | 363 node.visitChildren(this); |
| 344 } | 364 } |
| 345 | 365 |
| 346 @override | 366 @override |
| 347 void visitForStatement(ForStatement node) { | 367 void visitForStatement(ForStatement node) { |
| 348 if (node.condition != null) { | 368 if (node.condition != null) { |
| 349 checkBoolean(node.condition); | 369 checkBoolean(node.condition); |
| 350 } | 370 } |
| 351 node.visitChildren(this); | 371 node.visitChildren(this); |
| 352 } | 372 } |
| (...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 588 var returnType = _specializedBinaryReturnType( | 608 var returnType = _specializedBinaryReturnType( |
| 589 op, lhsType, rhsType, functionType.returnType); | 609 op, lhsType, rhsType, functionType.returnType); |
| 590 | 610 |
| 591 if (!rules.isSubtypeOf(returnType, lhsType)) { | 611 if (!rules.isSubtypeOf(returnType, lhsType)) { |
| 592 final numType = typeProvider.numType; | 612 final numType = typeProvider.numType; |
| 593 // Try to fix up the numerical case if possible. | 613 // Try to fix up the numerical case if possible. |
| 594 if (rules.isSubtypeOf(lhsType, numType) && | 614 if (rules.isSubtypeOf(lhsType, numType) && |
| 595 rules.isSubtypeOf(lhsType, rhsType)) { | 615 rules.isSubtypeOf(lhsType, rhsType)) { |
| 596 // This is also slightly different from spec, but allows us to keep | 616 // This is also slightly different from spec, but allows us to keep |
| 597 // compound operators in the int += num and num += dynamic cases. | 617 // compound operators in the int += num and num += dynamic cases. |
| 598 staticInfo = DownCast.create( | 618 staticInfo = |
| 599 rules, expr.rightHandSide, rhsType, lhsType); | 619 DownCast.create(rules, expr.rightHandSide, rhsType, lhsType); |
| 600 rhsType = lhsType; | 620 rhsType = lhsType; |
| 601 } else { | 621 } else { |
| 602 staticInfo = new StaticTypeError(rules, expr, lhsType); | 622 staticInfo = new StaticTypeError(rules, expr, lhsType); |
| 603 } | 623 } |
| 604 _recordMessage(staticInfo); | 624 _recordMessage(staticInfo); |
| 605 } | 625 } |
| 606 | 626 |
| 607 // Check the rhs type | 627 // Check the rhs type |
| 608 if (staticInfo is! CoercionInfo) { | 628 if (staticInfo is! CoercionInfo) { |
| 609 var paramType = paramTypes.first; | 629 var paramType = paramTypes.first; |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 656 op.type == TokenType.MINUS_MINUS) { | 676 op.type == TokenType.MINUS_MINUS) { |
| 657 if (_isDynamicTarget(node.operand)) { | 677 if (_isDynamicTarget(node.operand)) { |
| 658 _recordDynamicInvoke(node, node.operand); | 678 _recordDynamicInvoke(node, node.operand); |
| 659 } | 679 } |
| 660 // For ++ and --, even if it is not dynamic, we still need to check | 680 // For ++ and --, even if it is not dynamic, we still need to check |
| 661 // that the user defined method accepts an `int` as the RHS. | 681 // that the user defined method accepts an `int` as the RHS. |
| 662 // We assume Analyzer has done this already. | 682 // We assume Analyzer has done this already. |
| 663 } | 683 } |
| 664 } | 684 } |
| 665 | 685 |
| 666 /// Records a [DownCast] of [expr] to [toT], if there is one. | 686 /// Records a [DownCast] of [expr] from [from] to [to], if there is one. |
| 667 /// | 687 /// |
| 668 /// If [expr] does not require a downcast because it is not related to [toT] | 688 /// If [from] is omitted, uses the static type of [expr]. |
| 689 /// | |
| 690 /// If [expr] does not require a downcast because it is not related to [to] | |
| 669 /// or is already a subtype of it, does nothing. | 691 /// or is already a subtype of it, does nothing. |
| 670 void _checkDowncast(Expression expr, DartType toT) { | 692 void _checkDowncast(Expression expr, DartType to, {DartType from}) { |
| 671 DartType fromT = _getStaticType(expr); | 693 if (from == null) { |
| 694 from = _getStaticType(expr); | |
| 695 } | |
| 672 | 696 |
| 673 // We can use anything as void. | 697 // We can use anything as void. |
| 674 if (toT.isVoid) return; | 698 if (to.isVoid) return; |
| 675 | 699 |
| 676 // fromT <: toT, no coercion needed. | 700 // fromT <: toT, no coercion needed. |
| 677 if (rules.isSubtypeOf(fromT, toT)) return; | 701 if (rules.isSubtypeOf(from, to)) return; |
| 678 | 702 |
| 679 // TODO(vsm): We can get rid of the second clause if we disallow | 703 // TODO(vsm): We can get rid of the second clause if we disallow |
| 680 // all sideways casts - see TODO below. | 704 // all sideways casts - see TODO below. |
| 681 // ------- | 705 // ------- |
| 682 // Note: a function type is never assignable to a class per the Dart | 706 // Note: a function type is never assignable to a class per the Dart |
| 683 // spec - even if it has a compatible call method. We disallow as | 707 // spec - even if it has a compatible call method. We disallow as |
| 684 // well for consistency. | 708 // well for consistency. |
| 685 if ((fromT is FunctionType && rules.getCallMethodType(toT) != null) || | 709 if ((from is FunctionType && rules.getCallMethodType(to) != null) || |
| 686 (toT is FunctionType && rules.getCallMethodType(fromT) != null)) { | 710 (to is FunctionType && rules.getCallMethodType(from) != null)) { |
| 687 return; | 711 return; |
| 688 } | 712 } |
| 689 | 713 |
| 690 // Downcast if toT <: fromT | 714 // Downcast if toT <: fromT |
| 691 if (rules.isSubtypeOf(toT, fromT)) { | 715 if (rules.isSubtypeOf(to, from)) { |
| 692 _recordMessage(DownCast.create(rules, expr, fromT, toT)); | 716 _recordMessage(DownCast.create(rules, expr, from, to)); |
| 693 return; | 717 return; |
| 694 } | 718 } |
| 695 | 719 |
| 696 // TODO(vsm): Once we have generic methods, we should delete this | 720 // TODO(vsm): Once we have generic methods, we should delete this |
| 697 // workaround. These sideways casts are always ones we warn about | 721 // workaround. These sideways casts are always ones we warn about |
| 698 // - i.e., we think they are likely to fail at runtime. | 722 // - i.e., we think they are likely to fail at runtime. |
| 699 // ------- | 723 // ------- |
| 700 // Downcast if toT <===> fromT | 724 // Downcast if toT <===> fromT |
| 701 // The intention here is to allow casts that are sideways in the restricted | 725 // The intention here is to allow casts that are sideways in the restricted |
| 702 // type system, but allowed in the regular dart type system, since these | 726 // type system, but allowed in the regular dart type system, since these |
| 703 // are likely to succeed. The canonical example is List<dynamic> and | 727 // are likely to succeed. The canonical example is List<dynamic> and |
| 704 // Iterable<T> for some concrete T (e.g. Object). These are unrelated | 728 // Iterable<T> for some concrete T (e.g. Object). These are unrelated |
| 705 // in the restricted system, but List<dynamic> <: Iterable<T> in dart. | 729 // in the restricted system, but List<dynamic> <: Iterable<T> in dart. |
| 706 if (fromT.isAssignableTo(toT)) { | 730 if (from.isAssignableTo(to)) { |
| 707 _recordMessage(DownCast.create(rules, expr, fromT, toT)); | 731 _recordMessage(DownCast.create(rules, expr, from, to)); |
| 708 } | 732 } |
| 709 } | 733 } |
| 710 | 734 |
| 711 // Produce a coercion which coerces something of type fromT | 735 // Produce a coercion which coerces something of type fromT |
| 712 // to something of type toT. | 736 // to something of type toT. |
| 713 // Returns the error coercion if the types cannot be coerced | 737 // Returns the error coercion if the types cannot be coerced |
| 714 // according to our current criteria. | 738 // according to our current criteria. |
| 715 /// Gets the expected return type of the given function [body], either from | 739 /// Gets the expected return type of the given function [body], either from |
| 716 /// a normal return/yield, or from a yield*. | 740 /// a normal return/yield, or from a yield*. |
| 717 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) { | 741 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) { |
| (...skipping 553 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1271 } while (!current.isObject && !visited.contains(current)); | 1295 } while (!current.isObject && !visited.contains(current)); |
| 1272 } | 1296 } |
| 1273 | 1297 |
| 1274 void _recordMessage(StaticInfo info) { | 1298 void _recordMessage(StaticInfo info) { |
| 1275 if (info == null) return; | 1299 if (info == null) return; |
| 1276 var error = info.toAnalysisError(); | 1300 var error = info.toAnalysisError(); |
| 1277 if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true; | 1301 if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true; |
| 1278 _reporter.onError(error); | 1302 _reporter.onError(error); |
| 1279 } | 1303 } |
| 1280 } | 1304 } |
| OLD | NEW |