Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(7)

Side by Side Diff: pkg/analyzer/lib/src/generated/incremental_resolver.dart

Issue 2887443002: Revert "cleanup unused incremental resolution" (Closed)
Patch Set: Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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.incremental_resolver; 5 library analyzer.src.generated.incremental_resolver;
6 6
7 import 'dart:collection';
8 import 'dart:math' as math;
9
7 import 'package:analyzer/dart/ast/ast.dart'; 10 import 'package:analyzer/dart/ast/ast.dart';
11 import 'package:analyzer/dart/ast/token.dart';
8 import 'package:analyzer/dart/element/element.dart'; 12 import 'package:analyzer/dart/element/element.dart';
13 import 'package:analyzer/dart/element/visitor.dart';
14 import 'package:analyzer/error/error.dart';
15 import 'package:analyzer/error/listener.dart';
9 import 'package:analyzer/exception/exception.dart'; 16 import 'package:analyzer/exception/exception.dart';
17 import 'package:analyzer/src/context/cache.dart';
18 import 'package:analyzer/src/dart/ast/token.dart';
19 import 'package:analyzer/src/dart/ast/utilities.dart';
20 import 'package:analyzer/src/dart/element/builder.dart';
21 import 'package:analyzer/src/dart/element/element.dart';
22 import 'package:analyzer/src/dart/resolver/inheritance_manager.dart';
23 import 'package:analyzer/src/dart/scanner/reader.dart';
24 import 'package:analyzer/src/dart/scanner/scanner.dart';
25 import 'package:analyzer/src/generated/constant.dart';
26 import 'package:analyzer/src/generated/engine.dart';
27 import 'package:analyzer/src/generated/error_verifier.dart';
28 import 'package:analyzer/src/generated/incremental_logger.dart'
29 show logger, LoggingTimer;
30 import 'package:analyzer/src/generated/parser.dart';
10 import 'package:analyzer/src/generated/resolver.dart'; 31 import 'package:analyzer/src/generated/resolver.dart';
32 import 'package:analyzer/src/generated/source.dart';
33 import 'package:analyzer/src/generated/utilities_dart.dart';
34 import 'package:analyzer/src/task/dart.dart';
35 import 'package:analyzer/task/dart.dart';
36 import 'package:analyzer/task/general.dart' show CONTENT, LINE_INFO;
37 import 'package:analyzer/task/model.dart';
38
39 /**
40 * The [Delta] implementation used by incremental resolver.
41 * It keeps Dart results that are either don't change or are updated.
42 */
43 class IncrementalBodyDelta extends Delta {
44 /**
45 * The offset of the changed contents.
46 */
47 final int updateOffset;
48
49 /**
50 * The end of the changed contents in the old unit.
51 */
52 final int updateEndOld;
53
54 /**
55 * The end of the changed contents in the new unit.
56 */
57 final int updateEndNew;
58
59 /**
60 * The delta between [updateEndNew] and [updateEndOld].
61 */
62 final int updateDelta;
63
64 IncrementalBodyDelta(Source source, this.updateOffset, this.updateEndOld,
65 this.updateEndNew, this.updateDelta)
66 : super(source);
67
68 @override
69 DeltaResult validate(InternalAnalysisContext context, AnalysisTarget target,
70 ResultDescriptor descriptor, Object value) {
71 // A body change delta should never leak outside its source.
72 // It can cause invalidation of results (e.g. hints) in other sources,
73 // but only when a result in the updated source is INVALIDATE_NO_DELTA.
74 if (target.source != source) {
75 return DeltaResult.STOP;
76 }
77 // don't invalidate results of standard Dart tasks
78 bool isByTask(TaskDescriptor taskDescriptor) {
79 return taskDescriptor.results.contains(descriptor);
80 }
81
82 if (descriptor == CONTENT) {
83 return DeltaResult.KEEP_CONTINUE;
84 }
85 if (target is LibrarySpecificUnit && target.unit != source) {
86 if (isByTask(GatherUsedLocalElementsTask.DESCRIPTOR) ||
87 isByTask(GatherUsedImportedElementsTask.DESCRIPTOR)) {
88 return DeltaResult.KEEP_CONTINUE;
89 }
90 }
91 if (isByTask(BuildCompilationUnitElementTask.DESCRIPTOR) ||
92 isByTask(BuildDirectiveElementsTask.DESCRIPTOR) ||
93 isByTask(BuildEnumMemberElementsTask.DESCRIPTOR) ||
94 isByTask(BuildExportNamespaceTask.DESCRIPTOR) ||
95 isByTask(BuildLibraryElementTask.DESCRIPTOR) ||
96 isByTask(BuildPublicNamespaceTask.DESCRIPTOR) ||
97 isByTask(BuildSourceExportClosureTask.DESCRIPTOR) ||
98 isByTask(ComputeConstantDependenciesTask.DESCRIPTOR) ||
99 isByTask(ComputeConstantValueTask.DESCRIPTOR) ||
100 isByTask(ComputeInferableStaticVariableDependenciesTask.DESCRIPTOR) ||
101 isByTask(ComputeLibraryCycleTask.DESCRIPTOR) ||
102 isByTask(DartErrorsTask.DESCRIPTOR) ||
103 isByTask(ReadyLibraryElement2Task.DESCRIPTOR) ||
104 isByTask(ReadyLibraryElement5Task.DESCRIPTOR) ||
105 isByTask(ReadyLibraryElement7Task.DESCRIPTOR) ||
106 isByTask(ReadyResolvedUnitTask.DESCRIPTOR) ||
107 isByTask(EvaluateUnitConstantsTask.DESCRIPTOR) ||
108 isByTask(GenerateHintsTask.DESCRIPTOR) ||
109 isByTask(InferInstanceMembersInUnitTask.DESCRIPTOR) ||
110 isByTask(InferStaticVariableTypesInUnitTask.DESCRIPTOR) ||
111 isByTask(InferStaticVariableTypeTask.DESCRIPTOR) ||
112 isByTask(LibraryErrorsReadyTask.DESCRIPTOR) ||
113 isByTask(LibraryUnitErrorsTask.DESCRIPTOR) ||
114 isByTask(ParseDartTask.DESCRIPTOR) ||
115 isByTask(PartiallyResolveUnitReferencesTask.DESCRIPTOR) ||
116 isByTask(ScanDartTask.DESCRIPTOR) ||
117 isByTask(ResolveConstantExpressionTask.DESCRIPTOR) ||
118 isByTask(ResolveDirectiveElementsTask.DESCRIPTOR) ||
119 isByTask(ResolvedUnit7InLibraryClosureTask.DESCRIPTOR) ||
120 isByTask(ResolvedUnit7InLibraryTask.DESCRIPTOR) ||
121 isByTask(ResolveInstanceFieldsInUnitTask.DESCRIPTOR) ||
122 isByTask(ResolveLibraryReferencesTask.DESCRIPTOR) ||
123 isByTask(ResolveLibraryTask.DESCRIPTOR) ||
124 isByTask(ResolveLibraryTypeNamesTask.DESCRIPTOR) ||
125 isByTask(ResolveTopLevelLibraryTypeBoundsTask.DESCRIPTOR) ||
126 isByTask(ResolveTopLevelUnitTypeBoundsTask.DESCRIPTOR) ||
127 isByTask(ResolveUnitTask.DESCRIPTOR) ||
128 isByTask(ResolveUnitTypeNamesTask.DESCRIPTOR) ||
129 isByTask(ResolveVariableReferencesTask.DESCRIPTOR) ||
130 isByTask(StrongModeVerifyUnitTask.DESCRIPTOR) ||
131 isByTask(VerifyUnitTask.DESCRIPTOR)) {
132 return DeltaResult.KEEP_CONTINUE;
133 }
134 // invalidate all the other results
135 return DeltaResult.INVALIDATE_NO_DELTA;
136 }
137 }
138
139 /**
140 * Instances of the class [IncrementalResolver] resolve the smallest portion of
141 * an AST structure that we currently know how to resolve.
142 */
143 class IncrementalResolver {
144 /**
145 * The element of the compilation unit being resolved.
146 */
147 final CompilationUnitElementImpl _definingUnit;
148
149 /**
150 * The context the compilation unit being resolved in.
151 */
152 final AnalysisContext _context;
153
154 /**
155 * The object used to access the types from the core library.
156 */
157 final TypeProvider _typeProvider;
158
159 /**
160 * The type system primitives.
161 */
162 final TypeSystem _typeSystem;
163
164 /**
165 * The element for the library containing the compilation unit being resolved.
166 */
167 final LibraryElementImpl _definingLibrary;
168
169 final AnalysisCache _cache;
170
171 /**
172 * The [CacheEntry] corresponding to the source being resolved.
173 */
174 final CacheEntry newSourceEntry;
175
176 /**
177 * The [CacheEntry] corresponding to the [LibrarySpecificUnit] being resolved.
178 */
179 final CacheEntry newUnitEntry;
180
181 /**
182 * The source representing the compilation unit being visited.
183 */
184 final Source _source;
185
186 /**
187 * The source representing the library of the compilation unit being visited.
188 */
189 final Source _librarySource;
190
191 /**
192 * The offset of the changed contents.
193 */
194 final int _updateOffset;
195
196 /**
197 * The end of the changed contents in the old unit.
198 */
199 final int _updateEndOld;
200
201 /**
202 * The end of the changed contents in the new unit.
203 */
204 final int _updateEndNew;
205
206 /**
207 * The delta between [_updateEndNew] and [_updateEndOld].
208 */
209 final int _updateDelta;
210
211 /**
212 * The set of [AnalysisError]s that have been already shifted.
213 */
214 final Set<AnalysisError> _alreadyShiftedErrors = new HashSet.identity();
215
216 final RecordingErrorListener errorListener = new RecordingErrorListener();
217 ResolutionContext _resolutionContext;
218
219 List<AnalysisError> _resolveErrors = AnalysisError.NO_ERRORS;
220 List<AnalysisError> _verifyErrors = AnalysisError.NO_ERRORS;
221
222 /**
223 * Initialize a newly created incremental resolver to resolve a node in the
224 * given source in the given library.
225 */
226 IncrementalResolver(
227 this._cache,
228 this.newSourceEntry,
229 this.newUnitEntry,
230 CompilationUnitElementImpl definingUnit,
231 this._updateOffset,
232 int updateEndOld,
233 int updateEndNew)
234 : _definingUnit = definingUnit,
235 _context = definingUnit.context,
236 _typeProvider = definingUnit.context.typeProvider,
237 _typeSystem = definingUnit.context.typeSystem,
238 _definingLibrary = definingUnit.library,
239 _source = definingUnit.source,
240 _librarySource = definingUnit.library.source,
241 _updateEndOld = updateEndOld,
242 _updateEndNew = updateEndNew,
243 _updateDelta = updateEndNew - updateEndOld;
244
245 /**
246 * Resolve [body], reporting any errors or warnings to the given listener.
247 *
248 * [body] - the root of the AST structure to be resolved.
249 */
250 void resolve(BlockFunctionBody body) {
251 logger.enter('resolve: $_definingUnit');
252 try {
253 Declaration executable = _findResolutionRoot(body);
254 _prepareResolutionContext(executable);
255 // update elements
256 _updateCache();
257 _updateElementNameOffsets();
258 _buildElements(executable, body);
259 // resolve
260 _resolveReferences(executable);
261 _computeConstants(executable);
262 _resolveErrors = errorListener.getErrorsForSource(_source);
263 // verify
264 _verify(executable);
265 _context.invalidateLibraryHints(_librarySource);
266 // update entry errors
267 _updateEntry();
268 } finally {
269 logger.exit();
270 }
271 }
272
273 void _buildElements(Declaration executable, AstNode node) {
274 LoggingTimer timer = logger.startTimer();
275 try {
276 ElementHolder holder = new ElementHolder();
277 node.accept(new LocalElementBuilder(holder, _definingUnit));
278 // Move local elements into the ExecutableElementImpl.
279 ExecutableElementImpl executableElement =
280 executable.element as ExecutableElementImpl;
281 executableElement.localVariables = holder.localVariables;
282 executableElement.functions = holder.functions;
283 executableElement.labels = holder.labels;
284 holder.validate();
285 } finally {
286 timer.stop('build elements');
287 }
288 }
289
290 /**
291 * Compute a value for all of the constants in the given [node].
292 */
293 void _computeConstants(AstNode node) {
294 // compute values
295 {
296 CompilationUnit unit = node.getAncestor((n) => n is CompilationUnit);
297 ConstantValueComputer computer = new ConstantValueComputer(
298 _typeProvider, _context.declaredVariables, null, _typeSystem);
299 computer.add(unit);
300 computer.computeValues();
301 }
302 // validate
303 {
304 ErrorReporter errorReporter = new ErrorReporter(errorListener, _source);
305 ConstantVerifier constantVerifier = new ConstantVerifier(errorReporter,
306 _definingLibrary, _typeProvider, _context.declaredVariables);
307 node.accept(constantVerifier);
308 }
309 }
310
311 /**
312 * Starting at [node], find the smallest AST node that can be resolved
313 * independently of any other nodes. Return the node that was found.
314 *
315 * [node] - the node at which the search is to begin
316 *
317 * Throws [AnalysisException] if there is no such node.
318 */
319 Declaration _findResolutionRoot(AstNode node) {
320 while (node != null) {
321 if (node is ConstructorDeclaration ||
322 node is FunctionDeclaration ||
323 node is MethodDeclaration) {
324 return node;
325 }
326 node = node.parent;
327 }
328 throw new AnalysisException("Cannot resolve node: no resolvable node");
329 }
330
331 void _prepareResolutionContext(AstNode node) {
332 if (_resolutionContext == null) {
333 _resolutionContext = ResolutionContextBuilder.contextFor(node);
334 }
335 }
336
337 _resolveReferences(AstNode node) {
338 LoggingTimer timer = logger.startTimer();
339 try {
340 _prepareResolutionContext(node);
341 Scope scope = _resolutionContext.scope;
342 // resolve types
343 {
344 TypeResolverVisitor visitor = new TypeResolverVisitor(
345 _definingLibrary, _source, _typeProvider, errorListener,
346 nameScope: scope);
347 node.accept(visitor);
348 }
349 // resolve variables
350 {
351 VariableResolverVisitor visitor = new VariableResolverVisitor(
352 _definingLibrary, _source, _typeProvider, errorListener,
353 nameScope: scope);
354 node.accept(visitor);
355 }
356 // resolve references
357 {
358 ResolverVisitor visitor = new ResolverVisitor(
359 _definingLibrary, _source, _typeProvider, errorListener,
360 nameScope: scope);
361 if (_resolutionContext.enclosingClassDeclaration != null) {
362 visitor.visitClassDeclarationIncrementally(
363 _resolutionContext.enclosingClassDeclaration);
364 }
365 if (node is Comment) {
366 visitor.resolveOnlyCommentInFunctionBody = true;
367 node = node.parent;
368 }
369 visitor.initForIncrementalResolution();
370 node.accept(visitor);
371 }
372 } finally {
373 timer.stop('resolve references');
374 }
375 }
376
377 void _shiftEntryErrors() {
378 _shiftErrors_NEW(HINTS);
379 _shiftErrors_NEW(LINTS);
380 _shiftErrors_NEW(LIBRARY_UNIT_ERRORS);
381 _shiftErrors_NEW(RESOLVE_TYPE_NAMES_ERRORS);
382 _shiftErrors_NEW(RESOLVE_TYPE_BOUNDS_ERRORS);
383 _shiftErrors_NEW(RESOLVE_UNIT_ERRORS);
384 _shiftErrors_NEW(STATIC_VARIABLE_RESOLUTION_ERRORS_IN_UNIT);
385 _shiftErrors_NEW(STRONG_MODE_ERRORS);
386 _shiftErrors_NEW(VARIABLE_REFERENCE_ERRORS);
387 _shiftErrors_NEW(VERIFY_ERRORS);
388 }
389
390 void _shiftErrors(List<AnalysisError> errors) {
391 for (AnalysisError error in errors) {
392 if (_alreadyShiftedErrors.add(error)) {
393 int errorOffset = error.offset;
394 if (errorOffset > _updateOffset) {
395 error.offset += _updateDelta;
396 }
397 }
398 }
399 }
400
401 void _shiftErrors_NEW(ResultDescriptor<List<AnalysisError>> descriptor) {
402 List<AnalysisError> errors = newUnitEntry.getValue(descriptor);
403 _shiftErrors(errors);
404 }
405
406 void _updateCache() {
407 if (newSourceEntry != null) {
408 LoggingTimer timer = logger.startTimer();
409 try {
410 newSourceEntry.setState(CONTENT, CacheState.INVALID,
411 delta: new IncrementalBodyDelta(_source, _updateOffset,
412 _updateEndOld, _updateEndNew, _updateDelta));
413 } finally {
414 timer.stop('invalidate cache with delta');
415 }
416 }
417 }
418
419 void _updateElementNameOffsets() {
420 LoggingTimer timer = logger.startTimer();
421 try {
422 _definingUnit.accept(
423 new _ElementOffsetUpdater(_updateOffset, _updateDelta, _cache));
424 _definingUnit.afterIncrementalResolution();
425 } finally {
426 timer.stop('update element offsets');
427 }
428 }
429
430 void _updateEntry() {
431 _updateErrors_NEW(RESOLVE_TYPE_NAMES_ERRORS, []);
432 _updateErrors_NEW(RESOLVE_TYPE_BOUNDS_ERRORS, []);
433 _updateErrors_NEW(RESOLVE_UNIT_ERRORS, _resolveErrors);
434 _updateErrors_NEW(VARIABLE_REFERENCE_ERRORS, []);
435 _updateErrors_NEW(VERIFY_ERRORS, _verifyErrors);
436 // invalidate results we don't update incrementally
437 newUnitEntry.setState(STRONG_MODE_ERRORS, CacheState.INVALID);
438 newUnitEntry.setState(USED_IMPORTED_ELEMENTS, CacheState.INVALID);
439 newUnitEntry.setState(USED_LOCAL_ELEMENTS, CacheState.INVALID);
440 newUnitEntry.setState(HINTS, CacheState.INVALID);
441 newUnitEntry.setState(LINTS, CacheState.INVALID);
442 }
443
444 List<AnalysisError> _updateErrors(
445 List<AnalysisError> oldErrors, List<AnalysisError> newErrors) {
446 List<AnalysisError> errors = new List<AnalysisError>();
447 // add updated old errors
448 for (AnalysisError error in oldErrors) {
449 int errorOffset = error.offset;
450 if (errorOffset < _updateOffset) {
451 errors.add(error);
452 } else if (errorOffset > _updateEndOld) {
453 error.offset += _updateDelta;
454 errors.add(error);
455 }
456 }
457 // add new errors
458 for (AnalysisError error in newErrors) {
459 int errorOffset = error.offset;
460 if (errorOffset > _updateOffset && errorOffset < _updateEndNew) {
461 errors.add(error);
462 }
463 }
464 // done
465 return errors;
466 }
467
468 void _updateErrors_NEW(ResultDescriptor<List<AnalysisError>> descriptor,
469 List<AnalysisError> newErrors) {
470 List<AnalysisError> oldErrors = newUnitEntry.getValue(descriptor);
471 List<AnalysisError> errors = _updateErrors(oldErrors, newErrors);
472 newUnitEntry.setValueIncremental(descriptor, errors, true);
473 }
474
475 void _verify(AstNode node) {
476 LoggingTimer timer = logger.startTimer();
477 try {
478 RecordingErrorListener errorListener = new RecordingErrorListener();
479 ErrorReporter errorReporter = new ErrorReporter(errorListener, _source);
480 ErrorVerifier errorVerifier = new ErrorVerifier(
481 errorReporter,
482 _definingLibrary,
483 _typeProvider,
484 new InheritanceManager(_definingLibrary),
485 _context.analysisOptions.enableSuperMixins);
486 if (_resolutionContext.enclosingClassDeclaration != null) {
487 errorVerifier.visitClassDeclarationIncrementally(
488 _resolutionContext.enclosingClassDeclaration);
489 }
490 node.accept(errorVerifier);
491 _verifyErrors = errorListener.getErrorsForSource(_source);
492 } finally {
493 timer.stop('verify');
494 }
495 }
496 }
497
498 class PoorMansIncrementalResolver {
499 final TypeProvider _typeProvider;
500 final Source _unitSource;
501 final AnalysisCache _cache;
502
503 /**
504 * The [CacheEntry] corresponding to the source being resolved.
505 */
506 final CacheEntry _sourceEntry;
507
508 /**
509 * The [CacheEntry] corresponding to the [LibrarySpecificUnit] being resolved.
510 */
511 final CacheEntry _unitEntry;
512
513 final CompilationUnit _oldUnit;
514 CompilationUnitElement _unitElement;
515
516 int _updateOffset;
517 int _updateDelta;
518 int _updateEndOld;
519 int _updateEndNew;
520
521 LineInfo _newLineInfo;
522 List<AnalysisError> _newScanErrors = <AnalysisError>[];
523 List<AnalysisError> _newParseErrors = <AnalysisError>[];
524
525 PoorMansIncrementalResolver(
526 this._typeProvider,
527 this._unitSource,
528 this._cache,
529 this._sourceEntry,
530 this._unitEntry,
531 this._oldUnit,
532 bool resolveApiChanges);
533
534 /**
535 * Attempts to update [_oldUnit] to the state corresponding to [newCode].
536 * Returns `true` if success, or `false` otherwise.
537 * The [_oldUnit] might be damaged.
538 */
539 bool resolve(String newCode) {
540 logger.enter('diff/resolve $_unitSource');
541 try {
542 // prepare old unit
543 if (!_areCurlyBracketsBalanced(_oldUnit.beginToken)) {
544 logger.log('Unbalanced number of curly brackets in the old unit.');
545 return false;
546 }
547 _unitElement = _oldUnit.element;
548 // prepare new unit
549 CompilationUnit newUnit = _parseUnit(newCode);
550 if (!_areCurlyBracketsBalanced(newUnit.beginToken)) {
551 logger.log('Unbalanced number of curly brackets in the new unit.');
552 return false;
553 }
554 // find difference
555 _TokenPair firstPair =
556 _findFirstDifferentToken(_oldUnit.beginToken, newUnit.beginToken);
557 _TokenPair lastPair =
558 _findLastDifferentToken(_oldUnit.endToken, newUnit.endToken);
559 if (firstPair != null && lastPair != null) {
560 int firstOffsetOld = firstPair.oldToken.offset;
561 int firstOffsetNew = firstPair.newToken.offset;
562 int lastOffsetOld = lastPair.oldToken.end;
563 int lastOffsetNew = lastPair.newToken.end;
564 int beginOffsetOld = math.min(firstOffsetOld, lastOffsetOld);
565 int endOffsetOld = math.max(firstOffsetOld, lastOffsetOld);
566 int beginOffsetNew = math.min(firstOffsetNew, lastOffsetNew);
567 int endOffsetNew = math.max(firstOffsetNew, lastOffsetNew);
568 // A pure whitespace change.
569 if (identical(firstPair.oldToken, lastPair.oldToken) &&
570 identical(firstPair.newToken, lastPair.newToken) &&
571 firstPair.kind == _TokenDifferenceKind.OFFSET) {
572 _updateOffset = beginOffsetOld - 1;
573 _updateEndOld = endOffsetOld;
574 _updateEndNew = endOffsetNew;
575 _updateDelta = newUnit.length - _oldUnit.length;
576 logger.log('Whitespace change.');
577 _shiftTokens(firstPair.oldToken, true);
578 IncrementalResolver incrementalResolver = new IncrementalResolver(
579 _cache,
580 _sourceEntry,
581 _unitEntry,
582 _unitElement,
583 _updateOffset,
584 _updateEndOld,
585 _updateEndNew);
586 incrementalResolver._updateCache();
587 incrementalResolver._updateElementNameOffsets();
588 incrementalResolver._shiftEntryErrors();
589 _updateEntry();
590 logger.log('Success.');
591 return true;
592 }
593 // A Dart documentation comment change.
594 {
595 Token firstOldToken = firstPair.oldToken;
596 Token firstNewToken = firstPair.newToken;
597 Token lastOldToken = lastPair.oldToken;
598 Token lastNewToken = lastPair.newToken;
599 if (firstOldToken is DocumentationCommentToken &&
600 firstNewToken is DocumentationCommentToken &&
601 lastOldToken is DocumentationCommentToken &&
602 lastNewToken is DocumentationCommentToken &&
603 identical(firstOldToken.parent, lastOldToken.parent) &&
604 identical(firstNewToken.parent, lastNewToken.parent)) {
605 _updateOffset = beginOffsetOld;
606 _updateEndOld = firstOldToken.parent.offset;
607 _updateEndNew = firstNewToken.parent.offset;
608 _updateDelta = newUnit.length - _oldUnit.length;
609 bool success =
610 _resolveCommentDoc(newUnit, firstOldToken, firstNewToken);
611 logger.log('Documentation comment resolved: $success');
612 return success;
613 }
614 }
615 // Find nodes covering the "old" and "new" token ranges.
616 AstNode oldNode =
617 _findNodeCovering(_oldUnit, beginOffsetOld, endOffsetOld - 1);
618 AstNode newNode =
619 _findNodeCovering(newUnit, beginOffsetNew, endOffsetNew - 1);
620 logger.log(() => 'oldNode: $oldNode');
621 logger.log(() => 'newNode: $newNode');
622 // Try to find the smallest common node, a FunctionBody currently.
623 {
624 List<AstNode> oldParents = _getParents(oldNode);
625 List<AstNode> newParents = _getParents(newNode);
626 // fail if an initializer change
627 if (oldParents.any((n) => n is ConstructorInitializer) ||
628 newParents.any((n) => n is ConstructorInitializer)) {
629 logger.log('Failure: a change in a constructor initializer');
630 return false;
631 }
632 // find matching methods / bodies
633 int length = math.min(oldParents.length, newParents.length);
634 bool found = false;
635 for (int i = 0; i < length; i++) {
636 AstNode oldParent = oldParents[i];
637 AstNode newParent = newParents[i];
638 if (oldParent is CompilationUnit && newParent is CompilationUnit) {
639 int oldLength = oldParent.declarations.length;
640 int newLength = newParent.declarations.length;
641 if (oldLength != newLength) {
642 logger.log(
643 'Failure: unit declarations mismatch $oldLength vs. $newLeng th');
644 return false;
645 }
646 } else if (oldParent is ClassDeclaration &&
647 newParent is ClassDeclaration) {
648 int oldLength = oldParent.members.length;
649 int newLength = newParent.members.length;
650 if (oldLength != newLength) {
651 logger.log(
652 'Failure: class declarations mismatch $oldLength vs. $newLen gth');
653 return false;
654 }
655 } else if (oldParent is FunctionDeclaration &&
656 newParent is FunctionDeclaration ||
657 oldParent is ConstructorDeclaration &&
658 newParent is ConstructorDeclaration ||
659 oldParent is MethodDeclaration &&
660 newParent is MethodDeclaration) {
661 if (oldParents.length == i || newParents.length == i) {
662 return false;
663 }
664 } else if (oldParent is FunctionBody && newParent is FunctionBody) {
665 if (oldParent is BlockFunctionBody &&
666 newParent is BlockFunctionBody) {
667 if (oldParent.isAsynchronous != newParent.isAsynchronous) {
668 logger.log('Failure: body async mismatch.');
669 return false;
670 }
671 if (oldParent.isGenerator != newParent.isGenerator) {
672 logger.log('Failure: body generator mismatch.');
673 return false;
674 }
675 oldNode = oldParent;
676 newNode = newParent;
677 found = true;
678 break;
679 }
680 logger.log('Failure: not a block function body.');
681 return false;
682 } else if (oldParent is FunctionExpression &&
683 newParent is FunctionExpression) {
684 // skip
685 } else {
686 logger.log('Failure: old and new parent mismatch'
687 ' ${oldParent.runtimeType} vs. ${newParent.runtimeType}');
688 return false;
689 }
690 }
691 if (!found) {
692 logger.log('Failure: no enclosing function body or executable.');
693 return false;
694 }
695 }
696 logger.log(() => 'oldNode: $oldNode');
697 logger.log(() => 'newNode: $newNode');
698 // prepare update range
699 _updateOffset = oldNode.offset;
700 _updateEndOld = oldNode.end;
701 _updateEndNew = newNode.end;
702 _updateDelta = _updateEndNew - _updateEndOld;
703 // replace node
704 NodeReplacer.replace(oldNode, newNode);
705 // update token references
706 {
707 Token oldBeginToken = _getBeginTokenNotComment(oldNode);
708 Token newBeginToken = _getBeginTokenNotComment(newNode);
709 if (oldBeginToken.previous.type == TokenType.EOF) {
710 _oldUnit.beginToken = newBeginToken;
711 } else {
712 oldBeginToken.previous.setNext(newBeginToken);
713 }
714 newNode.endToken.setNext(oldNode.endToken.next);
715 _shiftTokens(oldNode.endToken.next);
716 }
717 // perform incremental resolution
718 IncrementalResolver incrementalResolver = new IncrementalResolver(
719 _cache,
720 _sourceEntry,
721 _unitEntry,
722 _unitElement,
723 _updateOffset,
724 _updateEndOld,
725 _updateEndNew);
726 incrementalResolver.resolve(newNode);
727 // update DartEntry
728 _updateEntry();
729 logger.log('Success.');
730 return true;
731 }
732 } catch (e, st) {
733 logger.logException(e, st);
734 logger.log('Failure: exception.');
735 // The incremental resolver log is usually turned off,
736 // so also log the exception to the instrumentation log.
737 AnalysisEngine.instance.logger.logError(
738 'Failure in incremental resolver', new CaughtException(e, st));
739 } finally {
740 logger.exit();
741 }
742 return false;
743 }
744
745 CompilationUnit _parseUnit(String code) {
746 LoggingTimer timer = logger.startTimer();
747 try {
748 Token token = _scan(code);
749 RecordingErrorListener errorListener = new RecordingErrorListener();
750 Parser parser = new Parser(_unitSource, errorListener);
751 AnalysisOptions options = _unitElement.context.analysisOptions;
752 parser.parseGenericMethodComments = options.strongMode;
753 CompilationUnit unit = parser.parseCompilationUnit(token);
754 _newParseErrors = errorListener.errors;
755 return unit;
756 } finally {
757 timer.stop('parse');
758 }
759 }
760
761 /**
762 * Attempts to resolve a documentation comment change.
763 * Returns `true` if success.
764 */
765 bool _resolveCommentDoc(
766 CompilationUnit newUnit, CommentToken oldToken, CommentToken newToken) {
767 if (oldToken == null || newToken == null) {
768 return false;
769 }
770 // find nodes
771 int offset = oldToken.offset;
772 logger.log('offset: $offset');
773 AstNode oldNode = _findNodeCovering(_oldUnit, offset, offset);
774 AstNode newNode = _findNodeCovering(newUnit, offset, offset);
775 if (oldNode is! Comment || newNode is! Comment) {
776 return false;
777 }
778 Comment oldComment = oldNode;
779 Comment newComment = newNode;
780 logger.log('oldComment.beginToken: ${oldComment.beginToken}');
781 logger.log('newComment.beginToken: ${newComment.beginToken}');
782 // update token references
783 _shiftTokens(oldToken.parent);
784 _setPrecedingComments(oldToken.parent, newComment.tokens.first);
785 // replace node
786 NodeReplacer.replace(oldComment, newComment);
787 // update elements
788 IncrementalResolver incrementalResolver = new IncrementalResolver(
789 _cache,
790 _sourceEntry,
791 _unitEntry,
792 _unitElement,
793 _updateOffset,
794 _updateEndOld,
795 _updateEndNew);
796 incrementalResolver._updateCache();
797 incrementalResolver._updateElementNameOffsets();
798 incrementalResolver._shiftEntryErrors();
799 _updateEntry();
800 // resolve references in the comment
801 incrementalResolver._resolveReferences(newComment);
802 // update 'documentationComment' of the parent element(s)
803 {
804 AstNode parent = newComment.parent;
805 if (parent is AnnotatedNode) {
806 setElementDocumentationForVariables(VariableDeclarationList list) {
807 for (VariableDeclaration variable in list.variables) {
808 Element variableElement = variable.element;
809 if (variableElement is ElementImpl) {
810 setElementDocumentationComment(variableElement, parent);
811 }
812 }
813 }
814
815 Element parentElement = ElementLocator.locate(newComment.parent);
816 if (parentElement is ElementImpl) {
817 setElementDocumentationComment(parentElement, parent);
818 } else if (parent is FieldDeclaration) {
819 setElementDocumentationForVariables(parent.fields);
820 } else if (parent is TopLevelVariableDeclaration) {
821 setElementDocumentationForVariables(parent.variables);
822 }
823 }
824 }
825 // OK
826 return true;
827 }
828
829 Token _scan(String code) {
830 RecordingErrorListener errorListener = new RecordingErrorListener();
831 CharSequenceReader reader = new CharSequenceReader(code);
832 Scanner scanner = new Scanner(_unitSource, reader, errorListener);
833 AnalysisOptions options = _unitElement.context.analysisOptions;
834 scanner.scanGenericMethodComments = options.strongMode;
835 Token token = scanner.tokenize();
836 _newLineInfo = new LineInfo(scanner.lineStarts);
837 _newScanErrors = errorListener.errors;
838 return token;
839 }
840
841 /**
842 * Set the given [comment] as a "precedingComments" for [token].
843 */
844 void _setPrecedingComments(Token token, CommentToken comment) {
845 if (token is BeginTokenWithComment) {
846 token.precedingComments = comment;
847 } else if (token is KeywordTokenWithComment) {
848 token.precedingComments = comment;
849 } else if (token is StringTokenWithComment) {
850 token.precedingComments = comment;
851 } else if (token is TokenWithComment) {
852 token.precedingComments = comment;
853 } else {
854 Type parentType = token?.runtimeType;
855 throw new AnalysisException('Uknown parent token type: $parentType');
856 }
857 }
858
859 void _shiftTokens(Token token, [bool goUpComment = false]) {
860 while (token != null) {
861 if (goUpComment && token is CommentToken) {
862 token = (token as CommentToken).parent;
863 }
864 if (token.offset > _updateOffset) {
865 token.offset += _updateDelta;
866 }
867 // comments
868 _shiftTokens(token.precedingComments);
869 if (token is DocumentationCommentToken) {
870 for (Token reference in token.references) {
871 _shiftTokens(reference);
872 }
873 }
874 // next
875 if (token.type == TokenType.EOF) {
876 break;
877 }
878 token = token.next;
879 }
880 }
881
882 void _updateEntry() {
883 // scan results
884 _sourceEntry.setValueIncremental(SCAN_ERRORS, _newScanErrors, true);
885 _sourceEntry.setValueIncremental(LINE_INFO, _newLineInfo, false);
886 // parse results
887 _sourceEntry.setValueIncremental(PARSE_ERRORS, _newParseErrors, true);
888 _sourceEntry.setValueIncremental(PARSED_UNIT, _oldUnit, false);
889 }
890
891 /**
892 * Checks if [token] has a balanced number of open and closed curly brackets.
893 */
894 static bool _areCurlyBracketsBalanced(Token token) {
895 int numOpen = _getTokenCount(token, TokenType.OPEN_CURLY_BRACKET);
896 int numOpen2 =
897 _getTokenCount(token, TokenType.STRING_INTERPOLATION_EXPRESSION);
898 int numClosed = _getTokenCount(token, TokenType.CLOSE_CURLY_BRACKET);
899 return numOpen + numOpen2 == numClosed;
900 }
901
902 static _TokenDifferenceKind _compareToken(
903 Token oldToken, Token newToken, int delta) {
904 if (oldToken == null && newToken == null) {
905 return null;
906 }
907 if (oldToken == null || newToken == null) {
908 return _TokenDifferenceKind.CONTENT;
909 }
910 if (oldToken.type != newToken.type) {
911 return _TokenDifferenceKind.CONTENT;
912 }
913 if (oldToken.lexeme != newToken.lexeme) {
914 return _TokenDifferenceKind.CONTENT;
915 }
916 if (newToken.offset - oldToken.offset != delta) {
917 return _TokenDifferenceKind.OFFSET;
918 }
919 return null;
920 }
921
922 static _TokenPair _findFirstDifferentToken(Token oldToken, Token newToken) {
923 while (oldToken.type != TokenType.EOF || newToken.type != TokenType.EOF) {
924 if (oldToken.type == TokenType.EOF || newToken.type == TokenType.EOF) {
925 return new _TokenPair(_TokenDifferenceKind.CONTENT, oldToken, newToken);
926 }
927 // compare comments
928 {
929 Token oldComment = oldToken.precedingComments;
930 Token newComment = newToken.precedingComments;
931 while (true) {
932 _TokenDifferenceKind diffKind =
933 _compareToken(oldComment, newComment, 0);
934 if (diffKind != null) {
935 return new _TokenPair(
936 diffKind, oldComment ?? oldToken, newComment ?? newToken);
937 }
938 if (oldComment == null && newComment == null) {
939 break;
940 }
941 oldComment = oldComment.next;
942 newComment = newComment.next;
943 }
944 }
945 // compare tokens
946 _TokenDifferenceKind diffKind = _compareToken(oldToken, newToken, 0);
947 if (diffKind != null) {
948 return new _TokenPair(diffKind, oldToken, newToken);
949 }
950 // next tokens
951 oldToken = oldToken.next;
952 newToken = newToken.next;
953 }
954 // no difference
955 return null;
956 }
957
958 static _TokenPair _findLastDifferentToken(Token oldToken, Token newToken) {
959 int delta = newToken.offset - oldToken.offset;
960 Token prevOldToken;
961 Token prevNewToken;
962 while (oldToken.previous != oldToken && newToken.previous != newToken) {
963 // compare tokens
964 _TokenDifferenceKind diffKind = _compareToken(oldToken, newToken, delta);
965 if (diffKind != null) {
966 return new _TokenPair(diffKind, prevOldToken, prevNewToken);
967 }
968 prevOldToken = oldToken;
969 prevNewToken = newToken;
970 // compare comments
971 {
972 Token oldComment = oldToken.precedingComments;
973 Token newComment = newToken.precedingComments;
974 while (oldComment?.next != null) {
975 oldComment = oldComment.next;
976 }
977 while (newComment?.next != null) {
978 newComment = newComment.next;
979 }
980 while (true) {
981 _TokenDifferenceKind diffKind =
982 _compareToken(oldComment, newComment, delta);
983 if (diffKind != null) {
984 return new _TokenPair(
985 diffKind, oldComment ?? oldToken, newComment ?? newToken);
986 }
987 if (oldComment == null && newComment == null) {
988 break;
989 }
990 prevOldToken = oldComment;
991 prevNewToken = newComment;
992 oldComment = oldComment.previous;
993 newComment = newComment.previous;
994 }
995 }
996 // next tokens
997 oldToken = oldToken.previous;
998 newToken = newToken.previous;
999 }
1000 return null;
1001 }
1002
1003 static AstNode _findNodeCovering(AstNode root, int offset, int end) {
1004 NodeLocator nodeLocator = new NodeLocator(offset, end);
1005 return nodeLocator.searchWithin(root);
1006 }
1007
1008 static Token _getBeginTokenNotComment(AstNode node) {
1009 Token oldBeginToken = node.beginToken;
1010 if (oldBeginToken is CommentToken) {
1011 return oldBeginToken.parent;
1012 }
1013 return oldBeginToken;
1014 }
1015
1016 static List<AstNode> _getParents(AstNode node) {
1017 List<AstNode> parents = <AstNode>[];
1018 while (node != null) {
1019 parents.insert(0, node);
1020 node = node.parent;
1021 }
1022 return parents;
1023 }
1024
1025 /**
1026 * Returns number of tokens with the given [type].
1027 */
1028 static int _getTokenCount(Token token, TokenType type) {
1029 int count = 0;
1030 while (token.type != TokenType.EOF) {
1031 if (token.type == type) {
1032 count++;
1033 }
1034 token = token.next;
1035 }
1036 return count;
1037 }
1038 }
11 1039
12 /** 1040 /**
13 * The context to resolve an [AstNode] in. 1041 * The context to resolve an [AstNode] in.
14 */ 1042 */
15 class ResolutionContext { 1043 class ResolutionContext {
16 CompilationUnitElement enclosingUnit; 1044 CompilationUnitElement enclosingUnit;
17 ClassDeclaration enclosingClassDeclaration; 1045 ClassDeclaration enclosingClassDeclaration;
18 ClassElement enclosingClass; 1046 ClassElement enclosingClass;
19 Scope scope; 1047 Scope scope;
20 } 1048 }
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after
155 Scope scope = builder._scopeFor(node); 1183 Scope scope = builder._scopeFor(node);
156 // prepare context 1184 // prepare context
157 ResolutionContext context = new ResolutionContext(); 1185 ResolutionContext context = new ResolutionContext();
158 context.scope = scope; 1186 context.scope = scope;
159 context.enclosingUnit = builder._enclosingUnit; 1187 context.enclosingUnit = builder._enclosingUnit;
160 context.enclosingClassDeclaration = builder._enclosingClassDeclaration; 1188 context.enclosingClassDeclaration = builder._enclosingClassDeclaration;
161 context.enclosingClass = builder._enclosingClass; 1189 context.enclosingClass = builder._enclosingClass;
162 return context; 1190 return context;
163 } 1191 }
164 } 1192 }
1193
1194 /**
1195 * Adjusts the location of each Element that moved.
1196 *
1197 * Since `==` and `hashCode` of a local variable or function Element are based
1198 * on the element name offsets, we also need to remove these elements from the
1199 * cache to avoid a memory leak. TODO(scheglov) fix and remove this
1200 */
1201 class _ElementOffsetUpdater extends GeneralizingElementVisitor {
1202 final int updateOffset;
1203 final int updateDelta;
1204 final AnalysisCache cache;
1205
1206 _ElementOffsetUpdater(this.updateOffset, this.updateDelta, this.cache);
1207
1208 @override
1209 visitElement(Element element) {
1210 // name offset
1211 int nameOffset = element.nameOffset;
1212 if (nameOffset > updateOffset) {
1213 (element as ElementImpl).nameOffset = nameOffset + updateDelta;
1214 if (element is ConstVariableElement) {
1215 Expression initializer = element.constantInitializer;
1216 if (initializer != null) {
1217 _shiftTokens(initializer.beginToken);
1218 }
1219 _shiftErrors(element.evaluationResult?.errors);
1220 }
1221 }
1222 // code range
1223 if (element is ElementImpl) {
1224 int oldOffset = element.codeOffset;
1225 int oldLength = element.codeLength;
1226 if (oldOffset != null) {
1227 int newOffset = oldOffset;
1228 int newLength = oldLength;
1229 newOffset += oldOffset > updateOffset ? updateDelta : 0;
1230 if (oldOffset <= updateOffset && updateOffset < oldOffset + oldLength) {
1231 newLength += updateDelta;
1232 }
1233 if (newOffset != oldOffset || newLength != oldLength) {
1234 element.setCodeRange(newOffset, newLength);
1235 }
1236 }
1237 }
1238 // visible range
1239 if (element is LocalElement) {
1240 SourceRange visibleRange = element.visibleRange;
1241 if (visibleRange != null) {
1242 int oldOffset = visibleRange.offset;
1243 int oldLength = visibleRange.length;
1244 int newOffset = oldOffset;
1245 int newLength = oldLength;
1246 newOffset += oldOffset > updateOffset ? updateDelta : 0;
1247 newLength += visibleRange.contains(updateOffset) ? updateDelta : 0;
1248 if (newOffset != oldOffset || newLength != oldLength) {
1249 if (element is FunctionElementImpl) {
1250 element.setVisibleRange(newOffset, newLength);
1251 } else if (element is LocalVariableElementImpl) {
1252 element.setVisibleRange(newOffset, newLength);
1253 } else if (element is ParameterElementImpl) {
1254 element.setVisibleRange(newOffset, newLength);
1255 }
1256 }
1257 }
1258 }
1259 super.visitElement(element);
1260 }
1261
1262 void _shiftErrors(List<AnalysisError> errors) {
1263 if (errors != null) {
1264 for (AnalysisError error in errors) {
1265 int errorOffset = error.offset;
1266 if (errorOffset > updateOffset) {
1267 error.offset += updateDelta;
1268 }
1269 }
1270 }
1271 }
1272
1273 void _shiftTokens(Token token) {
1274 while (token != null) {
1275 if (token.offset > updateOffset) {
1276 token.offset += updateDelta;
1277 }
1278 // comments
1279 _shiftTokens(token.precedingComments);
1280 if (token is DocumentationCommentToken) {
1281 for (Token reference in token.references) {
1282 _shiftTokens(reference);
1283 }
1284 }
1285 // next
1286 if (token.type == TokenType.EOF) {
1287 break;
1288 }
1289 token = token.next;
1290 }
1291 }
1292 }
1293
1294 /**
1295 * Describes how two [Token]s are different.
1296 */
1297 class _TokenDifferenceKind {
1298 static const CONTENT = const _TokenDifferenceKind('CONTENT');
1299 static const OFFSET = const _TokenDifferenceKind('OFFSET');
1300
1301 final String name;
1302
1303 const _TokenDifferenceKind(this.name);
1304
1305 @override
1306 String toString() => name;
1307 }
1308
1309 class _TokenPair {
1310 final _TokenDifferenceKind kind;
1311 final Token oldToken;
1312 final Token newToken;
1313 _TokenPair(this.kind, this.oldToken, this.newToken);
1314 }
OLDNEW
« no previous file with comments | « pkg/analyzer/lib/src/context/context.dart ('k') | pkg/analyzer/test/generated/incremental_resolver_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698