Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 library analyzer.src.generated.resolver; | 5 library analyzer.src.generated.resolver; |
| 6 | 6 |
| 7 import 'dart:collection'; | 7 import 'dart:collection'; |
| 8 | 8 |
| 9 import 'package:analyzer/dart/ast/ast.dart'; | 9 import 'package:analyzer/dart/ast/ast.dart'; |
| 10 import 'package:analyzer/dart/ast/token.dart'; | 10 import 'package:analyzer/dart/ast/token.dart'; |
| (...skipping 3481 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3492 * expression, or simple infinite loop such as `while(true)`. | 3492 * expression, or simple infinite loop such as `while(true)`. |
| 3493 */ | 3493 */ |
| 3494 class ExitDetector extends GeneralizingAstVisitor<bool> { | 3494 class ExitDetector extends GeneralizingAstVisitor<bool> { |
| 3495 /** | 3495 /** |
| 3496 * Set to `true` when a `break` is encountered, and reset to `false` when a | 3496 * Set to `true` when a `break` is encountered, and reset to `false` when a |
| 3497 * `do`, `while`, `for` or `switch` block is entered. | 3497 * `do`, `while`, `for` or `switch` block is entered. |
| 3498 */ | 3498 */ |
| 3499 bool _enclosingBlockContainsBreak = false; | 3499 bool _enclosingBlockContainsBreak = false; |
| 3500 | 3500 |
| 3501 /** | 3501 /** |
| 3502 * Set to `true` when a `continue` is encountered, and reset to `false` when a | |
| 3503 * `do`, `while`, `for` or `switch` block is entered. | |
| 3504 */ | |
| 3505 bool _enclosingBlockContainsContinue = false; | |
| 3506 | |
| 3507 /** | |
| 3502 * Add node when a labelled `break` is encountered. | 3508 * Add node when a labelled `break` is encountered. |
| 3503 */ | 3509 */ |
| 3504 Set<AstNode> _enclosingBlockBreaksLabel = new Set<AstNode>(); | 3510 Set<AstNode> _enclosingBlockBreaksLabel = new Set<AstNode>(); |
| 3505 | 3511 |
| 3506 @override | 3512 @override |
| 3507 bool visitArgumentList(ArgumentList node) => | 3513 bool visitArgumentList(ArgumentList node) => |
| 3508 _visitExpressions(node.arguments); | 3514 _visitExpressions(node.arguments); |
| 3509 | 3515 |
| 3510 @override | 3516 @override |
| 3511 bool visitAsExpression(AsExpression node) => _nodeExits(node.expression); | 3517 bool visitAsExpression(AsExpression node) => _nodeExits(node.expression); |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3599 if (_nodeExits(conditionExpression)) { | 3605 if (_nodeExits(conditionExpression)) { |
| 3600 return true; | 3606 return true; |
| 3601 } | 3607 } |
| 3602 if (thenStatement == null || elseStatement == null) { | 3608 if (thenStatement == null || elseStatement == null) { |
| 3603 return false; | 3609 return false; |
| 3604 } | 3610 } |
| 3605 return thenStatement.accept(this) && elseStatement.accept(this); | 3611 return thenStatement.accept(this) && elseStatement.accept(this); |
| 3606 } | 3612 } |
| 3607 | 3613 |
| 3608 @override | 3614 @override |
| 3609 bool visitContinueStatement(ContinueStatement node) => false; | 3615 bool visitContinueStatement(ContinueStatement node) { |
| 3616 _enclosingBlockContainsContinue = true; | |
| 3617 return false; | |
| 3618 } | |
| 3610 | 3619 |
| 3611 @override | 3620 @override |
| 3612 bool visitDoStatement(DoStatement node) { | 3621 bool visitDoStatement(DoStatement node) { |
| 3613 bool outerBreakValue = _enclosingBlockContainsBreak; | 3622 bool outerBreakValue = _enclosingBlockContainsBreak; |
| 3623 bool outerContinueValue = _enclosingBlockContainsContinue; | |
| 3614 _enclosingBlockContainsBreak = false; | 3624 _enclosingBlockContainsBreak = false; |
| 3625 _enclosingBlockContainsContinue = false; | |
| 3615 try { | 3626 try { |
| 3616 if (_nodeExits(node.body) && !_enclosingBlockContainsBreak) { | 3627 bool bodyExits = _nodeExits(node.body); |
| 3628 bool containsBreakOrContinue = | |
| 3629 _enclosingBlockContainsBreak || _enclosingBlockContainsContinue; | |
| 3630 // Even if we determine that the body "exits", there might be break or | |
| 3631 // continue statements that actually mean it _doesn't_ always exit. | |
| 3632 if (bodyExits && !containsBreakOrContinue) { | |
| 3617 return true; | 3633 return true; |
| 3618 } | 3634 } |
| 3619 Expression conditionExpression = node.condition; | 3635 Expression conditionExpression = node.condition; |
| 3620 if (_nodeExits(conditionExpression)) { | 3636 if (_nodeExits(conditionExpression)) { |
| 3621 return true; | 3637 return true; |
| 3622 } | 3638 } |
| 3623 // TODO(jwren) Do we want to take all constant expressions into account? | 3639 // TODO(jwren) Do we want to take all constant expressions into account? |
| 3624 if (conditionExpression is BooleanLiteral) { | 3640 if (conditionExpression is BooleanLiteral) { |
| 3625 // If do {} while (true), and the body doesn't break, then return true. | 3641 // If do {} while (true), and the body doesn't break, then return true. |
| 3626 if (conditionExpression.value && !_enclosingBlockContainsBreak) { | 3642 if (conditionExpression.value && !_enclosingBlockContainsBreak) { |
| 3627 return true; | 3643 return true; |
| 3628 } | 3644 } |
| 3629 } | 3645 } |
| 3630 return false; | 3646 return false; |
| 3631 } finally { | 3647 } finally { |
| 3632 _enclosingBlockContainsBreak = outerBreakValue; | 3648 _enclosingBlockContainsBreak = outerBreakValue; |
| 3649 _enclosingBlockContainsContinue = outerContinueValue; | |
| 3633 } | 3650 } |
| 3634 } | 3651 } |
| 3635 | 3652 |
| 3636 @override | 3653 @override |
| 3637 bool visitEmptyStatement(EmptyStatement node) => false; | 3654 bool visitEmptyStatement(EmptyStatement node) => false; |
| 3638 | 3655 |
| 3639 @override | 3656 @override |
| 3640 bool visitExpressionStatement(ExpressionStatement node) => | 3657 bool visitExpressionStatement(ExpressionStatement node) => |
| 3641 _nodeExits(node.expression); | 3658 _nodeExits(node.expression); |
| 3642 | 3659 |
| 3643 @override | 3660 @override |
| 3644 bool visitForEachStatement(ForEachStatement node) { | 3661 bool visitForEachStatement(ForEachStatement node) { |
| 3645 bool outerBreakValue = _enclosingBlockContainsBreak; | 3662 bool outerBreakValue = _enclosingBlockContainsBreak; |
| 3663 bool outerContinueValue = _enclosingBlockContainsContinue; | |
| 3646 _enclosingBlockContainsBreak = false; | 3664 _enclosingBlockContainsBreak = false; |
| 3665 _enclosingBlockContainsContinue = false; | |
| 3647 try { | 3666 try { |
| 3648 return _nodeExits(node.iterable); | 3667 return _nodeExits(node.iterable); |
| 3649 } finally { | 3668 } finally { |
| 3650 _enclosingBlockContainsBreak = outerBreakValue; | 3669 _enclosingBlockContainsBreak = outerBreakValue; |
| 3670 _enclosingBlockContainsContinue = outerContinueValue; | |
| 3651 } | 3671 } |
| 3652 } | 3672 } |
| 3653 | 3673 |
| 3654 @override | 3674 @override |
| 3655 bool visitForStatement(ForStatement node) { | 3675 bool visitForStatement(ForStatement node) { |
| 3656 bool outerBreakValue = _enclosingBlockContainsBreak; | 3676 bool outerBreakValue = _enclosingBlockContainsBreak; |
| 3677 bool outerContinueValue = _enclosingBlockContainsContinue; | |
| 3657 _enclosingBlockContainsBreak = false; | 3678 _enclosingBlockContainsBreak = false; |
| 3679 _enclosingBlockContainsContinue = false; | |
| 3658 try { | 3680 try { |
| 3659 if (node.variables != null && | 3681 if (node.variables != null && |
| 3660 _visitVariableDeclarations(node.variables.variables)) { | 3682 _visitVariableDeclarations(node.variables.variables)) { |
| 3661 return true; | 3683 return true; |
| 3662 } | 3684 } |
| 3663 if (node.initialization != null && _nodeExits(node.initialization)) { | 3685 if (node.initialization != null && _nodeExits(node.initialization)) { |
| 3664 return true; | 3686 return true; |
| 3665 } | 3687 } |
| 3666 Expression conditionExpression = node.condition; | 3688 Expression conditionExpression = node.condition; |
| 3667 if (conditionExpression != null && _nodeExits(conditionExpression)) { | 3689 if (conditionExpression != null && _nodeExits(conditionExpression)) { |
| 3668 return true; | 3690 return true; |
| 3669 } | 3691 } |
| 3670 if (_visitExpressions(node.updaters)) { | 3692 if (_visitExpressions(node.updaters)) { |
| 3671 return true; | 3693 return true; |
| 3672 } | 3694 } |
| 3673 bool blockReturns = _nodeExits(node.body); | 3695 bool blockReturns = _nodeExits(node.body); |
| 3674 // TODO(jwren) Do we want to take all constant expressions into account? | 3696 // TODO(jwren) Do we want to take all constant expressions into account? |
| 3675 // If for(; true; ) (or for(;;)), and the body doesn't return or the body | 3697 // If for(; true; ) (or for(;;)), and the body doesn't return or the body |
| 3676 // doesn't have a break, then return true. | 3698 // doesn't have a break, then return true. |
| 3677 bool implicitOrExplictTrue = conditionExpression == null || | 3699 bool implicitOrExplictTrue = conditionExpression == null || |
| 3678 (conditionExpression is BooleanLiteral && conditionExpression.value); | 3700 (conditionExpression is BooleanLiteral && conditionExpression.value); |
| 3679 if (implicitOrExplictTrue) { | 3701 if (implicitOrExplictTrue) { |
| 3680 if (blockReturns || !_enclosingBlockContainsBreak) { | 3702 if (blockReturns || !_enclosingBlockContainsBreak) { |
| 3681 return true; | 3703 return true; |
| 3682 } | 3704 } |
| 3683 } | 3705 } |
| 3684 return false; | 3706 return false; |
| 3685 } finally { | 3707 } finally { |
| 3686 _enclosingBlockContainsBreak = outerBreakValue; | 3708 _enclosingBlockContainsBreak = outerBreakValue; |
| 3709 _enclosingBlockContainsContinue = outerContinueValue; | |
| 3687 } | 3710 } |
| 3688 } | 3711 } |
| 3689 | 3712 |
| 3690 @override | 3713 @override |
| 3691 bool visitFunctionDeclarationStatement(FunctionDeclarationStatement node) => | 3714 bool visitFunctionDeclarationStatement(FunctionDeclarationStatement node) => |
| 3692 false; | 3715 false; |
| 3693 | 3716 |
| 3694 @override | 3717 @override |
| 3695 bool visitFunctionExpression(FunctionExpression node) => false; | 3718 bool visitFunctionExpression(FunctionExpression node) => false; |
| 3696 | 3719 |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3817 @override | 3840 @override |
| 3818 bool visitSwitchCase(SwitchCase node) => _visitStatements(node.statements); | 3841 bool visitSwitchCase(SwitchCase node) => _visitStatements(node.statements); |
| 3819 | 3842 |
| 3820 @override | 3843 @override |
| 3821 bool visitSwitchDefault(SwitchDefault node) => | 3844 bool visitSwitchDefault(SwitchDefault node) => |
| 3822 _visitStatements(node.statements); | 3845 _visitStatements(node.statements); |
| 3823 | 3846 |
| 3824 @override | 3847 @override |
| 3825 bool visitSwitchStatement(SwitchStatement node) { | 3848 bool visitSwitchStatement(SwitchStatement node) { |
| 3826 bool outerBreakValue = _enclosingBlockContainsBreak; | 3849 bool outerBreakValue = _enclosingBlockContainsBreak; |
| 3850 bool outerContinueValue = _enclosingBlockContainsContinue; | |
|
Paul Berry
2016/06/28 19:17:41
I don't think this method should be modified. Swi
srawlins
2016/06/28 20:00:55
Done. With testcase.
| |
| 3827 _enclosingBlockContainsBreak = false; | 3851 _enclosingBlockContainsBreak = false; |
| 3852 _enclosingBlockContainsContinue = false; | |
| 3828 try { | 3853 try { |
| 3829 bool hasDefault = false; | 3854 bool hasDefault = false; |
| 3830 bool hasNonExitingCase = false; | 3855 bool hasNonExitingCase = false; |
| 3831 List<SwitchMember> members = node.members; | 3856 List<SwitchMember> members = node.members; |
| 3832 for (int i = 0; i < members.length; i++) { | 3857 for (int i = 0; i < members.length; i++) { |
| 3833 SwitchMember switchMember = members[i]; | 3858 SwitchMember switchMember = members[i]; |
| 3834 if (switchMember is SwitchDefault) { | 3859 if (switchMember is SwitchDefault) { |
| 3835 hasDefault = true; | 3860 hasDefault = true; |
| 3836 // If this is the last member and there are no statements, then it | 3861 // If this is the last member and there are no statements, then it |
| 3837 // does not exit. | 3862 // does not exit. |
| 3838 if (switchMember.statements.isEmpty && i + 1 == members.length) { | 3863 if (switchMember.statements.isEmpty && i + 1 == members.length) { |
| 3839 hasNonExitingCase = true; | 3864 hasNonExitingCase = true; |
| 3840 continue; | 3865 continue; |
| 3841 } | 3866 } |
| 3842 } | 3867 } |
| 3843 // For switch members with no statements, don't visit the children. | 3868 // For switch members with no statements, don't visit the children. |
| 3844 // Otherwise, if there children statements don't exit, mark this as a | 3869 // Otherwise, if there children statements don't exit, mark this as a |
| 3845 // non-exiting case. | 3870 // non-exiting case. |
| 3846 if (!switchMember.statements.isEmpty && !switchMember.accept(this)) { | 3871 if (!switchMember.statements.isEmpty && !switchMember.accept(this)) { |
| 3847 hasNonExitingCase = true; | 3872 hasNonExitingCase = true; |
| 3848 } | 3873 } |
| 3849 } | 3874 } |
| 3850 if (hasNonExitingCase) { | 3875 if (hasNonExitingCase) { |
| 3851 return false; | 3876 return false; |
| 3852 } | 3877 } |
| 3853 // As all cases exit, return whether that list includes `default`. | 3878 // As all cases exit, return whether that list includes `default`. |
| 3854 return hasDefault; | 3879 return hasDefault; |
| 3855 } finally { | 3880 } finally { |
| 3856 _enclosingBlockContainsBreak = outerBreakValue; | 3881 _enclosingBlockContainsBreak = outerBreakValue; |
| 3882 _enclosingBlockContainsContinue = outerContinueValue; | |
| 3857 } | 3883 } |
| 3858 } | 3884 } |
| 3859 | 3885 |
| 3860 @override | 3886 @override |
| 3861 bool visitThisExpression(ThisExpression node) => false; | 3887 bool visitThisExpression(ThisExpression node) => false; |
| 3862 | 3888 |
| 3863 @override | 3889 @override |
| 3864 bool visitThrowExpression(ThrowExpression node) => true; | 3890 bool visitThrowExpression(ThrowExpression node) => true; |
| 3865 | 3891 |
| 3866 @override | 3892 @override |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3902 if (variables[i].accept(this)) { | 3928 if (variables[i].accept(this)) { |
| 3903 return true; | 3929 return true; |
| 3904 } | 3930 } |
| 3905 } | 3931 } |
| 3906 return false; | 3932 return false; |
| 3907 } | 3933 } |
| 3908 | 3934 |
| 3909 @override | 3935 @override |
| 3910 bool visitWhileStatement(WhileStatement node) { | 3936 bool visitWhileStatement(WhileStatement node) { |
| 3911 bool outerBreakValue = _enclosingBlockContainsBreak; | 3937 bool outerBreakValue = _enclosingBlockContainsBreak; |
| 3938 bool outerContinueValue = _enclosingBlockContainsContinue; | |
| 3912 _enclosingBlockContainsBreak = false; | 3939 _enclosingBlockContainsBreak = false; |
| 3940 _enclosingBlockContainsContinue = false; | |
| 3913 try { | 3941 try { |
| 3914 Expression conditionExpression = node.condition; | 3942 Expression conditionExpression = node.condition; |
| 3915 if (conditionExpression.accept(this)) { | 3943 if (conditionExpression.accept(this)) { |
| 3916 return true; | 3944 return true; |
| 3917 } | 3945 } |
| 3918 node.body.accept(this); | 3946 node.body.accept(this); |
| 3919 // TODO(jwren) Do we want to take all constant expressions into account? | 3947 // TODO(jwren) Do we want to take all constant expressions into account? |
| 3920 if (conditionExpression is BooleanLiteral) { | 3948 if (conditionExpression is BooleanLiteral) { |
| 3921 // If while(true), and the body doesn't have a break, then return true. | 3949 // If while(true), and the body doesn't have a break, then return true. |
| 3922 // The body might be found to exit, but if there are any break | 3950 // The body might be found to exit, but if there are any break |
| 3923 // statements, then it is a faulty finding. In other words: | 3951 // statements, then it is a faulty finding. In other words: |
| 3924 // | 3952 // |
| 3925 // * If the body exits, and does not contain a break statement, then | 3953 // * If the body exits, and does not contain a break statement, then |
| 3926 // it exits. | 3954 // it exits. |
| 3927 // * If the body does not exit, and does not contain a break statement, | 3955 // * If the body does not exit, and does not contain a break statement, |
| 3928 // then it loops infinitely (also an exit). | 3956 // then it loops infinitely (also an exit). |
| 3929 // | 3957 // |
| 3930 // As both conditions forbid any break statements to be found, the logic | 3958 // As both conditions forbid any break statements to be found, the logic |
| 3931 // just boils down to checking [_enclosingBlockContainsBreak]. | 3959 // just boils down to checking [_enclosingBlockContainsBreak]. |
| 3932 if (conditionExpression.value && !_enclosingBlockContainsBreak) { | 3960 if (conditionExpression.value && !_enclosingBlockContainsBreak) { |
| 3933 return true; | 3961 return true; |
| 3934 } | 3962 } |
| 3935 } | 3963 } |
| 3936 return false; | 3964 return false; |
| 3937 } finally { | 3965 } finally { |
| 3938 _enclosingBlockContainsBreak = outerBreakValue; | 3966 _enclosingBlockContainsBreak = outerBreakValue; |
| 3967 _enclosingBlockContainsContinue = outerContinueValue; | |
| 3939 } | 3968 } |
| 3940 } | 3969 } |
| 3941 | 3970 |
| 3942 @override | 3971 @override |
| 3943 bool visitYieldStatement(YieldStatement node) => _nodeExits(node.expression); | 3972 bool visitYieldStatement(YieldStatement node) => _nodeExits(node.expression); |
| 3944 | 3973 |
| 3945 /** | 3974 /** |
| 3946 * Return `true` if the given node exits. | 3975 * Return `true` if the given node exits. |
| 3947 * | 3976 * |
| 3948 * @param node the node being tested | 3977 * @param node the node being tested |
| (...skipping 7091 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 11040 return null; | 11069 return null; |
| 11041 } | 11070 } |
| 11042 if (identical(node.staticElement, variable)) { | 11071 if (identical(node.staticElement, variable)) { |
| 11043 if (node.inSetterContext()) { | 11072 if (node.inSetterContext()) { |
| 11044 result = true; | 11073 result = true; |
| 11045 } | 11074 } |
| 11046 } | 11075 } |
| 11047 return null; | 11076 return null; |
| 11048 } | 11077 } |
| 11049 } | 11078 } |
| OLD | NEW |