| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 import 'package:analyzer/dart/ast/token.dart'; | |
| 6 import 'package:analyzer/src/generated/ast.dart'; | |
| 7 import 'package:analyzer/src/generated/element.dart'; | |
| 8 | |
| 9 import 'ast_builder.dart'; | |
| 10 | |
| 11 /// Creates an index of variable assignments and declarations occurring in all | |
| 12 /// the provided [nodes]. | |
| 13 /// | |
| 14 /// Expands any compound assignment expression (e.g. `i += 2` yields an assigned | |
| 15 /// expression of `i + 2`), and treats declarations with no assignment as having | |
| 16 /// a `null` assigned expression. | |
| 17 Map<LocalVariableElement, List<Expression>> indexLocalAssignments( | |
| 18 Iterable<AstNode> nodes) { | |
| 19 var visitor = new _LocalAssignmentsVisitor(); | |
| 20 nodes.forEach((n) => n.accept(visitor)); | |
| 21 return visitor.assignedExpressions; | |
| 22 } | |
| 23 | |
| 24 /// Visits variable declarations and assignments and exposes an | |
| 25 /// [AssignmentIndex] interface. | |
| 26 /// | |
| 27 // TODO(ochafik): Introduce flow analysis (a variable may be nullable in | |
| 28 // some places and not in others). | |
| 29 class _LocalAssignmentsVisitor extends RecursiveAstVisitor { | |
| 30 final assignedExpressions = <LocalVariableElement, List<Expression>>{}; | |
| 31 | |
| 32 @override | |
| 33 visitVariableDeclaration(VariableDeclaration node) { | |
| 34 var element = node.element; | |
| 35 if (element is LocalVariableElement) { | |
| 36 _addAssignment(element, node.initializer ?? AstBuilder.nullLiteral()); | |
| 37 } | |
| 38 super.visitVariableDeclaration(node); | |
| 39 } | |
| 40 | |
| 41 @override | |
| 42 visitCatchClause(CatchClause node) { | |
| 43 for (var ident in [node.exceptionParameter, node.stackTraceParameter]) { | |
| 44 if (ident == null) continue; | |
| 45 assert(ident.staticElement is LocalVariableElement); | |
| 46 _addAssignment(ident.staticElement, AstBuilder.nullLiteral()); | |
| 47 } | |
| 48 } | |
| 49 | |
| 50 @override | |
| 51 visitAssignmentExpression(AssignmentExpression node) { | |
| 52 var lhs = node.leftHandSide; | |
| 53 var e = _getLocalVariable(lhs); | |
| 54 if (e != null) _addAssignment(e, node.rightHandSide); | |
| 55 super.visitAssignmentExpression(node); | |
| 56 } | |
| 57 | |
| 58 @override | |
| 59 visitBinaryExpression(BinaryExpression node) { | |
| 60 var op = node.operator.type; | |
| 61 if (op.isAssignmentOperator) { | |
| 62 var e = _getLocalVariable(node.leftOperand); | |
| 63 if (e != null) { | |
| 64 // TODO(ochafik): Once we have non-nullable types, compute the static | |
| 65 // type for this AST node. | |
| 66 _addAssignment( | |
| 67 e, | |
| 68 RawAstBuilder.binaryExpression(node.leftOperand, | |
| 69 _opToken(_expandAssignmentOp(op)), node.rightOperand)); | |
| 70 } | |
| 71 } | |
| 72 super.visitBinaryExpression(node); | |
| 73 } | |
| 74 | |
| 75 @override | |
| 76 visitPostfixExpression(PostfixExpression node) { | |
| 77 var op = node.operator.type; | |
| 78 if (op.isAssignmentOperator) { | |
| 79 // Treat `x++` as statically assigning `x + 1` to variable `x`. | |
| 80 var e = _getLocalVariable(node.operand); | |
| 81 if (e != null) _handleIncrOrDecr(e, node.operand, op); | |
| 82 } | |
| 83 super.visitPostfixExpression(node); | |
| 84 } | |
| 85 | |
| 86 @override | |
| 87 visitPrefixExpression(PrefixExpression node) { | |
| 88 var op = node.operator.type; | |
| 89 if (op.isAssignmentOperator) { | |
| 90 // Treat `++x` as statically assigning `x + 1` to variable `x`. | |
| 91 var e = _getLocalVariable(node.operand); | |
| 92 if (e != null) _handleIncrOrDecr(e, node.operand, op); | |
| 93 } | |
| 94 super.visitPrefixExpression(node); | |
| 95 } | |
| 96 | |
| 97 /// Note: we're not interested in differences between prefix & suffix. | |
| 98 _handleIncrOrDecr(LocalVariableElement e, Expression operand, TokenType op) { | |
| 99 if (!op.isIncrementOperator) throw new ArgumentError('Unexpected op: $op'); | |
| 100 // TODO(ochafik): Once we have non-nullable types, compute the static | |
| 101 // type for this AST node. | |
| 102 _addAssignment( | |
| 103 e, | |
| 104 RawAstBuilder.binaryExpression( | |
| 105 operand, _opToken(op), AstBuilder.integerLiteral(1))); | |
| 106 } | |
| 107 | |
| 108 void _addAssignment(VariableElement e, Expression value) => | |
| 109 assignedExpressions.putIfAbsent(e, () => <Expression>[]).add(value); | |
| 110 | |
| 111 LocalVariableElement _getLocalVariable(Expression target) { | |
| 112 if (target is SimpleIdentifier) { | |
| 113 var e = target.bestElement; | |
| 114 if (e is LocalVariableElement && e is! PropertyAccessorElement) { | |
| 115 return e; | |
| 116 } | |
| 117 } | |
| 118 return null; | |
| 119 } | |
| 120 } | |
| 121 | |
| 122 const Map<TokenType, TokenType> _opByAssignmentOp = const { | |
| 123 TokenType.AMPERSAND_EQ: TokenType.AMPERSAND, | |
| 124 TokenType.BAR_EQ: TokenType.BAR, | |
| 125 TokenType.CARET_EQ: TokenType.CARET, | |
| 126 TokenType.GT_GT_EQ: TokenType.GT_GT, | |
| 127 TokenType.LT_LT_EQ: TokenType.LT_LT, | |
| 128 TokenType.MINUS_EQ: TokenType.MINUS, | |
| 129 TokenType.PERCENT_EQ: TokenType.PERCENT, | |
| 130 TokenType.PLUS_EQ: TokenType.PLUS, | |
| 131 TokenType.QUESTION_QUESTION_EQ: TokenType.QUESTION_QUESTION, | |
| 132 TokenType.SLASH_EQ: TokenType.SLASH, | |
| 133 TokenType.STAR_EQ: TokenType.STAR, | |
| 134 TokenType.TILDE_SLASH_EQ: TokenType.TILDE_SLASH, | |
| 135 }; | |
| 136 | |
| 137 Token _opToken(TokenType t) => new Token(t, 0); | |
| 138 | |
| 139 /// Transforms `+=` to `+`, `??=` to `??`, etc. | |
| 140 TokenType _expandAssignmentOp(TokenType assignmentOp) { | |
| 141 assert(assignmentOp.isAssignmentOperator); | |
| 142 var op = _opByAssignmentOp[assignmentOp]; | |
| 143 if (op == null) throw new ArgumentError("Can't expand op $assignmentOp"); | |
| 144 return op; | |
| 145 } | |
| OLD | NEW |