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 services.src.correction.util; | 5 library services.src.correction.util; |
| 6 | 6 |
| 7 import 'dart:math'; | 7 import 'dart:math'; |
| 8 | 8 |
| 9 import 'package:analysis_server/src/protocol2.dart' show SourceEdit; | 9 import 'package:analysis_server/src/protocol2.dart' show SourceEdit; |
| 10 import 'package:analysis_server/src/services/correction/source_range.dart'; | 10 import 'package:analysis_server/src/services/correction/source_range.dart'; |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 87 ElementKind kind = element.kind; | 87 ElementKind kind = element.kind; |
| 88 if (kind == ElementKind.FIELD || kind == ElementKind.METHOD) { | 88 if (kind == ElementKind.FIELD || kind == ElementKind.METHOD) { |
| 89 return '${element.enclosingElement.displayName}.${element.displayName}'; | 89 return '${element.enclosingElement.displayName}.${element.displayName}'; |
| 90 } else { | 90 } else { |
| 91 return element.displayName; | 91 return element.displayName; |
| 92 } | 92 } |
| 93 } | 93 } |
| 94 | 94 |
| 95 | 95 |
| 96 /** | 96 /** |
| 97 * Returns a class or an unit member enclosing the given [node]. | |
| 98 */ | |
| 99 AstNode getEnclosingClassOrUnitMember(AstNode node) { | |
| 100 AstNode member = node; | |
| 101 while (node != null) { | |
| 102 if (node is ClassDeclaration) { | |
| 103 return member; | |
| 104 } | |
| 105 if (node is CompilationUnit) { | |
| 106 return member; | |
| 107 } | |
| 108 member = node; | |
| 109 node = node.parent; | |
| 110 } | |
| 111 return null; | |
| 112 } | |
| 113 | |
| 114 | |
| 115 | |
| 116 /** | |
| 97 * @return the [ExecutableElement] of the enclosing executable [AstNode]. | 117 * @return the [ExecutableElement] of the enclosing executable [AstNode]. |
| 98 */ | 118 */ |
| 99 ExecutableElement getEnclosingExecutableElement(AstNode node) { | 119 ExecutableElement getEnclosingExecutableElement(AstNode node) { |
| 100 while (node != null) { | 120 while (node != null) { |
| 101 if (node is FunctionDeclaration) { | 121 if (node is FunctionDeclaration) { |
| 102 return node.element; | 122 return node.element; |
| 103 } | 123 } |
| 104 if (node is ConstructorDeclaration) { | 124 if (node is ConstructorDeclaration) { |
| 105 return node.element; | 125 return node.element; |
| 106 } | 126 } |
| 107 if (node is MethodDeclaration) { | 127 if (node is MethodDeclaration) { |
| 108 return node.element; | 128 return node.element; |
| 109 } | 129 } |
| 110 node = node.parent; | 130 node = node.parent; |
| 111 } | 131 } |
| 112 return null; | 132 return null; |
| 113 } | 133 } |
| 114 | 134 |
| 115 | |
| 116 /** | 135 /** |
| 117 * @return the enclosing executable [AstNode]. | 136 * @return the enclosing executable [AstNode]. |
| 118 */ | 137 */ |
| 119 AstNode getEnclosingExecutableNode(AstNode node) { | 138 AstNode getEnclosingExecutableNode(AstNode node) { |
| 120 while (node != null) { | 139 while (node != null) { |
| 121 if (node is FunctionDeclaration) { | 140 if (node is FunctionDeclaration) { |
| 122 return node; | 141 return node; |
| 123 } | 142 } |
| 124 if (node is ConstructorDeclaration) { | 143 if (node is ConstructorDeclaration) { |
| 125 return node; | 144 return node; |
| 126 } | 145 } |
| 127 if (node is MethodDeclaration) { | 146 if (node is MethodDeclaration) { |
| 128 return node; | 147 return node; |
| 129 } | 148 } |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 161 | 180 |
| 162 /** | 181 /** |
| 163 * Returns the namespace of the given [ImportElement]. | 182 * Returns the namespace of the given [ImportElement]. |
| 164 */ | 183 */ |
| 165 Map<String, Element> getImportNamespace(ImportElement imp) { | 184 Map<String, Element> getImportNamespace(ImportElement imp) { |
| 166 NamespaceBuilder builder = new NamespaceBuilder(); | 185 NamespaceBuilder builder = new NamespaceBuilder(); |
| 167 Namespace namespace = builder.createImportNamespaceForDirective(imp); | 186 Namespace namespace = builder.createImportNamespaceForDirective(imp); |
| 168 return namespace.definedNames; | 187 return namespace.definedNames; |
| 169 } | 188 } |
| 170 | 189 |
| 190 | |
| 191 | |
| 192 /** | |
| 193 * @return the [LocalVariableElement] or [ParameterElement] if given | |
| 194 * [SimpleIdentifier] is the reference to local variable or parameter, o r | |
| 195 * <code>null</code> in the other case. | |
| 196 */ | |
| 197 VariableElement getLocalOrParameterVariableElement(SimpleIdentifier node) { | |
| 198 Element element = node.staticElement; | |
| 199 if (element is LocalVariableElement) { | |
| 200 return element; | |
| 201 } | |
| 202 if (element is ParameterElement) { | |
| 203 return element; | |
| 204 } | |
| 205 return null; | |
| 206 } | |
| 207 | |
| 208 | |
| 209 /** | |
| 210 * @return the [LocalVariableElement] if given [SimpleIdentifier] is the referen ce to | |
| 211 * local variable, or <code>null</code> in the other case. | |
| 212 */ | |
| 213 LocalVariableElement getLocalVariableElement(SimpleIdentifier node) { | |
| 214 Element element = node.staticElement; | |
| 215 if (element is LocalVariableElement) { | |
| 216 return element; | |
| 217 } | |
| 218 return null; | |
| 219 } | |
| 220 | |
| 221 | |
| 171 /** | 222 /** |
| 172 * @return the nearest common ancestor [AstNode] of the given [AstNode]s. | 223 * @return the nearest common ancestor [AstNode] of the given [AstNode]s. |
| 173 */ | 224 */ |
| 174 AstNode getNearestCommonAncestor(List<AstNode> nodes) { | 225 AstNode getNearestCommonAncestor(List<AstNode> nodes) { |
| 175 // may be no nodes | 226 // may be no nodes |
| 176 if (nodes.isEmpty) { | 227 if (nodes.isEmpty) { |
| 177 return null; | 228 return null; |
| 178 } | 229 } |
| 179 // prepare parents | 230 // prepare parents |
| 180 List<List<AstNode>> parents = []; | 231 List<List<AstNode>> parents = []; |
| 181 for (AstNode node in nodes) { | 232 for (AstNode node in nodes) { |
| 182 parents.add(getParents(node)); | 233 parents.add(getParents(node)); |
| 183 } | 234 } |
| 184 // find min length | 235 // find min length |
| 185 int minLength = 1 << 20; | 236 int minLength = 1 << 20; |
| 186 for (List<AstNode> parentList in parents) { | 237 for (List<AstNode> parentList in parents) { |
| 187 minLength = min(minLength, parentList.length); | 238 minLength = min(minLength, parentList.length); |
| 188 } | 239 } |
| 189 // find deepest parent | 240 // find deepest parent |
| 190 int i = 0; | 241 int i = 0; |
| 191 for (; i < minLength; i++) { | 242 for ( ; i < minLength; i++) { |
| 192 if (!allListsIdentical(parents, i)) { | 243 if (!allListsIdentical(parents, i)) { |
| 193 break; | 244 break; |
| 194 } | 245 } |
| 195 } | 246 } |
| 196 return parents[0][i - 1]; | 247 return parents[0][i - 1]; |
| 197 } | 248 } |
| 198 | 249 |
| 250 | |
| 199 /** | 251 /** |
| 200 * @return parent [AstNode]s from [CompilationUnit] (at index "0") to the given one. | 252 * @return parent [AstNode]s from [CompilationUnit] (at index "0") to the given one. |
| 201 */ | 253 */ |
| 202 List<AstNode> getParents(AstNode node) { | 254 List<AstNode> getParents(AstNode node) { |
| 203 // prepare number of parents | 255 // prepare number of parents |
| 204 int numParents = 0; | 256 int numParents = 0; |
| 205 { | 257 { |
| 206 AstNode current = node.parent; | 258 AstNode current = node.parent; |
| 207 while (current != null) { | 259 while (current != null) { |
| 208 numParents++; | 260 numParents++; |
| 209 current = current.parent; | 261 current = current.parent; |
| 210 } | 262 } |
| 211 } | 263 } |
| 212 // fill array of parents | 264 // fill array of parents |
| 213 List<AstNode> parents = new List<AstNode>(numParents); | 265 List<AstNode> parents = new List<AstNode>(numParents); |
| 214 AstNode current = node.parent; | 266 AstNode current = node.parent; |
| 215 int index = numParents; | 267 int index = numParents; |
| 216 while (current != null) { | 268 while (current != null) { |
| 217 parents[--index] = current; | 269 parents[--index] = current; |
| 218 current = current.parent; | 270 current = current.parent; |
| 219 } | 271 } |
| 220 return parents; | 272 return parents; |
| 221 } | 273 } |
| 222 | 274 |
| 223 | |
| 224 /** | 275 /** |
| 225 * If given [AstNode] is name of qualified property extraction, returns target f rom which | 276 * If given [AstNode] is name of qualified property extraction, returns target f rom which |
| 226 * this property is extracted. Otherwise `null`. | 277 * this property is extracted. Otherwise `null`. |
| 227 */ | 278 */ |
| 228 Expression getQualifiedPropertyTarget(AstNode node) { | 279 Expression getQualifiedPropertyTarget(AstNode node) { |
| 229 AstNode parent = node.parent; | 280 AstNode parent = node.parent; |
| 230 if (parent is PrefixedIdentifier) { | 281 if (parent is PrefixedIdentifier) { |
| 231 PrefixedIdentifier prefixed = parent; | 282 PrefixedIdentifier prefixed = parent; |
| 232 if (prefixed.identifier == node) { | 283 if (prefixed.identifier == node) { |
| 233 return parent.prefix; | 284 return parent.prefix; |
| 234 } | 285 } |
| 235 } | 286 } |
| 236 if (parent is PropertyAccess) { | 287 if (parent is PropertyAccess) { |
| 237 PropertyAccess access = parent; | 288 PropertyAccess access = parent; |
| 238 if (access.propertyName == node) { | 289 if (access.propertyName == node) { |
| 239 return access.realTarget; | 290 return access.realTarget; |
| 240 } | 291 } |
| 241 } | 292 } |
| 242 return null; | 293 return null; |
| 243 } | 294 } |
| 244 | 295 |
| 245 | |
| 246 /** | 296 /** |
| 247 * Returns the given [Statement] if not a [Block], or the first child | 297 * Returns the given [Statement] if not a [Block], or the first child |
| 248 * [Statement] if a [Block], or `null` if more than one child. | 298 * [Statement] if a [Block], or `null` if more than one child. |
| 249 */ | 299 */ |
| 250 Statement getSingleStatement(Statement statement) { | 300 Statement getSingleStatement(Statement statement) { |
| 251 if (statement is Block) { | 301 if (statement is Block) { |
| 252 List<Statement> blockStatements = statement.statements; | 302 List<Statement> blockStatements = statement.statements; |
| 253 if (blockStatements.length != 1) { | 303 if (blockStatements.length != 1) { |
| 254 return null; | 304 return null; |
| 255 } | 305 } |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 283 * Checks if the given [Element]'s display name equals to the given name. | 333 * Checks if the given [Element]'s display name equals to the given name. |
| 284 */ | 334 */ |
| 285 bool hasDisplayName(Element element, String name) { | 335 bool hasDisplayName(Element element, String name) { |
| 286 if (element == null) { | 336 if (element == null) { |
| 287 return false; | 337 return false; |
| 288 } | 338 } |
| 289 return element.displayName == name; | 339 return element.displayName == name; |
| 290 } | 340 } |
| 291 | 341 |
| 292 | 342 |
| 343 /** | |
| 344 * @return <code>true</code> if given [DartNode] is left hand side of assignment , or | |
| 345 * declaration of the variable. | |
| 346 */ | |
| 347 bool isLeftHandOfAssignment(SimpleIdentifier node) { | |
| 348 if (node.inSetterContext()) { | |
| 349 return true; | |
| 350 } | |
| 351 return node.parent is VariableDeclaration && | |
| 352 (node.parent as VariableDeclaration).name == node; | |
|
Brian Wilkerson
2014/08/25 07:44:00
Does this also want to ensure that there is an ini
scheglov
2014/08/25 15:36:29
I's not very useful to extract an empty variable d
| |
| 353 } | |
| 354 | |
| 355 | |
| 356 /** | |
| 357 * @return `true` if the given [SimpleIdentifier] is the name of the | |
| 358 * [NamedExpression]. | |
| 359 */ | |
| 360 bool isNamedExpressionName(SimpleIdentifier node) { | |
| 361 AstNode parent = node.parent; | |
| 362 if (parent is Label) { | |
| 363 Label label = parent; | |
| 364 if (identical(label.label, node)) { | |
| 365 AstNode parent2 = label.parent; | |
| 366 if (parent2 is NamedExpression) { | |
| 367 return identical(parent2.name, label); | |
| 368 } | |
| 369 } | |
| 370 } | |
| 371 return false; | |
| 372 } | |
| 373 | |
| 374 | |
| 293 class CorrectionUtils { | 375 class CorrectionUtils { |
| 294 final CompilationUnit unit; | 376 final CompilationUnit unit; |
| 295 | 377 |
| 296 LibraryElement _library; | 378 LibraryElement _library; |
| 297 String _buffer; | 379 String _buffer; |
| 298 String _endOfLine; | 380 String _endOfLine; |
| 299 | 381 |
| 300 CorrectionUtils(this.unit) { | 382 CorrectionUtils(this.unit) { |
| 301 CompilationUnitElement unitElement = unit.element; | 383 CompilationUnitElement unitElement = unit.element; |
| 302 this._library = unitElement.library; | 384 this._library = unitElement.library; |
| (...skipping 476 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 779 | 861 |
| 780 /** | 862 /** |
| 781 * Returns the source with indentation changed from [oldIndent] to | 863 * Returns the source with indentation changed from [oldIndent] to |
| 782 * [newIndent], keeping indentation of lines relative to each other. | 864 * [newIndent], keeping indentation of lines relative to each other. |
| 783 */ | 865 */ |
| 784 String replaceSourceIndent(String source, String oldIndent, | 866 String replaceSourceIndent(String source, String oldIndent, |
| 785 String newIndent) { | 867 String newIndent) { |
| 786 // prepare STRING token ranges | 868 // prepare STRING token ranges |
| 787 List<SourceRange> lineRanges = []; | 869 List<SourceRange> lineRanges = []; |
| 788 { | 870 { |
| 789 var token = unit.beginToken; | 871 List<Token> tokens = TokenUtils.getTokens(source); |
| 790 while (token != null && token.type != TokenType.EOF) { | 872 for (Token token in tokens) { |
| 791 if (token.type == TokenType.STRING) { | 873 if (token.type == TokenType.STRING) { |
| 792 lineRanges.add(rangeToken(token)); | 874 lineRanges.add(rangeToken(token)); |
| 793 } | 875 } |
| 794 token = token.next; | 876 token = token.next; |
| 795 } | 877 } |
| 796 } | 878 } |
| 797 // re-indent lines | 879 // re-indent lines |
| 798 StringBuffer sb = new StringBuffer(); | 880 StringBuffer sb = new StringBuffer(); |
| 799 String eol = endOfLine; | 881 String eol = endOfLine; |
| 800 List<String> lines = source.split(eol); | 882 List<String> lines = source.split(eol); |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 845 bool selectionIncludesNonWhitespaceOutsideNode(SourceRange selection, | 927 bool selectionIncludesNonWhitespaceOutsideNode(SourceRange selection, |
| 846 AstNode node) { | 928 AstNode node) { |
| 847 return _selectionIncludesNonWhitespaceOutsideRange( | 929 return _selectionIncludesNonWhitespaceOutsideRange( |
| 848 selection, | 930 selection, |
| 849 rangeNode(node)); | 931 rangeNode(node)); |
| 850 } | 932 } |
| 851 | 933 |
| 852 /** | 934 /** |
| 853 * @return <code>true</code> if given range of [BinaryExpression] can be extra cted. | 935 * @return <code>true</code> if given range of [BinaryExpression] can be extra cted. |
| 854 */ | 936 */ |
| 855 bool validateBinaryExpressionRange(BinaryExpression binaryExpression, SourceRa nge range) { | 937 bool validateBinaryExpressionRange(BinaryExpression binaryExpression, |
| 938 SourceRange range) { | |
| 856 // only parts of associative expression are safe to extract | 939 // only parts of associative expression are safe to extract |
| 857 if (!binaryExpression.operator.type.isAssociativeOperator) { | 940 if (!binaryExpression.operator.type.isAssociativeOperator) { |
| 858 return false; | 941 return false; |
| 859 } | 942 } |
| 860 // prepare selected operands | 943 // prepare selected operands |
| 861 List<Expression> operands = _getOperandsInOrderFor(binaryExpression); | 944 List<Expression> operands = _getOperandsInOrderFor(binaryExpression); |
| 862 List<Expression> subOperands = _getOperandsForSourceRange(operands, range); | 945 List<Expression> subOperands = _getOperandsForSourceRange(operands, range); |
| 863 // if empty, then something wrong with selection | 946 // if empty, then something wrong with selection |
| 864 if (subOperands.isEmpty) { | 947 if (subOperands.isEmpty) { |
| 865 return false; | 948 return false; |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 970 } | 1053 } |
| 971 return _invertCondition0(innerExpresion); | 1054 return _invertCondition0(innerExpresion); |
| 972 } | 1055 } |
| 973 DartType type = expression.bestType; | 1056 DartType type = expression.bestType; |
| 974 if (type.displayName == "bool") { | 1057 if (type.displayName == "bool") { |
| 975 return _InvertedCondition._simple("!${getNodeText(expression)}"); | 1058 return _InvertedCondition._simple("!${getNodeText(expression)}"); |
| 976 } | 1059 } |
| 977 return _InvertedCondition._simple(getNodeText(expression)); | 1060 return _InvertedCondition._simple(getNodeText(expression)); |
| 978 } | 1061 } |
| 979 | 1062 |
| 980 bool _selectionIncludesNonWhitespaceOutsideOperands(SourceRange selection, Lis t<Expression> operands) { | 1063 bool _selectionIncludesNonWhitespaceOutsideOperands(SourceRange selection, |
| 981 return _selectionIncludesNonWhitespaceOutsideRange(selection, rangeNodes(ope rands)); | 1064 List<Expression> operands) { |
| 1065 return _selectionIncludesNonWhitespaceOutsideRange( | |
| 1066 selection, | |
| 1067 rangeNodes(operands)); | |
| 982 } | 1068 } |
| 983 | 1069 |
| 984 /** | 1070 /** |
| 985 * @return <code>true</code> if "selection" covers "range" and there are any n on-whitespace tokens | 1071 * @return <code>true</code> if "selection" covers "range" and there are any n on-whitespace tokens |
| 986 * between "selection" and "range" start/end. | 1072 * between "selection" and "range" start/end. |
| 987 */ | 1073 */ |
| 988 bool _selectionIncludesNonWhitespaceOutsideRange(SourceRange selection, | 1074 bool _selectionIncludesNonWhitespaceOutsideRange(SourceRange selection, |
| 989 SourceRange range) { | 1075 SourceRange range) { |
| 990 // selection should cover range | 1076 // selection should cover range |
| 991 if (!selection.covers(range)) { | 1077 if (!selection.covers(range)) { |
| 992 return false; | 1078 return false; |
| 993 } | 1079 } |
| 994 // non-whitespace between selection start and range start | 1080 // non-whitespace between selection start and range start |
| 995 if (!isJustWhitespaceOrComment(rangeStartStart(selection, range))) { | 1081 if (!isJustWhitespaceOrComment(rangeStartStart(selection, range))) { |
| 996 return true; | 1082 return true; |
| 997 } | 1083 } |
| 998 // non-whitespace after range | 1084 // non-whitespace after range |
| 999 if (!isJustWhitespaceOrComment(rangeEndEnd(range, selection))) { | 1085 if (!isJustWhitespaceOrComment(rangeEndEnd(range, selection))) { |
| 1000 return true; | 1086 return true; |
| 1001 } | 1087 } |
| 1002 // only whitespace in selection around range | 1088 // only whitespace in selection around range |
| 1003 return false; | 1089 return false; |
| 1004 } | 1090 } |
| 1005 | 1091 |
| 1006 /** | 1092 /** |
| 1007 * @return [Expression]s from <code>operands</code> which are completely cover ed by given | 1093 * @return [Expression]s from <code>operands</code> which are completely cover ed by given |
| 1008 * [SourceRange]. Range should start and end between given [Expression ]s. | 1094 * [SourceRange]. Range should start and end between given [Expression ]s. |
| 1009 */ | 1095 */ |
| 1010 static List<Expression> _getOperandsForSourceRange(List<Expression> operands, SourceRange range) { | 1096 static List<Expression> _getOperandsForSourceRange(List<Expression> operands, |
| 1097 SourceRange range) { | |
| 1011 assert(!operands.isEmpty); | 1098 assert(!operands.isEmpty); |
| 1012 List<Expression> subOperands = []; | 1099 List<Expression> subOperands = []; |
| 1013 // track range enter/exit | 1100 // track range enter/exit |
| 1014 bool entered = false; | 1101 bool entered = false; |
| 1015 bool exited = false; | 1102 bool exited = false; |
| 1016 // may be range starts before or on first operand | 1103 // may be range starts before or on first operand |
| 1017 if (range.offset <= operands[0].offset) { | 1104 if (range.offset <= operands[0].offset) { |
| 1018 entered = true; | 1105 entered = true; |
| 1019 } | 1106 } |
| 1020 // iterate over gaps between operands | 1107 // iterate over gaps between operands |
| 1021 for (int i = 0; i < operands.length - 1; i++) { | 1108 for (int i = 0; i < operands.length - 1; i++) { |
| 1022 Expression operand = operands[i]; | 1109 Expression operand = operands[i]; |
| 1023 Expression nextOperand = operands[i + 1]; | 1110 Expression nextOperand = operands[i + 1]; |
| 1024 SourceRange inclusiveGap = rangeEndStart(operand, nextOperand).getMoveEnd( 1); | 1111 SourceRange inclusiveGap = |
| 1112 rangeEndStart(operand, nextOperand).getMoveEnd(1); | |
| 1025 // add operand, if already entered range | 1113 // add operand, if already entered range |
| 1026 if (entered) { | 1114 if (entered) { |
| 1027 subOperands.add(operand); | 1115 subOperands.add(operand); |
| 1028 // may be last operand in range | 1116 // may be last operand in range |
| 1029 if (range.endsIn(inclusiveGap)) { | 1117 if (range.endsIn(inclusiveGap)) { |
| 1030 exited = true; | 1118 exited = true; |
| 1031 } | 1119 } |
| 1032 } else { | 1120 } else { |
| 1033 // may be first operand in range | 1121 // may be first operand in range |
| 1034 if (range.startsIn(inclusiveGap)) { | 1122 if (range.startsIn(inclusiveGap)) { |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1185 | 1273 |
| 1186 @override | 1274 @override |
| 1187 Object visitExpression(Expression node) { | 1275 Object visitExpression(Expression node) { |
| 1188 if (node is BinaryExpression && node.operator.type == groupOperatorType) { | 1276 if (node is BinaryExpression && node.operator.type == groupOperatorType) { |
| 1189 return super.visitNode(node); | 1277 return super.visitNode(node); |
| 1190 } | 1278 } |
| 1191 operands.add(node); | 1279 operands.add(node); |
| 1192 return null; | 1280 return null; |
| 1193 } | 1281 } |
| 1194 } | 1282 } |
| OLD | NEW |