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 |