| 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 engine.incremental_resolver; | 5 library engine.incremental_resolver; |
| 6 | 6 |
| 7 import 'dart:collection'; | 7 import 'dart:collection'; |
| 8 import 'dart:math' as math; | 8 import 'dart:math' as math; |
| 9 | 9 |
| 10 import 'package:analyzer/src/services/lint.dart'; | 10 import 'package:analyzer/src/services/lint.dart'; |
| (...skipping 769 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 780 String toString() => name; | 780 String toString() => name; |
| 781 } | 781 } |
| 782 | 782 |
| 783 | 783 |
| 784 /** | 784 /** |
| 785 * Instances of the class [IncrementalResolver] resolve the smallest portion of | 785 * Instances of the class [IncrementalResolver] resolve the smallest portion of |
| 786 * an AST structure that we currently know how to resolve. | 786 * an AST structure that we currently know how to resolve. |
| 787 */ | 787 */ |
| 788 class IncrementalResolver { | 788 class IncrementalResolver { |
| 789 /** | 789 /** |
| 790 * All resolved units of the [_definingLibrary]. | |
| 791 */ | |
| 792 final Map<Source, CompilationUnit> _units; | |
| 793 | |
| 794 /** | |
| 795 * The element of the compilation unit being resolved. | 790 * The element of the compilation unit being resolved. |
| 796 */ | 791 */ |
| 797 final CompilationUnitElement _definingUnit; | 792 final CompilationUnitElement _definingUnit; |
| 798 | 793 |
| 799 /** | 794 /** |
| 800 * The context the compilation unit being resolved in. | 795 * The context the compilation unit being resolved in. |
| 801 */ | 796 */ |
| 802 AnalysisContextImpl _context; | 797 AnalysisContextImpl _context; |
| 803 | 798 |
| 804 /** | 799 /** |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 847 ResolutionContext _resolutionContext; | 842 ResolutionContext _resolutionContext; |
| 848 | 843 |
| 849 List<AnalysisError> _resolveErrors = AnalysisError.NO_ERRORS; | 844 List<AnalysisError> _resolveErrors = AnalysisError.NO_ERRORS; |
| 850 List<AnalysisError> _verifyErrors = AnalysisError.NO_ERRORS; | 845 List<AnalysisError> _verifyErrors = AnalysisError.NO_ERRORS; |
| 851 List<AnalysisError> _lints = AnalysisError.NO_ERRORS; | 846 List<AnalysisError> _lints = AnalysisError.NO_ERRORS; |
| 852 | 847 |
| 853 /** | 848 /** |
| 854 * Initialize a newly created incremental resolver to resolve a node in the | 849 * Initialize a newly created incremental resolver to resolve a node in the |
| 855 * given source in the given library. | 850 * given source in the given library. |
| 856 */ | 851 */ |
| 857 IncrementalResolver(this._units, this._definingUnit, this._updateOffset, | 852 IncrementalResolver(this._definingUnit, this._updateOffset, |
| 858 this._updateEndOld, this._updateEndNew) { | 853 this._updateEndOld, this._updateEndNew) { |
| 859 _updateDelta = _updateEndNew - _updateEndOld; | 854 _updateDelta = _updateEndNew - _updateEndOld; |
| 860 _definingLibrary = _definingUnit.library; | 855 _definingLibrary = _definingUnit.library; |
| 861 _librarySource = _definingLibrary.source; | 856 _librarySource = _definingLibrary.source; |
| 862 _source = _definingUnit.source; | 857 _source = _definingUnit.source; |
| 863 _context = _definingUnit.context; | 858 _context = _definingUnit.context; |
| 864 _typeProvider = _context.typeProvider; | 859 _typeProvider = _context.typeProvider; |
| 865 entry = _context.getReadableSourceEntryOrNull(_source); | 860 entry = _context.getReadableSourceEntryOrNull(_source); |
| 866 } | 861 } |
| 867 | 862 |
| (...skipping 12 matching lines...) Expand all Loading... |
| 880 // update elements | 875 // update elements |
| 881 _updateElementNameOffsets(); | 876 _updateElementNameOffsets(); |
| 882 _buildElements(rootNode); | 877 _buildElements(rootNode); |
| 883 if (!_canBeIncrementallyResolved(rootNode)) { | 878 if (!_canBeIncrementallyResolved(rootNode)) { |
| 884 return false; | 879 return false; |
| 885 } | 880 } |
| 886 // resolve | 881 // resolve |
| 887 _resolveReferences(rootNode); | 882 _resolveReferences(rootNode); |
| 888 // verify | 883 // verify |
| 889 _verify(rootNode); | 884 _verify(rootNode); |
| 890 _generateHints(rootNode); | 885 _context.invalidateLibraryHints(_librarySource); |
| 891 _generateLints(rootNode); | 886 _generateLints(rootNode); |
| 892 // update entry errors | 887 // update entry errors |
| 893 _updateEntry(); | 888 _updateEntry(); |
| 894 // OK | 889 // OK |
| 895 return true; | 890 return true; |
| 896 } finally { | 891 } finally { |
| 897 logger.exit(); | 892 logger.exit(); |
| 898 } | 893 } |
| 899 } | 894 } |
| 900 | 895 |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 971 AstNode _findResolutionRoot(AstNode node) { | 966 AstNode _findResolutionRoot(AstNode node) { |
| 972 while (node != null) { | 967 while (node != null) { |
| 973 if (_canBeResolved(node)) { | 968 if (_canBeResolved(node)) { |
| 974 return node; | 969 return node; |
| 975 } | 970 } |
| 976 node = node.parent; | 971 node = node.parent; |
| 977 } | 972 } |
| 978 throw new AnalysisException("Cannot resolve node: no resolvable node"); | 973 throw new AnalysisException("Cannot resolve node: no resolvable node"); |
| 979 } | 974 } |
| 980 | 975 |
| 981 void _generateHints(AstNode node) { | |
| 982 LoggingTimer timer = logger.startTimer(); | |
| 983 try { | |
| 984 RecordingErrorListener errorListener = new RecordingErrorListener(); | |
| 985 // prepare a list of the library units | |
| 986 List<CompilationUnit> units; | |
| 987 { | |
| 988 CompilationUnit unit = node.getAncestor((n) => n is CompilationUnit); | |
| 989 _units[_source] = unit; | |
| 990 units = _units.values.toList(); | |
| 991 } | |
| 992 // run the generator | |
| 993 AnalysisContext analysisContext = _definingLibrary.context; | |
| 994 HintGenerator hintGenerator = | |
| 995 new HintGenerator(units, analysisContext, errorListener); | |
| 996 hintGenerator.generateForLibrary(); | |
| 997 // remember hints | |
| 998 for (Source source in _units.keys) { | |
| 999 List<AnalysisError> hints = errorListener.getErrorsForSource(source); | |
| 1000 DartEntry entry = _context.getReadableSourceEntryOrNull(source); | |
| 1001 entry.setValueInLibrary(DartEntry.HINTS, _librarySource, hints); | |
| 1002 } | |
| 1003 } finally { | |
| 1004 timer.stop('generate hints'); | |
| 1005 } | |
| 1006 } | |
| 1007 | |
| 1008 void _generateLints(AstNode node) { | 976 void _generateLints(AstNode node) { |
| 1009 LoggingTimer timer = logger.startTimer(); | 977 LoggingTimer timer = logger.startTimer(); |
| 1010 try { | 978 try { |
| 1011 RecordingErrorListener errorListener = new RecordingErrorListener(); | 979 RecordingErrorListener errorListener = new RecordingErrorListener(); |
| 1012 CompilationUnit unit = node.getAncestor((n) => n is CompilationUnit); | 980 CompilationUnit unit = node.getAncestor((n) => n is CompilationUnit); |
| 1013 LintGenerator lintGenerator = | 981 LintGenerator lintGenerator = |
| 1014 new LintGenerator(<CompilationUnit>[unit], errorListener); | 982 new LintGenerator(<CompilationUnit>[unit], errorListener); |
| 1015 lintGenerator.generate(); | 983 lintGenerator.generate(); |
| 1016 _lints = errorListener.getErrorsForSource(_source); | 984 _lints = errorListener.getErrorsForSource(_source); |
| 1017 } finally { | 985 } finally { |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1182 _verifyErrors = errorListener.getErrorsForSource(_source); | 1150 _verifyErrors = errorListener.getErrorsForSource(_source); |
| 1183 } finally { | 1151 } finally { |
| 1184 timer.stop('verify'); | 1152 timer.stop('verify'); |
| 1185 } | 1153 } |
| 1186 } | 1154 } |
| 1187 } | 1155 } |
| 1188 | 1156 |
| 1189 | 1157 |
| 1190 class PoorMansIncrementalResolver { | 1158 class PoorMansIncrementalResolver { |
| 1191 final TypeProvider _typeProvider; | 1159 final TypeProvider _typeProvider; |
| 1192 final Map<Source, CompilationUnit> _units; | |
| 1193 final Source _unitSource; | 1160 final Source _unitSource; |
| 1194 final DartEntry _entry; | 1161 final DartEntry _entry; |
| 1162 final CompilationUnit _oldUnit; |
| 1195 CompilationUnitElement _unitElement; | 1163 CompilationUnitElement _unitElement; |
| 1196 | 1164 |
| 1197 int _updateOffset; | 1165 int _updateOffset; |
| 1198 int _updateDelta; | 1166 int _updateDelta; |
| 1199 int _updateEndOld; | 1167 int _updateEndOld; |
| 1200 int _updateEndNew; | 1168 int _updateEndNew; |
| 1201 | 1169 |
| 1202 List<AnalysisError> _newScanErrors = <AnalysisError>[]; | 1170 List<AnalysisError> _newScanErrors = <AnalysisError>[]; |
| 1203 List<AnalysisError> _newParseErrors = <AnalysisError>[]; | 1171 List<AnalysisError> _newParseErrors = <AnalysisError>[]; |
| 1204 | 1172 |
| 1205 PoorMansIncrementalResolver(this._typeProvider, this._units, this._unitSource, | 1173 PoorMansIncrementalResolver(this._typeProvider, this._unitSource, |
| 1206 this._entry, bool resolveApiChanges) { | 1174 this._entry, this._oldUnit, bool resolveApiChanges) { |
| 1207 _resolveApiChanges = resolveApiChanges; | 1175 _resolveApiChanges = resolveApiChanges; |
| 1208 } | 1176 } |
| 1209 | 1177 |
| 1210 /** | 1178 /** |
| 1211 * Attempts to update [oldUnit] to the state corresponding to [newCode]. | 1179 * Attempts to update [_oldUnit] to the state corresponding to [newCode]. |
| 1212 * Returns `true` if success, or `false` otherwise. | 1180 * Returns `true` if success, or `false` otherwise. |
| 1213 * The [oldUnit] might be damaged. | 1181 * The [_oldUnit] might be damaged. |
| 1214 */ | 1182 */ |
| 1215 bool resolve(String newCode) { | 1183 bool resolve(String newCode) { |
| 1216 logger.enter('diff/resolve $_unitSource'); | 1184 logger.enter('diff/resolve $_unitSource'); |
| 1217 try { | 1185 try { |
| 1218 // prepare old unit | 1186 // prepare old unit |
| 1219 CompilationUnit oldUnit = _units[_unitSource]; | 1187 if (!_areCurlyBracketsBalanced(_oldUnit.beginToken)) { |
| 1220 if (!_areCurlyBracketsBalanced(oldUnit.beginToken)) { | |
| 1221 logger.log('Unbalanced number of curly brackets in the old unit.'); | 1188 logger.log('Unbalanced number of curly brackets in the old unit.'); |
| 1222 return false; | 1189 return false; |
| 1223 } | 1190 } |
| 1224 _unitElement = oldUnit.element; | 1191 _unitElement = _oldUnit.element; |
| 1225 // prepare new unit | 1192 // prepare new unit |
| 1226 CompilationUnit newUnit = _parseUnit(newCode); | 1193 CompilationUnit newUnit = _parseUnit(newCode); |
| 1227 if (!_areCurlyBracketsBalanced(newUnit.beginToken)) { | 1194 if (!_areCurlyBracketsBalanced(newUnit.beginToken)) { |
| 1228 logger.log('Unbalanced number of curly brackets in the new unit.'); | 1195 logger.log('Unbalanced number of curly brackets in the new unit.'); |
| 1229 return false; | 1196 return false; |
| 1230 } | 1197 } |
| 1231 // find difference | 1198 // find difference |
| 1232 _TokenPair firstPair = | 1199 _TokenPair firstPair = |
| 1233 _findFirstDifferentToken(oldUnit.beginToken, newUnit.beginToken); | 1200 _findFirstDifferentToken(_oldUnit.beginToken, newUnit.beginToken); |
| 1234 _TokenPair lastPair = | 1201 _TokenPair lastPair = |
| 1235 _findLastDifferentToken(oldUnit.endToken, newUnit.endToken); | 1202 _findLastDifferentToken(_oldUnit.endToken, newUnit.endToken); |
| 1236 if (firstPair != null && lastPair != null) { | 1203 if (firstPair != null && lastPair != null) { |
| 1237 int firstOffsetOld = firstPair.oldToken.offset; | 1204 int firstOffsetOld = firstPair.oldToken.offset; |
| 1238 int firstOffsetNew = firstPair.newToken.offset; | 1205 int firstOffsetNew = firstPair.newToken.offset; |
| 1239 int lastOffsetOld = lastPair.oldToken.end; | 1206 int lastOffsetOld = lastPair.oldToken.end; |
| 1240 int lastOffsetNew = lastPair.newToken.end; | 1207 int lastOffsetNew = lastPair.newToken.end; |
| 1241 int beginOffsetOld = math.min(firstOffsetOld, lastOffsetOld); | 1208 int beginOffsetOld = math.min(firstOffsetOld, lastOffsetOld); |
| 1242 int endOffsetOld = math.max(firstOffsetOld, lastOffsetOld); | 1209 int endOffsetOld = math.max(firstOffsetOld, lastOffsetOld); |
| 1243 int beginOffsetNew = math.min(firstOffsetNew, lastOffsetNew); | 1210 int beginOffsetNew = math.min(firstOffsetNew, lastOffsetNew); |
| 1244 int endOffsetNew = math.max(firstOffsetNew, lastOffsetNew); | 1211 int endOffsetNew = math.max(firstOffsetNew, lastOffsetNew); |
| 1245 // check for a whitespace only change | 1212 // check for a whitespace only change |
| 1246 if (identical(lastPair.oldToken, firstPair.oldToken) && | 1213 if (identical(lastPair.oldToken, firstPair.oldToken) && |
| 1247 identical(lastPair.newToken, firstPair.newToken)) { | 1214 identical(lastPair.newToken, firstPair.newToken)) { |
| 1248 _updateOffset = beginOffsetOld - 1; | 1215 _updateOffset = beginOffsetOld - 1; |
| 1249 _updateEndOld = endOffsetOld; | 1216 _updateEndOld = endOffsetOld; |
| 1250 _updateEndNew = endOffsetNew; | 1217 _updateEndNew = endOffsetNew; |
| 1251 _updateDelta = newUnit.length - oldUnit.length; | 1218 _updateDelta = newUnit.length - _oldUnit.length; |
| 1252 // A Dart documentation comment change. | 1219 // A Dart documentation comment change. |
| 1253 if (firstPair.kind == _TokenDifferenceKind.COMMENT_DOC) { | 1220 if (firstPair.kind == _TokenDifferenceKind.COMMENT_DOC) { |
| 1254 bool success = _resolveComment(oldUnit, newUnit, firstPair); | 1221 bool success = _resolveComment(_oldUnit, newUnit, firstPair); |
| 1255 logger.log('Documentation comment resolved: $success'); | 1222 logger.log('Documentation comment resolved: $success'); |
| 1256 return success; | 1223 return success; |
| 1257 } | 1224 } |
| 1258 // A pure whitespace change. | 1225 // A pure whitespace change. |
| 1259 if (firstPair.kind == _TokenDifferenceKind.OFFSET) { | 1226 if (firstPair.kind == _TokenDifferenceKind.OFFSET) { |
| 1260 logger.log('Whitespace change.'); | 1227 logger.log('Whitespace change.'); |
| 1261 _shiftTokens(firstPair.oldToken); | 1228 _shiftTokens(firstPair.oldToken); |
| 1262 { | 1229 { |
| 1263 IncrementalResolver incrementalResolver = new IncrementalResolver( | 1230 IncrementalResolver incrementalResolver = new IncrementalResolver( |
| 1264 _units, | |
| 1265 _unitElement, | 1231 _unitElement, |
| 1266 _updateOffset, | 1232 _updateOffset, |
| 1267 _updateEndOld, | 1233 _updateEndOld, |
| 1268 _updateEndNew); | 1234 _updateEndNew); |
| 1269 incrementalResolver._updateElementNameOffsets(); | 1235 incrementalResolver._updateElementNameOffsets(); |
| 1270 incrementalResolver._shiftEntryErrors(); | 1236 incrementalResolver._shiftEntryErrors(); |
| 1271 } | 1237 } |
| 1272 _updateEntry(); | 1238 _updateEntry(); |
| 1273 logger.log('Success.'); | 1239 logger.log('Success.'); |
| 1274 return true; | 1240 return true; |
| 1275 } | 1241 } |
| 1276 // fall-through, end-of-line comment | 1242 // fall-through, end-of-line comment |
| 1277 } | 1243 } |
| 1278 // Find nodes covering the "old" and "new" token ranges. | 1244 // Find nodes covering the "old" and "new" token ranges. |
| 1279 AstNode oldNode = | 1245 AstNode oldNode = |
| 1280 _findNodeCovering(oldUnit, beginOffsetOld, endOffsetOld); | 1246 _findNodeCovering(_oldUnit, beginOffsetOld, endOffsetOld); |
| 1281 AstNode newNode = | 1247 AstNode newNode = |
| 1282 _findNodeCovering(newUnit, beginOffsetNew, endOffsetNew); | 1248 _findNodeCovering(newUnit, beginOffsetNew, endOffsetNew); |
| 1283 logger.log(() => 'oldNode: $oldNode'); | 1249 logger.log(() => 'oldNode: $oldNode'); |
| 1284 logger.log(() => 'newNode: $newNode'); | 1250 logger.log(() => 'newNode: $newNode'); |
| 1285 // Try to find the smallest common node, a FunctionBody currently. | 1251 // Try to find the smallest common node, a FunctionBody currently. |
| 1286 { | 1252 { |
| 1287 List<AstNode> oldParents = _getParents(oldNode); | 1253 List<AstNode> oldParents = _getParents(oldNode); |
| 1288 List<AstNode> newParents = _getParents(newNode); | 1254 List<AstNode> newParents = _getParents(newNode); |
| 1289 int length = math.min(oldParents.length, newParents.length); | 1255 int length = math.min(oldParents.length, newParents.length); |
| 1290 bool found = false; | 1256 bool found = false; |
| (...skipping 27 matching lines...) Expand all Loading... |
| 1318 _updateEndOld = oldNode.end; | 1284 _updateEndOld = oldNode.end; |
| 1319 _updateEndNew = newNode.end; | 1285 _updateEndNew = newNode.end; |
| 1320 _updateDelta = _updateEndNew - _updateEndOld; | 1286 _updateDelta = _updateEndNew - _updateEndOld; |
| 1321 // replace node | 1287 // replace node |
| 1322 NodeReplacer.replace(oldNode, newNode); | 1288 NodeReplacer.replace(oldNode, newNode); |
| 1323 // update token references | 1289 // update token references |
| 1324 { | 1290 { |
| 1325 Token oldBeginToken = _getBeginTokenNotComment(oldNode); | 1291 Token oldBeginToken = _getBeginTokenNotComment(oldNode); |
| 1326 Token newBeginToken = _getBeginTokenNotComment(newNode); | 1292 Token newBeginToken = _getBeginTokenNotComment(newNode); |
| 1327 if (oldBeginToken.previous.type == TokenType.EOF) { | 1293 if (oldBeginToken.previous.type == TokenType.EOF) { |
| 1328 oldUnit.beginToken = newBeginToken; | 1294 _oldUnit.beginToken = newBeginToken; |
| 1329 } else { | 1295 } else { |
| 1330 oldBeginToken.previous.setNext(newBeginToken); | 1296 oldBeginToken.previous.setNext(newBeginToken); |
| 1331 } | 1297 } |
| 1332 newNode.endToken.setNext(oldNode.endToken.next); | 1298 newNode.endToken.setNext(oldNode.endToken.next); |
| 1333 _shiftTokens(oldNode.endToken.next); | 1299 _shiftTokens(oldNode.endToken.next); |
| 1334 } | 1300 } |
| 1335 // perform incremental resolution | 1301 // perform incremental resolution |
| 1336 IncrementalResolver incrementalResolver = new IncrementalResolver( | 1302 IncrementalResolver incrementalResolver = new IncrementalResolver( |
| 1337 _units, | |
| 1338 _unitElement, | 1303 _unitElement, |
| 1339 _updateOffset, | 1304 _updateOffset, |
| 1340 _updateEndOld, | 1305 _updateEndOld, |
| 1341 _updateEndNew); | 1306 _updateEndNew); |
| 1342 bool success = incrementalResolver.resolve(newNode); | 1307 bool success = incrementalResolver.resolve(newNode); |
| 1343 // check if success | 1308 // check if success |
| 1344 if (!success) { | 1309 if (!success) { |
| 1345 logger.log('Failure: element model changed.'); | 1310 logger.log('Failure: element model changed.'); |
| 1346 return false; | 1311 return false; |
| 1347 } | 1312 } |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1395 logger.log('oldComment.beginToken: ${oldComment.beginToken}'); | 1360 logger.log('oldComment.beginToken: ${oldComment.beginToken}'); |
| 1396 logger.log('newComment.beginToken: ${newComment.beginToken}'); | 1361 logger.log('newComment.beginToken: ${newComment.beginToken}'); |
| 1397 _updateOffset = oldToken.offset - 1; | 1362 _updateOffset = oldToken.offset - 1; |
| 1398 // update token references | 1363 // update token references |
| 1399 _shiftTokens(firstPair.oldToken); | 1364 _shiftTokens(firstPair.oldToken); |
| 1400 _setPrecedingComments(oldToken, newComment.tokens.first); | 1365 _setPrecedingComments(oldToken, newComment.tokens.first); |
| 1401 // replace node | 1366 // replace node |
| 1402 NodeReplacer.replace(oldComment, newComment); | 1367 NodeReplacer.replace(oldComment, newComment); |
| 1403 // update elements | 1368 // update elements |
| 1404 IncrementalResolver incrementalResolver = new IncrementalResolver( | 1369 IncrementalResolver incrementalResolver = new IncrementalResolver( |
| 1405 _units, | |
| 1406 _unitElement, | 1370 _unitElement, |
| 1407 _updateOffset, | 1371 _updateOffset, |
| 1408 _updateEndOld, | 1372 _updateEndOld, |
| 1409 _updateEndNew); | 1373 _updateEndNew); |
| 1410 incrementalResolver._updateElementNameOffsets(); | 1374 incrementalResolver._updateElementNameOffsets(); |
| 1411 incrementalResolver._shiftEntryErrors(); | 1375 incrementalResolver._shiftEntryErrors(); |
| 1412 _updateEntry(); | 1376 _updateEntry(); |
| 1413 // resolve references in the comment | 1377 // resolve references in the comment |
| 1414 incrementalResolver._resolveReferences(newComment); | 1378 incrementalResolver._resolveReferences(newComment); |
| 1415 // OK | 1379 // OK |
| (...skipping 455 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1871 String toString() => name; | 1835 String toString() => name; |
| 1872 } | 1836 } |
| 1873 | 1837 |
| 1874 | 1838 |
| 1875 class _TokenPair { | 1839 class _TokenPair { |
| 1876 final _TokenDifferenceKind kind; | 1840 final _TokenDifferenceKind kind; |
| 1877 final Token oldToken; | 1841 final Token oldToken; |
| 1878 final Token newToken; | 1842 final Token newToken; |
| 1879 _TokenPair(this.kind, this.oldToken, this.newToken); | 1843 _TokenPair(this.kind, this.oldToken, this.newToken); |
| 1880 } | 1844 } |
| OLD | NEW |