| Index: packages/analyzer/lib/src/task/incremental_element_builder.dart
|
| diff --git a/packages/analyzer/lib/src/task/incremental_element_builder.dart b/packages/analyzer/lib/src/task/incremental_element_builder.dart
|
| index d6a0c1418d4569dc4e9c10db954daba9031b1055..6ae19ff630dcf1151a712895a07898786c242285 100644
|
| --- a/packages/analyzer/lib/src/task/incremental_element_builder.dart
|
| +++ b/packages/analyzer/lib/src/task/incremental_element_builder.dart
|
| @@ -6,11 +6,65 @@ library analyzer.src.task.incremental_element_builder;
|
|
|
| import 'dart:collection';
|
|
|
| -import 'package:analyzer/src/generated/ast.dart';
|
| -import 'package:analyzer/src/generated/element.dart';
|
| +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/src/dart/ast/token.dart';
|
| +import 'package:analyzer/src/dart/ast/utilities.dart';
|
| +import 'package:analyzer/src/dart/constant/utilities.dart';
|
| +import 'package:analyzer/src/dart/element/builder.dart';
|
| +import 'package:analyzer/src/dart/element/element.dart';
|
| import 'package:analyzer/src/generated/resolver.dart';
|
| -import 'package:analyzer/src/generated/scanner.dart';
|
| import 'package:analyzer/src/generated/source.dart';
|
| +import 'package:analyzer/src/task/dart.dart';
|
| +
|
| +/**
|
| + * The change of a single [ClassElement].
|
| + */
|
| +class ClassElementDelta {
|
| + final ClassElement _element;
|
| + final Source librarySource;
|
| + final String name;
|
| +
|
| + final Set<ClassElementDelta> superDeltas = new Set<ClassElementDelta>();
|
| +
|
| + bool hasAnnotationChanges = false;
|
| +
|
| + final List<PropertyAccessorElement> addedAccessors =
|
| + <PropertyAccessorElement>[];
|
| + final List<PropertyAccessorElement> removedAccessors =
|
| + <PropertyAccessorElement>[];
|
| +
|
| + final List<ConstructorElement> addedConstructors = <ConstructorElement>[];
|
| + final List<ConstructorElement> removedConstructors = <ConstructorElement>[];
|
| + bool hasUnnamedConstructorChange = false;
|
| +
|
| + final List<MethodElement> addedMethods = <MethodElement>[];
|
| + final List<MethodElement> removedMethods = <MethodElement>[];
|
| +
|
| + ClassElementDelta(this._element, this.librarySource, this.name);
|
| +
|
| + /**
|
| + * Return `true` if this delta has changes to the [name] visible in the
|
| + * given [librarySource].
|
| + */
|
| + bool hasChanges(Source librarySource, String name) {
|
| + if (Identifier.isPrivateName(name) && librarySource != this.librarySource) {
|
| + return false;
|
| + }
|
| + return _hasElementWithName(addedAccessors, name) ||
|
| + _hasElementWithName(removedAccessors, name) ||
|
| + _hasElementWithName(addedConstructors, name) ||
|
| + _hasElementWithName(removedConstructors, name) ||
|
| + _hasElementWithName(addedMethods, name) ||
|
| + _hasElementWithName(removedMethods, name);
|
| + }
|
| +
|
| + static bool _hasElementWithName(List<Element> elements, String name) {
|
| + return elements.any((e) => e.displayName == name);
|
| + }
|
| +}
|
|
|
| /**
|
| * The change of a single [CompilationUnitElement].
|
| @@ -30,6 +84,12 @@ class CompilationUnitElementDelta {
|
| * The list of removed top-level elements.
|
| */
|
| final List<Element> removedDeclarations = <Element>[];
|
| +
|
| + /**
|
| + * The map from names of changed classes to the change deltas.
|
| + */
|
| + final Map<String, ClassElementDelta> classDeltas =
|
| + <String, ClassElementDelta>{};
|
| }
|
|
|
| /**
|
| @@ -42,7 +102,10 @@ class IncrementalCompilationUnitElementBuilder {
|
| final CompilationUnit oldUnit;
|
| final CompilationUnitElementImpl unitElement;
|
| final CompilationUnit newUnit;
|
| - final ElementHolder holder = new ElementHolder();
|
| + final ElementHolder unitElementHolder = new ElementHolder();
|
| +
|
| + final List<ConstantEvaluationTarget> unitConstants =
|
| + <ConstantEvaluationTarget>[];
|
|
|
| /**
|
| * The change between element models of [oldUnit] and [newUnit].
|
| @@ -69,30 +132,265 @@ class IncrementalCompilationUnitElementBuilder {
|
| * Fills [unitDelta] with added/remove elements.
|
| */
|
| void build() {
|
| + _materializeLazyElements();
|
| new CompilationUnitBuilder()
|
| .buildCompilationUnit(unitSource, newUnit, librarySource);
|
| + newUnit.accept(new EnumMemberBuilder(unitElement.context.typeProvider));
|
| _processDirectives();
|
| _processUnitMembers();
|
| - newUnit.element = unitElement;
|
| _replaceUnitContents(oldUnit, newUnit);
|
| + _findConstants();
|
| + newUnit.element = unitElement;
|
| + unitElement.setCodeRange(0, newUnit.endToken.end);
|
| }
|
|
|
| - void _addElementToHolder(Element element) {
|
| - if (element is PropertyAccessorElement) {
|
| - holder.addAccessor(element);
|
| - } else if (element is ClassElement) {
|
| + void _addElementToUnitHolder(Element element) {
|
| + if (element is ClassElement) {
|
| if (element.isEnum) {
|
| - holder.addEnum(element);
|
| + unitElementHolder.addEnum(element);
|
| } else {
|
| - holder.addType(element);
|
| + unitElementHolder.addType(element);
|
| }
|
| } else if (element is FunctionElement) {
|
| - holder.addFunction(element);
|
| + unitElementHolder.addFunction(element);
|
| } else if (element is FunctionTypeAliasElement) {
|
| - holder.addTypeAlias(element);
|
| + unitElementHolder.addTypeAlias(element);
|
| + } else if (element is PropertyAccessorElement) {
|
| + unitElementHolder.addAccessor(element);
|
| } else if (element is TopLevelVariableElement) {
|
| - holder.addTopLevelVariable(element);
|
| + unitElementHolder.addTopLevelVariable(element);
|
| + }
|
| + }
|
| +
|
| + void _findConstants() {
|
| + ConstantFinder finder = new ConstantFinder();
|
| + oldUnit.accept(finder);
|
| + unitConstants.addAll(finder.constantsToCompute);
|
| + // Update annotation constants to using the old unit element.
|
| + for (ConstantEvaluationTarget constant in unitConstants) {
|
| + if (constant is ElementAnnotationImpl) {
|
| + constant.compilationUnit = unitElement;
|
| + }
|
| + }
|
| + }
|
| +
|
| + void _materializeLazyElements() {
|
| + unitElement.accept(new RecursiveElementVisitor());
|
| + }
|
| +
|
| + ClassElementDelta _processClassMembers(
|
| + ClassDeclaration oldClass, ClassDeclaration newClass) {
|
| + // If the class hierarchy or type parameters are changed,
|
| + // then the class changed too much - don't compute the delta.
|
| + if (newClass.abstractKeyword != null && oldClass.abstractKeyword == null ||
|
| + newClass.abstractKeyword == null && oldClass.abstractKeyword != null ||
|
| + TokenUtils.getFullCode(newClass.typeParameters) !=
|
| + TokenUtils.getFullCode(oldClass.typeParameters) ||
|
| + TokenUtils.getFullCode(newClass.extendsClause) !=
|
| + TokenUtils.getFullCode(oldClass.extendsClause) ||
|
| + TokenUtils.getFullCode(newClass.withClause) !=
|
| + TokenUtils.getFullCode(oldClass.withClause) ||
|
| + TokenUtils.getFullCode(newClass.implementsClause) !=
|
| + TokenUtils.getFullCode(oldClass.implementsClause)) {
|
| + return null;
|
| + }
|
| + // Build the old class members map.
|
| + Map<String, ClassMember> oldNodeMap = new HashMap<String, ClassMember>();
|
| + for (ClassMember oldNode in oldClass.members) {
|
| + String code = TokenUtils.getFullCode(oldNode);
|
| + oldNodeMap[code] = oldNode;
|
| + }
|
| + // Prepare elements.
|
| + ClassElement newElement = newClass.element;
|
| + ClassElement oldElement = oldClass.element;
|
| + // Use the old element for the new node.
|
| + newClass.name.staticElement = oldElement;
|
| + if (newElement is ClassElementImpl && oldElement is ClassElementImpl) {
|
| + oldElement.nameOffset = newElement.nameOffset;
|
| + oldElement.setCodeRange(newElement.codeOffset, newElement.codeLength);
|
| + oldElement.typeParameters = newElement.typeParameters;
|
| + }
|
| + // Prepare delta.
|
| + ClassElementImpl classElement = oldClass.element;
|
| + ElementHolder classElementHolder = new ElementHolder();
|
| + ClassElementDelta classDelta =
|
| + new ClassElementDelta(classElement, librarySource, classElement.name);
|
| + // Check for annotation changes.
|
| + {
|
| + String oldAnnotationsCode =
|
| + TokenUtils.getFullCodeOfList(oldClass.metadata);
|
| + String newAnnotationsCode =
|
| + TokenUtils.getFullCodeOfList(newClass.metadata);
|
| + classDelta.hasAnnotationChanges =
|
| + oldAnnotationsCode != newAnnotationsCode;
|
| + }
|
| + // Prepare all old member elements.
|
| + var removedAccessors = new Set<PropertyAccessorElement>.identity();
|
| + var removedConstructors = new Set<ConstructorElement>.identity();
|
| + var removedMethods = new Set<MethodElement>.identity();
|
| + removedAccessors.addAll(classElement.accessors);
|
| + removedConstructors.addAll(classElement.constructors);
|
| + removedMethods.addAll(classElement.methods);
|
| + // Utilities.
|
| + void processConstructorDeclaration(
|
| + ConstructorDeclaration node, bool isNew) {
|
| + ConstructorElement element = node.element;
|
| + if (element != null) {
|
| + classElementHolder.addConstructor(element);
|
| + if (isNew) {
|
| + classDelta.addedConstructors.add(element);
|
| + } else {
|
| + removedConstructors.remove(element);
|
| + }
|
| + }
|
| + }
|
| +
|
| + void processFieldDeclaration(FieldDeclaration node, bool isNew) {
|
| + for (VariableDeclaration field in node.fields.variables) {
|
| + PropertyInducingElement element = field.element;
|
| + if (element != null) {
|
| + PropertyAccessorElement getter = element.getter;
|
| + PropertyAccessorElement setter = element.setter;
|
| + if (getter != null) {
|
| + classElementHolder.addAccessor(getter);
|
| + if (isNew) {
|
| + classDelta.addedAccessors.add(getter);
|
| + } else {
|
| + removedAccessors.remove(getter);
|
| + }
|
| + }
|
| + if (setter != null) {
|
| + classElementHolder.addAccessor(setter);
|
| + if (isNew) {
|
| + classDelta.addedAccessors.add(setter);
|
| + } else {
|
| + removedAccessors.remove(setter);
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + void processMethodDeclaration(MethodDeclaration node, bool isNew) {
|
| + Element element = node.element;
|
| + if (element is MethodElement) {
|
| + classElementHolder.addMethod(element);
|
| + if (isNew) {
|
| + classDelta.addedMethods.add(element);
|
| + } else {
|
| + removedMethods.remove(element);
|
| + }
|
| + } else if (element is PropertyAccessorElement) {
|
| + classElementHolder.addAccessor(element);
|
| + if (isNew) {
|
| + classDelta.addedAccessors.add(element);
|
| + } else {
|
| + removedAccessors.remove(element);
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Replace new nodes with the identical old nodes.
|
| + bool newHasConstructor = false;
|
| + for (ClassMember newNode in newClass.members) {
|
| + String code = TokenUtils.getFullCode(newNode);
|
| + ClassMember oldNode = oldNodeMap.remove(code);
|
| + // When we type a name before a constructor with a documentation
|
| + // comment, this makes the comment disappear from AST. So, even though
|
| + // tokens are the same, the nodes are not the same.
|
| + if (oldNode != null) {
|
| + if (oldNode.documentationComment == null &&
|
| + newNode.documentationComment != null ||
|
| + oldNode.documentationComment != null &&
|
| + newNode.documentationComment == null) {
|
| + oldNode = null;
|
| + }
|
| + }
|
| + // Add the new element.
|
| + if (oldNode == null) {
|
| + if (newNode is ConstructorDeclaration) {
|
| + newHasConstructor = true;
|
| + processConstructorDeclaration(newNode, true);
|
| + }
|
| + if (newNode is FieldDeclaration) {
|
| + processFieldDeclaration(newNode, true);
|
| + }
|
| + if (newNode is MethodDeclaration) {
|
| + processMethodDeclaration(newNode, true);
|
| + }
|
| + continue;
|
| + }
|
| + // Do replacement.
|
| + _replaceNode(newNode, oldNode);
|
| + if (oldNode is ConstructorDeclaration) {
|
| + processConstructorDeclaration(oldNode, false);
|
| + }
|
| + if (oldNode is FieldDeclaration) {
|
| + processFieldDeclaration(oldNode, false);
|
| + }
|
| + if (oldNode is MethodDeclaration) {
|
| + processMethodDeclaration(oldNode, false);
|
| + }
|
| }
|
| + // If the class had only a default synthetic constructor, and there are
|
| + // no explicit constructors in the new AST, keep the constructor.
|
| + if (!newHasConstructor) {
|
| + List<ConstructorElement> constructors = classElement.constructors;
|
| + if (constructors.length == 1) {
|
| + ConstructorElement constructor = constructors[0];
|
| + if (constructor.isSynthetic && constructor.isDefaultConstructor) {
|
| + classElementHolder.addConstructor(constructor);
|
| + removedConstructors.remove(constructor);
|
| + }
|
| + }
|
| + }
|
| + // Update the delta.
|
| + classDelta.removedAccessors.addAll(removedAccessors);
|
| + classDelta.removedConstructors.addAll(removedConstructors);
|
| + classDelta.removedMethods.addAll(removedMethods);
|
| + // Prepare fields.
|
| + List<PropertyAccessorElement> newAccessors = classElementHolder.accessors;
|
| + Map<String, FieldElement> newFields = <String, FieldElement>{};
|
| + for (PropertyAccessorElement accessor in newAccessors) {
|
| + newFields[accessor.displayName] = accessor.variable;
|
| + }
|
| + // Update references to fields from constructors.
|
| + for (ClassMember member in newClass.members) {
|
| + if (member is ConstructorDeclaration) {
|
| + for (FormalParameter parameter in member.parameters.parameters) {
|
| + FormalParameter normalParameter = parameter;
|
| + if (parameter is DefaultFormalParameter) {
|
| + normalParameter = parameter.parameter;
|
| + }
|
| + if (normalParameter is FieldFormalParameter) {
|
| + FieldFormalParameterElementImpl parameterElement =
|
| + normalParameter.element as FieldFormalParameterElementImpl;
|
| + parameterElement.field = newFields[parameterElement.name];
|
| + }
|
| + }
|
| + }
|
| + }
|
| + // Update ClassElement.
|
| + classElement.metadata = newElement.metadata;
|
| + classElement.accessors = newAccessors;
|
| + classElement.constructors = classElementHolder.constructors;
|
| + classElement.fields = newFields.values.toList();
|
| + classElement.methods = classElementHolder.methods;
|
| + classElement.version++;
|
| + classElementHolder.validate();
|
| + // Ensure at least a default synthetic constructor.
|
| + if (classElement.constructors.isEmpty) {
|
| + ConstructorElementImpl constructor =
|
| + new ConstructorElementImpl.forNode(null);
|
| + constructor.synthetic = true;
|
| + classElement.constructors = <ConstructorElement>[constructor];
|
| + classDelta.addedConstructors.add(constructor);
|
| + }
|
| + classDelta.hasUnnamedConstructorChange =
|
| + classDelta.addedConstructors.any((c) => c.name == '') ||
|
| + classDelta.removedConstructors.any((c) => c.name == '');
|
| + // OK
|
| + return classDelta;
|
| }
|
|
|
| void _processDirectives() {
|
| @@ -114,7 +412,11 @@ class IncrementalCompilationUnitElementBuilder {
|
| // URI's must be resolved to the same sources.
|
| if (newDirective is UriBasedDirective &&
|
| oldDirective is UriBasedDirective) {
|
| - if (oldDirective.source != newDirective.source) {
|
| + Source source(UriBasedDirective directive) =>
|
| + directive is NamespaceDirective
|
| + ? directive.selectedSource
|
| + : directive.uriSource;
|
| + if (source(oldDirective) != source(newDirective)) {
|
| continue;
|
| }
|
| }
|
| @@ -131,9 +433,14 @@ class IncrementalCompilationUnitElementBuilder {
|
| void _processUnitMembers() {
|
| Map<String, CompilationUnitMember> oldNodeMap =
|
| new HashMap<String, CompilationUnitMember>();
|
| + Map<String, ClassDeclaration> nameToOldClassMap =
|
| + new HashMap<String, ClassDeclaration>();
|
| for (CompilationUnitMember oldNode in oldUnit.declarations) {
|
| String code = TokenUtils.getFullCode(oldNode);
|
| oldNodeMap[code] = oldNode;
|
| + if (oldNode is ClassDeclaration) {
|
| + nameToOldClassMap[oldNode.name.name] = oldNode;
|
| + }
|
| }
|
| // Prepare all old top-level elements.
|
| Set<Element> removedElements = new Set<Element>();
|
| @@ -146,29 +453,43 @@ class IncrementalCompilationUnitElementBuilder {
|
| // Replace new nodes with the identical old nodes.
|
| for (CompilationUnitMember newNode in newUnit.declarations) {
|
| String code = TokenUtils.getFullCode(newNode);
|
| - // Prepare an old node.
|
| CompilationUnitMember oldNode = oldNodeMap[code];
|
| + // Add the new element.
|
| if (oldNode == null) {
|
| + // Compute a delta for the class.
|
| + if (newNode is ClassDeclaration) {
|
| + ClassDeclaration oldClass = nameToOldClassMap[newNode.name.name];
|
| + if (oldClass != null) {
|
| + ClassElementDelta delta = _processClassMembers(oldClass, newNode);
|
| + if (delta != null) {
|
| + unitDelta.classDeltas[delta._element.name] = delta;
|
| + _addElementToUnitHolder(delta._element);
|
| + removedElements.remove(delta._element);
|
| + continue;
|
| + }
|
| + }
|
| + }
|
| + // Add the new node elements.
|
| List<Element> elements = _getElements(newNode);
|
| - elements.forEach(_addElementToHolder);
|
| + elements.forEach(_addElementToUnitHolder);
|
| elements.forEach(unitDelta.addedDeclarations.add);
|
| continue;
|
| }
|
| // Do replacement.
|
| _replaceNode(newNode, oldNode);
|
| List<Element> elements = _getElements(oldNode);
|
| - elements.forEach(_addElementToHolder);
|
| + elements.forEach(_addElementToUnitHolder);
|
| elements.forEach(removedElements.remove);
|
| }
|
| unitDelta.removedDeclarations.addAll(removedElements);
|
| // Update CompilationUnitElement.
|
| - unitElement.accessors = holder.accessors;
|
| - unitElement.enums = holder.enums;
|
| - unitElement.functions = holder.functions;
|
| - unitElement.typeAliases = holder.typeAliases;
|
| - unitElement.types = holder.types;
|
| - unitElement.topLevelVariables = holder.topLevelVariables;
|
| - holder.validate();
|
| + unitElement.accessors = unitElementHolder.accessors;
|
| + unitElement.enums = unitElementHolder.enums;
|
| + unitElement.functions = unitElementHolder.functions;
|
| + unitElement.typeAliases = unitElementHolder.typeAliases;
|
| + unitElement.types = unitElementHolder.types;
|
| + unitElement.topLevelVariables = unitElementHolder.topLevelVariables;
|
| + unitElementHolder.validate();
|
| }
|
|
|
| /**
|
| @@ -179,16 +500,14 @@ class IncrementalCompilationUnitElementBuilder {
|
| // Replace node.
|
| NodeReplacer.replace(newNode, oldNode);
|
| // Replace tokens.
|
| - {
|
| - Token oldBeginToken = TokenUtils.getBeginTokenNotComment(newNode);
|
| - Token newBeginToken = TokenUtils.getBeginTokenNotComment(oldNode);
|
| - oldBeginToken.previous.setNext(newBeginToken);
|
| - oldNode.endToken.setNext(newNode.endToken.next);
|
| - }
|
| + Token oldBeginToken = TokenUtils.getBeginTokenNotComment(oldNode);
|
| + Token newBeginToken = TokenUtils.getBeginTokenNotComment(newNode);
|
| + newBeginToken.previous.setNext(oldBeginToken);
|
| + oldNode.endToken.setNext(newNode.endToken.next);
|
| // Change tokens offsets.
|
| Map<int, int> offsetMap = new HashMap<int, int>();
|
| - TokenUtils.copyTokenOffsets(offsetMap, oldNode.beginToken,
|
| - newNode.beginToken, oldNode.endToken, newNode.endToken, true);
|
| + TokenUtils.copyTokenOffsets(offsetMap, oldBeginToken, newBeginToken,
|
| + oldNode.endToken, newNode.endToken);
|
| // Change elements offsets.
|
| {
|
| var visitor = new _UpdateElementOffsetsVisitor(offsetMap);
|
| @@ -208,21 +527,29 @@ class IncrementalCompilationUnitElementBuilder {
|
| */
|
| static List<Element> _getElements(AstNode node) {
|
| List<Element> elements = <Element>[];
|
| - if (node is TopLevelVariableDeclaration) {
|
| - VariableDeclarationList variableList = node.variables;
|
| + void addPropertyAccessors(VariableDeclarationList variableList) {
|
| if (variableList != null) {
|
| for (VariableDeclaration variable in variableList.variables) {
|
| - TopLevelVariableElement element = variable.element;
|
| - elements.add(element);
|
| - if (element.getter != null) {
|
| - elements.add(element.getter);
|
| - }
|
| - if (element.setter != null) {
|
| - elements.add(element.setter);
|
| + PropertyInducingElement element = variable.element;
|
| + if (element != null) {
|
| + elements.add(element);
|
| + if (element.getter != null) {
|
| + elements.add(element.getter);
|
| + }
|
| + if (element.setter != null) {
|
| + elements.add(element.setter);
|
| + }
|
| }
|
| }
|
| }
|
| + }
|
| +
|
| + if (node is FieldDeclaration) {
|
| + addPropertyAccessors(node.fields);
|
| + } else if (node is TopLevelVariableDeclaration) {
|
| + addPropertyAccessors(node.variables);
|
| } else if (node is PartDirective || node is PartOfDirective) {
|
| + // Ignore.
|
| } else if (node is Directive && node.element != null) {
|
| elements.add(node.element);
|
| } else if (node is Declaration && node.element != null) {
|
| @@ -236,7 +563,7 @@ class IncrementalCompilationUnitElementBuilder {
|
| }
|
|
|
| /**
|
| - * Replaces contents of the [to] unit with the contenxts of the [from] unit.
|
| + * Replaces contents of the [to] unit with the contexts of the [from] unit.
|
| */
|
| static void _replaceUnitContents(CompilationUnit to, CompilationUnit from) {
|
| to.directives.clear();
|
| @@ -247,6 +574,7 @@ class IncrementalCompilationUnitElementBuilder {
|
| to.declarations.addAll(from.declarations);
|
| to.element = to.element;
|
| to.lineInfo = from.lineInfo;
|
| + to.endToken = from.endToken;
|
| }
|
| }
|
|
|
| @@ -260,16 +588,37 @@ class TokenUtils {
|
| * Copy offsets from [newToken]s to [oldToken]s.
|
| */
|
| static void copyTokenOffsets(Map<int, int> offsetMap, Token oldToken,
|
| - Token newToken, Token oldEndToken, Token newEndToken,
|
| - [bool goUpComment = false]) {
|
| + Token newToken, Token oldEndToken, Token newEndToken) {
|
| if (oldToken is CommentToken && newToken is CommentToken) {
|
| - if (goUpComment) {
|
| - copyTokenOffsets(offsetMap, (oldToken as CommentToken).parent,
|
| - (newToken as CommentToken).parent, oldEndToken, newEndToken);
|
| - }
|
| + // Update documentation tokens.
|
| while (oldToken != null) {
|
| offsetMap[oldToken.offset] = newToken.offset;
|
| + offsetMap[oldToken.end] = newToken.end;
|
| oldToken.offset = newToken.offset;
|
| + // Update (otherwise unlinked) reference tokens in documentation.
|
| + if (oldToken is DocumentationCommentToken &&
|
| + newToken is DocumentationCommentToken) {
|
| + List<Token> oldReferences = oldToken.references;
|
| + List<Token> newReferences = newToken.references;
|
| + assert(oldReferences.length == newReferences.length);
|
| + for (int i = 0; i < oldReferences.length; i++) {
|
| + Token oldToken = oldReferences[i];
|
| + Token newToken = newReferences[i];
|
| + // For [new Name] the 'Name' token is the reference.
|
| + // But we need to process all tokens, including 'new'.
|
| + while (oldToken.previous != null &&
|
| + oldToken.previous.type != TokenType.EOF) {
|
| + oldToken = oldToken.previous;
|
| + }
|
| + while (newToken.previous != null &&
|
| + newToken.previous.type != TokenType.EOF) {
|
| + newToken = newToken.previous;
|
| + }
|
| + copyTokenOffsets(
|
| + offsetMap, oldToken, newToken, oldEndToken, newEndToken);
|
| + }
|
| + }
|
| + // Next tokens.
|
| oldToken = oldToken.next;
|
| newToken = newToken.next;
|
| }
|
| @@ -284,7 +633,12 @@ class TokenUtils {
|
| newToken.precedingComments, oldEndToken, newEndToken);
|
| }
|
| offsetMap[oldToken.offset] = newToken.offset;
|
| + offsetMap[oldToken.end] = newToken.end;
|
| oldToken.offset = newToken.offset;
|
| + if (oldToken.type == TokenType.EOF) {
|
| + assert(newToken.type == TokenType.EOF);
|
| + break;
|
| + }
|
| if (oldToken == oldEndToken) {
|
| assert(newToken == newEndToken);
|
| break;
|
| @@ -297,27 +651,44 @@ class TokenUtils {
|
| static Token getBeginTokenNotComment(AstNode node) {
|
| Token oldBeginToken = node.beginToken;
|
| if (oldBeginToken is CommentToken) {
|
| - oldBeginToken = (oldBeginToken as CommentToken).parent;
|
| + return oldBeginToken.parent;
|
| }
|
| return oldBeginToken;
|
| }
|
|
|
| /**
|
| - * Return the token string of all the [node] tokens.
|
| + * Return the token string of all the [node].
|
| */
|
| static String getFullCode(AstNode node) {
|
| + if (node == null) {
|
| + return '';
|
| + }
|
| List<Token> tokens = getTokens(node);
|
| return joinTokens(tokens);
|
| }
|
|
|
| /**
|
| - * Returns all tokends (including comments) of the given [node].
|
| + * Return the token string of all the [nodes].
|
| + */
|
| + static String getFullCodeOfList(List<AstNode> nodes) {
|
| + if (nodes == null) {
|
| + return '';
|
| + }
|
| + return nodes.map(getFullCode).join(_SEPARATOR);
|
| + }
|
| +
|
| + /**
|
| + * Returns all tokens (including comments) of the given [node].
|
| */
|
| static List<Token> getTokens(AstNode node) {
|
| List<Token> tokens = <Token>[];
|
| Token token = getBeginTokenNotComment(node);
|
| Token endToken = node.endToken;
|
| while (true) {
|
| + // stop if past the end token
|
| + if (token.offset > endToken.end) {
|
| + break;
|
| + }
|
| // append comment tokens
|
| for (Token commentToken = token.precedingComments;
|
| commentToken != null;
|
| @@ -349,18 +720,65 @@ class _UpdateElementOffsetsVisitor extends GeneralizingElementVisitor {
|
| _UpdateElementOffsetsVisitor(this.map);
|
|
|
| void visitElement(Element element) {
|
| - if (element is CompilationUnitElement) {
|
| - return;
|
| - }
|
| - if (element.isSynthetic) {
|
| - return;
|
| - }
|
| - int oldOffset = element.nameOffset;
|
| - int newOffset = map[oldOffset];
|
| - assert(newOffset != null);
|
| - (element as ElementImpl).nameOffset = newOffset;
|
| - if (element is! LibraryElement) {
|
| - super.visitElement(element);
|
| + if (element is ElementImpl) {
|
| + // name offset
|
| + {
|
| + int oldOffset = element.nameOffset;
|
| + int newOffset = map[oldOffset];
|
| + // Some synthetic elements have new offsets, e.g. synthetic accessors
|
| + // of property inducing elements. But some are purely synthetic, e.g.
|
| + // synthetic enum fields and their accessors.
|
| + // PrefixElement(s) can be shared between import directives, so
|
| + // their name offsets are outside of the second and subsequent import
|
| + // directives. But we update the name offsets while visiting the first
|
| + // import directive.
|
| + if (newOffset == null) {
|
| + assert(element.isSynthetic || element is PrefixElement);
|
| + return;
|
| + }
|
| + element.nameOffset = newOffset;
|
| + }
|
| + // stop here for LibraryElement
|
| + if (element is LibraryElementImpl) {
|
| + return;
|
| + }
|
| + // code range
|
| + {
|
| + int oldOffset = element.codeOffset;
|
| + if (oldOffset != null) {
|
| + int oldEnd = oldOffset + element.codeLength;
|
| + int newOffset = map[oldOffset];
|
| + int newEnd = map[oldEnd];
|
| + assert(newOffset != null);
|
| + assert(newEnd != null);
|
| + int newLength = newEnd - newOffset;
|
| + element.setCodeRange(newOffset, newLength);
|
| + }
|
| + }
|
| + // visible range
|
| + if (element is LocalElement) {
|
| + SourceRange oldVisibleRange = (element as LocalElement).visibleRange;
|
| + if (oldVisibleRange != null) {
|
| + int oldOffset = oldVisibleRange.offset;
|
| + int oldLength = oldVisibleRange.length;
|
| + int oldEnd = oldOffset + oldLength;
|
| + int newOffset = map[oldOffset];
|
| + int newEnd = map[oldEnd];
|
| + assert(newOffset != null);
|
| + assert(newEnd != null);
|
| + int newLength = newEnd - newOffset;
|
| + 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);
|
| }
|
| }
|
|
|