| Index: pkg/analyzer/lib/src/generated/incremental_resolver.dart
|
| diff --git a/pkg/analyzer/lib/src/generated/incremental_resolver.dart b/pkg/analyzer/lib/src/generated/incremental_resolver.dart
|
| index a9da4e95d45afc1cff82317b8ed5f8f32eec97f2..f1b26100b1dc4069e922b3324772b1a2d3a8f900 100644
|
| --- a/pkg/analyzer/lib/src/generated/incremental_resolver.dart
|
| +++ b/pkg/analyzer/lib/src/generated/incremental_resolver.dart
|
| @@ -4,1038 +4,10 @@
|
|
|
| library analyzer.src.generated.incremental_resolver;
|
|
|
| -import 'dart:collection';
|
| -import 'dart:math' as math;
|
| -
|
| import 'package:analyzer/dart/ast/ast.dart';
|
| -import 'package:analyzer/dart/ast/token.dart';
|
| import 'package:analyzer/dart/element/element.dart';
|
| -import 'package:analyzer/dart/element/visitor.dart';
|
| -import 'package:analyzer/error/error.dart';
|
| -import 'package:analyzer/error/listener.dart';
|
| import 'package:analyzer/exception/exception.dart';
|
| -import 'package:analyzer/src/context/cache.dart';
|
| -import 'package:analyzer/src/dart/ast/token.dart';
|
| -import 'package:analyzer/src/dart/ast/utilities.dart';
|
| -import 'package:analyzer/src/dart/element/builder.dart';
|
| -import 'package:analyzer/src/dart/element/element.dart';
|
| -import 'package:analyzer/src/dart/resolver/inheritance_manager.dart';
|
| -import 'package:analyzer/src/dart/scanner/reader.dart';
|
| -import 'package:analyzer/src/dart/scanner/scanner.dart';
|
| -import 'package:analyzer/src/generated/constant.dart';
|
| -import 'package:analyzer/src/generated/engine.dart';
|
| -import 'package:analyzer/src/generated/error_verifier.dart';
|
| -import 'package:analyzer/src/generated/incremental_logger.dart'
|
| - show logger, LoggingTimer;
|
| -import 'package:analyzer/src/generated/parser.dart';
|
| import 'package:analyzer/src/generated/resolver.dart';
|
| -import 'package:analyzer/src/generated/source.dart';
|
| -import 'package:analyzer/src/generated/utilities_dart.dart';
|
| -import 'package:analyzer/src/task/dart.dart';
|
| -import 'package:analyzer/task/dart.dart';
|
| -import 'package:analyzer/task/general.dart' show CONTENT, LINE_INFO;
|
| -import 'package:analyzer/task/model.dart';
|
| -
|
| -/**
|
| - * The [Delta] implementation used by incremental resolver.
|
| - * It keeps Dart results that are either don't change or are updated.
|
| - */
|
| -class IncrementalBodyDelta extends Delta {
|
| - /**
|
| - * The offset of the changed contents.
|
| - */
|
| - final int updateOffset;
|
| -
|
| - /**
|
| - * The end of the changed contents in the old unit.
|
| - */
|
| - final int updateEndOld;
|
| -
|
| - /**
|
| - * The end of the changed contents in the new unit.
|
| - */
|
| - final int updateEndNew;
|
| -
|
| - /**
|
| - * The delta between [updateEndNew] and [updateEndOld].
|
| - */
|
| - final int updateDelta;
|
| -
|
| - IncrementalBodyDelta(Source source, this.updateOffset, this.updateEndOld,
|
| - this.updateEndNew, this.updateDelta)
|
| - : super(source);
|
| -
|
| - @override
|
| - DeltaResult validate(InternalAnalysisContext context, AnalysisTarget target,
|
| - ResultDescriptor descriptor, Object value) {
|
| - // A body change delta should never leak outside its source.
|
| - // It can cause invalidation of results (e.g. hints) in other sources,
|
| - // but only when a result in the updated source is INVALIDATE_NO_DELTA.
|
| - if (target.source != source) {
|
| - return DeltaResult.STOP;
|
| - }
|
| - // don't invalidate results of standard Dart tasks
|
| - bool isByTask(TaskDescriptor taskDescriptor) {
|
| - return taskDescriptor.results.contains(descriptor);
|
| - }
|
| -
|
| - if (descriptor == CONTENT) {
|
| - return DeltaResult.KEEP_CONTINUE;
|
| - }
|
| - if (target is LibrarySpecificUnit && target.unit != source) {
|
| - if (isByTask(GatherUsedLocalElementsTask.DESCRIPTOR) ||
|
| - isByTask(GatherUsedImportedElementsTask.DESCRIPTOR)) {
|
| - return DeltaResult.KEEP_CONTINUE;
|
| - }
|
| - }
|
| - if (isByTask(BuildCompilationUnitElementTask.DESCRIPTOR) ||
|
| - isByTask(BuildDirectiveElementsTask.DESCRIPTOR) ||
|
| - isByTask(BuildEnumMemberElementsTask.DESCRIPTOR) ||
|
| - isByTask(BuildExportNamespaceTask.DESCRIPTOR) ||
|
| - isByTask(BuildLibraryElementTask.DESCRIPTOR) ||
|
| - isByTask(BuildPublicNamespaceTask.DESCRIPTOR) ||
|
| - isByTask(BuildSourceExportClosureTask.DESCRIPTOR) ||
|
| - isByTask(ComputeConstantDependenciesTask.DESCRIPTOR) ||
|
| - isByTask(ComputeConstantValueTask.DESCRIPTOR) ||
|
| - isByTask(ComputeInferableStaticVariableDependenciesTask.DESCRIPTOR) ||
|
| - isByTask(ComputeLibraryCycleTask.DESCRIPTOR) ||
|
| - isByTask(DartErrorsTask.DESCRIPTOR) ||
|
| - isByTask(ReadyLibraryElement2Task.DESCRIPTOR) ||
|
| - isByTask(ReadyLibraryElement5Task.DESCRIPTOR) ||
|
| - isByTask(ReadyLibraryElement7Task.DESCRIPTOR) ||
|
| - isByTask(ReadyResolvedUnitTask.DESCRIPTOR) ||
|
| - isByTask(EvaluateUnitConstantsTask.DESCRIPTOR) ||
|
| - isByTask(GenerateHintsTask.DESCRIPTOR) ||
|
| - isByTask(InferInstanceMembersInUnitTask.DESCRIPTOR) ||
|
| - isByTask(InferStaticVariableTypesInUnitTask.DESCRIPTOR) ||
|
| - isByTask(InferStaticVariableTypeTask.DESCRIPTOR) ||
|
| - isByTask(LibraryErrorsReadyTask.DESCRIPTOR) ||
|
| - isByTask(LibraryUnitErrorsTask.DESCRIPTOR) ||
|
| - isByTask(ParseDartTask.DESCRIPTOR) ||
|
| - isByTask(PartiallyResolveUnitReferencesTask.DESCRIPTOR) ||
|
| - isByTask(ScanDartTask.DESCRIPTOR) ||
|
| - isByTask(ResolveConstantExpressionTask.DESCRIPTOR) ||
|
| - isByTask(ResolveDirectiveElementsTask.DESCRIPTOR) ||
|
| - isByTask(ResolvedUnit7InLibraryClosureTask.DESCRIPTOR) ||
|
| - isByTask(ResolvedUnit7InLibraryTask.DESCRIPTOR) ||
|
| - isByTask(ResolveInstanceFieldsInUnitTask.DESCRIPTOR) ||
|
| - isByTask(ResolveLibraryReferencesTask.DESCRIPTOR) ||
|
| - isByTask(ResolveLibraryTask.DESCRIPTOR) ||
|
| - isByTask(ResolveLibraryTypeNamesTask.DESCRIPTOR) ||
|
| - isByTask(ResolveTopLevelLibraryTypeBoundsTask.DESCRIPTOR) ||
|
| - isByTask(ResolveTopLevelUnitTypeBoundsTask.DESCRIPTOR) ||
|
| - isByTask(ResolveUnitTask.DESCRIPTOR) ||
|
| - isByTask(ResolveUnitTypeNamesTask.DESCRIPTOR) ||
|
| - isByTask(ResolveVariableReferencesTask.DESCRIPTOR) ||
|
| - isByTask(StrongModeVerifyUnitTask.DESCRIPTOR) ||
|
| - isByTask(VerifyUnitTask.DESCRIPTOR)) {
|
| - return DeltaResult.KEEP_CONTINUE;
|
| - }
|
| - // invalidate all the other results
|
| - return DeltaResult.INVALIDATE_NO_DELTA;
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Instances of the class [IncrementalResolver] resolve the smallest portion of
|
| - * an AST structure that we currently know how to resolve.
|
| - */
|
| -class IncrementalResolver {
|
| - /**
|
| - * The element of the compilation unit being resolved.
|
| - */
|
| - final CompilationUnitElementImpl _definingUnit;
|
| -
|
| - /**
|
| - * The context the compilation unit being resolved in.
|
| - */
|
| - final AnalysisContext _context;
|
| -
|
| - /**
|
| - * The object used to access the types from the core library.
|
| - */
|
| - final TypeProvider _typeProvider;
|
| -
|
| - /**
|
| - * The type system primitives.
|
| - */
|
| - final TypeSystem _typeSystem;
|
| -
|
| - /**
|
| - * The element for the library containing the compilation unit being resolved.
|
| - */
|
| - final LibraryElementImpl _definingLibrary;
|
| -
|
| - final AnalysisCache _cache;
|
| -
|
| - /**
|
| - * The [CacheEntry] corresponding to the source being resolved.
|
| - */
|
| - final CacheEntry newSourceEntry;
|
| -
|
| - /**
|
| - * The [CacheEntry] corresponding to the [LibrarySpecificUnit] being resolved.
|
| - */
|
| - final CacheEntry newUnitEntry;
|
| -
|
| - /**
|
| - * The source representing the compilation unit being visited.
|
| - */
|
| - final Source _source;
|
| -
|
| - /**
|
| - * The source representing the library of the compilation unit being visited.
|
| - */
|
| - final Source _librarySource;
|
| -
|
| - /**
|
| - * The offset of the changed contents.
|
| - */
|
| - final int _updateOffset;
|
| -
|
| - /**
|
| - * The end of the changed contents in the old unit.
|
| - */
|
| - final int _updateEndOld;
|
| -
|
| - /**
|
| - * The end of the changed contents in the new unit.
|
| - */
|
| - final int _updateEndNew;
|
| -
|
| - /**
|
| - * The delta between [_updateEndNew] and [_updateEndOld].
|
| - */
|
| - final int _updateDelta;
|
| -
|
| - /**
|
| - * The set of [AnalysisError]s that have been already shifted.
|
| - */
|
| - final Set<AnalysisError> _alreadyShiftedErrors = new HashSet.identity();
|
| -
|
| - final RecordingErrorListener errorListener = new RecordingErrorListener();
|
| - ResolutionContext _resolutionContext;
|
| -
|
| - List<AnalysisError> _resolveErrors = AnalysisError.NO_ERRORS;
|
| - List<AnalysisError> _verifyErrors = AnalysisError.NO_ERRORS;
|
| -
|
| - /**
|
| - * Initialize a newly created incremental resolver to resolve a node in the
|
| - * given source in the given library.
|
| - */
|
| - IncrementalResolver(
|
| - this._cache,
|
| - this.newSourceEntry,
|
| - this.newUnitEntry,
|
| - CompilationUnitElementImpl definingUnit,
|
| - this._updateOffset,
|
| - int updateEndOld,
|
| - int updateEndNew)
|
| - : _definingUnit = definingUnit,
|
| - _context = definingUnit.context,
|
| - _typeProvider = definingUnit.context.typeProvider,
|
| - _typeSystem = definingUnit.context.typeSystem,
|
| - _definingLibrary = definingUnit.library,
|
| - _source = definingUnit.source,
|
| - _librarySource = definingUnit.library.source,
|
| - _updateEndOld = updateEndOld,
|
| - _updateEndNew = updateEndNew,
|
| - _updateDelta = updateEndNew - updateEndOld;
|
| -
|
| - /**
|
| - * Resolve [body], reporting any errors or warnings to the given listener.
|
| - *
|
| - * [body] - the root of the AST structure to be resolved.
|
| - */
|
| - void resolve(BlockFunctionBody body) {
|
| - logger.enter('resolve: $_definingUnit');
|
| - try {
|
| - Declaration executable = _findResolutionRoot(body);
|
| - _prepareResolutionContext(executable);
|
| - // update elements
|
| - _updateCache();
|
| - _updateElementNameOffsets();
|
| - _buildElements(executable, body);
|
| - // resolve
|
| - _resolveReferences(executable);
|
| - _computeConstants(executable);
|
| - _resolveErrors = errorListener.getErrorsForSource(_source);
|
| - // verify
|
| - _verify(executable);
|
| - _context.invalidateLibraryHints(_librarySource);
|
| - // update entry errors
|
| - _updateEntry();
|
| - } finally {
|
| - logger.exit();
|
| - }
|
| - }
|
| -
|
| - void _buildElements(Declaration executable, AstNode node) {
|
| - LoggingTimer timer = logger.startTimer();
|
| - try {
|
| - ElementHolder holder = new ElementHolder();
|
| - node.accept(new LocalElementBuilder(holder, _definingUnit));
|
| - // Move local elements into the ExecutableElementImpl.
|
| - ExecutableElementImpl executableElement =
|
| - executable.element as ExecutableElementImpl;
|
| - executableElement.localVariables = holder.localVariables;
|
| - executableElement.functions = holder.functions;
|
| - executableElement.labels = holder.labels;
|
| - holder.validate();
|
| - } finally {
|
| - timer.stop('build elements');
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Compute a value for all of the constants in the given [node].
|
| - */
|
| - void _computeConstants(AstNode node) {
|
| - // compute values
|
| - {
|
| - CompilationUnit unit = node.getAncestor((n) => n is CompilationUnit);
|
| - ConstantValueComputer computer = new ConstantValueComputer(
|
| - _typeProvider, _context.declaredVariables, null, _typeSystem);
|
| - computer.add(unit);
|
| - computer.computeValues();
|
| - }
|
| - // validate
|
| - {
|
| - ErrorReporter errorReporter = new ErrorReporter(errorListener, _source);
|
| - ConstantVerifier constantVerifier = new ConstantVerifier(errorReporter,
|
| - _definingLibrary, _typeProvider, _context.declaredVariables);
|
| - node.accept(constantVerifier);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Starting at [node], find the smallest AST node that can be resolved
|
| - * independently of any other nodes. Return the node that was found.
|
| - *
|
| - * [node] - the node at which the search is to begin
|
| - *
|
| - * Throws [AnalysisException] if there is no such node.
|
| - */
|
| - Declaration _findResolutionRoot(AstNode node) {
|
| - while (node != null) {
|
| - if (node is ConstructorDeclaration ||
|
| - node is FunctionDeclaration ||
|
| - node is MethodDeclaration) {
|
| - return node;
|
| - }
|
| - node = node.parent;
|
| - }
|
| - throw new AnalysisException("Cannot resolve node: no resolvable node");
|
| - }
|
| -
|
| - void _prepareResolutionContext(AstNode node) {
|
| - if (_resolutionContext == null) {
|
| - _resolutionContext = ResolutionContextBuilder.contextFor(node);
|
| - }
|
| - }
|
| -
|
| - _resolveReferences(AstNode node) {
|
| - LoggingTimer timer = logger.startTimer();
|
| - try {
|
| - _prepareResolutionContext(node);
|
| - Scope scope = _resolutionContext.scope;
|
| - // resolve types
|
| - {
|
| - TypeResolverVisitor visitor = new TypeResolverVisitor(
|
| - _definingLibrary, _source, _typeProvider, errorListener,
|
| - nameScope: scope);
|
| - node.accept(visitor);
|
| - }
|
| - // resolve variables
|
| - {
|
| - VariableResolverVisitor visitor = new VariableResolverVisitor(
|
| - _definingLibrary, _source, _typeProvider, errorListener,
|
| - nameScope: scope);
|
| - node.accept(visitor);
|
| - }
|
| - // resolve references
|
| - {
|
| - ResolverVisitor visitor = new ResolverVisitor(
|
| - _definingLibrary, _source, _typeProvider, errorListener,
|
| - nameScope: scope);
|
| - if (_resolutionContext.enclosingClassDeclaration != null) {
|
| - visitor.visitClassDeclarationIncrementally(
|
| - _resolutionContext.enclosingClassDeclaration);
|
| - }
|
| - if (node is Comment) {
|
| - visitor.resolveOnlyCommentInFunctionBody = true;
|
| - node = node.parent;
|
| - }
|
| - visitor.initForIncrementalResolution();
|
| - node.accept(visitor);
|
| - }
|
| - } finally {
|
| - timer.stop('resolve references');
|
| - }
|
| - }
|
| -
|
| - void _shiftEntryErrors() {
|
| - _shiftErrors_NEW(HINTS);
|
| - _shiftErrors_NEW(LINTS);
|
| - _shiftErrors_NEW(LIBRARY_UNIT_ERRORS);
|
| - _shiftErrors_NEW(RESOLVE_TYPE_NAMES_ERRORS);
|
| - _shiftErrors_NEW(RESOLVE_TYPE_BOUNDS_ERRORS);
|
| - _shiftErrors_NEW(RESOLVE_UNIT_ERRORS);
|
| - _shiftErrors_NEW(STATIC_VARIABLE_RESOLUTION_ERRORS_IN_UNIT);
|
| - _shiftErrors_NEW(STRONG_MODE_ERRORS);
|
| - _shiftErrors_NEW(VARIABLE_REFERENCE_ERRORS);
|
| - _shiftErrors_NEW(VERIFY_ERRORS);
|
| - }
|
| -
|
| - void _shiftErrors(List<AnalysisError> errors) {
|
| - for (AnalysisError error in errors) {
|
| - if (_alreadyShiftedErrors.add(error)) {
|
| - int errorOffset = error.offset;
|
| - if (errorOffset > _updateOffset) {
|
| - error.offset += _updateDelta;
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - void _shiftErrors_NEW(ResultDescriptor<List<AnalysisError>> descriptor) {
|
| - List<AnalysisError> errors = newUnitEntry.getValue(descriptor);
|
| - _shiftErrors(errors);
|
| - }
|
| -
|
| - void _updateCache() {
|
| - if (newSourceEntry != null) {
|
| - LoggingTimer timer = logger.startTimer();
|
| - try {
|
| - newSourceEntry.setState(CONTENT, CacheState.INVALID,
|
| - delta: new IncrementalBodyDelta(_source, _updateOffset,
|
| - _updateEndOld, _updateEndNew, _updateDelta));
|
| - } finally {
|
| - timer.stop('invalidate cache with delta');
|
| - }
|
| - }
|
| - }
|
| -
|
| - void _updateElementNameOffsets() {
|
| - LoggingTimer timer = logger.startTimer();
|
| - try {
|
| - _definingUnit.accept(
|
| - new _ElementOffsetUpdater(_updateOffset, _updateDelta, _cache));
|
| - _definingUnit.afterIncrementalResolution();
|
| - } finally {
|
| - timer.stop('update element offsets');
|
| - }
|
| - }
|
| -
|
| - void _updateEntry() {
|
| - _updateErrors_NEW(RESOLVE_TYPE_NAMES_ERRORS, []);
|
| - _updateErrors_NEW(RESOLVE_TYPE_BOUNDS_ERRORS, []);
|
| - _updateErrors_NEW(RESOLVE_UNIT_ERRORS, _resolveErrors);
|
| - _updateErrors_NEW(VARIABLE_REFERENCE_ERRORS, []);
|
| - _updateErrors_NEW(VERIFY_ERRORS, _verifyErrors);
|
| - // invalidate results we don't update incrementally
|
| - newUnitEntry.setState(STRONG_MODE_ERRORS, CacheState.INVALID);
|
| - newUnitEntry.setState(USED_IMPORTED_ELEMENTS, CacheState.INVALID);
|
| - newUnitEntry.setState(USED_LOCAL_ELEMENTS, CacheState.INVALID);
|
| - newUnitEntry.setState(HINTS, CacheState.INVALID);
|
| - newUnitEntry.setState(LINTS, CacheState.INVALID);
|
| - }
|
| -
|
| - List<AnalysisError> _updateErrors(
|
| - List<AnalysisError> oldErrors, List<AnalysisError> newErrors) {
|
| - List<AnalysisError> errors = new List<AnalysisError>();
|
| - // add updated old errors
|
| - for (AnalysisError error in oldErrors) {
|
| - int errorOffset = error.offset;
|
| - if (errorOffset < _updateOffset) {
|
| - errors.add(error);
|
| - } else if (errorOffset > _updateEndOld) {
|
| - error.offset += _updateDelta;
|
| - errors.add(error);
|
| - }
|
| - }
|
| - // add new errors
|
| - for (AnalysisError error in newErrors) {
|
| - int errorOffset = error.offset;
|
| - if (errorOffset > _updateOffset && errorOffset < _updateEndNew) {
|
| - errors.add(error);
|
| - }
|
| - }
|
| - // done
|
| - return errors;
|
| - }
|
| -
|
| - void _updateErrors_NEW(ResultDescriptor<List<AnalysisError>> descriptor,
|
| - List<AnalysisError> newErrors) {
|
| - List<AnalysisError> oldErrors = newUnitEntry.getValue(descriptor);
|
| - List<AnalysisError> errors = _updateErrors(oldErrors, newErrors);
|
| - newUnitEntry.setValueIncremental(descriptor, errors, true);
|
| - }
|
| -
|
| - void _verify(AstNode node) {
|
| - LoggingTimer timer = logger.startTimer();
|
| - try {
|
| - RecordingErrorListener errorListener = new RecordingErrorListener();
|
| - ErrorReporter errorReporter = new ErrorReporter(errorListener, _source);
|
| - ErrorVerifier errorVerifier = new ErrorVerifier(
|
| - errorReporter,
|
| - _definingLibrary,
|
| - _typeProvider,
|
| - new InheritanceManager(_definingLibrary),
|
| - _context.analysisOptions.enableSuperMixins);
|
| - if (_resolutionContext.enclosingClassDeclaration != null) {
|
| - errorVerifier.visitClassDeclarationIncrementally(
|
| - _resolutionContext.enclosingClassDeclaration);
|
| - }
|
| - node.accept(errorVerifier);
|
| - _verifyErrors = errorListener.getErrorsForSource(_source);
|
| - } finally {
|
| - timer.stop('verify');
|
| - }
|
| - }
|
| -}
|
| -
|
| -class PoorMansIncrementalResolver {
|
| - final TypeProvider _typeProvider;
|
| - final Source _unitSource;
|
| - final AnalysisCache _cache;
|
| -
|
| - /**
|
| - * The [CacheEntry] corresponding to the source being resolved.
|
| - */
|
| - final CacheEntry _sourceEntry;
|
| -
|
| - /**
|
| - * The [CacheEntry] corresponding to the [LibrarySpecificUnit] being resolved.
|
| - */
|
| - final CacheEntry _unitEntry;
|
| -
|
| - final CompilationUnit _oldUnit;
|
| - CompilationUnitElement _unitElement;
|
| -
|
| - int _updateOffset;
|
| - int _updateDelta;
|
| - int _updateEndOld;
|
| - int _updateEndNew;
|
| -
|
| - LineInfo _newLineInfo;
|
| - List<AnalysisError> _newScanErrors = <AnalysisError>[];
|
| - List<AnalysisError> _newParseErrors = <AnalysisError>[];
|
| -
|
| - PoorMansIncrementalResolver(
|
| - this._typeProvider,
|
| - this._unitSource,
|
| - this._cache,
|
| - this._sourceEntry,
|
| - this._unitEntry,
|
| - this._oldUnit,
|
| - bool resolveApiChanges);
|
| -
|
| - /**
|
| - * Attempts to update [_oldUnit] to the state corresponding to [newCode].
|
| - * Returns `true` if success, or `false` otherwise.
|
| - * The [_oldUnit] might be damaged.
|
| - */
|
| - bool resolve(String newCode) {
|
| - logger.enter('diff/resolve $_unitSource');
|
| - try {
|
| - // prepare old unit
|
| - if (!_areCurlyBracketsBalanced(_oldUnit.beginToken)) {
|
| - logger.log('Unbalanced number of curly brackets in the old unit.');
|
| - return false;
|
| - }
|
| - _unitElement = _oldUnit.element;
|
| - // prepare new unit
|
| - CompilationUnit newUnit = _parseUnit(newCode);
|
| - if (!_areCurlyBracketsBalanced(newUnit.beginToken)) {
|
| - logger.log('Unbalanced number of curly brackets in the new unit.');
|
| - return false;
|
| - }
|
| - // find difference
|
| - _TokenPair firstPair =
|
| - _findFirstDifferentToken(_oldUnit.beginToken, newUnit.beginToken);
|
| - _TokenPair lastPair =
|
| - _findLastDifferentToken(_oldUnit.endToken, newUnit.endToken);
|
| - if (firstPair != null && lastPair != null) {
|
| - int firstOffsetOld = firstPair.oldToken.offset;
|
| - int firstOffsetNew = firstPair.newToken.offset;
|
| - int lastOffsetOld = lastPair.oldToken.end;
|
| - int lastOffsetNew = lastPair.newToken.end;
|
| - int beginOffsetOld = math.min(firstOffsetOld, lastOffsetOld);
|
| - int endOffsetOld = math.max(firstOffsetOld, lastOffsetOld);
|
| - int beginOffsetNew = math.min(firstOffsetNew, lastOffsetNew);
|
| - int endOffsetNew = math.max(firstOffsetNew, lastOffsetNew);
|
| - // A pure whitespace change.
|
| - if (identical(firstPair.oldToken, lastPair.oldToken) &&
|
| - identical(firstPair.newToken, lastPair.newToken) &&
|
| - firstPair.kind == _TokenDifferenceKind.OFFSET) {
|
| - _updateOffset = beginOffsetOld - 1;
|
| - _updateEndOld = endOffsetOld;
|
| - _updateEndNew = endOffsetNew;
|
| - _updateDelta = newUnit.length - _oldUnit.length;
|
| - logger.log('Whitespace change.');
|
| - _shiftTokens(firstPair.oldToken, true);
|
| - IncrementalResolver incrementalResolver = new IncrementalResolver(
|
| - _cache,
|
| - _sourceEntry,
|
| - _unitEntry,
|
| - _unitElement,
|
| - _updateOffset,
|
| - _updateEndOld,
|
| - _updateEndNew);
|
| - incrementalResolver._updateCache();
|
| - incrementalResolver._updateElementNameOffsets();
|
| - incrementalResolver._shiftEntryErrors();
|
| - _updateEntry();
|
| - logger.log('Success.');
|
| - return true;
|
| - }
|
| - // A Dart documentation comment change.
|
| - {
|
| - Token firstOldToken = firstPair.oldToken;
|
| - Token firstNewToken = firstPair.newToken;
|
| - Token lastOldToken = lastPair.oldToken;
|
| - Token lastNewToken = lastPair.newToken;
|
| - if (firstOldToken is DocumentationCommentToken &&
|
| - firstNewToken is DocumentationCommentToken &&
|
| - lastOldToken is DocumentationCommentToken &&
|
| - lastNewToken is DocumentationCommentToken &&
|
| - identical(firstOldToken.parent, lastOldToken.parent) &&
|
| - identical(firstNewToken.parent, lastNewToken.parent)) {
|
| - _updateOffset = beginOffsetOld;
|
| - _updateEndOld = firstOldToken.parent.offset;
|
| - _updateEndNew = firstNewToken.parent.offset;
|
| - _updateDelta = newUnit.length - _oldUnit.length;
|
| - bool success =
|
| - _resolveCommentDoc(newUnit, firstOldToken, firstNewToken);
|
| - logger.log('Documentation comment resolved: $success');
|
| - return success;
|
| - }
|
| - }
|
| - // Find nodes covering the "old" and "new" token ranges.
|
| - AstNode oldNode =
|
| - _findNodeCovering(_oldUnit, beginOffsetOld, endOffsetOld - 1);
|
| - AstNode newNode =
|
| - _findNodeCovering(newUnit, beginOffsetNew, endOffsetNew - 1);
|
| - logger.log(() => 'oldNode: $oldNode');
|
| - logger.log(() => 'newNode: $newNode');
|
| - // Try to find the smallest common node, a FunctionBody currently.
|
| - {
|
| - List<AstNode> oldParents = _getParents(oldNode);
|
| - List<AstNode> newParents = _getParents(newNode);
|
| - // fail if an initializer change
|
| - if (oldParents.any((n) => n is ConstructorInitializer) ||
|
| - newParents.any((n) => n is ConstructorInitializer)) {
|
| - logger.log('Failure: a change in a constructor initializer');
|
| - return false;
|
| - }
|
| - // find matching methods / bodies
|
| - int length = math.min(oldParents.length, newParents.length);
|
| - bool found = false;
|
| - for (int i = 0; i < length; i++) {
|
| - AstNode oldParent = oldParents[i];
|
| - AstNode newParent = newParents[i];
|
| - if (oldParent is CompilationUnit && newParent is CompilationUnit) {
|
| - int oldLength = oldParent.declarations.length;
|
| - int newLength = newParent.declarations.length;
|
| - if (oldLength != newLength) {
|
| - logger.log(
|
| - 'Failure: unit declarations mismatch $oldLength vs. $newLength');
|
| - return false;
|
| - }
|
| - } else if (oldParent is ClassDeclaration &&
|
| - newParent is ClassDeclaration) {
|
| - int oldLength = oldParent.members.length;
|
| - int newLength = newParent.members.length;
|
| - if (oldLength != newLength) {
|
| - logger.log(
|
| - 'Failure: class declarations mismatch $oldLength vs. $newLength');
|
| - return false;
|
| - }
|
| - } else if (oldParent is FunctionDeclaration &&
|
| - newParent is FunctionDeclaration ||
|
| - oldParent is ConstructorDeclaration &&
|
| - newParent is ConstructorDeclaration ||
|
| - oldParent is MethodDeclaration &&
|
| - newParent is MethodDeclaration) {
|
| - if (oldParents.length == i || newParents.length == i) {
|
| - return false;
|
| - }
|
| - } else if (oldParent is FunctionBody && newParent is FunctionBody) {
|
| - if (oldParent is BlockFunctionBody &&
|
| - newParent is BlockFunctionBody) {
|
| - if (oldParent.isAsynchronous != newParent.isAsynchronous) {
|
| - logger.log('Failure: body async mismatch.');
|
| - return false;
|
| - }
|
| - if (oldParent.isGenerator != newParent.isGenerator) {
|
| - logger.log('Failure: body generator mismatch.');
|
| - return false;
|
| - }
|
| - oldNode = oldParent;
|
| - newNode = newParent;
|
| - found = true;
|
| - break;
|
| - }
|
| - logger.log('Failure: not a block function body.');
|
| - return false;
|
| - } else if (oldParent is FunctionExpression &&
|
| - newParent is FunctionExpression) {
|
| - // skip
|
| - } else {
|
| - logger.log('Failure: old and new parent mismatch'
|
| - ' ${oldParent.runtimeType} vs. ${newParent.runtimeType}');
|
| - return false;
|
| - }
|
| - }
|
| - if (!found) {
|
| - logger.log('Failure: no enclosing function body or executable.');
|
| - return false;
|
| - }
|
| - }
|
| - logger.log(() => 'oldNode: $oldNode');
|
| - logger.log(() => 'newNode: $newNode');
|
| - // prepare update range
|
| - _updateOffset = oldNode.offset;
|
| - _updateEndOld = oldNode.end;
|
| - _updateEndNew = newNode.end;
|
| - _updateDelta = _updateEndNew - _updateEndOld;
|
| - // replace node
|
| - NodeReplacer.replace(oldNode, newNode);
|
| - // update token references
|
| - {
|
| - Token oldBeginToken = _getBeginTokenNotComment(oldNode);
|
| - Token newBeginToken = _getBeginTokenNotComment(newNode);
|
| - if (oldBeginToken.previous.type == TokenType.EOF) {
|
| - _oldUnit.beginToken = newBeginToken;
|
| - } else {
|
| - oldBeginToken.previous.setNext(newBeginToken);
|
| - }
|
| - newNode.endToken.setNext(oldNode.endToken.next);
|
| - _shiftTokens(oldNode.endToken.next);
|
| - }
|
| - // perform incremental resolution
|
| - IncrementalResolver incrementalResolver = new IncrementalResolver(
|
| - _cache,
|
| - _sourceEntry,
|
| - _unitEntry,
|
| - _unitElement,
|
| - _updateOffset,
|
| - _updateEndOld,
|
| - _updateEndNew);
|
| - incrementalResolver.resolve(newNode);
|
| - // update DartEntry
|
| - _updateEntry();
|
| - logger.log('Success.');
|
| - return true;
|
| - }
|
| - } catch (e, st) {
|
| - logger.logException(e, st);
|
| - logger.log('Failure: exception.');
|
| - // The incremental resolver log is usually turned off,
|
| - // so also log the exception to the instrumentation log.
|
| - AnalysisEngine.instance.logger.logError(
|
| - 'Failure in incremental resolver', new CaughtException(e, st));
|
| - } finally {
|
| - logger.exit();
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - CompilationUnit _parseUnit(String code) {
|
| - LoggingTimer timer = logger.startTimer();
|
| - try {
|
| - Token token = _scan(code);
|
| - RecordingErrorListener errorListener = new RecordingErrorListener();
|
| - Parser parser = new Parser(_unitSource, errorListener);
|
| - AnalysisOptions options = _unitElement.context.analysisOptions;
|
| - parser.parseGenericMethodComments = options.strongMode;
|
| - CompilationUnit unit = parser.parseCompilationUnit(token);
|
| - _newParseErrors = errorListener.errors;
|
| - return unit;
|
| - } finally {
|
| - timer.stop('parse');
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Attempts to resolve a documentation comment change.
|
| - * Returns `true` if success.
|
| - */
|
| - bool _resolveCommentDoc(
|
| - CompilationUnit newUnit, CommentToken oldToken, CommentToken newToken) {
|
| - if (oldToken == null || newToken == null) {
|
| - return false;
|
| - }
|
| - // find nodes
|
| - int offset = oldToken.offset;
|
| - logger.log('offset: $offset');
|
| - AstNode oldNode = _findNodeCovering(_oldUnit, offset, offset);
|
| - AstNode newNode = _findNodeCovering(newUnit, offset, offset);
|
| - if (oldNode is! Comment || newNode is! Comment) {
|
| - return false;
|
| - }
|
| - Comment oldComment = oldNode;
|
| - Comment newComment = newNode;
|
| - logger.log('oldComment.beginToken: ${oldComment.beginToken}');
|
| - logger.log('newComment.beginToken: ${newComment.beginToken}');
|
| - // update token references
|
| - _shiftTokens(oldToken.parent);
|
| - _setPrecedingComments(oldToken.parent, newComment.tokens.first);
|
| - // replace node
|
| - NodeReplacer.replace(oldComment, newComment);
|
| - // update elements
|
| - IncrementalResolver incrementalResolver = new IncrementalResolver(
|
| - _cache,
|
| - _sourceEntry,
|
| - _unitEntry,
|
| - _unitElement,
|
| - _updateOffset,
|
| - _updateEndOld,
|
| - _updateEndNew);
|
| - incrementalResolver._updateCache();
|
| - incrementalResolver._updateElementNameOffsets();
|
| - incrementalResolver._shiftEntryErrors();
|
| - _updateEntry();
|
| - // resolve references in the comment
|
| - incrementalResolver._resolveReferences(newComment);
|
| - // update 'documentationComment' of the parent element(s)
|
| - {
|
| - AstNode parent = newComment.parent;
|
| - if (parent is AnnotatedNode) {
|
| - setElementDocumentationForVariables(VariableDeclarationList list) {
|
| - for (VariableDeclaration variable in list.variables) {
|
| - Element variableElement = variable.element;
|
| - if (variableElement is ElementImpl) {
|
| - setElementDocumentationComment(variableElement, parent);
|
| - }
|
| - }
|
| - }
|
| -
|
| - Element parentElement = ElementLocator.locate(newComment.parent);
|
| - if (parentElement is ElementImpl) {
|
| - setElementDocumentationComment(parentElement, parent);
|
| - } else if (parent is FieldDeclaration) {
|
| - setElementDocumentationForVariables(parent.fields);
|
| - } else if (parent is TopLevelVariableDeclaration) {
|
| - setElementDocumentationForVariables(parent.variables);
|
| - }
|
| - }
|
| - }
|
| - // OK
|
| - return true;
|
| - }
|
| -
|
| - Token _scan(String code) {
|
| - RecordingErrorListener errorListener = new RecordingErrorListener();
|
| - CharSequenceReader reader = new CharSequenceReader(code);
|
| - Scanner scanner = new Scanner(_unitSource, reader, errorListener);
|
| - AnalysisOptions options = _unitElement.context.analysisOptions;
|
| - scanner.scanGenericMethodComments = options.strongMode;
|
| - Token token = scanner.tokenize();
|
| - _newLineInfo = new LineInfo(scanner.lineStarts);
|
| - _newScanErrors = errorListener.errors;
|
| - return token;
|
| - }
|
| -
|
| - /**
|
| - * Set the given [comment] as a "precedingComments" for [token].
|
| - */
|
| - void _setPrecedingComments(Token token, CommentToken comment) {
|
| - if (token is BeginTokenWithComment) {
|
| - token.precedingComments = comment;
|
| - } else if (token is KeywordTokenWithComment) {
|
| - token.precedingComments = comment;
|
| - } else if (token is StringTokenWithComment) {
|
| - token.precedingComments = comment;
|
| - } else if (token is TokenWithComment) {
|
| - token.precedingComments = comment;
|
| - } else {
|
| - Type parentType = token?.runtimeType;
|
| - throw new AnalysisException('Uknown parent token type: $parentType');
|
| - }
|
| - }
|
| -
|
| - void _shiftTokens(Token token, [bool goUpComment = false]) {
|
| - while (token != null) {
|
| - if (goUpComment && token is CommentToken) {
|
| - token = (token as CommentToken).parent;
|
| - }
|
| - if (token.offset > _updateOffset) {
|
| - token.offset += _updateDelta;
|
| - }
|
| - // comments
|
| - _shiftTokens(token.precedingComments);
|
| - if (token is DocumentationCommentToken) {
|
| - for (Token reference in token.references) {
|
| - _shiftTokens(reference);
|
| - }
|
| - }
|
| - // next
|
| - if (token.type == TokenType.EOF) {
|
| - break;
|
| - }
|
| - token = token.next;
|
| - }
|
| - }
|
| -
|
| - void _updateEntry() {
|
| - // scan results
|
| - _sourceEntry.setValueIncremental(SCAN_ERRORS, _newScanErrors, true);
|
| - _sourceEntry.setValueIncremental(LINE_INFO, _newLineInfo, false);
|
| - // parse results
|
| - _sourceEntry.setValueIncremental(PARSE_ERRORS, _newParseErrors, true);
|
| - _sourceEntry.setValueIncremental(PARSED_UNIT, _oldUnit, false);
|
| - }
|
| -
|
| - /**
|
| - * Checks if [token] has a balanced number of open and closed curly brackets.
|
| - */
|
| - static bool _areCurlyBracketsBalanced(Token token) {
|
| - int numOpen = _getTokenCount(token, TokenType.OPEN_CURLY_BRACKET);
|
| - int numOpen2 =
|
| - _getTokenCount(token, TokenType.STRING_INTERPOLATION_EXPRESSION);
|
| - int numClosed = _getTokenCount(token, TokenType.CLOSE_CURLY_BRACKET);
|
| - return numOpen + numOpen2 == numClosed;
|
| - }
|
| -
|
| - static _TokenDifferenceKind _compareToken(
|
| - Token oldToken, Token newToken, int delta) {
|
| - if (oldToken == null && newToken == null) {
|
| - return null;
|
| - }
|
| - if (oldToken == null || newToken == null) {
|
| - return _TokenDifferenceKind.CONTENT;
|
| - }
|
| - if (oldToken.type != newToken.type) {
|
| - return _TokenDifferenceKind.CONTENT;
|
| - }
|
| - if (oldToken.lexeme != newToken.lexeme) {
|
| - return _TokenDifferenceKind.CONTENT;
|
| - }
|
| - if (newToken.offset - oldToken.offset != delta) {
|
| - return _TokenDifferenceKind.OFFSET;
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - static _TokenPair _findFirstDifferentToken(Token oldToken, Token newToken) {
|
| - while (oldToken.type != TokenType.EOF || newToken.type != TokenType.EOF) {
|
| - if (oldToken.type == TokenType.EOF || newToken.type == TokenType.EOF) {
|
| - return new _TokenPair(_TokenDifferenceKind.CONTENT, oldToken, newToken);
|
| - }
|
| - // compare comments
|
| - {
|
| - Token oldComment = oldToken.precedingComments;
|
| - Token newComment = newToken.precedingComments;
|
| - while (true) {
|
| - _TokenDifferenceKind diffKind =
|
| - _compareToken(oldComment, newComment, 0);
|
| - if (diffKind != null) {
|
| - return new _TokenPair(
|
| - diffKind, oldComment ?? oldToken, newComment ?? newToken);
|
| - }
|
| - if (oldComment == null && newComment == null) {
|
| - break;
|
| - }
|
| - oldComment = oldComment.next;
|
| - newComment = newComment.next;
|
| - }
|
| - }
|
| - // compare tokens
|
| - _TokenDifferenceKind diffKind = _compareToken(oldToken, newToken, 0);
|
| - if (diffKind != null) {
|
| - return new _TokenPair(diffKind, oldToken, newToken);
|
| - }
|
| - // next tokens
|
| - oldToken = oldToken.next;
|
| - newToken = newToken.next;
|
| - }
|
| - // no difference
|
| - return null;
|
| - }
|
| -
|
| - static _TokenPair _findLastDifferentToken(Token oldToken, Token newToken) {
|
| - int delta = newToken.offset - oldToken.offset;
|
| - Token prevOldToken;
|
| - Token prevNewToken;
|
| - while (oldToken.previous != oldToken && newToken.previous != newToken) {
|
| - // compare tokens
|
| - _TokenDifferenceKind diffKind = _compareToken(oldToken, newToken, delta);
|
| - if (diffKind != null) {
|
| - return new _TokenPair(diffKind, prevOldToken, prevNewToken);
|
| - }
|
| - prevOldToken = oldToken;
|
| - prevNewToken = newToken;
|
| - // compare comments
|
| - {
|
| - Token oldComment = oldToken.precedingComments;
|
| - Token newComment = newToken.precedingComments;
|
| - while (oldComment?.next != null) {
|
| - oldComment = oldComment.next;
|
| - }
|
| - while (newComment?.next != null) {
|
| - newComment = newComment.next;
|
| - }
|
| - while (true) {
|
| - _TokenDifferenceKind diffKind =
|
| - _compareToken(oldComment, newComment, delta);
|
| - if (diffKind != null) {
|
| - return new _TokenPair(
|
| - diffKind, oldComment ?? oldToken, newComment ?? newToken);
|
| - }
|
| - if (oldComment == null && newComment == null) {
|
| - break;
|
| - }
|
| - prevOldToken = oldComment;
|
| - prevNewToken = newComment;
|
| - oldComment = oldComment.previous;
|
| - newComment = newComment.previous;
|
| - }
|
| - }
|
| - // next tokens
|
| - oldToken = oldToken.previous;
|
| - newToken = newToken.previous;
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - static AstNode _findNodeCovering(AstNode root, int offset, int end) {
|
| - NodeLocator nodeLocator = new NodeLocator(offset, end);
|
| - return nodeLocator.searchWithin(root);
|
| - }
|
| -
|
| - static Token _getBeginTokenNotComment(AstNode node) {
|
| - Token oldBeginToken = node.beginToken;
|
| - if (oldBeginToken is CommentToken) {
|
| - return oldBeginToken.parent;
|
| - }
|
| - return oldBeginToken;
|
| - }
|
| -
|
| - static List<AstNode> _getParents(AstNode node) {
|
| - List<AstNode> parents = <AstNode>[];
|
| - while (node != null) {
|
| - parents.insert(0, node);
|
| - node = node.parent;
|
| - }
|
| - return parents;
|
| - }
|
| -
|
| - /**
|
| - * Returns number of tokens with the given [type].
|
| - */
|
| - static int _getTokenCount(Token token, TokenType type) {
|
| - int count = 0;
|
| - while (token.type != TokenType.EOF) {
|
| - if (token.type == type) {
|
| - count++;
|
| - }
|
| - token = token.next;
|
| - }
|
| - return count;
|
| - }
|
| -}
|
|
|
| /**
|
| * The context to resolve an [AstNode] in.
|
| @@ -1190,125 +162,3 @@ class ResolutionContextBuilder {
|
| return context;
|
| }
|
| }
|
| -
|
| -/**
|
| - * Adjusts the location of each Element that moved.
|
| - *
|
| - * Since `==` and `hashCode` of a local variable or function Element are based
|
| - * on the element name offsets, we also need to remove these elements from the
|
| - * cache to avoid a memory leak. TODO(scheglov) fix and remove this
|
| - */
|
| -class _ElementOffsetUpdater extends GeneralizingElementVisitor {
|
| - final int updateOffset;
|
| - final int updateDelta;
|
| - final AnalysisCache cache;
|
| -
|
| - _ElementOffsetUpdater(this.updateOffset, this.updateDelta, this.cache);
|
| -
|
| - @override
|
| - visitElement(Element element) {
|
| - // name offset
|
| - int nameOffset = element.nameOffset;
|
| - if (nameOffset > updateOffset) {
|
| - (element as ElementImpl).nameOffset = nameOffset + updateDelta;
|
| - if (element is ConstVariableElement) {
|
| - Expression initializer = element.constantInitializer;
|
| - if (initializer != null) {
|
| - _shiftTokens(initializer.beginToken);
|
| - }
|
| - _shiftErrors(element.evaluationResult?.errors);
|
| - }
|
| - }
|
| - // code range
|
| - if (element is ElementImpl) {
|
| - int oldOffset = element.codeOffset;
|
| - int oldLength = element.codeLength;
|
| - if (oldOffset != null) {
|
| - int newOffset = oldOffset;
|
| - int newLength = oldLength;
|
| - newOffset += oldOffset > updateOffset ? updateDelta : 0;
|
| - if (oldOffset <= updateOffset && updateOffset < oldOffset + oldLength) {
|
| - newLength += updateDelta;
|
| - }
|
| - if (newOffset != oldOffset || newLength != oldLength) {
|
| - element.setCodeRange(newOffset, newLength);
|
| - }
|
| - }
|
| - }
|
| - // visible range
|
| - if (element is LocalElement) {
|
| - SourceRange visibleRange = element.visibleRange;
|
| - if (visibleRange != null) {
|
| - int oldOffset = visibleRange.offset;
|
| - int oldLength = visibleRange.length;
|
| - int newOffset = oldOffset;
|
| - int newLength = oldLength;
|
| - newOffset += oldOffset > updateOffset ? updateDelta : 0;
|
| - newLength += visibleRange.contains(updateOffset) ? updateDelta : 0;
|
| - if (newOffset != oldOffset || newLength != oldLength) {
|
| - if (element is FunctionElementImpl) {
|
| - element.setVisibleRange(newOffset, newLength);
|
| - } else if (element is LocalVariableElementImpl) {
|
| - element.setVisibleRange(newOffset, newLength);
|
| - } else if (element is ParameterElementImpl) {
|
| - element.setVisibleRange(newOffset, newLength);
|
| - }
|
| - }
|
| - }
|
| - }
|
| - super.visitElement(element);
|
| - }
|
| -
|
| - void _shiftErrors(List<AnalysisError> errors) {
|
| - if (errors != null) {
|
| - for (AnalysisError error in errors) {
|
| - int errorOffset = error.offset;
|
| - if (errorOffset > updateOffset) {
|
| - error.offset += updateDelta;
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - void _shiftTokens(Token token) {
|
| - while (token != null) {
|
| - if (token.offset > updateOffset) {
|
| - token.offset += updateDelta;
|
| - }
|
| - // comments
|
| - _shiftTokens(token.precedingComments);
|
| - if (token is DocumentationCommentToken) {
|
| - for (Token reference in token.references) {
|
| - _shiftTokens(reference);
|
| - }
|
| - }
|
| - // next
|
| - if (token.type == TokenType.EOF) {
|
| - break;
|
| - }
|
| - token = token.next;
|
| - }
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Describes how two [Token]s are different.
|
| - */
|
| -class _TokenDifferenceKind {
|
| - static const CONTENT = const _TokenDifferenceKind('CONTENT');
|
| - static const OFFSET = const _TokenDifferenceKind('OFFSET');
|
| -
|
| - final String name;
|
| -
|
| - const _TokenDifferenceKind(this.name);
|
| -
|
| - @override
|
| - String toString() => name;
|
| -}
|
| -
|
| -class _TokenPair {
|
| - final _TokenDifferenceKind kind;
|
| - final Token oldToken;
|
| - final Token newToken;
|
| - _TokenPair(this.kind, this.oldToken, this.newToken);
|
| -}
|
|
|