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 3452 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3463 * to terminate by executing a `return` statement, `throw` expression, `rethrow` | 3463 * to terminate by executing a `return` statement, `throw` expression, `rethrow` |
3464 * expression, or simple infinite loop such as `while(true)`. | 3464 * expression, or simple infinite loop such as `while(true)`. |
3465 */ | 3465 */ |
3466 class ExitDetector extends GeneralizingAstVisitor<bool> { | 3466 class ExitDetector extends GeneralizingAstVisitor<bool> { |
3467 /** | 3467 /** |
3468 * Set to `true` when a `break` is encountered, and reset to `false` when a | 3468 * Set to `true` when a `break` is encountered, and reset to `false` when a |
3469 * `do`, `while`, `for` or `switch` block is entered. | 3469 * `do`, `while`, `for` or `switch` block is entered. |
3470 */ | 3470 */ |
3471 bool _enclosingBlockContainsBreak = false; | 3471 bool _enclosingBlockContainsBreak = false; |
3472 | 3472 |
| 3473 /** |
| 3474 * Add node when a labelled `break` is encountered. |
| 3475 */ |
| 3476 Set<AstNode> _enclosingBlockBreaksLabel = new Set<AstNode>(); |
| 3477 |
3473 @override | 3478 @override |
3474 bool visitArgumentList(ArgumentList node) => | 3479 bool visitArgumentList(ArgumentList node) => |
3475 _visitExpressions(node.arguments); | 3480 _visitExpressions(node.arguments); |
3476 | 3481 |
3477 @override | 3482 @override |
3478 bool visitAsExpression(AsExpression node) => _nodeExits(node.expression); | 3483 bool visitAsExpression(AsExpression node) => _nodeExits(node.expression); |
3479 | 3484 |
3480 @override | 3485 @override |
3481 bool visitAssertStatement(AssertStatement node) => false; | 3486 bool visitAssertStatement(AssertStatement node) => false; |
3482 | 3487 |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3538 | 3543 |
3539 @override | 3544 @override |
3540 bool visitBlock(Block node) => _visitStatements(node.statements); | 3545 bool visitBlock(Block node) => _visitStatements(node.statements); |
3541 | 3546 |
3542 @override | 3547 @override |
3543 bool visitBlockFunctionBody(BlockFunctionBody node) => _nodeExits(node.block); | 3548 bool visitBlockFunctionBody(BlockFunctionBody node) => _nodeExits(node.block); |
3544 | 3549 |
3545 @override | 3550 @override |
3546 bool visitBreakStatement(BreakStatement node) { | 3551 bool visitBreakStatement(BreakStatement node) { |
3547 _enclosingBlockContainsBreak = true; | 3552 _enclosingBlockContainsBreak = true; |
| 3553 if (node.label != null) { |
| 3554 _enclosingBlockBreaksLabel.add(node.target); |
| 3555 } |
3548 return false; | 3556 return false; |
3549 } | 3557 } |
3550 | 3558 |
3551 @override | 3559 @override |
3552 bool visitCascadeExpression(CascadeExpression node) => | 3560 bool visitCascadeExpression(CascadeExpression node) => |
3553 _nodeExits(node.target) || _visitExpressions(node.cascadeSections); | 3561 _nodeExits(node.target) || _visitExpressions(node.cascadeSections); |
3554 | 3562 |
3555 @override | 3563 @override |
3556 bool visitConditionalExpression(ConditionalExpression node) { | 3564 bool visitConditionalExpression(ConditionalExpression node) { |
3557 Expression conditionExpression = node.condition; | 3565 Expression conditionExpression = node.condition; |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3627 if (node.initialization != null && _nodeExits(node.initialization)) { | 3635 if (node.initialization != null && _nodeExits(node.initialization)) { |
3628 return true; | 3636 return true; |
3629 } | 3637 } |
3630 Expression conditionExpression = node.condition; | 3638 Expression conditionExpression = node.condition; |
3631 if (conditionExpression != null && _nodeExits(conditionExpression)) { | 3639 if (conditionExpression != null && _nodeExits(conditionExpression)) { |
3632 return true; | 3640 return true; |
3633 } | 3641 } |
3634 if (_visitExpressions(node.updaters)) { | 3642 if (_visitExpressions(node.updaters)) { |
3635 return true; | 3643 return true; |
3636 } | 3644 } |
| 3645 bool blockReturns = _nodeExits(node.body); |
3637 // TODO(jwren) Do we want to take all constant expressions into account? | 3646 // TODO(jwren) Do we want to take all constant expressions into account? |
3638 // If for(; true; ) (or for(;;)), and the body doesn't return or the body | 3647 // If for(; true; ) (or for(;;)), and the body doesn't return or the body |
3639 // doesn't have a break, then return true. | 3648 // doesn't have a break, then return true. |
3640 bool implicitOrExplictTrue = conditionExpression == null || | 3649 bool implicitOrExplictTrue = conditionExpression == null || |
3641 (conditionExpression is BooleanLiteral && conditionExpression.value); | 3650 (conditionExpression is BooleanLiteral && conditionExpression.value); |
3642 if (implicitOrExplictTrue) { | 3651 if (implicitOrExplictTrue) { |
3643 bool blockReturns = _nodeExits(node.body); | |
3644 if (blockReturns || !_enclosingBlockContainsBreak) { | 3652 if (blockReturns || !_enclosingBlockContainsBreak) { |
3645 return true; | 3653 return true; |
3646 } | 3654 } |
3647 } | 3655 } |
3648 return false; | 3656 return false; |
3649 } finally { | 3657 } finally { |
3650 _enclosingBlockContainsBreak = outerBreakValue; | 3658 _enclosingBlockContainsBreak = outerBreakValue; |
3651 } | 3659 } |
3652 } | 3660 } |
3653 | 3661 |
(...skipping 19 matching lines...) Expand all Loading... |
3673 bool visitIfStatement(IfStatement node) { | 3681 bool visitIfStatement(IfStatement node) { |
3674 Expression conditionExpression = node.condition; | 3682 Expression conditionExpression = node.condition; |
3675 Statement thenStatement = node.thenStatement; | 3683 Statement thenStatement = node.thenStatement; |
3676 Statement elseStatement = node.elseStatement; | 3684 Statement elseStatement = node.elseStatement; |
3677 if (_nodeExits(conditionExpression)) { | 3685 if (_nodeExits(conditionExpression)) { |
3678 return true; | 3686 return true; |
3679 } | 3687 } |
3680 // TODO(jwren) Do we want to take all constant expressions into account? | 3688 // TODO(jwren) Do we want to take all constant expressions into account? |
3681 if (conditionExpression is BooleanLiteral) { | 3689 if (conditionExpression is BooleanLiteral) { |
3682 if (conditionExpression.value) { | 3690 if (conditionExpression.value) { |
3683 // if(true) ... | 3691 // if (true) ... |
3684 return _nodeExits(thenStatement); | 3692 return _nodeExits(thenStatement); |
3685 } else if (elseStatement != null) { | 3693 } else if (elseStatement != null) { |
3686 // if (false) ... | 3694 // if (false) ... |
3687 return _nodeExits(elseStatement); | 3695 return _nodeExits(elseStatement); |
3688 } | 3696 } |
3689 } | 3697 } |
| 3698 bool thenExits = _nodeExits(thenStatement); |
| 3699 bool elseExits = _nodeExits(elseStatement); |
3690 if (thenStatement == null || elseStatement == null) { | 3700 if (thenStatement == null || elseStatement == null) { |
3691 return false; | 3701 return false; |
3692 } | 3702 } |
3693 bool thenExits = _nodeExits(thenStatement); | |
3694 bool elseExits = _nodeExits(elseStatement); | |
3695 return thenExits && elseExits; | 3703 return thenExits && elseExits; |
3696 } | 3704 } |
3697 | 3705 |
3698 @override | 3706 @override |
3699 bool visitIndexExpression(IndexExpression node) { | 3707 bool visitIndexExpression(IndexExpression node) { |
3700 Expression target = node.realTarget; | 3708 Expression target = node.realTarget; |
3701 if (_nodeExits(target)) { | 3709 if (_nodeExits(target)) { |
3702 return true; | 3710 return true; |
3703 } | 3711 } |
3704 if (_nodeExits(node.index)) { | 3712 if (_nodeExits(node.index)) { |
3705 return true; | 3713 return true; |
3706 } | 3714 } |
3707 return false; | 3715 return false; |
3708 } | 3716 } |
3709 | 3717 |
3710 @override | 3718 @override |
3711 bool visitInstanceCreationExpression(InstanceCreationExpression node) => | 3719 bool visitInstanceCreationExpression(InstanceCreationExpression node) => |
3712 _nodeExits(node.argumentList); | 3720 _nodeExits(node.argumentList); |
3713 | 3721 |
3714 @override | 3722 @override |
3715 bool visitIsExpression(IsExpression node) => node.expression.accept(this); | 3723 bool visitIsExpression(IsExpression node) => node.expression.accept(this); |
3716 | 3724 |
3717 @override | 3725 @override |
3718 bool visitLabel(Label node) => false; | 3726 bool visitLabel(Label node) => false; |
3719 | 3727 |
3720 @override | 3728 @override |
3721 bool visitLabeledStatement(LabeledStatement node) => | 3729 bool visitLabeledStatement(LabeledStatement node) { |
3722 node.statement.accept(this); | 3730 try { |
| 3731 bool statementExits = _nodeExits(node.statement); |
| 3732 bool neverBrokeFromLabel = |
| 3733 !_enclosingBlockBreaksLabel.contains(node.statement); |
| 3734 return statementExits && neverBrokeFromLabel; |
| 3735 } finally { |
| 3736 _enclosingBlockBreaksLabel.remove(node.statement); |
| 3737 } |
| 3738 } |
3723 | 3739 |
3724 @override | 3740 @override |
3725 bool visitLiteral(Literal node) => false; | 3741 bool visitLiteral(Literal node) => false; |
3726 | 3742 |
3727 @override | 3743 @override |
3728 bool visitMethodInvocation(MethodInvocation node) { | 3744 bool visitMethodInvocation(MethodInvocation node) { |
3729 Expression target = node.realTarget; | 3745 Expression target = node.realTarget; |
3730 if (target != null) { | 3746 if (target != null) { |
3731 if (target.accept(this)) { | 3747 if (target.accept(this)) { |
3732 return true; | 3748 return true; |
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3866 | 3882 |
3867 @override | 3883 @override |
3868 bool visitWhileStatement(WhileStatement node) { | 3884 bool visitWhileStatement(WhileStatement node) { |
3869 bool outerBreakValue = _enclosingBlockContainsBreak; | 3885 bool outerBreakValue = _enclosingBlockContainsBreak; |
3870 _enclosingBlockContainsBreak = false; | 3886 _enclosingBlockContainsBreak = false; |
3871 try { | 3887 try { |
3872 Expression conditionExpression = node.condition; | 3888 Expression conditionExpression = node.condition; |
3873 if (conditionExpression.accept(this)) { | 3889 if (conditionExpression.accept(this)) { |
3874 return true; | 3890 return true; |
3875 } | 3891 } |
| 3892 bool blockReturns = node.body.accept(this); |
3876 // TODO(jwren) Do we want to take all constant expressions into account? | 3893 // TODO(jwren) Do we want to take all constant expressions into account? |
3877 if (conditionExpression is BooleanLiteral) { | 3894 if (conditionExpression is BooleanLiteral) { |
3878 // If while(true), and the body doesn't return or the body doesn't have | 3895 // If while(true), and the body doesn't return or the body doesn't have |
3879 // a break, then return true. | 3896 // a break, then return true. |
3880 bool blockReturns = node.body.accept(this); | |
3881 if (conditionExpression.value && | 3897 if (conditionExpression.value && |
3882 (blockReturns || !_enclosingBlockContainsBreak)) { | 3898 (blockReturns || !_enclosingBlockContainsBreak)) { |
3883 return true; | 3899 return true; |
3884 } | 3900 } |
3885 } | 3901 } |
3886 return false; | 3902 return false; |
3887 } finally { | 3903 } finally { |
3888 _enclosingBlockContainsBreak = outerBreakValue; | 3904 _enclosingBlockContainsBreak = outerBreakValue; |
3889 } | 3905 } |
3890 } | 3906 } |
(...skipping 14 matching lines...) Expand all Loading... |
3905 bool _visitExpressions(NodeList<Expression> expressions) { | 3921 bool _visitExpressions(NodeList<Expression> expressions) { |
3906 for (int i = expressions.length - 1; i >= 0; i--) { | 3922 for (int i = expressions.length - 1; i >= 0; i--) { |
3907 if (expressions[i].accept(this)) { | 3923 if (expressions[i].accept(this)) { |
3908 return true; | 3924 return true; |
3909 } | 3925 } |
3910 } | 3926 } |
3911 return false; | 3927 return false; |
3912 } | 3928 } |
3913 | 3929 |
3914 bool _visitStatements(NodeList<Statement> statements) { | 3930 bool _visitStatements(NodeList<Statement> statements) { |
3915 for (int i = statements.length - 1; i >= 0; i--) { | 3931 for (int i = 0; i < statements.length; i++) { |
3916 if (statements[i].accept(this)) { | 3932 if (statements[i].accept(this)) { |
3917 return true; | 3933 return true; |
3918 } | 3934 } |
3919 } | 3935 } |
3920 return false; | 3936 return false; |
3921 } | 3937 } |
3922 | 3938 |
3923 bool _visitVariableDeclarations( | 3939 bool _visitVariableDeclarations( |
3924 NodeList<VariableDeclaration> variableDeclarations) { | 3940 NodeList<VariableDeclaration> variableDeclarations) { |
3925 for (int i = variableDeclarations.length - 1; i >= 0; i--) { | 3941 for (int i = variableDeclarations.length - 1; i >= 0; i--) { |
(...skipping 7055 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
10981 return null; | 10997 return null; |
10982 } | 10998 } |
10983 if (identical(node.staticElement, variable)) { | 10999 if (identical(node.staticElement, variable)) { |
10984 if (node.inSetterContext()) { | 11000 if (node.inSetterContext()) { |
10985 result = true; | 11001 result = true; |
10986 } | 11002 } |
10987 } | 11003 } |
10988 return null; | 11004 return null; |
10989 } | 11005 } |
10990 } | 11006 } |
OLD | NEW |