// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

library analyzer.src.dart.element.element;

import 'dart:collection';
import 'dart:math' show min;

import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/constant/value.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/dart/element/visitor.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dart/constant/value.dart';
import 'package:analyzer/src/dart/element/handle.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/error/codes.dart' show CompileTimeErrorCode;
import 'package:analyzer/src/generated/constant.dart' show EvaluationResultImpl;
import 'package:analyzer/src/generated/engine.dart'
    show AnalysisContext, AnalysisEngine;
import 'package:analyzer/src/generated/java_engine.dart';
import 'package:analyzer/src/generated/resolver.dart';
import 'package:analyzer/src/generated/sdk.dart' show DartSdk;
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/testing/ast_factory.dart';
import 'package:analyzer/src/generated/utilities_collection.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
import 'package:analyzer/src/generated/utilities_general.dart';
import 'package:analyzer/src/summary/idl.dart';
import 'package:analyzer/src/task/dart.dart';

/**
 * A concrete implementation of a [ClassElement].
 */
abstract class AbstractClassElementImpl extends ElementImpl
    implements ClassElement {
  /**
   * A list containing all of the accessors (getters and setters) contained in
   * this class.
   */
  List<PropertyAccessorElement> _accessors;

  /**
   * A list containing all of the fields contained in this class.
   */
  List<FieldElement> _fields;

  /**
   * Initialize a newly created class element to have the given [name] at the
   * given [offset] in the file that contains the declaration of this element.
   */
  AbstractClassElementImpl(String name, int offset) : super(name, offset);

  /**
   * Initialize a newly created class element to have the given [name].
   */
  AbstractClassElementImpl.forNode(Identifier name) : super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  AbstractClassElementImpl.forSerialized(
      CompilationUnitElementImpl enclosingUnit)
      : super.forSerialized(enclosingUnit);

  @override
  List<PropertyAccessorElement> get accessors {
    return _accessors ?? const <PropertyAccessorElement>[];
  }

  /**
   * Set the accessors contained in this class to the given [accessors].
   */
  void set accessors(List<PropertyAccessorElement> accessors) {
    for (PropertyAccessorElement accessor in accessors) {
      (accessor as PropertyAccessorElementImpl).enclosingElement = this;
    }
    this._accessors = accessors;
  }

  @override
  String get displayName => name;

  @override
  List<FieldElement> get fields => _fields ?? const <FieldElement>[];

  /**
   * Set the fields contained in this class to the given [fields].
   */
  void set fields(List<FieldElement> fields) {
    for (FieldElement field in fields) {
      (field as FieldElementImpl).enclosingElement = this;
    }
    this._fields = fields;
  }

  @override
  bool get isEnum;

  @override
  ElementKind get kind => ElementKind.CLASS;

  @override
  accept(ElementVisitor visitor) => visitor.visitClassElement(this);

  @override
  NamedCompilationUnitMember computeNode() {
    if (isEnum) {
      return getNodeMatching((node) => node is EnumDeclaration);
    } else {
      return getNodeMatching(
          (node) => node is ClassDeclaration || node is ClassTypeAlias);
    }
  }

  @override
  ElementImpl getChild(String identifier) {
    //
    // The casts in this method are safe because the set methods would have
    // thrown a CCE if any of the elements in the arrays were not of the
    // expected types.
    //
    for (PropertyAccessorElement accessor in accessors) {
      PropertyAccessorElementImpl accessorImpl = accessor;
      if (accessorImpl.identifier == identifier) {
        return accessorImpl;
      }
    }
    for (FieldElement field in fields) {
      FieldElementImpl fieldImpl = field;
      if (fieldImpl.identifier == identifier) {
        return fieldImpl;
      }
    }
    return null;
  }

  @override
  FieldElement getField(String name) {
    for (FieldElement fieldElement in fields) {
      if (name == fieldElement.name) {
        return fieldElement;
      }
    }
    return null;
  }

  @override
  PropertyAccessorElement getGetter(String getterName) {
    int length = accessors.length;
    for (int i = 0; i < length; i++) {
      PropertyAccessorElement accessor = accessors[i];
      if (accessor.isGetter && accessor.name == getterName) {
        return accessor;
      }
    }
    return null;
  }

  @override
  PropertyAccessorElement getSetter(String setterName) {
    // TODO (jwren) revisit- should we append '=' here or require clients to
    // include it?
    // Do we need the check for isSetter below?
    if (!StringUtilities.endsWithChar(setterName, 0x3D)) {
      setterName += '=';
    }
    for (PropertyAccessorElement accessor in accessors) {
      if (accessor.isSetter && accessor.name == setterName) {
        return accessor;
      }
    }
    return null;
  }

  @override
  MethodElement lookUpConcreteMethod(
          String methodName, LibraryElement library) =>
      _internalLookUpConcreteMethod(
          methodName, library, true, new HashSet<ClassElement>());

  @override
  PropertyAccessorElement lookUpGetter(
          String getterName, LibraryElement library) =>
      _internalLookUpGetter(getterName, library, true);

  @override
  PropertyAccessorElement lookUpInheritedConcreteGetter(
          String getterName, LibraryElement library) =>
      _internalLookUpConcreteGetter(getterName, library, false);

  @override
  MethodElement lookUpInheritedConcreteMethod(
          String methodName, LibraryElement library) =>
      _internalLookUpConcreteMethod(
          methodName, library, false, new HashSet<ClassElement>());

  @override
  PropertyAccessorElement lookUpInheritedConcreteSetter(
          String setterName, LibraryElement library) =>
      _internalLookUpConcreteSetter(setterName, library, false);

  @override
  MethodElement lookUpInheritedMethod(
          String methodName, LibraryElement library) =>
      _internalLookUpMethod(
          methodName, library, false, new HashSet<ClassElement>());

  @override
  MethodElement lookUpMethod(String methodName, LibraryElement library) =>
      _internalLookUpMethod(
          methodName, library, true, new HashSet<ClassElement>());

  @override
  PropertyAccessorElement lookUpSetter(
          String setterName, LibraryElement library) =>
      _internalLookUpSetter(setterName, library, true);

  @override
  void visitChildren(ElementVisitor visitor) {
    super.visitChildren(visitor);
    safelyVisitChildren(accessors, visitor);
    safelyVisitChildren(fields, visitor);
  }

  PropertyAccessorElement _internalLookUpConcreteGetter(
      String getterName, LibraryElement library, bool includeThisClass) {
    PropertyAccessorElement getter =
        _internalLookUpGetter(getterName, library, includeThisClass);
    while (getter != null && getter.isAbstract) {
      Element definingClass = getter.enclosingElement;
      if (definingClass is! ClassElement) {
        return null;
      }
      getter = getImpl(definingClass)
          ._internalLookUpGetter(getterName, library, false);
    }
    return getter;
  }

  MethodElement _internalLookUpConcreteMethod(
      String methodName,
      LibraryElement library,
      bool includeThisClass,
      HashSet<ClassElement> visitedClasses) {
    MethodElement method = _internalLookUpMethod(
        methodName, library, includeThisClass, visitedClasses);
    while (method != null && method.isAbstract) {
      ClassElement definingClass = method.enclosingElement;
      if (definingClass == null) {
        return null;
      }
      method = getImpl(definingClass)
          ._internalLookUpMethod(methodName, library, false, visitedClasses);
    }
    return method;
  }

  PropertyAccessorElement _internalLookUpConcreteSetter(
      String setterName, LibraryElement library, bool includeThisClass) {
    PropertyAccessorElement setter =
        _internalLookUpSetter(setterName, library, includeThisClass);
    while (setter != null && setter.isAbstract) {
      Element definingClass = setter.enclosingElement;
      if (definingClass is ClassElementImpl) {
        setter =
            definingClass._internalLookUpSetter(setterName, library, false);
      } else {
        return null;
      }
    }
    return setter;
  }

  PropertyAccessorElement _internalLookUpGetter(
      String getterName, LibraryElement library, bool includeThisClass) {
    HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>();
    ClassElement currentElement = this;
    if (includeThisClass) {
      PropertyAccessorElement element = currentElement.getGetter(getterName);
      if (element != null && element.isAccessibleIn(library)) {
        return element;
      }
    }
    while (currentElement != null && visitedClasses.add(currentElement)) {
      for (InterfaceType mixin in currentElement.mixins.reversed) {
        ClassElement mixinElement = mixin.element;
        if (mixinElement != null) {
          PropertyAccessorElement element = mixinElement.getGetter(getterName);
          if (element != null && element.isAccessibleIn(library)) {
            return element;
          }
        }
      }
      InterfaceType supertype = currentElement.supertype;
      if (supertype == null) {
        return null;
      }
      currentElement = supertype.element;
      PropertyAccessorElement element = currentElement.getGetter(getterName);
      if (element != null && element.isAccessibleIn(library)) {
        return element;
      }
    }
    return null;
  }

  MethodElement _internalLookUpMethod(String methodName, LibraryElement library,
      bool includeThisClass, HashSet<ClassElement> visitedClasses) {
    ClassElement currentElement = this;
    if (includeThisClass) {
      MethodElement element = currentElement.getMethod(methodName);
      if (element != null && element.isAccessibleIn(library)) {
        return element;
      }
    }
    while (currentElement != null && visitedClasses.add(currentElement)) {
      for (InterfaceType mixin in currentElement.mixins.reversed) {
        ClassElement mixinElement = mixin.element;
        if (mixinElement != null) {
          MethodElement element = mixinElement.getMethod(methodName);
          if (element != null && element.isAccessibleIn(library)) {
            return element;
          }
        }
      }
      InterfaceType supertype = currentElement.supertype;
      if (supertype == null) {
        return null;
      }
      currentElement = supertype.element;
      MethodElement element = currentElement.getMethod(methodName);
      if (element != null && element.isAccessibleIn(library)) {
        return element;
      }
    }
    return null;
  }

  PropertyAccessorElement _internalLookUpSetter(
      String setterName, LibraryElement library, bool includeThisClass) {
    HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>();
    ClassElement currentElement = this;
    if (includeThisClass) {
      PropertyAccessorElement element = currentElement.getSetter(setterName);
      if (element != null && element.isAccessibleIn(library)) {
        return element;
      }
    }
    while (currentElement != null && visitedClasses.add(currentElement)) {
      for (InterfaceType mixin in currentElement.mixins.reversed) {
        ClassElement mixinElement = mixin.element;
        if (mixinElement != null) {
          PropertyAccessorElement element = mixinElement.getSetter(setterName);
          if (element != null && element.isAccessibleIn(library)) {
            return element;
          }
        }
      }
      InterfaceType supertype = currentElement.supertype;
      if (supertype == null) {
        return null;
      }
      currentElement = supertype.element;
      PropertyAccessorElement element = currentElement.getSetter(setterName);
      if (element != null && element.isAccessibleIn(library)) {
        return element;
      }
    }
    return null;
  }

  /**
   * Return the [AbstractClassElementImpl] of the given [classElement].  May
   * throw an exception if the [AbstractClassElementImpl] cannot be provided
   * (should not happen though).
   */
  static AbstractClassElementImpl getImpl(ClassElement classElement) {
    if (classElement is ClassElementHandle) {
      return getImpl(classElement.actualElement);
    }
    return classElement as AbstractClassElementImpl;
  }
}

/**
 * For AST nodes that could be in both the getter and setter contexts
 * ([IndexExpression]s and [SimpleIdentifier]s), the additional resolved
 * elements are stored in the AST node, in an [AuxiliaryElements]. Because
 * resolved elements are either statically resolved or resolved using propagated
 * type information, this class is a wrapper for a pair of [ExecutableElement]s,
 * not just a single [ExecutableElement].
 */
class AuxiliaryElements {
  /**
   * The element based on propagated type information, or `null` if the AST
   * structure has not been resolved or if the node could not be resolved.
   */
  final ExecutableElement propagatedElement;

  /**
   * The element based on static type information, or `null` if the AST
   * structure has not been resolved or if the node could not be resolved.
   */
  final ExecutableElement staticElement;

  /**
   * Initialize a newly created pair to have both the [staticElement] and the
   * [propagatedElement].
   */
  AuxiliaryElements(this.staticElement, this.propagatedElement);
}

/**
 * An [AbstractClassElementImpl] which is a class.
 */
class ClassElementImpl extends AbstractClassElementImpl
    with TypeParameterizedElementMixin {
  /**
   * The unlinked representation of the class in the summary.
   */
  final UnlinkedClass _unlinkedClass;

  /**
   * A list containing all of the type parameters defined for this class.
   */
  List<TypeParameterElement> _typeParameters = TypeParameterElement.EMPTY_LIST;

  /**
   * The superclass of the class, or `null` for [Object].
   */
  InterfaceType _supertype;

  /**
   * The type defined by the class.
   */
  InterfaceType _type;

  /**
   * A list containing all of the mixins that are applied to the class being
   * extended in order to derive the superclass of this class.
   */
  List<InterfaceType> _mixins;

  /**
   * A list containing all of the interfaces that are implemented by this class.
   */
  List<InterfaceType> _interfaces;

  /**
   * For classes which are not mixin applications, a list containing all of the
   * constructors contained in this class, or `null` if the list of
   * constructors has not yet been built.
   *
   * For classes which are mixin applications, the list of constructors is
   * computed on the fly by the [constructors] getter, and this field is
   * `null`.
   */
  List<ConstructorElement> _constructors;

  /**
   * A list containing all of the methods contained in this class.
   */
  List<MethodElement> _methods;

  /**
   * A flag indicating whether the types associated with the instance members of
   * this class have been inferred.
   */
  bool _hasBeenInferred = false;

  /**
   * The version of this element. The version is changed when the element is
   * incrementally updated, so that its lists of constructors, accessors and
   * methods might be different.
   */
  int version = 0;

  /**
   * Initialize a newly created class element to have the given [name] at the
   * given [offset] in the file that contains the declaration of this element.
   */
  ClassElementImpl(String name, int offset)
      : _unlinkedClass = null,
        super(name, offset);

  /**
   * Initialize a newly created class element to have the given [name].
   */
  ClassElementImpl.forNode(Identifier name)
      : _unlinkedClass = null,
        super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  ClassElementImpl.forSerialized(
      this._unlinkedClass, CompilationUnitElementImpl enclosingUnit)
      : super.forSerialized(enclosingUnit);

  /**
   * Set whether this class is abstract.
   */
  void set abstract(bool isAbstract) {
    assert(_unlinkedClass == null);
    setModifier(Modifier.ABSTRACT, isAbstract);
  }

  @override
  List<PropertyAccessorElement> get accessors {
    if (_unlinkedClass != null && _accessors == null) {
      _resynthesizeFieldsAndPropertyAccessors();
    }
    return _accessors ?? const <PropertyAccessorElement>[];
  }

  @override
  void set accessors(List<PropertyAccessorElement> accessors) {
    assert(_unlinkedClass == null);
    super.accessors = accessors;
  }

  @override
  List<InterfaceType> get allSupertypes {
    List<InterfaceType> list = new List<InterfaceType>();
    _collectAllSupertypes(list);
    return list;
  }

  @override
  int get codeLength {
    if (_unlinkedClass != null) {
      return _unlinkedClass.codeRange?.length;
    }
    return super.codeLength;
  }

  @override
  int get codeOffset {
    if (_unlinkedClass != null) {
      return _unlinkedClass.codeRange?.offset;
    }
    return super.codeOffset;
  }

  @override
  List<ConstructorElement> get constructors {
    if (isMixinApplication) {
      return _computeMixinAppConstructors();
    }
    if (_unlinkedClass != null && _constructors == null) {
      _constructors = _unlinkedClass.executables
          .where((e) => e.kind == UnlinkedExecutableKind.constructor)
          .map((e) => new ConstructorElementImpl.forSerialized(e, this))
          .toList(growable: false);
      // Ensure at least implicit default constructor.
      if (_constructors.isEmpty) {
        ConstructorElementImpl constructor = new ConstructorElementImpl('', -1);
        constructor.synthetic = true;
        constructor.enclosingElement = this;
        _constructors = <ConstructorElement>[constructor];
      }
    }
    assert(_constructors != null);
    return _constructors ?? const <ConstructorElement>[];
  }

  /**
   * Set the constructors contained in this class to the given [constructors].
   *
   * Should only be used for class elements that are not mixin applications.
   */
  void set constructors(List<ConstructorElement> constructors) {
    assert(_unlinkedClass == null);
    assert(!isMixinApplication);
    for (ConstructorElement constructor in constructors) {
      (constructor as ConstructorElementImpl).enclosingElement = this;
    }
    this._constructors = constructors;
  }

  @override
  String get documentationComment {
    if (_unlinkedClass != null) {
      return _unlinkedClass?.documentationComment?.text;
    }
    return super.documentationComment;
  }

  /**
   * Return `true` if [CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS] should
   * be reported for this class.
   */
  bool get doesMixinLackConstructors {
    if (!isMixinApplication && mixins.isEmpty) {
      // This class is not a mixin application and it doesn't have a "with"
      // clause, so CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS is
      // inapplicable.
      return false;
    }
    if (supertype == null) {
      // Should never happen, since Object is the only class that has no
      // supertype, and it should have been caught by the test above.
      assert(false);
      return false;
    }
    // Find the nearest class in the supertype chain that is not a mixin
    // application.
    ClassElement nearestNonMixinClass = supertype.element;
    if (nearestNonMixinClass.isMixinApplication) {
      // Use a list to keep track of the classes we've seen, so that we won't
      // go into an infinite loop in the event of a non-trivial loop in the
      // class hierarchy.
      List<ClassElement> classesSeen = <ClassElement>[this];
      while (nearestNonMixinClass.isMixinApplication) {
        if (classesSeen.contains(nearestNonMixinClass)) {
          // Loop in the class hierarchy (which is reported elsewhere).  Don't
          // confuse the user with further errors.
          return false;
        }
        classesSeen.add(nearestNonMixinClass);
        if (nearestNonMixinClass.supertype == null) {
          // Should never happen, since Object is the only class that has no
          // supertype, and it is not a mixin application.
          assert(false);
          return false;
        }
        nearestNonMixinClass = nearestNonMixinClass.supertype.element;
      }
    }
    return !nearestNonMixinClass.constructors.any(isSuperConstructorAccessible);
  }

  @override
  TypeParameterizedElementMixin get enclosingTypeParameterContext => null;

  @override
  List<FieldElement> get fields {
    if (_unlinkedClass != null && _fields == null) {
      _resynthesizeFieldsAndPropertyAccessors();
    }
    return _fields ?? const <FieldElement>[];
  }

  @override
  void set fields(List<FieldElement> fields) {
    assert(_unlinkedClass == null);
    super.fields = fields;
  }

  bool get hasBeenInferred {
    if (_unlinkedClass != null) {
      return context.analysisOptions.strongMode;
    }
    return _hasBeenInferred;
  }

  void set hasBeenInferred(bool hasBeenInferred) {
    assert(_unlinkedClass == null);
    _hasBeenInferred = hasBeenInferred;
  }

  @override
  bool get hasNonFinalField {
    List<ClassElement> classesToVisit = new List<ClassElement>();
    HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>();
    classesToVisit.add(this);
    while (!classesToVisit.isEmpty) {
      ClassElement currentElement = classesToVisit.removeAt(0);
      if (visitedClasses.add(currentElement)) {
        // check fields
        for (FieldElement field in currentElement.fields) {
          if (!field.isFinal &&
              !field.isConst &&
              !field.isStatic &&
              !field.isSynthetic) {
            return true;
          }
        }
        // check mixins
        for (InterfaceType mixinType in currentElement.mixins) {
          ClassElement mixinElement = mixinType.element;
          classesToVisit.add(mixinElement);
        }
        // check super
        InterfaceType supertype = currentElement.supertype;
        if (supertype != null) {
          ClassElement superElement = supertype.element;
          if (superElement != null) {
            classesToVisit.add(superElement);
          }
        }
      }
    }
    // not found
    return false;
  }

  /**
   * Return `true` if the class has a `noSuchMethod()` method distinct from the
   * one declared in class `Object`, as per the Dart Language Specification
   * (section 10.4).
   */
  bool get hasNoSuchMethod {
    MethodElement method =
        lookUpMethod(FunctionElement.NO_SUCH_METHOD_METHOD_NAME, library);
    ClassElement definingClass = method?.enclosingElement;
    return definingClass != null && !definingClass.type.isObject;
  }

  @override
  bool get hasReferenceToSuper => hasModifier(Modifier.REFERENCES_SUPER);

  /**
   * Set whether this class references 'super'.
   */
  void set hasReferenceToSuper(bool isReferencedSuper) {
    setModifier(Modifier.REFERENCES_SUPER, isReferencedSuper);
  }

  @override
  bool get hasStaticMember {
    for (MethodElement method in methods) {
      if (method.isStatic) {
        return true;
      }
    }
    for (PropertyAccessorElement accessor in accessors) {
      if (accessor.isStatic) {
        return true;
      }
    }
    return false;
  }

  @override
  List<InterfaceType> get interfaces {
    if (_unlinkedClass != null && _interfaces == null) {
      ResynthesizerContext context = enclosingUnit.resynthesizerContext;
      _interfaces = _unlinkedClass.interfaces
          .map((EntityRef t) => context.resolveTypeRef(t, this))
          .where((DartType type) => type is InterfaceType)
          .toList(growable: false);
    }
    return _interfaces ?? const <InterfaceType>[];
  }

  void set interfaces(List<InterfaceType> interfaces) {
    assert(_unlinkedClass == null);
    _interfaces = interfaces;
  }

  @override
  bool get isAbstract {
    if (_unlinkedClass != null) {
      return _unlinkedClass.isAbstract;
    }
    return hasModifier(Modifier.ABSTRACT);
  }

  @override
  bool get isEnum => false;

  @override
  bool get isMixinApplication {
    if (_unlinkedClass != null) {
      return _unlinkedClass.isMixinApplication;
    }
    return hasModifier(Modifier.MIXIN_APPLICATION);
  }

  @override
  bool get isOrInheritsProxy =>
      _safeIsOrInheritsProxy(this, new HashSet<ClassElement>());

  @override
  bool get isProxy {
    for (ElementAnnotation annotation in metadata) {
      if (annotation.isProxy) {
        return true;
      }
    }
    return false;
  }

  @override
  bool get isValidMixin {
    if (!context.analysisOptions.enableSuperMixins) {
      if (hasReferenceToSuper) {
        return false;
      }
      if (!supertype.isObject) {
        return false;
      }
    }
    for (ConstructorElement constructor in constructors) {
      if (!constructor.isSynthetic && !constructor.isFactory) {
        return false;
      }
    }
    return true;
  }

  @override
  List<ElementAnnotation> get metadata {
    if (_unlinkedClass != null) {
      return _metadata ??=
          _buildAnnotations(enclosingUnit, _unlinkedClass.annotations);
    }
    return super.metadata;
  }

  @override
  List<MethodElement> get methods {
    if (_unlinkedClass != null) {
      _methods ??= _unlinkedClass.executables
          .where((e) => e.kind == UnlinkedExecutableKind.functionOrMethod)
          .map((e) => new MethodElementImpl.forSerialized(e, this))
          .toList(growable: false);
    }
    return _methods ?? const <MethodElement>[];
  }

  /**
   * Set the methods contained in this class to the given [methods].
   */
  void set methods(List<MethodElement> methods) {
    assert(_unlinkedClass == null);
    for (MethodElement method in methods) {
      (method as MethodElementImpl).enclosingElement = this;
    }
    _methods = methods;
  }

  /**
   * Set whether this class is a mixin application.
   */
  void set mixinApplication(bool isMixinApplication) {
    assert(_unlinkedClass == null);
    setModifier(Modifier.MIXIN_APPLICATION, isMixinApplication);
  }

  @override
  List<InterfaceType> get mixins {
    if (_unlinkedClass != null && _mixins == null) {
      ResynthesizerContext context = enclosingUnit.resynthesizerContext;
      _mixins = _unlinkedClass.mixins
          .map((EntityRef t) => context.resolveTypeRef(t, this))
          .where((DartType type) => type is InterfaceType)
          .toList(growable: false);
    }
    return _mixins ?? const <InterfaceType>[];
  }

  void set mixins(List<InterfaceType> mixins) {
    assert(_unlinkedClass == null);
    _mixins = mixins;
  }

  @override
  String get name {
    if (_unlinkedClass != null) {
      return _unlinkedClass.name;
    }
    return super.name;
  }

  @override
  int get nameOffset {
    if (_unlinkedClass != null) {
      return _unlinkedClass.nameOffset;
    }
    return super.nameOffset;
  }

  @override
  InterfaceType get supertype {
    if (_unlinkedClass != null && _supertype == null) {
      if (_unlinkedClass.supertype != null) {
        DartType type = enclosingUnit.resynthesizerContext
            .resolveTypeRef(_unlinkedClass.supertype, this);
        if (type is InterfaceType) {
          _supertype = type;
        } else {
          _supertype = context.typeProvider.objectType;
        }
      } else if (_unlinkedClass.hasNoSupertype) {
        return null;
      } else {
        _supertype = context.typeProvider.objectType;
      }
    }
    return _supertype;
  }

  void set supertype(InterfaceType supertype) {
    assert(_unlinkedClass == null);
    _supertype = supertype;
  }

  @override
  InterfaceType get type {
    if (_type == null) {
      InterfaceTypeImpl type = new InterfaceTypeImpl(this);
      type.typeArguments = typeParameterTypes;
      _type = type;
    }
    return _type;
  }

  @override
  TypeParameterizedElementMixin get typeParameterContext => this;

  @override
  List<TypeParameterElement> get typeParameters {
    if (_unlinkedClass != null) {
      return super.typeParameters;
    }
    return _typeParameters;
  }

  /**
   * Set the type parameters defined for this class to the given
   * [typeParameters].
   */
  void set typeParameters(List<TypeParameterElement> typeParameters) {
    assert(_unlinkedClass == null);
    for (TypeParameterElement typeParameter in typeParameters) {
      (typeParameter as TypeParameterElementImpl).enclosingElement = this;
    }
    this._typeParameters = typeParameters;
  }

  @override
  List<UnlinkedTypeParam> get unlinkedTypeParams =>
      _unlinkedClass.typeParameters;

  @override
  ConstructorElement get unnamedConstructor {
    for (ConstructorElement element in constructors) {
      String name = element.displayName;
      if (name == null || name.isEmpty) {
        return element;
      }
    }
    return null;
  }

  @override
  void appendTo(StringBuffer buffer) {
    if (isAbstract) {
      buffer.write('abstract ');
    }
    buffer.write('class ');
    String name = displayName;
    if (name == null) {
      buffer.write("{unnamed class}");
    } else {
      buffer.write(name);
    }
    int variableCount = typeParameters.length;
    if (variableCount > 0) {
      buffer.write("<");
      for (int i = 0; i < variableCount; i++) {
        if (i > 0) {
          buffer.write(", ");
        }
        (typeParameters[i] as TypeParameterElementImpl).appendTo(buffer);
      }
      buffer.write(">");
    }
    if (supertype != null && !supertype.isObject) {
      buffer.write(' extends ');
      buffer.write(supertype.displayName);
    }
    if (mixins.isNotEmpty) {
      buffer.write(' with ');
      buffer.write(mixins.map((t) => t.displayName).join(', '));
    }
    if (interfaces.isNotEmpty) {
      buffer.write(' implements ');
      buffer.write(interfaces.map((t) => t.displayName).join(', '));
    }
  }

  @override
  ElementImpl getChild(String identifier) {
    ElementImpl child = super.getChild(identifier);
    if (child != null) {
      return child;
    }
    //
    // The casts in this method are safe because the set methods would have
    // thrown a CCE if any of the elements in the arrays were not of the
    // expected types.
    //
    for (ConstructorElement constructor in _constructors) {
      ConstructorElementImpl constructorImpl = constructor;
      if (constructorImpl.identifier == identifier) {
        return constructorImpl;
      }
    }
    for (MethodElement method in methods) {
      MethodElementImpl methodImpl = method;
      if (methodImpl.identifier == identifier) {
        return methodImpl;
      }
    }
    for (TypeParameterElement typeParameter in typeParameters) {
      TypeParameterElementImpl typeParameterImpl = typeParameter;
      if (typeParameterImpl.identifier == identifier) {
        return typeParameterImpl;
      }
    }
    return null;
  }

  @override
  MethodElement getMethod(String methodName) {
    int length = methods.length;
    for (int i = 0; i < length; i++) {
      MethodElement method = methods[i];
      if (method.name == methodName) {
        return method;
      }
    }
    return null;
  }

  @override
  ConstructorElement getNamedConstructor(String name) {
    for (ConstructorElement element in constructors) {
      String elementName = element.name;
      if (elementName != null && elementName == name) {
        return element;
      }
    }
    return null;
  }

  @override
  bool isSuperConstructorAccessible(ConstructorElement constructor) {
    // If this class has no mixins, then all superclass constructors are
    // accessible.
    if (mixins.isEmpty) {
      return true;
    }
    // Otherwise only constructors that lack optional parameters are
    // accessible (see dartbug.com/19576).
    for (ParameterElement parameter in constructor.parameters) {
      if (parameter.parameterKind != ParameterKind.REQUIRED) {
        return false;
      }
    }
    return true;
  }

  @override
  void visitChildren(ElementVisitor visitor) {
    super.visitChildren(visitor);
    safelyVisitChildren(_constructors, visitor);
    safelyVisitChildren(methods, visitor);
    safelyVisitChildren(_typeParameters, visitor);
  }

  void _collectAllSupertypes(List<InterfaceType> supertypes) {
    List<InterfaceType> typesToVisit = new List<InterfaceType>();
    List<ClassElement> visitedClasses = new List<ClassElement>();
    typesToVisit.add(this.type);
    while (!typesToVisit.isEmpty) {
      InterfaceType currentType = typesToVisit.removeAt(0);
      ClassElement currentElement = currentType.element;
      if (!visitedClasses.contains(currentElement)) {
        visitedClasses.add(currentElement);
        if (!identical(currentType, this.type)) {
          supertypes.add(currentType);
        }
        InterfaceType supertype = currentType.superclass;
        if (supertype != null) {
          typesToVisit.add(supertype);
        }
        for (InterfaceType type in currentElement.interfaces) {
          typesToVisit.add(type);
        }
        for (InterfaceType type in currentElement.mixins) {
          ClassElement element = type.element;
          if (!visitedClasses.contains(element)) {
            supertypes.add(type);
          }
        }
      }
    }
  }

  /**
   * Compute a list of constructors for this class, which is a mixin
   * application.  If specified, [visitedClasses] is a list of the other mixin
   * application classes which have been visited on the way to reaching this
   * one (this is used to detect circularities).
   */
  List<ConstructorElement> _computeMixinAppConstructors(
      [List<ClassElementImpl> visitedClasses = null]) {
    // First get the list of constructors of the superclass which need to be
    // forwarded to this class.
    Iterable<ConstructorElement> constructorsToForward;
    if (supertype == null) {
      // Shouldn't ever happen, since the only class with no supertype is
      // Object, and it isn't a mixin application.  But for safety's sake just
      // assume an empty list.
      assert(false);
      constructorsToForward = <ConstructorElement>[];
    } else if (!supertype.element.isMixinApplication) {
      List<ConstructorElement> superclassConstructors =
          supertype.element.constructors;
      // Filter out any constructors with optional parameters (see
      // dartbug.com/15101).
      constructorsToForward =
          superclassConstructors.where(isSuperConstructorAccessible);
    } else {
      if (visitedClasses == null) {
        visitedClasses = <ClassElementImpl>[this];
      } else {
        if (visitedClasses.contains(this)) {
          // Loop in the class hierarchy.  Don't try to forward any
          // constructors.
          return <ConstructorElement>[];
        }
        visitedClasses.add(this);
      }
      try {
        ClassElementImpl superElement = AbstractClassElementImpl
            .getImpl(supertype.element) as ClassElementImpl;
        constructorsToForward =
            superElement._computeMixinAppConstructors(visitedClasses);
      } finally {
        visitedClasses.removeLast();
      }
    }

    // Figure out the type parameter substitution we need to perform in order
    // to produce constructors for this class.  We want to be robust in the
    // face of errors, so drop any extra type arguments and fill in any missing
    // ones with `dynamic`.
    List<DartType> parameterTypes =
        TypeParameterTypeImpl.getTypes(supertype.typeParameters);
    List<DartType> argumentTypes = new List<DartType>.filled(
        parameterTypes.length, DynamicTypeImpl.instance);
    for (int i = 0; i < supertype.typeArguments.length; i++) {
      if (i >= argumentTypes.length) {
        break;
      }
      argumentTypes[i] = supertype.typeArguments[i];
    }

    // Now create an implicit constructor for every constructor found above,
    // substituting type parameters as appropriate.
    return constructorsToForward
        .map((ConstructorElement superclassConstructor) {
      ConstructorElementImpl implicitConstructor =
          new ConstructorElementImpl(superclassConstructor.name, -1);
      implicitConstructor.synthetic = true;
      implicitConstructor.redirectedConstructor = superclassConstructor;
      List<ParameterElement> superParameters = superclassConstructor.parameters;
      int count = superParameters.length;
      if (count > 0) {
        List<ParameterElement> implicitParameters =
            new List<ParameterElement>(count);
        for (int i = 0; i < count; i++) {
          ParameterElement superParameter = superParameters[i];
          ParameterElementImpl implicitParameter =
              new ParameterElementImpl(superParameter.name, -1);
          implicitParameter.const3 = superParameter.isConst;
          implicitParameter.final2 = superParameter.isFinal;
          implicitParameter.parameterKind = superParameter.parameterKind;
          implicitParameter.synthetic = true;
          implicitParameter.type =
              superParameter.type.substitute2(argumentTypes, parameterTypes);
          implicitParameters[i] = implicitParameter;
        }
        implicitConstructor.parameters = implicitParameters;
      }
      implicitConstructor.enclosingElement = this;
      return implicitConstructor;
    }).toList(growable: false);
  }

  /**
   * Resynthesize explicit fields and property accessors and fill [_fields] and
   * [_accessors] with explicit and implicit elements.
   */
  void _resynthesizeFieldsAndPropertyAccessors() {
    assert(_fields == null);
    assert(_accessors == null);
    // Build explicit fields and implicit property accessors.
    var explicitFields = <FieldElement>[];
    var implicitAccessors = <PropertyAccessorElement>[];
    for (UnlinkedVariable v in _unlinkedClass.fields) {
      FieldElementImpl field =
          new FieldElementImpl.forSerializedFactory(v, this);
      explicitFields.add(field);
      implicitAccessors.add(
          new PropertyAccessorElementImpl_ImplicitGetter(field)
            ..enclosingElement = this);
      if (!field.isConst && !field.isFinal) {
        implicitAccessors.add(
            new PropertyAccessorElementImpl_ImplicitSetter(field)
              ..enclosingElement = this);
      }
    }
    // Build explicit property accessors and implicit fields.
    var explicitAccessors = <PropertyAccessorElement>[];
    var implicitFields = <String, FieldElementImpl>{};
    for (UnlinkedExecutable e in _unlinkedClass.executables) {
      if (e.kind == UnlinkedExecutableKind.getter ||
          e.kind == UnlinkedExecutableKind.setter) {
        PropertyAccessorElementImpl accessor =
            new PropertyAccessorElementImpl.forSerialized(e, this);
        explicitAccessors.add(accessor);
        // Prepare the field type.
        DartType fieldType;
        if (e.kind == UnlinkedExecutableKind.getter) {
          fieldType = accessor.returnType;
        } else {
          fieldType = accessor.parameters[0].type;
        }
        // Create or update the implicit field.
        String fieldName = accessor.displayName;
        FieldElementImpl field = implicitFields[fieldName];
        if (field == null) {
          field = new FieldElementImpl(fieldName, -1);
          implicitFields[fieldName] = field;
          field.enclosingElement = this;
          field.synthetic = true;
          field.final2 = e.kind == UnlinkedExecutableKind.getter;
          field.type = fieldType;
        } else {
          field.final2 = false;
        }
        accessor.variable = field;
        if (e.kind == UnlinkedExecutableKind.getter) {
          field.getter = accessor;
        } else {
          field.setter = accessor;
        }
      }
    }
    // Combine explicit and implicit fields and property accessors.
    _fields = <FieldElement>[]
      ..addAll(explicitFields)
      ..addAll(implicitFields.values);
    _accessors = <PropertyAccessorElement>[]
      ..addAll(explicitAccessors)
      ..addAll(implicitAccessors);
  }

  bool _safeIsOrInheritsProxy(
      ClassElement element, HashSet<ClassElement> visited) {
    if (visited.contains(element)) {
      return false;
    }
    visited.add(element);
    if (element.isProxy) {
      return true;
    } else if (element.supertype != null &&
        _safeIsOrInheritsProxy(element.supertype.element, visited)) {
      return true;
    }
    List<InterfaceType> supertypes = element.interfaces;
    for (int i = 0; i < supertypes.length; i++) {
      if (_safeIsOrInheritsProxy(supertypes[i].element, visited)) {
        return true;
      }
    }
    supertypes = element.mixins;
    for (int i = 0; i < supertypes.length; i++) {
      if (_safeIsOrInheritsProxy(supertypes[i].element, visited)) {
        return true;
      }
    }
    return false;
  }
}

/**
 * A concrete implementation of a [CompilationUnitElement].
 */
class CompilationUnitElementImpl extends UriReferencedElementImpl
    implements CompilationUnitElement {
  /**
   * The context in which this unit is resynthesized, or `null` if the
   * element is not resynthesized a summary.
   */
  final ResynthesizerContext resynthesizerContext;

  /**
   * The unlinked representation of the unit in the summary.
   */
  final UnlinkedUnit _unlinkedUnit;

  /**
   * The unlinked representation of the part in the summary.
   */
  final UnlinkedPart _unlinkedPart;

  /**
   * The source that corresponds to this compilation unit.
   */
  @override
  Source source;

  /**
   * The source of the library containing this compilation unit.
   *
   * This is the same as the source of the containing [LibraryElement],
   * except that it does not require the containing [LibraryElement] to be
   * computed.
   */
  Source librarySource;

  /**
   * A table mapping the offset of a directive to the annotations associated
   * with that directive, or `null` if none of the annotations in the
   * compilation unit have annotations.
   */
  Map<int, List<ElementAnnotation>> annotationMap = null;

  /**
   * A list containing all of the top-level accessors (getters and setters)
   * contained in this compilation unit.
   */
  List<PropertyAccessorElement> _accessors;

  /**
   * A list containing all of the enums contained in this compilation unit.
   */
  List<ClassElement> _enums;

  /**
   * A list containing all of the top-level functions contained in this
   * compilation unit.
   */
  List<FunctionElement> _functions;

  /**
   * A list containing all of the function type aliases contained in this
   * compilation unit.
   */
  List<FunctionTypeAliasElement> _typeAliases;

  /**
   * A list containing all of the types contained in this compilation unit.
   */
  List<ClassElement> _types;

  /**
   * A list containing all of the variables contained in this compilation unit.
   */
  List<TopLevelVariableElement> _variables;

  /**
   * A map from offsets to elements of this unit at these offsets.
   */
  final Map<int, Element> _offsetToElementMap = new HashMap<int, Element>();

  /**
   * Resynthesized explicit top-level property accessors.
   */
  UnitExplicitTopLevelAccessors _explicitTopLevelAccessors;

  /**
   * Resynthesized explicit top-level variables.
   */
  UnitExplicitTopLevelVariables _explicitTopLevelVariables;

  /**
   * Description of top-level variable replacements that should be applied
   * to implicit top-level variables because of re-linking top-level property
   * accessors between different unit of the same library.
   */
  Map<TopLevelVariableElement, TopLevelVariableElement>
      _topLevelVariableReplaceMap;

  /**
   * Initialize a newly created compilation unit element to have the given
   * [name].
   */
  CompilationUnitElementImpl(String name)
      : resynthesizerContext = null,
        _unlinkedUnit = null,
        _unlinkedPart = null,
        super(name, -1);

  /**
   * Initialize using the given serialized information.
   */
  CompilationUnitElementImpl.forSerialized(
      LibraryElementImpl enclosingLibrary,
      this.resynthesizerContext,
      this._unlinkedUnit,
      this._unlinkedPart,
      String name)
      : super.forSerialized(null) {
    _enclosingElement = enclosingLibrary;
    _name = name;
    _nameOffset = -1;
  }

  @override
  List<PropertyAccessorElement> get accessors {
    if (_unlinkedUnit != null) {
      if (_accessors == null) {
        _explicitTopLevelAccessors ??=
            resynthesizerContext.buildTopLevelAccessors();
        _explicitTopLevelVariables ??=
            resynthesizerContext.buildTopLevelVariables();
        List<PropertyAccessorElementImpl> accessors =
            <PropertyAccessorElementImpl>[];
        accessors.addAll(_explicitTopLevelAccessors.accessors);
        accessors.addAll(_explicitTopLevelVariables.implicitAccessors);
        _accessors = accessors;
      }
    }
    return _accessors ?? PropertyAccessorElement.EMPTY_LIST;
  }

  /**
   * Set the top-level accessors (getters and setters) contained in this
   * compilation unit to the given [accessors].
   */
  void set accessors(List<PropertyAccessorElement> accessors) {
    for (PropertyAccessorElement accessor in accessors) {
      (accessor as PropertyAccessorElementImpl).enclosingElement = this;
    }
    this._accessors = accessors;
  }

  @override
  int get codeLength {
    if (_unlinkedUnit != null) {
      return _unlinkedUnit.codeRange?.length;
    }
    return super.codeLength;
  }

  @override
  int get codeOffset {
    if (_unlinkedUnit != null) {
      return _unlinkedUnit.codeRange?.offset;
    }
    return super.codeOffset;
  }

  @override
  LibraryElement get enclosingElement =>
      super.enclosingElement as LibraryElement;

  @override
  CompilationUnitElementImpl get enclosingUnit {
    return this;
  }

  @override
  List<ClassElement> get enums {
    if (_unlinkedUnit != null) {
      _enums ??= _unlinkedUnit.enums
          .map((e) => new EnumElementImpl.forSerialized(e, this))
          .toList(growable: false);
    }
    return _enums ?? const <ClassElement>[];
  }

  /**
   * Set the enums contained in this compilation unit to the given [enums].
   */
  void set enums(List<ClassElement> enums) {
    assert(_unlinkedUnit == null);
    for (ClassElement enumDeclaration in enums) {
      (enumDeclaration as EnumElementImpl).enclosingElement = this;
    }
    this._enums = enums;
  }

  @override
  List<FunctionElement> get functions {
    if (_unlinkedUnit != null) {
      _functions ??= _unlinkedUnit.executables
          .where((e) => e.kind == UnlinkedExecutableKind.functionOrMethod)
          .map((e) => new FunctionElementImpl.forSerialized(e, this))
          .toList(growable: false);
    }
    return _functions ?? const <FunctionElement>[];
  }

  /**
   * Set the top-level functions contained in this compilation unit to the given
   * [functions].
   */
  void set functions(List<FunctionElement> functions) {
    for (FunctionElement function in functions) {
      (function as FunctionElementImpl).enclosingElement = this;
    }
    this._functions = functions;
  }

  @override
  List<FunctionTypeAliasElement> get functionTypeAliases {
    if (_unlinkedUnit != null) {
      _typeAliases ??= _unlinkedUnit.typedefs
          .map((t) => new FunctionTypeAliasElementImpl.forSerialized(t, this))
          .toList(growable: false);
    }
    return _typeAliases ?? const <FunctionTypeAliasElement>[];
  }

  @override
  int get hashCode => source.hashCode;

  @override
  bool get hasLoadLibraryFunction {
    List<FunctionElement> functions = this.functions;
    for (int i = 0; i < functions.length; i++) {
      if (functions[i].name == FunctionElement.LOAD_LIBRARY_NAME) {
        return true;
      }
    }
    return false;
  }

  @override
  String get identifier => source.encoding;

  @override
  ElementKind get kind => ElementKind.COMPILATION_UNIT;

  @override
  List<ElementAnnotation> get metadata {
    if (_unlinkedPart != null) {
      return _metadata ??= _buildAnnotations(
          library.definingCompilationUnit as CompilationUnitElementImpl,
          _unlinkedPart.annotations);
    }
    return super.metadata;
  }

  @override
  List<TopLevelVariableElement> get topLevelVariables {
    if (_unlinkedUnit != null) {
      if (_variables == null) {
        _explicitTopLevelAccessors ??=
            resynthesizerContext.buildTopLevelAccessors();
        _explicitTopLevelVariables ??=
            resynthesizerContext.buildTopLevelVariables();
        List<TopLevelVariableElementImpl> variables =
            <TopLevelVariableElementImpl>[];
        variables.addAll(_explicitTopLevelVariables.variables);
        variables.addAll(_explicitTopLevelAccessors.implicitVariables);
        // Ensure that getters and setters in different units use
        // the same top-level variables.
        (enclosingElement as LibraryElementImpl)
            .resynthesizerContext
            .patchTopLevelAccessors();
        _variables = variables;
        _topLevelVariableReplaceMap?.forEach((from, to) {
          int index = _variables.indexOf(from);
          _variables[index] = to;
        });
        _topLevelVariableReplaceMap = null;
      }
    }
    return _variables ?? TopLevelVariableElement.EMPTY_LIST;
  }

  /**
   * Set the top-level variables contained in this compilation unit to the given
   * [variables].
   */
  void set topLevelVariables(List<TopLevelVariableElement> variables) {
    assert(!isResynthesized);
    for (TopLevelVariableElement field in variables) {
      (field as TopLevelVariableElementImpl).enclosingElement = this;
    }
    this._variables = variables;
  }

  /**
   * Set the function type aliases contained in this compilation unit to the
   * given [typeAliases].
   */
  void set typeAliases(List<FunctionTypeAliasElement> typeAliases) {
    assert(_unlinkedUnit == null);
    for (FunctionTypeAliasElement typeAlias in typeAliases) {
      (typeAlias as FunctionTypeAliasElementImpl).enclosingElement = this;
    }
    this._typeAliases = typeAliases;
  }

  @override
  TypeParameterizedElementMixin get typeParameterContext => null;

  @override
  List<ClassElement> get types {
    if (_unlinkedUnit != null) {
      _types ??= _unlinkedUnit.classes
          .map((c) => new ClassElementImpl.forSerialized(c, this))
          .toList(growable: false);
    }
    return _types ?? const <ClassElement>[];
  }

  /**
   * Set the types contained in this compilation unit to the given [types].
   */
  void set types(List<ClassElement> types) {
    assert(_unlinkedUnit == null);
    for (ClassElement type in types) {
      // Another implementation of ClassElement is _DeferredClassElement,
      // which is used to resynthesize classes lazily. We cannot cast it
      // to ClassElementImpl, and it already can provide correct values of the
      // 'enclosingElement' property.
      if (type is ClassElementImpl) {
        type.enclosingElement = this;
      }
    }
    this._types = types;
  }

  @override
  bool operator ==(Object object) =>
      object is CompilationUnitElementImpl && source == object.source;

  @override
  accept(ElementVisitor visitor) => visitor.visitCompilationUnitElement(this);

  /**
   * This method is invoked after this unit was incrementally resolved.
   */
  void afterIncrementalResolution() {
    _offsetToElementMap.clear();
  }

  @override
  void appendTo(StringBuffer buffer) {
    if (source == null) {
      buffer.write("{compilation unit}");
    } else {
      buffer.write(source.fullName);
    }
  }

  @override
  CompilationUnit computeNode() => unit;

  /**
   * Return the annotations associated with the directive at the given [offset],
   * or an empty list if the directive has no annotations or if there is no
   * directive at the given offset.
   */
  List<ElementAnnotation> getAnnotations(int offset) {
    if (annotationMap == null) {
      return const <ElementAnnotation>[];
    }
    return annotationMap[offset] ?? const <ElementAnnotation>[];
  }

  @override
  ElementImpl getChild(String identifier) {
    //
    // The casts in this method are safe because the set methods would have
    // thrown a CCE if any of the elements in the arrays were not of the
    // expected types.
    //
    for (PropertyAccessorElement accessor in accessors) {
      PropertyAccessorElementImpl accessorImpl = accessor;
      if (accessorImpl.identifier == identifier) {
        return accessorImpl;
      }
    }
    for (TopLevelVariableElement variable in topLevelVariables) {
      TopLevelVariableElementImpl variableImpl = variable;
      if (variableImpl.identifier == identifier) {
        return variableImpl;
      }
    }
    for (FunctionElement function in functions) {
      FunctionElementImpl functionImpl = function;
      if (functionImpl.identifier == identifier) {
        return functionImpl;
      }
    }
    for (FunctionTypeAliasElement typeAlias in functionTypeAliases) {
      FunctionTypeAliasElementImpl typeAliasImpl = typeAlias;
      if (typeAliasImpl.identifier == identifier) {
        return typeAliasImpl;
      }
    }
    for (ClassElement type in types) {
      ClassElementImpl typeImpl = type;
      if (typeImpl.name == identifier) {
        return typeImpl;
      }
    }
    for (ClassElement type in _enums) {
      EnumElementImpl typeImpl = type;
      if (typeImpl.identifier == identifier) {
        return typeImpl;
      }
    }
    return null;
  }

  @override
  Element getElementAt(int offset) {
    if (_offsetToElementMap.isEmpty) {
      accept(new _BuildOffsetToElementMap(_offsetToElementMap));
    }
    return _offsetToElementMap[offset];
  }

  @override
  ClassElement getEnum(String enumName) {
    for (ClassElement enumDeclaration in _enums) {
      if (enumDeclaration.name == enumName) {
        return enumDeclaration;
      }
    }
    return null;
  }

  @override
  ClassElement getType(String className) {
    for (ClassElement type in types) {
      if (type.name == className) {
        return type;
      }
    }
    return null;
  }

  /**
   * Replace the given [from] top-level variable with [to] in this compilation unit.
   */
  void replaceTopLevelVariable(
      TopLevelVariableElement from, TopLevelVariableElement to) {
    if (_unlinkedUnit != null) {
      // Getters and setter in different units should be patched to use the
      // same variables before these variables were asked and returned.
      assert(_variables == null);
      _topLevelVariableReplaceMap ??=
          <TopLevelVariableElement, TopLevelVariableElement>{};
      _topLevelVariableReplaceMap[from] = to;
    } else {
      int index = _variables.indexOf(from);
      _variables[index] = to;
    }
  }

  /**
   * Set the annotations associated with the directive at the given [offset] to
   * the given list of [annotations].
   */
  void setAnnotations(int offset, List<ElementAnnotation> annotations) {
    annotationMap ??= new HashMap<int, List<ElementAnnotation>>();
    annotationMap[offset] = annotations;
  }

  @override
  void visitChildren(ElementVisitor visitor) {
    super.visitChildren(visitor);
    safelyVisitChildren(accessors, visitor);
    safelyVisitChildren(_enums, visitor);
    safelyVisitChildren(functions, visitor);
    safelyVisitChildren(functionTypeAliases, visitor);
    safelyVisitChildren(types, visitor);
    safelyVisitChildren(topLevelVariables, visitor);
  }
}

/**
 * A [FieldElement] for a 'const' or 'final' field that has an initializer.
 *
 * TODO(paulberry): we should rename this class to reflect the fact that it's
 * used for both const and final fields.  However, we shouldn't do so until
 * we've created an API for reading the values of constants; until that API is
 * available, clients are likely to read constant values by casting to
 * ConstFieldElementImpl, so it would be a breaking change to rename this
 * class.
 */
class ConstFieldElementImpl extends FieldElementImpl with ConstVariableElement {
  /**
   * Initialize a newly created synthetic field element to have the given
   * [name] and [offset].
   */
  ConstFieldElementImpl(String name, int offset) : super(name, offset);

  /**
   * Initialize a newly created field element to have the given [name].
   */
  ConstFieldElementImpl.forNode(Identifier name) : super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  ConstFieldElementImpl.forSerialized(
      UnlinkedVariable unlinkedVariable, ElementImpl enclosingElement)
      : super.forSerialized(unlinkedVariable, enclosingElement);
}

/**
 * A field element representing an enum constant.
 */
class ConstFieldElementImpl_EnumValue extends ConstFieldElementImpl_ofEnum {
  final UnlinkedEnumValue _unlinkedEnumValue;
  final int _index;

  ConstFieldElementImpl_EnumValue(
      EnumElementImpl enumElement, this._unlinkedEnumValue, this._index)
      : super(enumElement);

  @override
  String get documentationComment {
    if (_unlinkedEnumValue != null) {
      return _unlinkedEnumValue?.documentationComment?.text;
    }
    return super.documentationComment;
  }

  @override
  EvaluationResultImpl get evaluationResult {
    if (_evaluationResult == null) {
      Map<String, DartObjectImpl> fieldMap = <String, DartObjectImpl>{
        name: new DartObjectImpl(
            context.typeProvider.intType, new IntState(_index))
      };
      DartObjectImpl value =
          new DartObjectImpl(type, new GenericState(fieldMap));
      _evaluationResult = new EvaluationResultImpl(value);
    }
    return _evaluationResult;
  }

  @override
  String get name {
    if (_unlinkedEnumValue != null) {
      return _unlinkedEnumValue.name;
    }
    return super.name;
  }

  @override
  int get nameOffset {
    if (_unlinkedEnumValue != null) {
      return _unlinkedEnumValue.nameOffset;
    }
    return super.nameOffset;
  }

  @override
  InterfaceType get type => _enum.type;
}

/**
 * The synthetic `values` field of an enum.
 */
class ConstFieldElementImpl_EnumValues extends ConstFieldElementImpl_ofEnum {
  ConstFieldElementImpl_EnumValues(EnumElementImpl enumElement)
      : super(enumElement) {
    synthetic = true;
  }

  @override
  EvaluationResultImpl get evaluationResult {
    if (_evaluationResult == null) {
      List<DartObjectImpl> constantValues = <DartObjectImpl>[];
      for (FieldElement field in _enum.fields) {
        if (field is ConstFieldElementImpl_EnumValue) {
          constantValues.add(field.evaluationResult.value);
        }
      }
      _evaluationResult = new EvaluationResultImpl(
          new DartObjectImpl(type, new ListState(constantValues)));
    }
    return _evaluationResult;
  }

  @override
  String get name => 'values';

  @override
  InterfaceType get type {
    if (_type == null) {
      InterfaceType listType = context.typeProvider.listType;
      return _type = listType.instantiate(<DartType>[_enum.type]);
    }
    return _type;
  }
}

/**
 * An abstract constant field of an enum.
 */
abstract class ConstFieldElementImpl_ofEnum extends ConstFieldElementImpl {
  final EnumElementImpl _enum;

  ConstFieldElementImpl_ofEnum(this._enum) : super(null, -1) {
    enclosingElement = _enum;
  }

  @override
  void set const3(bool isConst) {
    assert(false);
  }

  @override
  void set evaluationResult(_) {
    assert(false);
  }

  @override
  void set final2(bool isFinal) {
    assert(false);
  }

  @override
  bool get isConst => true;

  @override
  bool get isStatic => true;

  @override
  void set static(bool isStatic) {
    assert(false);
  }

  void set type(DartType type) {
    assert(false);
  }
}

/**
 * A [LocalVariableElement] for a local 'const' variable that has an
 * initializer.
 */
class ConstLocalVariableElementImpl extends LocalVariableElementImpl
    with ConstVariableElement {
  /**
   * Initialize a newly created local variable element to have the given [name]
   * and [offset].
   */
  ConstLocalVariableElementImpl(String name, int offset) : super(name, offset);

  /**
   * Initialize a newly created local variable element to have the given [name].
   */
  ConstLocalVariableElementImpl.forNode(Identifier name) : super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  ConstLocalVariableElementImpl.forSerialized(UnlinkedVariable unlinkedVariable,
      ExecutableElementImpl enclosingExecutable)
      : super.forSerialized(unlinkedVariable, enclosingExecutable);
}

/**
 * A concrete implementation of a [ConstructorElement].
 */
class ConstructorElementImpl extends ExecutableElementImpl
    implements ConstructorElement {
  /**
   * The constructor to which this constructor is redirecting.
   */
  ConstructorElement _redirectedConstructor;

  /**
   * The initializers for this constructor (used for evaluating constant
   * instance creation expressions).
   */
  List<ConstructorInitializer> _constantInitializers;

  /**
   * The offset of the `.` before this constructor name or `null` if not named.
   */
  int _periodOffset;

  /**
   * Return the offset of the character immediately following the last character
   * of this constructor's name, or `null` if not named.
   */
  int _nameEnd;

  /**
   * True if this constructor has been found by constant evaluation to be free
   * of redirect cycles, and is thus safe to evaluate.
   */
  bool _isCycleFree = false;

  /**
   * Initialize a newly created constructor element to have the given [name] and
   * [offset].
   */
  ConstructorElementImpl(String name, int offset) : super(name, offset);

  /**
   * Initialize a newly created constructor element to have the given [name].
   */
  ConstructorElementImpl.forNode(Identifier name) : super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  ConstructorElementImpl.forSerialized(
      UnlinkedExecutable serializedExecutable, ClassElementImpl enclosingClass)
      : super.forSerialized(serializedExecutable, enclosingClass);

  /**
   * Set whether this constructor represents a 'const' constructor.
   */
  void set const2(bool isConst) {
    assert(serializedExecutable == null);
    setModifier(Modifier.CONST, isConst);
  }

  List<ConstructorInitializer> get constantInitializers {
    if (serializedExecutable != null && _constantInitializers == null) {
      _constantInitializers ??= serializedExecutable.constantInitializers
          .map((i) => _buildConstructorInitializer(i))
          .toList(growable: false);
    }
    return _constantInitializers;
  }

  void set constantInitializers(
      List<ConstructorInitializer> constantInitializers) {
    assert(serializedExecutable == null);
    _constantInitializers = constantInitializers;
  }

  @override
  ClassElementImpl get enclosingElement =>
      super.enclosingElement as ClassElementImpl;

  @override
  TypeParameterizedElementMixin get enclosingTypeParameterContext =>
      super.enclosingElement as ClassElementImpl;

  /**
   * Set whether this constructor represents a factory method.
   */
  void set factory(bool isFactory) {
    assert(serializedExecutable == null);
    setModifier(Modifier.FACTORY, isFactory);
  }

  @override
  bool get isConst {
    if (serializedExecutable != null) {
      return serializedExecutable.isConst;
    }
    return hasModifier(Modifier.CONST);
  }

  bool get isCycleFree {
    if (serializedExecutable != null) {
      return serializedExecutable.isConst &&
          !enclosingUnit.resynthesizerContext
              .isInConstCycle(serializedExecutable.constCycleSlot);
    }
    return _isCycleFree;
  }

  void set isCycleFree(bool isCycleFree) {
    // This property is updated in ConstantEvaluationEngine even for
    // resynthesized constructors, so we don't have the usual assert here.
    _isCycleFree = isCycleFree;
  }

  @override
  bool get isDefaultConstructor {
    // unnamed
    String name = this.name;
    if (name != null && name.length != 0) {
      return false;
    }
    // no required parameters
    for (ParameterElement parameter in parameters) {
      if (parameter.parameterKind == ParameterKind.REQUIRED) {
        return false;
      }
    }
    // OK, can be used as default constructor
    return true;
  }

  @override
  bool get isFactory {
    if (serializedExecutable != null) {
      return serializedExecutable.isFactory;
    }
    return hasModifier(Modifier.FACTORY);
  }

  @override
  bool get isStatic => false;

  @override
  ElementKind get kind => ElementKind.CONSTRUCTOR;

  @override
  int get nameEnd {
    if (serializedExecutable != null) {
      if (serializedExecutable.name.isNotEmpty) {
        return serializedExecutable.nameEnd;
      } else {
        return serializedExecutable.nameOffset + enclosingElement.name.length;
      }
    }
    return _nameEnd;
  }

  void set nameEnd(int nameEnd) {
    assert(serializedExecutable == null);
    _nameEnd = nameEnd;
  }

  @override
  int get periodOffset {
    if (serializedExecutable != null) {
      if (serializedExecutable.name.isNotEmpty) {
        return serializedExecutable.periodOffset;
      }
    }
    return _periodOffset;
  }

  void set periodOffset(int periodOffset) {
    assert(serializedExecutable == null);
    _periodOffset = periodOffset;
  }

  @override
  ConstructorElement get redirectedConstructor {
    if (serializedExecutable != null && _redirectedConstructor == null) {
      if (serializedExecutable.isRedirectedConstructor) {
        if (serializedExecutable.isFactory) {
          _redirectedConstructor = enclosingUnit.resynthesizerContext
              .resolveConstructorRef(
                  enclosingElement, serializedExecutable.redirectedConstructor);
        } else {
          _redirectedConstructor = enclosingElement.getNamedConstructor(
              serializedExecutable.redirectedConstructorName);
        }
      } else {
        return null;
      }
    }
    return _redirectedConstructor;
  }

  void set redirectedConstructor(ConstructorElement redirectedConstructor) {
    assert(serializedExecutable == null);
    _redirectedConstructor = redirectedConstructor;
  }

  @override
  DartType get returnType => enclosingElement.type;

  void set returnType(DartType returnType) {
    assert(false);
  }

  @override
  FunctionType get type {
    return _type ??= new FunctionTypeImpl(this);
  }

  void set type(FunctionType type) {
    assert(false);
  }

  @override
  accept(ElementVisitor visitor) => visitor.visitConstructorElement(this);

  @override
  void appendTo(StringBuffer buffer) {
    if (enclosingElement == null) {
      String message;
      String name = displayName;
      if (name != null && !name.isEmpty) {
        message =
            'Found constructor element named $name with no enclosing element';
      } else {
        message = 'Found unnamed constructor element with no enclosing element';
      }
      AnalysisEngine.instance.logger.logError(message);
      buffer.write('<unknown class>');
    } else {
      buffer.write(enclosingElement.displayName);
    }
    String name = displayName;
    if (name != null && !name.isEmpty) {
      buffer.write(".");
      buffer.write(name);
    }
    super.appendTo(buffer);
  }

  @override
  ConstructorDeclaration computeNode() =>
      getNodeMatching((node) => node is ConstructorDeclaration);

  /**
   * Resynthesize the AST for the given serialized constructor initializer.
   */
  ConstructorInitializer _buildConstructorInitializer(
      UnlinkedConstructorInitializer serialized) {
    UnlinkedConstructorInitializerKind kind = serialized.kind;
    String name = serialized.name;
    List<Expression> arguments = <Expression>[];
    {
      int numArguments = serialized.arguments.length;
      int numNames = serialized.argumentNames.length;
      for (int i = 0; i < numArguments; i++) {
        Expression expression = enclosingUnit.resynthesizerContext
            .buildExpression(this, serialized.arguments[i]);
        int nameIndex = numNames + i - numArguments;
        if (nameIndex >= 0) {
          expression = AstFactory.namedExpression2(
              serialized.argumentNames[nameIndex], expression);
        }
        arguments.add(expression);
      }
    }
    switch (kind) {
      case UnlinkedConstructorInitializerKind.field:
        ConstructorFieldInitializer initializer =
            AstFactory.constructorFieldInitializer(
                false,
                name,
                enclosingUnit.resynthesizerContext
                    .buildExpression(this, serialized.expression));
        initializer.fieldName.staticElement = enclosingElement.getField(name);
        return initializer;
      case UnlinkedConstructorInitializerKind.superInvocation:
        SuperConstructorInvocation initializer =
            AstFactory.superConstructorInvocation2(
                name.isNotEmpty ? name : null, arguments);
        ClassElement superElement = enclosingElement.supertype.element;
        ConstructorElement element = name.isEmpty
            ? superElement.unnamedConstructor
            : superElement.getNamedConstructor(name);
        initializer.staticElement = element;
        initializer.constructorName?.staticElement = element;
        return initializer;
      case UnlinkedConstructorInitializerKind.thisInvocation:
        RedirectingConstructorInvocation initializer =
            AstFactory.redirectingConstructorInvocation2(
                name.isNotEmpty ? name : null, arguments);
        ConstructorElement element = name.isEmpty
            ? enclosingElement.unnamedConstructor
            : enclosingElement.getNamedConstructor(name);
        initializer.staticElement = element;
        initializer.constructorName?.staticElement = element;
        return initializer;
    }
    return null;
  }
}

/**
 * A [TopLevelVariableElement] for a top-level 'const' variable that has an
 * initializer.
 */
class ConstTopLevelVariableElementImpl extends TopLevelVariableElementImpl
    with ConstVariableElement {
  /**
   * Initialize a newly created synthetic top-level variable element to have the
   * given [name] and [offset].
   */
  ConstTopLevelVariableElementImpl(String name, int offset)
      : super(name, offset);

  /**
   * Initialize a newly created top-level variable element to have the given
   * [name].
   */
  ConstTopLevelVariableElementImpl.forNode(Identifier name)
      : super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  ConstTopLevelVariableElementImpl.forSerialized(
      UnlinkedVariable unlinkedVariable, ElementImpl enclosingElement)
      : super.forSerialized(unlinkedVariable, enclosingElement);
}

/**
 * Mixin used by elements that represent constant variables and have
 * initializers.
 *
 * Note that in correct Dart code, all constant variables must have
 * initializers.  However, analyzer also needs to handle incorrect Dart code,
 * in which case there might be some constant variables that lack initializers.
 * This interface is only used for constant variables that have initializers.
 *
 * This class is not intended to be part of the public API for analyzer.
 */
abstract class ConstVariableElement
    implements ElementImpl, ConstantEvaluationTarget {
  /**
   * If this element represents a constant variable, and it has an initializer,
   * a copy of the initializer for the constant.  Otherwise `null`.
   *
   * Note that in correct Dart code, all constant variables must have
   * initializers.  However, analyzer also needs to handle incorrect Dart code,
   * in which case there might be some constant variables that lack
   * initializers.
   */
  Expression _constantInitializer;

  EvaluationResultImpl _evaluationResult;

  Expression get constantInitializer {
    if (_constantInitializer == null && _unlinkedConst != null) {
      _constantInitializer = enclosingUnit.resynthesizerContext
          .buildExpression(this, _unlinkedConst);
    }
    return _constantInitializer;
  }

  void set constantInitializer(Expression constantInitializer) {
    assert(_unlinkedConst == null);
    _constantInitializer = constantInitializer;
  }

  EvaluationResultImpl get evaluationResult => _evaluationResult;

  void set evaluationResult(EvaluationResultImpl evaluationResult) {
    _evaluationResult = evaluationResult;
  }

  /**
   * If this element is resynthesized from the summary, return the unlinked
   * initializer, otherwise return `null`.
   */
  UnlinkedConst get _unlinkedConst;

  /**
   * Return a representation of the value of this variable, forcing the value
   * to be computed if it had not previously been computed, or `null` if either
   * this variable was not declared with the 'const' modifier or if the value of
   * this variable could not be computed because of errors.
   */
  DartObject computeConstantValue() {
    if (evaluationResult == null) {
      context?.computeResult(this, CONSTANT_VALUE);
    }
    return evaluationResult?.value;
  }
}

/**
 * A [FieldFormalParameterElementImpl] for parameters that have an initializer.
 */
class DefaultFieldFormalParameterElementImpl
    extends FieldFormalParameterElementImpl with ConstVariableElement {
  /**
   * Initialize a newly created parameter element to have the given [name] and
   * [nameOffset].
   */
  DefaultFieldFormalParameterElementImpl(String name, int nameOffset)
      : super(name, nameOffset);

  /**
   * Initialize a newly created parameter element to have the given [name].
   */
  DefaultFieldFormalParameterElementImpl.forNode(Identifier name)
      : super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  DefaultFieldFormalParameterElementImpl.forSerialized(
      UnlinkedParam unlinkedParam, ElementImpl enclosingElement)
      : super.forSerialized(unlinkedParam, enclosingElement);
}

/**
 * A [ParameterElement] for parameters that have an initializer.
 */
class DefaultParameterElementImpl extends ParameterElementImpl
    with ConstVariableElement {
  /**
   * Initialize a newly created parameter element to have the given [name] and
   * [nameOffset].
   */
  DefaultParameterElementImpl(String name, int nameOffset)
      : super(name, nameOffset);

  /**
   * Initialize a newly created parameter element to have the given [name].
   */
  DefaultParameterElementImpl.forNode(Identifier name) : super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  DefaultParameterElementImpl.forSerialized(
      UnlinkedParam unlinkedParam, ElementImpl enclosingElement)
      : super.forSerialized(unlinkedParam, enclosingElement);

  @override
  DefaultFormalParameter computeNode() =>
      getNodeMatching((node) => node is DefaultFormalParameter);
}

/**
 * The synthetic element representing the declaration of the type `dynamic`.
 */
class DynamicElementImpl extends ElementImpl implements TypeDefiningElement {
  /**
   * Return the unique instance of this class.
   */
  static DynamicElementImpl get instance =>
      DynamicTypeImpl.instance.element as DynamicElementImpl;

  @override
  DynamicTypeImpl type;

  /**
   * Initialize a newly created instance of this class. Instances of this class
   * should <b>not</b> be created except as part of creating the type associated
   * with this element. The single instance of this class should be accessed
   * through the method [instance].
   */
  DynamicElementImpl() : super(Keyword.DYNAMIC.syntax, -1) {
    setModifier(Modifier.SYNTHETIC, true);
  }

  @override
  ElementKind get kind => ElementKind.DYNAMIC;

  @override
  accept(ElementVisitor visitor) => null;
}

/**
 * A concrete implementation of an [ElementAnnotation].
 */
class ElementAnnotationImpl implements ElementAnnotation {
  /**
   * The name of the top-level variable used to mark a method parameter as
   * covariant.
   */
  static String _COVARIANT_VARIABLE_NAME = "checked";

  /**
   * The name of the class used to mark an element as being deprecated.
   */
  static String _DEPRECATED_CLASS_NAME = "Deprecated";

  /**
   * The name of the top-level variable used to mark an element as being
   * deprecated.
   */
  static String _DEPRECATED_VARIABLE_NAME = "deprecated";

  /**
   * The name of the top-level variable used to mark a method as being a
   * factory.
   */
  static String _FACTORY_VARIABLE_NAME = "factory";

  /**
   * The name of the class used to JS annotate an element.
   */
  static String _JS_CLASS_NAME = "JS";

  /**
   * The name of `js` library, used to define JS annotations.
   */
  static String _JS_LIB_NAME = "js";

  /**
   * The name of `meta` library, used to define analysis annotations.
   */
  static String _META_LIB_NAME = "meta";

  /**
   * The name of the top-level variable used to mark a method as requiring
   * overriders to call super.
   */
  static String _MUST_CALL_SUPER_VARIABLE_NAME = "mustCallSuper";

  /**
   * The name of the top-level variable used to mark a method as being expected
   * to override an inherited method.
   */
  static String _OVERRIDE_VARIABLE_NAME = "override";

  /**
   * The name of the top-level variable used to mark a method as being
   * protected.
   */
  static String _PROTECTED_VARIABLE_NAME = "protected";

  /**
   * The name of the top-level variable used to mark a class as implementing a
   * proxy object.
   */
  static String PROXY_VARIABLE_NAME = "proxy";

  /**
   * The name of the class used to mark a parameter as being required.
   */
  static String _REQUIRED_CLASS_NAME = "Required";

  /**
   * The name of the top-level variable used to mark a parameter as being
   * required.
   */
  static String _REQUIRED_VARIABLE_NAME = "required";

  /**
   * The name of the top-level variable used to mark a member as intended to be
   * overridden.
   */
  static String _VIRTUAL_VARIABLE_NAME = "virtual";

  /**
   * The element representing the field, variable, or constructor being used as
   * an annotation.
   */
  Element element;

  /**
   * The compilation unit in which this annotation appears.
   */
  CompilationUnitElementImpl compilationUnit;

  /**
   * The AST of the annotation itself, cloned from the resolved AST for the
   * source code.
   */
  Annotation annotationAst;

  /**
   * The result of evaluating this annotation as a compile-time constant
   * expression, or `null` if the compilation unit containing the variable has
   * not been resolved.
   */
  EvaluationResultImpl evaluationResult;

  /**
   * Initialize a newly created annotation. The given [compilationUnit] is the
   * compilation unit in which the annotation appears.
   */
  ElementAnnotationImpl(this.compilationUnit);

  @override
  DartObject get constantValue => evaluationResult?.value;

  @override
  AnalysisContext get context => compilationUnit.library.context;

  /**
   * Return `true` if this annotation marks the associated parameter as being
   * covariant, meaning it is allowed to have a narrower type in an override.
   */
  bool get isCovariant =>
      element is PropertyAccessorElement &&
      element.name == _COVARIANT_VARIABLE_NAME &&
      element.library?.name == _META_LIB_NAME;

  @override
  bool get isDeprecated {
    if (element?.library?.isDartCore == true) {
      if (element is ConstructorElement) {
        return element.enclosingElement.name == _DEPRECATED_CLASS_NAME;
      } else if (element is PropertyAccessorElement) {
        return element.name == _DEPRECATED_VARIABLE_NAME;
      }
    }
    return false;
  }

  @override
  bool get isFactory =>
      element is PropertyAccessorElement &&
      element.name == _FACTORY_VARIABLE_NAME &&
      element.library?.name == _META_LIB_NAME;

  @override
  bool get isJS =>
      element is ConstructorElement &&
      element.enclosingElement.name == _JS_CLASS_NAME &&
      element.library?.name == _JS_LIB_NAME;

  @override
  bool get isMustCallSuper =>
      element is PropertyAccessorElement &&
      element.name == _MUST_CALL_SUPER_VARIABLE_NAME &&
      element.library?.name == _META_LIB_NAME;

  @override
  bool get isOverride =>
      element is PropertyAccessorElement &&
      element.name == _OVERRIDE_VARIABLE_NAME &&
      element.library?.isDartCore == true;

  @override
  bool get isProtected =>
      element is PropertyAccessorElement &&
      element.name == _PROTECTED_VARIABLE_NAME &&
      element.library?.name == _META_LIB_NAME;

  @override
  bool get isProxy =>
      element is PropertyAccessorElement &&
      element.name == PROXY_VARIABLE_NAME &&
      element.library?.isDartCore == true;

  @override
  bool get isRequired =>
      element is ConstructorElement &&
          element.enclosingElement.name == _REQUIRED_CLASS_NAME &&
          element.library?.name == _META_LIB_NAME ||
      element is PropertyAccessorElement &&
          element.name == _REQUIRED_VARIABLE_NAME &&
          element.library?.name == _META_LIB_NAME;

  /**
   * Return `true` if this annotation marks the associated member as supporting
   * overrides.
   *
   * This is currently used by fields in Strong Mode, as other members are
   * already virtual-by-default.
   */
  bool get isVirtual =>
      element is PropertyAccessorElement &&
      element.name == _VIRTUAL_VARIABLE_NAME &&
      element.library?.name == _META_LIB_NAME;

  /**
   * Get the library containing this annotation.
   */
  Source get librarySource => compilationUnit.librarySource;

  @override
  Source get source => compilationUnit.source;

  @override
  DartObject computeConstantValue() {
    if (evaluationResult == null) {
      context?.computeResult(this, CONSTANT_VALUE);
    }
    return constantValue;
  }

  @override
  String toString() => '@$element';
}

/**
 * A base class for concrete implementations of an [Element].
 */
abstract class ElementImpl implements Element {
  /**
   * An Unicode right arrow.
   */
  static final String RIGHT_ARROW = " \u2192 ";

  static int _NEXT_ID = 0;

  final int id = _NEXT_ID++;

  /**
   * The enclosing element of this element, or `null` if this element is at the
   * root of the element structure.
   */
  ElementImpl _enclosingElement;

  /**
   * The name of this element.
   */
  String _name;

  /**
   * The offset of the name of this element in the file that contains the
   * declaration of this element.
   */
  int _nameOffset = 0;

  /**
   * A bit-encoded form of the modifiers associated with this element.
   */
  int _modifiers = 0;

  /**
   * A list containing all of the metadata associated with this element.
   */
  List<ElementAnnotation> _metadata;

  /**
   * A cached copy of the calculated hashCode for this element.
   */
  int _cachedHashCode;

  /**
   * A cached copy of the calculated location for this element.
   */
  ElementLocation _cachedLocation;

  /**
   * The documentation comment for this element.
   */
  String _docComment;

  /**
   * The offset of the beginning of the element's code in the file that contains
   * the element, or `null` if the element is synthetic.
   */
  int _codeOffset;

  /**
   * The length of the element's code, or `null` if the element is synthetic.
   */
  int _codeLength;

  /**
   * Initialize a newly created element to have the given [name] at the given
   * [_nameOffset].
   */
  ElementImpl(String name, this._nameOffset) {
    this._name = StringUtilities.intern(name);
  }

  /**
   * Initialize a newly created element to have the given [name].
   */
  ElementImpl.forNode(Identifier name)
      : this(name == null ? "" : name.name, name == null ? -1 : name.offset);

  /**
   * Initialize from serialized information.
   */
  ElementImpl.forSerialized(this._enclosingElement);

  /**
   * The length of the element's code, or `null` if the element is synthetic.
   */
  int get codeLength => _codeLength;

  /**
   * The offset of the beginning of the element's code in the file that contains
   * the element, or `null` if the element is synthetic.
   */
  int get codeOffset => _codeOffset;

  @override
  AnalysisContext get context {
    if (_enclosingElement == null) {
      return null;
    }
    return _enclosingElement.context;
  }

  @override
  String get displayName => _name;

  @override
  String get documentationComment => _docComment;

  /**
   * The documentation comment source for this element.
   */
  void set documentationComment(String doc) {
    assert(!isResynthesized);
    _docComment = doc?.replaceAll('\r\n', '\n');
  }

  @override
  Element get enclosingElement => _enclosingElement;

  /**
   * Set the enclosing element of this element to the given [element].
   *
   * Throws [FrozenHashCodeException] if the hashCode can't be changed.
   */
  void set enclosingElement(Element element) {
    _enclosingElement = element as ElementImpl;
  }

  /**
   * Return the enclosing unit element (which might be the same as `this`), or
   * `null` if this element is not contained in any compilation unit.
   */
  CompilationUnitElementImpl get enclosingUnit {
    return _enclosingElement?.enclosingUnit;
  }

  @override
  int get hashCode {
    // TODO: We might want to re-visit this optimization in the future.
    // We cache the hash code value as this is a very frequently called method.
    if (_cachedHashCode == null) {
      _cachedHashCode = location.hashCode;
    }
    return _cachedHashCode;
  }

  /**
   * Return an identifier that uniquely identifies this element among the
   * children of this element's parent.
   */
  String get identifier => name;

  @override
  bool get isDeprecated {
    for (ElementAnnotation annotation in metadata) {
      if (annotation.isDeprecated) {
        return true;
      }
    }
    return false;
  }

  @override
  bool get isFactory {
    for (ElementAnnotation annotation in metadata) {
      if (annotation.isFactory) {
        return true;
      }
    }
    return false;
  }

  @override
  bool get isJS {
    for (ElementAnnotation annotation in metadata) {
      if (annotation.isJS) {
        return true;
      }
    }
    return false;
  }

  @override
  bool get isOverride {
    for (ElementAnnotation annotation in metadata) {
      if (annotation.isOverride) {
        return true;
      }
    }
    return false;
  }

  @override
  bool get isPrivate {
    String name = displayName;
    if (name == null) {
      return true;
    }
    return Identifier.isPrivateName(name);
  }

  @override
  bool get isProtected {
    for (ElementAnnotation annotation in metadata) {
      if (annotation.isProtected) {
        return true;
      }
    }
    return false;
  }

  @override
  bool get isPublic => !isPrivate;

  @override
  bool get isRequired {
    for (ElementAnnotation annotation in metadata) {
      if (annotation.isRequired) {
        return true;
      }
    }
    return false;
  }

  /**
   * Return `true` if this element is resynthesized from a summary.
   */
  bool get isResynthesized => enclosingUnit?.resynthesizerContext != null;

  @override
  bool get isSynthetic => hasModifier(Modifier.SYNTHETIC);

  @override
  LibraryElement get library =>
      getAncestor((element) => element is LibraryElement);

  @override
  Source get librarySource => library?.source;

  @override
  ElementLocation get location {
    if (_cachedLocation == null) {
      if (library == null) {
        return new ElementLocationImpl.con1(this);
      }
      _cachedLocation = new ElementLocationImpl.con1(this);
    }
    return _cachedLocation;
  }

  List<ElementAnnotation> get metadata {
    return _metadata ?? const <ElementAnnotation>[];
  }

  void set metadata(List<ElementAnnotation> metadata) {
    assert(!isResynthesized);
    _metadata = metadata;
  }

  @override
  String get name => _name;

  /**
   * Changes the name of this element.
   *
   * Throws [FrozenHashCodeException] if the hashCode can't be changed.
   */
  void set name(String name) {
    this._name = name;
  }

  @override
  int get nameLength => displayName != null ? displayName.length : 0;

  @override
  int get nameOffset => _nameOffset;

  /**
   * Sets the offset of the name of this element in the file that contains the
   * declaration of this element.
   *
   * Throws [FrozenHashCodeException] if the hashCode can't be changed.
   */
  void set nameOffset(int offset) {
    _nameOffset = offset;
  }

  @override
  Source get source {
    if (_enclosingElement == null) {
      return null;
    }
    return _enclosingElement.source;
  }

  /**
   * Set whether this element is synthetic.
   */
  void set synthetic(bool isSynthetic) {
    setModifier(Modifier.SYNTHETIC, isSynthetic);
  }

  /**
   * Return the context to resolve type parameters in, or `null` if neither this
   * element nor any of its ancestors is of a kind that can declare type
   * parameters.
   */
  TypeParameterizedElementMixin get typeParameterContext {
    return _enclosingElement?.typeParameterContext;
  }

  @override
  CompilationUnit get unit => context.resolveCompilationUnit(source, library);

  @override
  bool operator ==(Object object) {
    if (identical(this, object)) {
      return true;
    }
    return object is Element &&
        object.kind == kind &&
        object.location == location;
  }

  /**
   * Append to the given [buffer] a comma-separated list of the names of the
   * types of this element and every enclosing element.
   */
  void appendPathTo(StringBuffer buffer) {
    Element element = this;
    while (element != null) {
      if (element != this) {
        buffer.write(', ');
      }
      buffer.write(element.runtimeType);
      String name = element.name;
      if (name != null) {
        buffer.write(' (');
        buffer.write(name);
        buffer.write(')');
      }
      element = element.enclosingElement;
    }
  }

  /**
   * Append a textual representation of this element to the given [buffer].
   */
  void appendTo(StringBuffer buffer) {
    if (_name == null) {
      buffer.write("<unnamed ");
      buffer.write(runtimeType.toString());
      buffer.write(">");
    } else {
      buffer.write(_name);
    }
  }

  @override
  String computeDocumentationComment() => documentationComment;

  @override
  AstNode computeNode() => getNodeMatching((node) => node is AstNode);

  /**
   * Set this element as the enclosing element for given [element].
   */
  void encloseElement(ElementImpl element) {
    element.enclosingElement = this;
  }

  @override
  Element/*=E*/ getAncestor/*<E extends Element >*/(
      Predicate<Element> predicate) {
    Element ancestor = _enclosingElement;
    while (ancestor != null && !predicate(ancestor)) {
      ancestor = ancestor.enclosingElement;
    }
    return ancestor as Element/*=E*/;
  }

  /**
   * Return the child of this element that is uniquely identified by the given
   * [identifier], or `null` if there is no such child.
   */
  ElementImpl getChild(String identifier) => null;

  @override
  String getExtendedDisplayName(String shortName) {
    if (shortName == null) {
      shortName = displayName;
    }
    Source source = this.source;
    if (source != null) {
      return "$shortName (${source.fullName})";
    }
    return shortName;
  }

  /**
   * Return the resolved [AstNode] of the given type enclosing [getNameOffset].
   */
  AstNode getNodeMatching(Predicate<AstNode> predicate) {
    CompilationUnit unit = this.unit;
    if (unit == null) {
      return null;
    }
    int offset = nameOffset;
    AstNode node = new NodeLocator(offset).searchWithin(unit);
    if (node == null) {
      return null;
    }
    return node.getAncestor(predicate);
  }

  /**
   * Return `true` if this element has the given [modifier] associated with it.
   */
  bool hasModifier(Modifier modifier) =>
      BooleanArray.get(_modifiers, modifier.ordinal);

  @override
  bool isAccessibleIn(LibraryElement library) {
    if (Identifier.isPrivateName(name)) {
      return library == this.library;
    }
    return true;
  }

  /**
   * Use the given [visitor] to visit all of the [children] in the given array.
   */
  void safelyVisitChildren(List<Element> children, ElementVisitor visitor) {
    if (children != null) {
      for (Element child in children) {
        child.accept(visitor);
      }
    }
  }

  /**
   * Set the code range for this element.
   */
  void setCodeRange(int offset, int length) {
    assert(!isResynthesized);
    _codeOffset = offset;
    _codeLength = length;
  }

  /**
   * Set whether the given [modifier] is associated with this element to
   * correspond to the given [value].
   */
  void setModifier(Modifier modifier, bool value) {
    _modifiers = BooleanArray.set(_modifiers, modifier.ordinal, value);
  }

  @override
  String toString() {
    StringBuffer buffer = new StringBuffer();
    appendTo(buffer);
    return buffer.toString();
  }

  @override
  void visitChildren(ElementVisitor visitor) {
    // There are no children to visit
  }

  /**
   * Return annotations for the given [unlinkedConsts] in the [unit].
   */
  List<ElementAnnotation> _buildAnnotations(
      CompilationUnitElementImpl unit, List<UnlinkedConst> unlinkedConsts) {
    int length = unlinkedConsts.length;
    if (length != 0) {
      List<ElementAnnotation> annotations = new List<ElementAnnotation>(length);
      ResynthesizerContext context = unit.resynthesizerContext;
      for (int i = 0; i < length; i++) {
        annotations[i] = context.buildAnnotation(this, unlinkedConsts[i]);
      }
      return annotations;
    } else {
      return const <ElementAnnotation>[];
    }
  }

  static int _findElementIndexUsingIdentical(List items, Object item) {
    int length = items.length;
    for (int i = 0; i < length; i++) {
      if (identical(items[i], item)) {
        return i;
      }
    }
    throw new StateError('Unable to find $item in $items');
  }
}

/**
 * A concrete implementation of an [ElementLocation].
 */
class ElementLocationImpl implements ElementLocation {
  /**
   * The character used to separate components in the encoded form.
   */
  static int _SEPARATOR_CHAR = 0x3B;

  /**
   * The path to the element whose location is represented by this object.
   */
  List<String> _components;

  /**
   * The object managing [indexKeyId] and [indexLocationId].
   */
  Object indexOwner;

  /**
   * A cached id of this location in index.
   */
  int indexKeyId;

  /**
   * A cached id of this location in index.
   */
  int indexLocationId;

  /**
   * Initialize a newly created location to represent the given [element].
   */
  ElementLocationImpl.con1(Element element) {
    List<String> components = new List<String>();
    Element ancestor = element;
    while (ancestor != null) {
      components.insert(0, (ancestor as ElementImpl).identifier);
      ancestor = ancestor.enclosingElement;
    }
    this._components = components;
  }

  /**
   * Initialize a newly created location from the given [encoding].
   */
  ElementLocationImpl.con2(String encoding) {
    this._components = _decode(encoding);
  }

  /**
   * Initialize a newly created location from the given [components].
   */
  ElementLocationImpl.con3(List<String> components) {
    this._components = components;
  }

  @override
  List<String> get components => _components;

  @override
  String get encoding {
    StringBuffer buffer = new StringBuffer();
    int length = _components.length;
    for (int i = 0; i < length; i++) {
      if (i > 0) {
        buffer.writeCharCode(_SEPARATOR_CHAR);
      }
      _encode(buffer, _components[i]);
    }
    return buffer.toString();
  }

  @override
  int get hashCode {
    int result = 0;
    for (int i = 0; i < _components.length; i++) {
      String component = _components[i];
      result = JenkinsSmiHash.combine(result, component.hashCode);
    }
    return result;
  }

  @override
  bool operator ==(Object object) {
    if (identical(this, object)) {
      return true;
    }
    if (object is ElementLocationImpl) {
      List<String> otherComponents = object._components;
      int length = _components.length;
      if (otherComponents.length != length) {
        return false;
      }
      for (int i = 0; i < length; i++) {
        if (_components[i] != otherComponents[i]) {
          return false;
        }
      }
      return true;
    }
    return false;
  }

  @override
  String toString() => encoding;

  /**
   * Decode the [encoding] of a location into a list of components and return
   * the components.
   */
  List<String> _decode(String encoding) {
    List<String> components = new List<String>();
    StringBuffer buffer = new StringBuffer();
    int index = 0;
    int length = encoding.length;
    while (index < length) {
      int currentChar = encoding.codeUnitAt(index);
      if (currentChar == _SEPARATOR_CHAR) {
        if (index + 1 < length &&
            encoding.codeUnitAt(index + 1) == _SEPARATOR_CHAR) {
          buffer.writeCharCode(_SEPARATOR_CHAR);
          index += 2;
        } else {
          components.add(buffer.toString());
          buffer = new StringBuffer();
          index++;
        }
      } else {
        buffer.writeCharCode(currentChar);
        index++;
      }
    }
    components.add(buffer.toString());
    return components;
  }

  /**
   * Append an encoded form of the given [component] to the given [buffer].
   */
  void _encode(StringBuffer buffer, String component) {
    int length = component.length;
    for (int i = 0; i < length; i++) {
      int currentChar = component.codeUnitAt(i);
      if (currentChar == _SEPARATOR_CHAR) {
        buffer.writeCharCode(_SEPARATOR_CHAR);
      }
      buffer.writeCharCode(currentChar);
    }
  }
}

/**
 * An [AbstractClassElementImpl] which is an enum.
 */
class EnumElementImpl extends AbstractClassElementImpl {
  /**
   * The unlinked representation of the enum in the summary.
   */
  final UnlinkedEnum _unlinkedEnum;

  /**
   * The type defined by the enum.
   */
  InterfaceType _type;

  /**
   * Initialize a newly created class element to have the given [name] at the
   * given [offset] in the file that contains the declaration of this element.
   */
  EnumElementImpl(String name, int offset)
      : _unlinkedEnum = null,
        super(name, offset);

  /**
   * Initialize a newly created class element to have the given [name].
   */
  EnumElementImpl.forNode(Identifier name)
      : _unlinkedEnum = null,
        super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  EnumElementImpl.forSerialized(
      this._unlinkedEnum, CompilationUnitElementImpl enclosingUnit)
      : super.forSerialized(enclosingUnit);

  /**
   * Set whether this class is abstract.
   */
  void set abstract(bool isAbstract) {
    assert(_unlinkedEnum == null);
  }

  @override
  List<PropertyAccessorElement> get accessors {
    if (_unlinkedEnum != null && _accessors == null) {
      _resynthesizeFieldsAndPropertyAccessors();
    }
    return _accessors ?? const <PropertyAccessorElement>[];
  }

  @override
  void set accessors(List<PropertyAccessorElement> accessors) {
    assert(_unlinkedEnum == null);
    super.accessors = accessors;
  }

  @override
  List<InterfaceType> get allSupertypes => <InterfaceType>[supertype];

  @override
  int get codeLength {
    if (_unlinkedEnum != null) {
      return _unlinkedEnum.codeRange?.length;
    }
    return super.codeLength;
  }

  @override
  int get codeOffset {
    if (_unlinkedEnum != null) {
      return _unlinkedEnum.codeRange?.offset;
    }
    return super.codeOffset;
  }

  @override
  List<ConstructorElement> get constructors {
    // The equivalent code for enums in the spec shows a single constructor,
    // but that constructor is not callable (since it is a compile-time error
    // to subclass, mix-in, implement, or explicitly instantiate an enum).
    // So we represent this as having no constructors.
    return const <ConstructorElement>[];
  }

  @override
  String get documentationComment {
    if (_unlinkedEnum != null) {
      return _unlinkedEnum?.documentationComment?.text;
    }
    return super.documentationComment;
  }

  @override
  List<FieldElement> get fields {
    if (_unlinkedEnum != null && _fields == null) {
      _resynthesizeFieldsAndPropertyAccessors();
    }
    return _fields ?? const <FieldElement>[];
  }

  @override
  void set fields(List<FieldElement> fields) {
    assert(_unlinkedEnum == null);
    super.fields = fields;
  }

  @override
  bool get hasNonFinalField => false;

  @override
  bool get hasReferenceToSuper => false;

  @override
  bool get hasStaticMember => true;

  @override
  List<InterfaceType> get interfaces => const <InterfaceType>[];

  @override
  bool get isAbstract => false;

  @override
  bool get isEnum => true;

  @override
  bool get isMixinApplication => false;

  @override
  bool get isOrInheritsProxy => false;

  @override
  bool get isProxy => false;

  @override
  bool get isValidMixin => false;

  @override
  List<ElementAnnotation> get metadata {
    if (_unlinkedEnum != null) {
      return _metadata ??=
          _buildAnnotations(enclosingUnit, _unlinkedEnum.annotations);
    }
    return super.metadata;
  }

  @override
  List<MethodElement> get methods => const <MethodElement>[];

  @override
  List<InterfaceType> get mixins => const <InterfaceType>[];

  @override
  String get name {
    if (_unlinkedEnum != null) {
      return _unlinkedEnum.name;
    }
    return super.name;
  }

  @override
  int get nameOffset {
    if (_unlinkedEnum != null) {
      return _unlinkedEnum.nameOffset;
    }
    return super.nameOffset;
  }

  @override
  InterfaceType get supertype => context.typeProvider.objectType;

  @override
  InterfaceType get type {
    if (_type == null) {
      InterfaceTypeImpl type = new InterfaceTypeImpl(this);
      type.typeArguments = const <DartType>[];
      _type = type;
    }
    return _type;
  }

  @override
  List<TypeParameterElement> get typeParameters =>
      const <TypeParameterElement>[];

  @override
  ConstructorElement get unnamedConstructor => null;

  @override
  void appendTo(StringBuffer buffer) {
    buffer.write('enum ');
    String name = displayName;
    if (name == null) {
      buffer.write("{unnamed enum}");
    } else {
      buffer.write(name);
    }
  }

  @override
  MethodElement getMethod(String name) => null;

  @override
  ConstructorElement getNamedConstructor(String name) => null;

  @override
  bool isSuperConstructorAccessible(ConstructorElement constructor) => false;

  void _resynthesizeFieldsAndPropertyAccessors() {
    List<FieldElementImpl> fields = <FieldElementImpl>[];
    // Build the 'index' field.
    fields.add(new FieldElementImpl('index', -1)
      ..enclosingElement = this
      ..synthetic = true
      ..final2 = true
      ..type = context.typeProvider.intType);
    // Build the 'values' field.
    fields.add(new ConstFieldElementImpl_EnumValues(this));
    // Build fields for all enum constants.
    for (int i = 0; i < _unlinkedEnum.values.length; i++) {
      UnlinkedEnumValue unlinkedValue = _unlinkedEnum.values[i];
      ConstFieldElementImpl_EnumValue field =
          new ConstFieldElementImpl_EnumValue(this, unlinkedValue, i);
      fields.add(field);
    }
    // done
    _fields = fields;
    _accessors = fields
        .map((FieldElementImpl field) =>
            new PropertyAccessorElementImpl_ImplicitGetter(field)
              ..enclosingElement = this)
        .toList(growable: false);
  }
}

/**
 * A base class for concrete implementations of an [ExecutableElement].
 */
abstract class ExecutableElementImpl extends ElementImpl
    with TypeParameterizedElementMixin
    implements ExecutableElement {
  /**
   * The unlinked representation of the executable in the summary.
   */
  final UnlinkedExecutable serializedExecutable;

  /**
   * A list containing all of the functions defined within this executable
   * element.
   */
  List<FunctionElement> _functions;

  /**
   * A list containing all of the labels defined within this executable element.
   */
  List<LabelElement> _labels;

  /**
   * A list containing all of the local variables defined within this executable
   * element.
   */
  List<LocalVariableElement> _localVariables;

  /**
   * A list containing all of the parameters defined by this executable element.
   */
  List<ParameterElement> _parameters;

  /**
   * A list containing all of the type parameters defined for this executable
   * element.
   */
  List<TypeParameterElement> _typeParameters;

  /**
   * The return type defined by this executable element.
   */
  DartType _returnType;

  /**
   * The type of function defined by this executable element.
   */
  FunctionType _type;

  /**
   * Initialize a newly created executable element to have the given [name] and
   * [offset].
   */
  ExecutableElementImpl(String name, int offset)
      : serializedExecutable = null,
        super(name, offset);

  /**
   * Initialize a newly created executable element to have the given [name].
   */
  ExecutableElementImpl.forNode(Identifier name)
      : serializedExecutable = null,
        super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  ExecutableElementImpl.forSerialized(
      this.serializedExecutable, ElementImpl enclosingElement)
      : super.forSerialized(enclosingElement);

  /**
   * Set whether this executable element's body is asynchronous.
   */
  void set asynchronous(bool isAsynchronous) {
    assert(serializedExecutable == null);
    setModifier(Modifier.ASYNCHRONOUS, isAsynchronous);
  }

  @override
  int get codeLength {
    if (serializedExecutable != null) {
      return serializedExecutable.codeRange?.length;
    }
    return super.codeLength;
  }

  @override
  int get codeOffset {
    if (serializedExecutable != null) {
      return serializedExecutable.codeRange?.offset;
    }
    return super.codeOffset;
  }

  @override
  String get displayName {
    if (serializedExecutable != null) {
      return serializedExecutable.name;
    }
    return super.displayName;
  }

  @override
  String get documentationComment {
    if (serializedExecutable != null) {
      return serializedExecutable?.documentationComment?.text;
    }
    return super.documentationComment;
  }

  /**
   * Set whether this executable element is external.
   */
  void set external(bool isExternal) {
    assert(serializedExecutable == null);
    setModifier(Modifier.EXTERNAL, isExternal);
  }

  @override
  List<FunctionElement> get functions {
    if (serializedExecutable != null) {
      _functions ??= FunctionElementImpl.resynthesizeList(
          this, serializedExecutable.localFunctions);
    }
    return _functions ?? const <FunctionElement>[];
  }

  /**
   * Set the functions defined within this executable element to the given
   * [functions].
   */
  void set functions(List<FunctionElement> functions) {
    assert(serializedExecutable == null);
    for (FunctionElement function in functions) {
      (function as FunctionElementImpl).enclosingElement = this;
    }
    this._functions = functions;
  }

  /**
   * Set whether this method's body is a generator.
   */
  void set generator(bool isGenerator) {
    assert(serializedExecutable == null);
    setModifier(Modifier.GENERATOR, isGenerator);
  }

  @override
  bool get hasImplicitReturnType {
    if (serializedExecutable != null) {
      return serializedExecutable.returnType == null &&
          serializedExecutable.kind != UnlinkedExecutableKind.constructor;
    }
    return hasModifier(Modifier.IMPLICIT_TYPE);
  }

  /**
   * Set whether this executable element has an implicit return type.
   */
  void set hasImplicitReturnType(bool hasImplicitReturnType) {
    assert(serializedExecutable == null);
    setModifier(Modifier.IMPLICIT_TYPE, hasImplicitReturnType);
  }

  @override
  bool get isAbstract {
    if (serializedExecutable != null) {
      return serializedExecutable.isAbstract;
    }
    return hasModifier(Modifier.ABSTRACT);
  }

  @override
  bool get isAsynchronous {
    if (serializedExecutable != null) {
      return serializedExecutable.isAsynchronous;
    }
    return hasModifier(Modifier.ASYNCHRONOUS);
  }

  @override
  bool get isExternal {
    if (serializedExecutable != null) {
      return serializedExecutable.isExternal;
    }
    return hasModifier(Modifier.EXTERNAL);
  }

  @override
  bool get isGenerator {
    if (serializedExecutable != null) {
      return serializedExecutable.isGenerator;
    }
    return hasModifier(Modifier.GENERATOR);
  }

  @override
  bool get isOperator => false;

  @override
  bool get isSynchronous => !isAsynchronous;

  @override
  List<LabelElement> get labels {
    if (serializedExecutable != null) {
      _labels ??= LabelElementImpl.resynthesizeList(
          this, serializedExecutable.localLabels);
    }
    return _labels ?? const <LabelElement>[];
  }

  /**
   * Set the labels defined within this executable element to the given
   * [labels].
   */
  void set labels(List<LabelElement> labels) {
    assert(serializedExecutable == null);
    for (LabelElement label in labels) {
      (label as LabelElementImpl).enclosingElement = this;
    }
    this._labels = labels;
  }

  @override
  List<LocalVariableElement> get localVariables {
    if (serializedExecutable != null && _localVariables == null) {
      List<UnlinkedVariable> unlinkedVariables =
          serializedExecutable.localVariables;
      int length = unlinkedVariables.length;
      if (length != 0) {
        List<LocalVariableElementImpl> localVariables =
            new List<LocalVariableElementImpl>(length);
        for (int i = 0; i < length; i++) {
          localVariables[i] = new LocalVariableElementImpl.forSerializedFactory(
              unlinkedVariables[i], this);
        }
        _localVariables = localVariables;
      } else {
        _localVariables = const <LocalVariableElement>[];
      }
    }
    return _localVariables ?? const <LocalVariableElement>[];
  }

  /**
   * Set the local variables defined within this executable element to the given
   * [variables].
   */
  void set localVariables(List<LocalVariableElement> variables) {
    assert(serializedExecutable == null);
    for (LocalVariableElement variable in variables) {
      (variable as LocalVariableElementImpl).enclosingElement = this;
    }
    this._localVariables = variables;
  }

  @override
  List<ElementAnnotation> get metadata {
    if (serializedExecutable != null) {
      return _metadata ??=
          _buildAnnotations(enclosingUnit, serializedExecutable.annotations);
    }
    return super.metadata;
  }

  @override
  String get name {
    if (serializedExecutable != null) {
      return serializedExecutable.name;
    }
    return super.name;
  }

  @override
  int get nameOffset {
    if (serializedExecutable != null) {
      return serializedExecutable.nameOffset;
    }
    return super.nameOffset;
  }

  @override
  List<ParameterElement> get parameters {
    if (serializedExecutable != null) {
      _parameters ??= ParameterElementImpl.resynthesizeList(
          serializedExecutable.parameters, this);
    }
    return _parameters ?? const <ParameterElement>[];
  }

  /**
   * Set the parameters defined by this executable element to the given
   * [parameters].
   */
  void set parameters(List<ParameterElement> parameters) {
    assert(serializedExecutable == null);
    for (ParameterElement parameter in parameters) {
      (parameter as ParameterElementImpl).enclosingElement = this;
    }
    this._parameters = parameters;
  }

  @override
  DartType get returnType {
    if (serializedExecutable != null && _returnType == null) {
      bool isSetter =
          serializedExecutable.kind == UnlinkedExecutableKind.setter;
      _returnType = enclosingUnit.resynthesizerContext.resolveLinkedType(
              serializedExecutable.inferredReturnTypeSlot,
              typeParameterContext) ??
          enclosingUnit.resynthesizerContext.resolveTypeRef(
              serializedExecutable.returnType, typeParameterContext,
              defaultVoid: isSetter && context.analysisOptions.strongMode);
    }
    return _returnType;
  }

  void set returnType(DartType returnType) {
    assert(serializedExecutable == null);
    _returnType = returnType;
  }

  @override
  FunctionType get type {
    if (serializedExecutable != null) {
      _type ??= new FunctionTypeImpl.elementWithNameAndArgs(
          this, null, allEnclosingTypeParameterTypes, false);
    }
    return _type;
  }

  void set type(FunctionType type) {
    assert(serializedExecutable == null);
    _type = type;
  }

  @override
  TypeParameterizedElementMixin get typeParameterContext => this;

  @override
  List<TypeParameterElement> get typeParameters {
    if (serializedExecutable != null) {
      return super.typeParameters;
    }
    return _typeParameters ?? const <TypeParameterElement>[];
  }

  /**
   * Set the type parameters defined by this executable element to the given
   * [typeParameters].
   */
  void set typeParameters(List<TypeParameterElement> typeParameters) {
    assert(serializedExecutable == null);
    for (TypeParameterElement parameter in typeParameters) {
      (parameter as TypeParameterElementImpl).enclosingElement = this;
    }
    this._typeParameters = typeParameters;
  }

  @override
  List<UnlinkedTypeParam> get unlinkedTypeParams =>
      serializedExecutable.typeParameters;

  @override
  void appendTo(StringBuffer buffer) {
    if (this.kind != ElementKind.GETTER) {
      int typeParameterCount = typeParameters.length;
      if (typeParameterCount > 0) {
        buffer.write('<');
        for (int i = 0; i < typeParameterCount; i++) {
          if (i > 0) {
            buffer.write(", ");
          }
          (typeParameters[i] as TypeParameterElementImpl).appendTo(buffer);
        }
        buffer.write('>');
      }
      buffer.write("(");
      String closing = null;
      ParameterKind kind = ParameterKind.REQUIRED;
      int parameterCount = parameters.length;
      for (int i = 0; i < parameterCount; i++) {
        if (i > 0) {
          buffer.write(", ");
        }
        ParameterElement parameter = parameters[i];
        ParameterKind parameterKind = parameter.parameterKind;
        if (parameterKind != kind) {
          if (closing != null) {
            buffer.write(closing);
          }
          if (parameterKind == ParameterKind.POSITIONAL) {
            buffer.write("[");
            closing = "]";
          } else if (parameterKind == ParameterKind.NAMED) {
            buffer.write("{");
            closing = "}";
          } else {
            closing = null;
          }
        }
        kind = parameterKind;
        parameter.appendToWithoutDelimiters(buffer);
      }
      if (closing != null) {
        buffer.write(closing);
      }
      buffer.write(")");
    }
    if (type != null) {
      buffer.write(ElementImpl.RIGHT_ARROW);
      buffer.write(type.returnType);
    }
  }

  @override
  ElementImpl getChild(String identifier) {
    for (FunctionElement function in _functions) {
      FunctionElementImpl functionImpl = function;
      if (functionImpl.identifier == identifier) {
        return functionImpl;
      }
    }
    for (LabelElement label in _labels) {
      LabelElementImpl labelImpl = label;
      if (labelImpl.identifier == identifier) {
        return labelImpl;
      }
    }
    for (LocalVariableElement variable in _localVariables) {
      LocalVariableElementImpl variableImpl = variable;
      if (variableImpl.identifier == identifier) {
        return variableImpl;
      }
    }
    for (ParameterElement parameter in parameters) {
      ParameterElementImpl parameterImpl = parameter;
      if (parameterImpl.identifier == identifier) {
        return parameterImpl;
      }
    }
    return null;
  }

  @override
  void visitChildren(ElementVisitor visitor) {
    super.visitChildren(visitor);
    safelyVisitChildren(typeParameters, visitor);
    safelyVisitChildren(parameters, visitor);
    safelyVisitChildren(_functions, visitor);
    safelyVisitChildren(_labels, visitor);
    safelyVisitChildren(_localVariables, visitor);
  }
}

/**
 * A concrete implementation of an [ExportElement].
 */
class ExportElementImpl extends UriReferencedElementImpl
    implements ExportElement {
  /**
   * The unlinked representation of the export in the summary.
   */
  final UnlinkedExportPublic _unlinkedExportPublic;

  /**
   * The unlinked representation of the export in the summary.
   */
  final UnlinkedExportNonPublic _unlinkedExportNonPublic;

  /**
   * The library that is exported from this library by this export directive.
   */
  LibraryElement _exportedLibrary;

  /**
   * The combinators that were specified as part of the export directive in the
   * order in which they were specified.
   */
  List<NamespaceCombinator> _combinators;

  /**
   * The URI that was selected based on the [context] declared variables.
   */
  String _selectedUri;

  /**
   * Initialize a newly created export element at the given [offset].
   */
  ExportElementImpl(int offset)
      : _unlinkedExportPublic = null,
        _unlinkedExportNonPublic = null,
        super(null, offset);

  /**
   * Initialize using the given serialized information.
   */
  ExportElementImpl.forSerialized(this._unlinkedExportPublic,
      this._unlinkedExportNonPublic, LibraryElementImpl enclosingLibrary)
      : super.forSerialized(enclosingLibrary);

  @override
  List<NamespaceCombinator> get combinators {
    if (_unlinkedExportPublic != null && _combinators == null) {
      _combinators = ImportElementImpl
          ._buildCombinators(_unlinkedExportPublic.combinators);
    }
    return _combinators ?? const <NamespaceCombinator>[];
  }

  void set combinators(List<NamespaceCombinator> combinators) {
    assert(_unlinkedExportPublic == null);
    _combinators = combinators;
  }

  @override
  LibraryElement get exportedLibrary {
    if (_unlinkedExportNonPublic != null && _exportedLibrary == null) {
      LibraryElementImpl library = enclosingElement as LibraryElementImpl;
      _exportedLibrary = library.resynthesizerContext.buildExportedLibrary(uri);
    }
    return _exportedLibrary;
  }

  void set exportedLibrary(LibraryElement exportedLibrary) {
    assert(_unlinkedExportNonPublic == null);
    _exportedLibrary = exportedLibrary;
  }

  @override
  String get identifier => exportedLibrary.name;

  @override
  ElementKind get kind => ElementKind.EXPORT;

  @override
  List<ElementAnnotation> get metadata {
    if (_unlinkedExportNonPublic != null) {
      return _metadata ??= _buildAnnotations(
          library.definingCompilationUnit as CompilationUnitElementImpl,
          _unlinkedExportNonPublic.annotations);
    }
    return super.metadata;
  }

  void set metadata(List<ElementAnnotation> metadata) {
    assert(_unlinkedExportNonPublic == null);
    super.metadata = metadata;
  }

  @override
  int get nameOffset {
    if (_unlinkedExportNonPublic != null) {
      return _unlinkedExportNonPublic.offset;
    }
    return super.nameOffset;
  }

  @override
  String get uri {
    if (_unlinkedExportPublic != null) {
      return _selectedUri ??= _selectUri(
          _unlinkedExportPublic.uri, _unlinkedExportPublic.configurations);
    }
    return super.uri;
  }

  @override
  void set uri(String uri) {
    assert(_unlinkedExportPublic == null);
    super.uri = uri;
  }

  @override
  int get uriEnd {
    if (_unlinkedExportNonPublic != null) {
      return _unlinkedExportNonPublic.uriEnd;
    }
    return super.uriEnd;
  }

  @override
  void set uriEnd(int uriEnd) {
    assert(_unlinkedExportNonPublic == null);
    super.uriEnd = uriEnd;
  }

  @override
  int get uriOffset {
    if (_unlinkedExportNonPublic != null) {
      return _unlinkedExportNonPublic.uriOffset;
    }
    return super.uriOffset;
  }

  @override
  void set uriOffset(int uriOffset) {
    assert(_unlinkedExportNonPublic == null);
    super.uriOffset = uriOffset;
  }

  @override
  accept(ElementVisitor visitor) => visitor.visitExportElement(this);

  @override
  void appendTo(StringBuffer buffer) {
    buffer.write("export ");
    (exportedLibrary as LibraryElementImpl).appendTo(buffer);
  }
}

/**
 * A concrete implementation of a [FieldElement].
 */
class FieldElementImpl extends PropertyInducingElementImpl
    implements FieldElement {
  /**
   * Initialize a newly created synthetic field element to have the given [name]
   * at the given [offset].
   */
  FieldElementImpl(String name, int offset) : super(name, offset);

  /**
   * Initialize a newly created field element to have the given [name].
   */
  FieldElementImpl.forNode(Identifier name) : super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  FieldElementImpl.forSerialized(
      UnlinkedVariable unlinkedVariable, ElementImpl enclosingElement)
      : super.forSerialized(unlinkedVariable, enclosingElement);

  /**
   * Initialize using the given serialized information.
   */
  factory FieldElementImpl.forSerializedFactory(
      UnlinkedVariable unlinkedVariable, ClassElementImpl enclosingClass) {
    if (unlinkedVariable.initializer?.bodyExpr != null &&
        (unlinkedVariable.isConst ||
            unlinkedVariable.isFinal && !unlinkedVariable.isStatic)) {
      return new ConstFieldElementImpl.forSerialized(
          unlinkedVariable, enclosingClass);
    } else {
      return new FieldElementImpl.forSerialized(
          unlinkedVariable, enclosingClass);
    }
  }

  @override
  ClassElement get enclosingElement => super.enclosingElement as ClassElement;

  @override
  bool get isEnumConstant =>
      enclosingElement != null ? enclosingElement.isEnum : false;

  @override
  bool get isStatic {
    if (_unlinkedVariable != null) {
      return _unlinkedVariable.isStatic;
    }
    return hasModifier(Modifier.STATIC);
  }

  @override
  bool get isVirtual {
    for (ElementAnnotationImpl annotation in metadata) {
      if (annotation.isVirtual) {
        return true;
      }
    }
    return false;
  }

  @override
  ElementKind get kind => ElementKind.FIELD;

  /**
   * Set whether this field is static.
   */
  void set static(bool isStatic) {
    assert(_unlinkedVariable == null);
    setModifier(Modifier.STATIC, isStatic);
  }

  @override
  accept(ElementVisitor visitor) => visitor.visitFieldElement(this);

  @override
  AstNode computeNode() {
    if (isEnumConstant) {
      return getNodeMatching((node) => node is EnumConstantDeclaration);
    } else {
      return getNodeMatching((node) => node is VariableDeclaration);
    }
  }
}

/**
 * A [ParameterElementImpl] that has the additional information of the
 * [FieldElement] associated with the parameter.
 */
class FieldFormalParameterElementImpl extends ParameterElementImpl
    implements FieldFormalParameterElement {
  /**
   * The field associated with this field formal parameter.
   */
  FieldElement _field;

  /**
   * Initialize a newly created parameter element to have the given [name] and
   * [nameOffset].
   */
  FieldFormalParameterElementImpl(String name, int nameOffset)
      : super(name, nameOffset);

  /**
   * Initialize a newly created parameter element to have the given [name].
   */
  FieldFormalParameterElementImpl.forNode(Identifier name)
      : super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  FieldFormalParameterElementImpl.forSerialized(
      UnlinkedParam unlinkedParam, ElementImpl enclosingElement)
      : super.forSerialized(unlinkedParam, enclosingElement);

  @override
  FieldElement get field {
    if (_unlinkedParam != null && _field == null) {
      Element enclosingClass = enclosingElement?.enclosingElement;
      if (enclosingClass is ClassElement) {
        _field = enclosingClass.getField(_unlinkedParam.name);
      }
    }
    return _field;
  }

  void set field(FieldElement field) {
    assert(_unlinkedParam == null);
    _field = field;
  }

  @override
  bool get isInitializingFormal => true;

  @override
  DartType get type {
    if (_unlinkedParam != null && _unlinkedParam.type == null) {
      _type ??= field?.type ?? DynamicTypeImpl.instance;
    }
    return super.type;
  }

  @override
  void set type(DartType type) {
    assert(_unlinkedParam == null);
    _type = type;
  }

  @override
  accept(ElementVisitor visitor) =>
      visitor.visitFieldFormalParameterElement(this);
}

/**
 * A concrete implementation of a [FunctionElement].
 */
class FunctionElementImpl extends ExecutableElementImpl
    implements FunctionElement {
  /**
   * The offset to the beginning of the visible range for this element.
   */
  int _visibleRangeOffset = 0;

  /**
   * The length of the visible range for this element, or `-1` if this element
   * does not have a visible range.
   */
  int _visibleRangeLength = -1;

  /**
   * Initialize a newly created function element to have the given [name] and
   * [offset].
   */
  FunctionElementImpl(String name, int offset) : super(name, offset);

  /**
   * Initialize a newly created function element to have the given [name].
   */
  FunctionElementImpl.forNode(Identifier name) : super.forNode(name);

  /**
   * Initialize a newly created function element to have no name and the given
   * [nameOffset]. This is used for function expressions, that have no name.
   */
  FunctionElementImpl.forOffset(int nameOffset) : super("", nameOffset);

  /**
   * Initialize using the given serialized information.
   */
  FunctionElementImpl.forSerialized(
      UnlinkedExecutable serializedExecutable, ElementImpl enclosingElement)
      : super.forSerialized(serializedExecutable, enclosingElement);

  /**
   * Synthesize an unnamed function element that takes [parameters] and returns
   * [returnType].
   */
  FunctionElementImpl.synthetic(
      List<ParameterElement> parameters, DartType returnType)
      : super("", -1) {
    synthetic = true;
    this.returnType = returnType;
    this.parameters = parameters;

    type = new FunctionTypeImpl(this);
  }

  @override
  TypeParameterizedElementMixin get enclosingTypeParameterContext {
    return (enclosingElement as ElementImpl).typeParameterContext;
  }

  @override
  String get identifier {
    String identifier = super.identifier;
    Element enclosing = this.enclosingElement;
    if (enclosing is ExecutableElement) {
      int id = ElementImpl._findElementIndexUsingIdentical(
          enclosing.functions, this);
      identifier += "@$id";
    }
    return identifier;
  }

  @override
  bool get isEntryPoint {
    return isStatic && displayName == FunctionElement.MAIN_FUNCTION_NAME;
  }

  @override
  bool get isStatic => enclosingElement is CompilationUnitElement;

  @override
  ElementKind get kind => ElementKind.FUNCTION;

  @override
  SourceRange get visibleRange {
    if (serializedExecutable != null) {
      if (serializedExecutable.visibleLength == 0) {
        return null;
      }
      return new SourceRange(serializedExecutable.visibleOffset,
          serializedExecutable.visibleLength);
    }
    if (_visibleRangeLength < 0) {
      return null;
    }
    return new SourceRange(_visibleRangeOffset, _visibleRangeLength);
  }

  @override
  accept(ElementVisitor visitor) => visitor.visitFunctionElement(this);

  @override
  void appendTo(StringBuffer buffer) {
    String name = displayName;
    if (name != null) {
      buffer.write(name);
    }
    super.appendTo(buffer);
  }

  @override
  FunctionDeclaration computeNode() =>
      getNodeMatching((node) => node is FunctionDeclaration);

  /**
   * Set the visible range for this element to the range starting at the given
   * [offset] with the given [length].
   */
  void setVisibleRange(int offset, int length) {
    assert(serializedExecutable == null);
    _visibleRangeOffset = offset;
    _visibleRangeLength = length;
  }

  /**
   * Set the parameters defined by this type alias to the given [parameters]
   * without becoming the parent of the parameters. This should only be used by
   * the [TypeResolverVisitor] when creating a synthetic type alias.
   */
  void shareParameters(List<ParameterElement> parameters) {
    this._parameters = parameters;
  }

  /**
   * Set the type parameters defined by this type alias to the given
   * [parameters] without becoming the parent of the parameters. This should
   * only be used by the [TypeResolverVisitor] when creating a synthetic type
   * alias.
   */
  void shareTypeParameters(List<TypeParameterElement> typeParameters) {
    this._typeParameters = typeParameters;
  }

  /**
   * Create and return [FunctionElement]s for the given [unlinkedFunctions].
   */
  static List<FunctionElement> resynthesizeList(
      ExecutableElementImpl executableElement,
      List<UnlinkedExecutable> unlinkedFunctions) {
    int length = unlinkedFunctions.length;
    if (length != 0) {
      List<FunctionElement> elements = new List<FunctionElement>(length);
      for (int i = 0; i < length; i++) {
        elements[i] = new FunctionElementImpl.forSerialized(
            unlinkedFunctions[i], executableElement);
      }
      return elements;
    } else {
      return const <FunctionElement>[];
    }
  }
}

/**
 * Implementation of [FunctionElementImpl] for a function typed parameter.
 */
class FunctionElementImpl_forFunctionTypedParameter
    extends FunctionElementImpl {
  @override
  final CompilationUnitElementImpl enclosingUnit;

  /**
   * The enclosing function typed [ParameterElementImpl].
   */
  final ParameterElementImpl _parameter;

  FunctionElementImpl_forFunctionTypedParameter(
      this.enclosingUnit, this._parameter)
      : super('', -1);

  @override
  TypeParameterizedElementMixin get enclosingTypeParameterContext =>
      _parameter.typeParameterContext;

  @override
  bool get isSynthetic => true;
}

/**
 * Implementation of [FunctionElementImpl] for a synthetic function element
 * that was synthesized by a LUB computation.
 */
class FunctionElementImpl_forLUB extends FunctionElementImpl {
  @override
  final CompilationUnitElementImpl enclosingUnit;

  @override
  final TypeParameterizedElementMixin enclosingTypeParameterContext;

  final EntityRef _entityRef;

  FunctionElementImpl_forLUB(
      this.enclosingUnit, this.enclosingTypeParameterContext, this._entityRef)
      : super('', -1);

  @override
  bool get isSynthetic => true;

  @override
  List<ParameterElement> get parameters {
    return _parameters ??= ParameterElementImpl
        .resynthesizeList(_entityRef.syntheticParams, this, synthetic: true);
  }

  @override
  void set parameters(List<ParameterElement> parameters) {
    assert(false);
  }

  @override
  DartType get returnType {
    return _returnType ??= enclosingUnit.resynthesizerContext
        .resolveTypeRef(_entityRef.syntheticReturnType, typeParameterContext);
  }

  @override
  void set returnType(DartType returnType) {
    assert(false);
  }

  @override
  FunctionType get type {
    return _type ??=
        new FunctionTypeImpl.elementWithNameAndArgs(this, null, null, false);
  }

  @override
  void set type(FunctionType type) {
    assert(false);
  }
}

/**
 * A concrete implementation of a [FunctionTypeAliasElement].
 */
class FunctionTypeAliasElementImpl extends ElementImpl
    with TypeParameterizedElementMixin
    implements FunctionTypeAliasElement {
  /**
   * The unlinked representation of the type in the summary.
   */
  final UnlinkedTypedef _unlinkedTypedef;

  /**
   * A list containing all of the parameters defined by this type alias.
   */
  List<ParameterElement> _parameters;

  /**
   * The return type defined by this type alias.
   */
  DartType _returnType;

  /**
   * The type of function defined by this type alias.
   */
  FunctionType _type;

  /**
   * A list containing all of the type parameters defined for this type.
   */
  List<TypeParameterElement> _typeParameters = TypeParameterElement.EMPTY_LIST;

  /**
   * Initialize a newly created type alias element to have the given name.
   *
   * [name] the name of this element
   * [nameOffset] the offset of the name of this element in the file that
   *    contains the declaration of this element
   */
  FunctionTypeAliasElementImpl(String name, int nameOffset)
      : _unlinkedTypedef = null,
        super(name, nameOffset);

  /**
   * Initialize a newly created type alias element to have the given [name].
   */
  FunctionTypeAliasElementImpl.forNode(Identifier name)
      : _unlinkedTypedef = null,
        super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  FunctionTypeAliasElementImpl.forSerialized(
      this._unlinkedTypedef, CompilationUnitElementImpl enclosingUnit)
      : super.forSerialized(enclosingUnit);

  @override
  int get codeLength {
    if (_unlinkedTypedef != null) {
      return _unlinkedTypedef.codeRange?.length;
    }
    return super.codeLength;
  }

  @override
  int get codeOffset {
    if (_unlinkedTypedef != null) {
      return _unlinkedTypedef.codeRange?.offset;
    }
    return super.codeOffset;
  }

  @override
  String get displayName => name;

  @override
  String get documentationComment {
    if (_unlinkedTypedef != null) {
      return _unlinkedTypedef?.documentationComment?.text;
    }
    return super.documentationComment;
  }

  @override
  CompilationUnitElement get enclosingElement =>
      super.enclosingElement as CompilationUnitElement;

  @override
  TypeParameterizedElementMixin get enclosingTypeParameterContext => null;

  @override
  CompilationUnitElementImpl get enclosingUnit =>
      _enclosingElement as CompilationUnitElementImpl;

  @override
  ElementKind get kind => ElementKind.FUNCTION_TYPE_ALIAS;

  @override
  List<ElementAnnotation> get metadata {
    if (_unlinkedTypedef != null) {
      return _metadata ??=
          _buildAnnotations(enclosingUnit, _unlinkedTypedef.annotations);
    }
    return super.metadata;
  }

  @override
  String get name {
    if (_unlinkedTypedef != null) {
      return _unlinkedTypedef.name;
    }
    return super.name;
  }

  @override
  int get nameOffset {
    if (_unlinkedTypedef != null) {
      return _unlinkedTypedef.nameOffset;
    }
    return super.nameOffset;
  }

  @override
  List<ParameterElement> get parameters {
    if (_unlinkedTypedef != null) {
      _parameters ??= ParameterElementImpl.resynthesizeList(
          _unlinkedTypedef.parameters, this);
    }
    return _parameters ?? const <ParameterElement>[];
  }

  /**
   * Set the parameters defined by this type alias to the given [parameters].
   */
  void set parameters(List<ParameterElement> parameters) {
    assert(_unlinkedTypedef == null);
    if (parameters != null) {
      for (ParameterElement parameter in parameters) {
        (parameter as ParameterElementImpl).enclosingElement = this;
      }
    }
    this._parameters = parameters;
  }

  @override
  DartType get returnType {
    if (_unlinkedTypedef != null && _returnType == null) {
      _returnType = enclosingUnit.resynthesizerContext
          .resolveTypeRef(_unlinkedTypedef.returnType, this);
    }
    return _returnType;
  }

  void set returnType(DartType returnType) {
    assert(_unlinkedTypedef == null);
    _returnType = returnType;
  }

  @override
  FunctionType get type {
    if (_unlinkedTypedef != null && _type == null) {
      _type = new FunctionTypeImpl.forTypedef(this);
    }
    return _type;
  }

  void set type(FunctionType type) {
    assert(_unlinkedTypedef == null);
    _type = type;
  }

  @override
  TypeParameterizedElementMixin get typeParameterContext => this;

  @override
  List<TypeParameterElement> get typeParameters {
    if (_unlinkedTypedef != null) {
      return super.typeParameters;
    }
    return _typeParameters;
  }

  /**
   * Set the type parameters defined for this type to the given
   * [typeParameters].
   */
  void set typeParameters(List<TypeParameterElement> typeParameters) {
    assert(_unlinkedTypedef == null);
    for (TypeParameterElement typeParameter in typeParameters) {
      (typeParameter as TypeParameterElementImpl).enclosingElement = this;
    }
    this._typeParameters = typeParameters;
  }

  @override
  List<UnlinkedTypeParam> get unlinkedTypeParams =>
      _unlinkedTypedef.typeParameters;

  @override
  accept(ElementVisitor visitor) => visitor.visitFunctionTypeAliasElement(this);

  @override
  void appendTo(StringBuffer buffer) {
    buffer.write("typedef ");
    buffer.write(displayName);
    int typeParameterCount = _typeParameters.length;
    if (typeParameterCount > 0) {
      buffer.write("<");
      for (int i = 0; i < typeParameterCount; i++) {
        if (i > 0) {
          buffer.write(", ");
        }
        (_typeParameters[i] as TypeParameterElementImpl).appendTo(buffer);
      }
      buffer.write(">");
    }
    buffer.write("(");
    int parameterCount = _parameters.length;
    for (int i = 0; i < parameterCount; i++) {
      if (i > 0) {
        buffer.write(", ");
      }
      (_parameters[i] as ParameterElementImpl).appendTo(buffer);
    }
    buffer.write(")");
    if (type != null) {
      buffer.write(ElementImpl.RIGHT_ARROW);
      buffer.write(type.returnType);
    } else if (returnType != null) {
      buffer.write(ElementImpl.RIGHT_ARROW);
      buffer.write(returnType);
    }
  }

  @override
  FunctionTypeAlias computeNode() =>
      getNodeMatching((node) => node is FunctionTypeAlias);

  @override
  ElementImpl getChild(String identifier) {
    for (ParameterElement parameter in parameters) {
      ParameterElementImpl parameterImpl = parameter;
      if (parameterImpl.identifier == identifier) {
        return parameterImpl;
      }
    }
    for (TypeParameterElement typeParameter in _typeParameters) {
      TypeParameterElementImpl typeParameterImpl = typeParameter;
      if (typeParameterImpl.identifier == identifier) {
        return typeParameterImpl;
      }
    }
    return null;
  }

  @override
  void visitChildren(ElementVisitor visitor) {
    super.visitChildren(visitor);
    safelyVisitChildren(parameters, visitor);
    safelyVisitChildren(_typeParameters, visitor);
  }
}

/**
 * A concrete implementation of a [HideElementCombinator].
 */
class HideElementCombinatorImpl implements HideElementCombinator {
  /**
   * The unlinked representation of the combinator in the summary.
   */
  final UnlinkedCombinator _unlinkedCombinator;

  /**
   * The names that are not to be made visible in the importing library even if
   * they are defined in the imported library.
   */
  List<String> _hiddenNames;

  HideElementCombinatorImpl() : _unlinkedCombinator = null;

  /**
   * Initialize using the given serialized information.
   */
  HideElementCombinatorImpl.forSerialized(this._unlinkedCombinator);

  @override
  List<String> get hiddenNames {
    if (_unlinkedCombinator != null) {
      _hiddenNames ??= _unlinkedCombinator.hides.toList(growable: false);
    }
    return _hiddenNames ?? const <String>[];
  }

  void set hiddenNames(List<String> hiddenNames) {
    assert(_unlinkedCombinator == null);
    _hiddenNames = hiddenNames;
  }

  @override
  String toString() {
    StringBuffer buffer = new StringBuffer();
    buffer.write("show ");
    int count = hiddenNames.length;
    for (int i = 0; i < count; i++) {
      if (i > 0) {
        buffer.write(", ");
      }
      buffer.write(hiddenNames[i]);
    }
    return buffer.toString();
  }
}

/**
 * A concrete implementation of an [ImportElement].
 */
class ImportElementImpl extends UriReferencedElementImpl
    implements ImportElement {
  /**
   * The unlinked representation of the import in the summary.
   */
  final UnlinkedImport _unlinkedImport;

  /**
   * The index of the dependency in the `imports` list.
   */
  final int _linkedDependency;

  /**
   * The offset of the prefix of this import in the file that contains the this
   * import directive, or `-1` if this import is synthetic.
   */
  int _prefixOffset = 0;

  /**
   * The library that is imported into this library by this import directive.
   */
  LibraryElement _importedLibrary;

  /**
   * The combinators that were specified as part of the import directive in the
   * order in which they were specified.
   */
  List<NamespaceCombinator> _combinators;

  /**
   * The prefix that was specified as part of the import directive, or `null` if
   * there was no prefix specified.
   */
  PrefixElement _prefix;

  /**
   * The URI that was selected based on the [context] declared variables.
   */
  String _selectedUri;

  /**
   * Initialize a newly created import element at the given [offset].
   * The offset may be `-1` if the import is synthetic.
   */
  ImportElementImpl(int offset)
      : _unlinkedImport = null,
        _linkedDependency = null,
        super(null, offset);

  /**
   * Initialize using the given serialized information.
   */
  ImportElementImpl.forSerialized(this._unlinkedImport, this._linkedDependency,
      LibraryElementImpl enclosingLibrary)
      : super.forSerialized(enclosingLibrary);

  @override
  List<NamespaceCombinator> get combinators {
    if (_unlinkedImport != null && _combinators == null) {
      _combinators = _buildCombinators(_unlinkedImport.combinators);
    }
    return _combinators ?? const <NamespaceCombinator>[];
  }

  void set combinators(List<NamespaceCombinator> combinators) {
    assert(_unlinkedImport == null);
    _combinators = combinators;
  }

  /**
   * Set whether this import is for a deferred library.
   */
  void set deferred(bool isDeferred) {
    assert(_unlinkedImport == null);
    setModifier(Modifier.DEFERRED, isDeferred);
  }

  @override
  String get identifier => "${importedLibrary.identifier}@$nameOffset";

  @override
  LibraryElement get importedLibrary {
    if (_linkedDependency != null) {
      if (_importedLibrary == null) {
        LibraryElementImpl library = enclosingElement as LibraryElementImpl;
        if (_linkedDependency == 0) {
          _importedLibrary = library;
        } else {
          _importedLibrary = library.resynthesizerContext
              .buildImportedLibrary(_linkedDependency);
        }
      }
    }
    return _importedLibrary;
  }

  void set importedLibrary(LibraryElement importedLibrary) {
    assert(_unlinkedImport == null);
    _importedLibrary = importedLibrary;
  }

  @override
  bool get isDeferred {
    if (_unlinkedImport != null) {
      return _unlinkedImport.isDeferred;
    }
    return hasModifier(Modifier.DEFERRED);
  }

  @override
  bool get isSynthetic {
    if (_unlinkedImport != null) {
      return _unlinkedImport.isImplicit;
    }
    return super.isSynthetic;
  }

  @override
  ElementKind get kind => ElementKind.IMPORT;

  @override
  List<ElementAnnotation> get metadata {
    if (_unlinkedImport != null) {
      return _metadata ??= _buildAnnotations(
          library.definingCompilationUnit as CompilationUnitElementImpl,
          _unlinkedImport.annotations);
    }
    return super.metadata;
  }

  void set metadata(List<ElementAnnotation> metadata) {
    assert(_unlinkedImport == null);
    super.metadata = metadata;
  }

  @override
  int get nameOffset {
    if (_unlinkedImport != null) {
      if (_unlinkedImport.isImplicit) {
        return -1;
      }
      return _unlinkedImport.offset;
    }
    return super.nameOffset;
  }

  PrefixElement get prefix {
    if (_unlinkedImport != null) {
      if (_unlinkedImport.prefixReference != 0 && _prefix == null) {
        LibraryElementImpl library = enclosingElement as LibraryElementImpl;
        _prefix = new PrefixElementImpl.forSerialized(_unlinkedImport, library);
      }
    }
    return _prefix;
  }

  void set prefix(PrefixElement prefix) {
    assert(_unlinkedImport == null);
    _prefix = prefix;
  }

  @override
  int get prefixOffset {
    if (_unlinkedImport != null) {
      return _unlinkedImport.prefixOffset;
    }
    return _prefixOffset;
  }

  void set prefixOffset(int prefixOffset) {
    assert(_unlinkedImport == null);
    _prefixOffset = prefixOffset;
  }

  @override
  String get uri {
    if (_unlinkedImport != null) {
      if (_unlinkedImport.isImplicit) {
        return null;
      }
      return _selectedUri ??=
          _selectUri(_unlinkedImport.uri, _unlinkedImport.configurations);
    }
    return super.uri;
  }

  @override
  void set uri(String uri) {
    assert(_unlinkedImport == null);
    super.uri = uri;
  }

  @override
  int get uriEnd {
    if (_unlinkedImport != null) {
      if (_unlinkedImport.isImplicit) {
        return -1;
      }
      return _unlinkedImport.uriEnd;
    }
    return super.uriEnd;
  }

  @override
  void set uriEnd(int uriEnd) {
    assert(_unlinkedImport == null);
    super.uriEnd = uriEnd;
  }

  @override
  int get uriOffset {
    if (_unlinkedImport != null) {
      if (_unlinkedImport.isImplicit) {
        return -1;
      }
      return _unlinkedImport.uriOffset;
    }
    return super.uriOffset;
  }

  @override
  void set uriOffset(int uriOffset) {
    assert(_unlinkedImport == null);
    super.uriOffset = uriOffset;
  }

  @override
  accept(ElementVisitor visitor) => visitor.visitImportElement(this);

  @override
  void appendTo(StringBuffer buffer) {
    buffer.write("import ");
    (importedLibrary as LibraryElementImpl).appendTo(buffer);
  }

  @override
  void visitChildren(ElementVisitor visitor) {
    super.visitChildren(visitor);
    prefix?.accept(visitor);
  }

  static List<NamespaceCombinator> _buildCombinators(
      List<UnlinkedCombinator> unlinkedCombinators) {
    int length = unlinkedCombinators.length;
    if (length != 0) {
      List<NamespaceCombinator> combinators =
          new List<NamespaceCombinator>(length);
      for (int i = 0; i < length; i++) {
        UnlinkedCombinator unlinkedCombinator = unlinkedCombinators[i];
        combinators[i] = unlinkedCombinator.shows.isNotEmpty
            ? new ShowElementCombinatorImpl.forSerialized(unlinkedCombinator)
            : new HideElementCombinatorImpl.forSerialized(unlinkedCombinator);
      }
      return combinators;
    } else {
      return const <NamespaceCombinator>[];
    }
  }
}

/**
 * A concrete implementation of a [LabelElement].
 */
class LabelElementImpl extends ElementImpl implements LabelElement {
  /**
   * The unlinked representation of the label in the summary.
   */
  final UnlinkedLabel _unlinkedLabel;

  /**
   * A flag indicating whether this label is associated with a `switch`
   * statement.
   */
  // TODO(brianwilkerson) Make this a modifier.
  final bool _onSwitchStatement;

  /**
   * A flag indicating whether this label is associated with a `switch` member
   * (`case` or `default`).
   */
  // TODO(brianwilkerson) Make this a modifier.
  final bool _onSwitchMember;

  /**
   * Initialize a newly created label element to have the given [name].
   * [onSwitchStatement] should be `true` if this label is associated with a
   * `switch` statement and [onSwitchMember] should be `true` if this label is
   * associated with a `switch` member.
   */
  LabelElementImpl(String name, int nameOffset, this._onSwitchStatement,
      this._onSwitchMember)
      : _unlinkedLabel = null,
        super(name, nameOffset);

  /**
   * Initialize a newly created label element to have the given [name].
   * [_onSwitchStatement] should be `true` if this label is associated with a
   * `switch` statement and [_onSwitchMember] should be `true` if this label is
   * associated with a `switch` member.
   */
  LabelElementImpl.forNode(
      Identifier name, this._onSwitchStatement, this._onSwitchMember)
      : _unlinkedLabel = null,
        super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  LabelElementImpl.forSerialized(
      UnlinkedLabel unlinkedLabel, ExecutableElementImpl enclosingExecutable)
      : _unlinkedLabel = unlinkedLabel,
        _onSwitchStatement = unlinkedLabel.isOnSwitchStatement,
        _onSwitchMember = unlinkedLabel.isOnSwitchMember,
        super.forSerialized(enclosingExecutable);

  @override
  String get displayName => name;

  @override
  ExecutableElement get enclosingElement =>
      super.enclosingElement as ExecutableElement;

  /**
   * Return `true` if this label is associated with a `switch` member (`case` or
   * `default`).
   */
  bool get isOnSwitchMember => _onSwitchMember;

  /**
   * Return `true` if this label is associated with a `switch` statement.
   */
  bool get isOnSwitchStatement => _onSwitchStatement;

  @override
  ElementKind get kind => ElementKind.LABEL;

  @override
  String get name {
    if (_unlinkedLabel != null) {
      return _unlinkedLabel.name;
    }
    return super.name;
  }

  @override
  int get nameOffset {
    if (_unlinkedLabel != null) {
      return _unlinkedLabel.nameOffset;
    }
    return super.nameOffset;
  }

  @override
  accept(ElementVisitor visitor) => visitor.visitLabelElement(this);

  /**
   * Create and return [LabelElement]s for the given [unlinkedLabels].
   */
  static List<LabelElement> resynthesizeList(
      ExecutableElementImpl enclosingExecutable,
      List<UnlinkedLabel> unlinkedLabels) {
    int length = unlinkedLabels.length;
    if (length != 0) {
      List<LabelElement> elements = new List<LabelElement>(length);
      for (int i = 0; i < length; i++) {
        elements[i] = new LabelElementImpl.forSerialized(
            unlinkedLabels[i], enclosingExecutable);
      }
      return elements;
    } else {
      return const <LabelElement>[];
    }
  }
}

/**
 * A concrete implementation of a [LibraryElement].
 */
class LibraryElementImpl extends ElementImpl implements LibraryElement {
  /**
   * The analysis context in which this library is defined.
   */
  final AnalysisContext context;

  final LibraryResynthesizerContext resynthesizerContext;

  final UnlinkedUnit _unlinkedDefiningUnit;

  /**
   * The compilation unit that defines this library.
   */
  CompilationUnitElement _definingCompilationUnit;

  /**
   * The entry point for this library, or `null` if this library does not have
   * an entry point.
   */
  FunctionElement _entryPoint;

  /**
   * A list containing specifications of all of the imports defined in this
   * library.
   */
  List<ImportElement> _imports;

  /**
   * A list containing specifications of all of the exports defined in this
   * library.
   */
  List<ExportElement> _exports;

  /**
   * A list containing the strongly connected component in the import/export
   * graph in which the current library resides.  Computed on demand, null
   * if not present.  If _libraryCycle is set, then the _libraryCycle field
   * for all libraries reachable from this library in the import/export graph
   * is also set.
   */
  List<LibraryElement> _libraryCycle = null;

  /**
   * A list containing all of the compilation units that are included in this
   * library using a `part` directive.
   */
  List<CompilationUnitElement> _parts = CompilationUnitElement.EMPTY_LIST;

  /**
   * The element representing the synthetic function `loadLibrary` that is
   * defined for this library, or `null` if the element has not yet been created.
   */
  FunctionElement _loadLibraryFunction;

  @override
  final int nameLength;

  /**
   * The export [Namespace] of this library, `null` if it has not been
   * computed yet.
   */
  Namespace _exportNamespace;

  /**
   * The public [Namespace] of this library, `null` if it has not been
   * computed yet.
   */
  Namespace _publicNamespace;

  /**
   * A bit-encoded form of the capabilities associated with this library.
   */
  int _resolutionCapabilities = 0;

  /**
   * The cached list of prefixes.
   */
  List<PrefixElement> _prefixes;

  /**
   * Initialize a newly created library element in the given [context] to have
   * the given [name] and [offset].
   */
  LibraryElementImpl(this.context, String name, int offset, this.nameLength)
      : resynthesizerContext = null,
        _unlinkedDefiningUnit = null,
        super(name, offset);

  /**
   * Initialize a newly created library element in the given [context] to have
   * the given [name].
   */
  LibraryElementImpl.forNode(this.context, LibraryIdentifier name)
      : nameLength = name != null ? name.length : 0,
        resynthesizerContext = null,
        _unlinkedDefiningUnit = null,
        super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  LibraryElementImpl.forSerialized(this.context, String name, int offset,
      this.nameLength, this.resynthesizerContext, this._unlinkedDefiningUnit)
      : super.forSerialized(null) {
    _name = name;
    _nameOffset = offset;
    setResolutionCapability(
        LibraryResolutionCapability.resolvedTypeNames, true);
    setResolutionCapability(
        LibraryResolutionCapability.constantExpressions, true);
  }

  @override
  int get codeLength {
    CompilationUnitElement unit = _definingCompilationUnit;
    if (unit is CompilationUnitElementImpl) {
      return unit.codeLength;
    }
    return null;
  }

  @override
  int get codeOffset {
    CompilationUnitElement unit = _definingCompilationUnit;
    if (unit is CompilationUnitElementImpl) {
      return unit.codeOffset;
    }
    return null;
  }

  @override
  CompilationUnitElement get definingCompilationUnit =>
      _definingCompilationUnit;

  /**
   * Set the compilation unit that defines this library to the given compilation
   * [unit].
   */
  void set definingCompilationUnit(CompilationUnitElement unit) {
    assert((unit as CompilationUnitElementImpl).librarySource == unit.source);
    (unit as CompilationUnitElementImpl).enclosingElement = this;
    this._definingCompilationUnit = unit;
  }

  @override
  String get documentationComment {
    if (_unlinkedDefiningUnit != null) {
      return _unlinkedDefiningUnit?.libraryDocumentationComment?.text;
    }
    return super.documentationComment;
  }

  FunctionElement get entryPoint {
    if (resynthesizerContext != null) {
      _entryPoint ??= resynthesizerContext.findEntryPoint();
    }
    return _entryPoint;
  }

  void set entryPoint(FunctionElement entryPoint) {
    _entryPoint = entryPoint;
  }

  @override
  List<LibraryElement> get exportedLibraries {
    HashSet<LibraryElement> libraries = new HashSet<LibraryElement>();
    for (ExportElement element in exports) {
      LibraryElement library = element.exportedLibrary;
      if (library != null) {
        libraries.add(library);
      }
    }
    return libraries.toList(growable: false);
  }

  @override
  Namespace get exportNamespace {
    if (resynthesizerContext != null) {
      _exportNamespace ??= resynthesizerContext.buildExportNamespace();
    }
    return _exportNamespace;
  }

  void set exportNamespace(Namespace exportNamespace) {
    _exportNamespace = exportNamespace;
  }

  @override
  List<ExportElement> get exports {
    if (_unlinkedDefiningUnit != null && _exports == null) {
      List<UnlinkedExportNonPublic> unlinkedNonPublicExports =
          _unlinkedDefiningUnit.exports;
      List<UnlinkedExportPublic> unlinkedPublicExports =
          _unlinkedDefiningUnit.publicNamespace.exports;
      assert(
          _unlinkedDefiningUnit.exports.length == unlinkedPublicExports.length);
      int length = unlinkedNonPublicExports.length;
      if (length != 0) {
        List<ExportElement> exports = new List<ExportElement>(length);
        for (int i = 0; i < length; i++) {
          UnlinkedExportPublic serializedExportPublic =
              unlinkedPublicExports[i];
          UnlinkedExportNonPublic serializedExportNonPublic =
              unlinkedNonPublicExports[i];
          exports[i] = new ExportElementImpl.forSerialized(
              serializedExportPublic, serializedExportNonPublic, library);
        }
        _exports = exports;
      } else {
        _exports = const <ExportElement>[];
      }
    }
    return _exports ?? const <ExportElement>[];
  }

  /**
   * Set the specifications of all of the exports defined in this library to the
   * given list of [exports].
   */
  void set exports(List<ExportElement> exports) {
    assert(_unlinkedDefiningUnit == null);
    for (ExportElement exportElement in exports) {
      (exportElement as ExportElementImpl).enclosingElement = this;
    }
    this._exports = exports;
  }

  @override
  bool get hasExtUri => hasModifier(Modifier.HAS_EXT_URI);

  /**
   * Set whether this library has an import of a "dart-ext" URI.
   */
  void set hasExtUri(bool hasExtUri) {
    setModifier(Modifier.HAS_EXT_URI, hasExtUri);
  }

  @override
  bool get hasLoadLibraryFunction {
    if (_definingCompilationUnit.hasLoadLibraryFunction) {
      return true;
    }
    for (int i = 0; i < _parts.length; i++) {
      if (_parts[i].hasLoadLibraryFunction) {
        return true;
      }
    }
    return false;
  }

  @override
  String get identifier => _definingCompilationUnit.source.encoding;

  @override
  List<LibraryElement> get importedLibraries {
    HashSet<LibraryElement> libraries = new HashSet<LibraryElement>();
    for (ImportElement element in imports) {
      LibraryElement library = element.importedLibrary;
      if (library != null) {
        libraries.add(library);
      }
    }
    return libraries.toList(growable: false);
  }

  @override
  List<ImportElement> get imports {
    if (_unlinkedDefiningUnit != null && _imports == null) {
      List<UnlinkedImport> unlinkedImports = _unlinkedDefiningUnit.imports;
      int length = unlinkedImports.length;
      if (length != 0) {
        List<ImportElement> imports = new List<ImportElement>(length);
        LinkedLibrary linkedLibrary = resynthesizerContext.linkedLibrary;
        for (int i = 0; i < length; i++) {
          imports[i] = new ImportElementImpl.forSerialized(
              unlinkedImports[i], linkedLibrary.importDependencies[i], library);
        }
        _imports = imports;
      } else {
        _imports = const <ImportElement>[];
      }
    }
    return _imports ?? ImportElement.EMPTY_LIST;
  }

  /**
   * Set the specifications of all of the imports defined in this library to the
   * given list of [imports].
   */
  void set imports(List<ImportElement> imports) {
    assert(_unlinkedDefiningUnit == null);
    for (ImportElement importElement in imports) {
      (importElement as ImportElementImpl).enclosingElement = this;
      PrefixElementImpl prefix = importElement.prefix as PrefixElementImpl;
      if (prefix != null) {
        prefix.enclosingElement = this;
      }
    }
    this._imports = imports;
    this._prefixes = null;
  }

  @override
  bool get isBrowserApplication =>
      entryPoint != null && isOrImportsBrowserLibrary;

  @override
  bool get isDartAsync => name == "dart.async";

  @override
  bool get isDartCore => name == "dart.core";

  @override
  bool get isInSdk =>
      StringUtilities.startsWith5(name, 0, 0x64, 0x61, 0x72, 0x74, 0x2E);

  /**
   * Return `true` if the receiver directly or indirectly imports the
   * 'dart:html' libraries.
   */
  bool get isOrImportsBrowserLibrary {
    List<LibraryElement> visited = new List<LibraryElement>();
    Source htmlLibSource = context.sourceFactory.forUri(DartSdk.DART_HTML);
    visited.add(this);
    for (int index = 0; index < visited.length; index++) {
      LibraryElement library = visited[index];
      Source source = library.definingCompilationUnit.source;
      if (source == htmlLibSource) {
        return true;
      }
      for (LibraryElement importedLibrary in library.importedLibraries) {
        if (!visited.contains(importedLibrary)) {
          visited.add(importedLibrary);
        }
      }
      for (LibraryElement exportedLibrary in library.exportedLibraries) {
        if (!visited.contains(exportedLibrary)) {
          visited.add(exportedLibrary);
        }
      }
    }
    return false;
  }

  @override
  bool get isResynthesized {
    return resynthesizerContext != null;
  }

  @override
  ElementKind get kind => ElementKind.LIBRARY;

  @override
  LibraryElement get library => this;

  @override
  List<LibraryElement> get libraryCycle {
    if (_libraryCycle != null) {
      return _libraryCycle;
    }

    // Global counter for this run of the algorithm
    int counter = 0;
    // The discovery times of each library
    Map<LibraryElementImpl, int> indices = {};
    // The set of scc candidates
    Set<LibraryElementImpl> active = new Set();
    // The stack of discovered elements
    List<LibraryElementImpl> stack = [];
    // For a given library that has not yet been processed by this run of the
    // algorithm, compute the strongly connected components.
    int scc(LibraryElementImpl library) {
      int index = counter++;
      int root = index;
      indices[library] = index;
      active.add(library);
      stack.add(library);
      LibraryElementImpl getActualLibrary(LibraryElement lib) {
        // TODO(paulberry): this means that computing a library cycle will be
        // expensive for libraries resynthesized from summaries, since it will
        // require fully resynthesizing all the libraries in the cycle as well
        // as any libraries they import or export.  Try to find a better way.
        if (lib is LibraryElementHandle) {
          return lib.actualElement;
        } else {
          return lib;
        }
      }

      void recurse(LibraryElementImpl child) {
        if (!indices.containsKey(child)) {
          // We haven't visited this child yet, so recurse on the child,
          // returning the lowest numbered node reachable from the child.  If
          // the child can reach a root which is lower numbered than anything
          // we've reached so far, update the root.
          root = min(root, scc(child));
        } else if (active.contains(child)) {
          // The child has been visited, but has not yet been placed into a
          // component.  If the child is higher than anything we've seen so far
          // update the root appropriately.
          root = min(root, indices[child]);
        }
      }

      // Recurse on all of the children in the import/export graph, filtering
      // out those for which library cycles have already been computed.
      library.exportedLibraries
          .map(getActualLibrary)
          .where((l) => l._libraryCycle == null)
          .forEach(recurse);
      library.importedLibraries
          .map(getActualLibrary)
          .where((l) => l._libraryCycle == null)
          .forEach(recurse);

      if (root == index) {
        // This is the root of a strongly connected component.
        // Pop the elements, and share the component across all
        // of the elements.
        List<LibraryElement> component = <LibraryElement>[];
        LibraryElementImpl cur = null;
        do {
          cur = stack.removeLast();
          active.remove(cur);
          component.add(cur);
          cur._libraryCycle = component;
        } while (cur != library);
      }
      return root;
    }

    scc(library);
    return _libraryCycle;
  }

  @override
  FunctionElement get loadLibraryFunction {
    assert(_loadLibraryFunction != null);
    return _loadLibraryFunction;
  }

  @override
  List<ElementAnnotation> get metadata {
    if (_unlinkedDefiningUnit != null) {
      _metadata ??= _buildAnnotations(
          _definingCompilationUnit as CompilationUnitElementImpl,
          _unlinkedDefiningUnit.libraryAnnotations);
      return _metadata;
    }
    return super.metadata;
  }

  @override
  List<CompilationUnitElement> get parts => _parts;

  /**
   * Set the compilation units that are included in this library using a `part`
   * directive to the given list of [parts].
   */
  void set parts(List<CompilationUnitElement> parts) {
    for (CompilationUnitElement compilationUnit in parts) {
      assert((compilationUnit as CompilationUnitElementImpl).librarySource ==
          source);
      (compilationUnit as CompilationUnitElementImpl).enclosingElement = this;
    }
    this._parts = parts;
  }

  @override
  List<PrefixElement> get prefixes {
    if (_prefixes == null) {
      HashSet<PrefixElement> prefixes = new HashSet<PrefixElement>();
      for (ImportElement element in imports) {
        PrefixElement prefix = element.prefix;
        if (prefix != null) {
          prefixes.add(prefix);
        }
      }
      _prefixes = prefixes.toList(growable: false);
    }
    return _prefixes;
  }

  @override
  Namespace get publicNamespace {
    if (resynthesizerContext != null) {
      _publicNamespace ??= resynthesizerContext.buildPublicNamespace();
    }
    return _publicNamespace;
  }

  void set publicNamespace(Namespace publicNamespace) {
    _publicNamespace = publicNamespace;
  }

  @override
  Source get source {
    if (_definingCompilationUnit == null) {
      return null;
    }
    return _definingCompilationUnit.source;
  }

  @override
  List<CompilationUnitElement> get units {
    List<CompilationUnitElement> units = new List<CompilationUnitElement>();
    units.add(_definingCompilationUnit);
    units.addAll(_parts);
    return units;
  }

  @override
  accept(ElementVisitor visitor) => visitor.visitLibraryElement(this);

  /**
   * Create the [FunctionElement] to be returned by [loadLibraryFunction],
   * using types provided by [typeProvider].
   */
  void createLoadLibraryFunction(TypeProvider typeProvider) {
    FunctionElementImpl function =
        new FunctionElementImpl(FunctionElement.LOAD_LIBRARY_NAME, -1);
    function.synthetic = true;
    function.enclosingElement = this;
    function.returnType = typeProvider.futureDynamicType;
    function.type = new FunctionTypeImpl(function);
    _loadLibraryFunction = function;
  }

  @override
  ElementImpl getChild(String identifier) {
    CompilationUnitElementImpl unitImpl = _definingCompilationUnit;
    if (unitImpl.identifier == identifier) {
      return unitImpl;
    }
    for (CompilationUnitElement part in _parts) {
      CompilationUnitElementImpl partImpl = part;
      if (partImpl.identifier == identifier) {
        return partImpl;
      }
    }
    for (ImportElement importElement in imports) {
      ImportElementImpl importElementImpl = importElement;
      if (importElementImpl.identifier == identifier) {
        return importElementImpl;
      }
    }
    for (ExportElement exportElement in exports) {
      ExportElementImpl exportElementImpl = exportElement;
      if (exportElementImpl.identifier == identifier) {
        return exportElementImpl;
      }
    }
    return null;
  }

  @override
  List<ImportElement> getImportsWithPrefix(PrefixElement prefixElement) {
    var imports = this.imports;
    int count = imports.length;
    List<ImportElement> importList = new List<ImportElement>();
    for (int i = 0; i < count; i++) {
      if (identical(imports[i].prefix, prefixElement)) {
        importList.add(imports[i]);
      }
    }
    return importList;
  }

  @override
  ClassElement getType(String className) {
    ClassElement type = _definingCompilationUnit.getType(className);
    if (type != null) {
      return type;
    }
    for (CompilationUnitElement part in _parts) {
      type = part.getType(className);
      if (type != null) {
        return type;
      }
    }
    return null;
  }

  /** Given an update to this library which may have added or deleted edges
   * in the import/export graph originating from this node only, remove any
   * cached library cycles in the element model which may have been invalidated.
   */
  void invalidateLibraryCycles() {
    // If we have pre-computed library cycle information, then we must
    // invalidate the information both on this element, and on certain
    // other elements.  Edges originating at this node may have been
    // added or deleted.  A deleted edge that points outside of this cycle
    // cannot change the cycle information for anything outside of this cycle,
    // and so it is sufficient to delete the cached library information on this
    // cycle.  An added edge which points to another node within the cycle
    // only invalidates the cycle.  An added edge which points to a node earlier
    // in the topological sort of cycles induces no invalidation (since there
    // are by definition no back edges from earlier cycles in the topological
    // order, and hence no possible cycle can have been introduced.  The only
    // remaining case is that we have added an edge to a node which is later
    // in the topological sort of cycles.  This can induce cycles, since it
    // represents a new back edge.  It would be sufficient to invalidate the
    // cycle information for all nodes that are between the target and the
    // node in the topological order.  For simplicity, we simply invalidate
    // all nodes which are reachable from the source node.
    // Note that in the invalidation phase, we do not cut off when we encounter
    // a node with no library cycle information, since we do not know whether
    // we are in the case where invalidation has already been performed, or we
    // are in the case where library cycles have simply never been computed from
    // a newly reachable node.
    Set<LibraryElementImpl> active = new HashSet();
    void invalidate(LibraryElement element) {
      LibraryElementImpl library =
          element is LibraryElementHandle ? element.actualElement : element;
      if (active.add(library)) {
        if (library._libraryCycle != null) {
          library._libraryCycle.forEach(invalidate);
          library._libraryCycle = null;
        }
        library.exportedLibraries.forEach(invalidate);
        library.importedLibraries.forEach(invalidate);
      }
    }

    invalidate(this);
  }

  /**
   * Set whether the library has the given [capability] to
   * correspond to the given [value].
   */
  void setResolutionCapability(
      LibraryResolutionCapability capability, bool value) {
    _resolutionCapabilities =
        BooleanArray.set(_resolutionCapabilities, capability.index, value);
  }

  @override
  void visitChildren(ElementVisitor visitor) {
    super.visitChildren(visitor);
    _definingCompilationUnit?.accept(visitor);
    safelyVisitChildren(exports, visitor);
    safelyVisitChildren(imports, visitor);
    safelyVisitChildren(_parts, visitor);
  }

  /**
   * Return `true` if the [library] has the given [capability].
   */
  static bool hasResolutionCapability(
      LibraryElement library, LibraryResolutionCapability capability) {
    return library is LibraryElementImpl &&
        BooleanArray.get(library._resolutionCapabilities, capability.index);
  }
}

/**
 * Enum of possible resolution capabilities that a [LibraryElementImpl] has.
 */
enum LibraryResolutionCapability {
  /**
   * All elements have their types resolved.
   */
  resolvedTypeNames,

  /**
   * All (potentially) constants expressions are set into corresponding
   * elements.
   */
  constantExpressions,
}

/**
 * The context in which the library is resynthesized.
 */
abstract class LibraryResynthesizerContext {
  /**
   * Return the [LinkedLibrary] that corresponds to the library being
   * resynthesized.
   */
  LinkedLibrary get linkedLibrary;

  /**
   * Return the exported [LibraryElement] for with the given [relativeUri].
   */
  LibraryElement buildExportedLibrary(String relativeUri);

  /**
   * Return the export namespace of the library.
   */
  Namespace buildExportNamespace();

  /**
   * Return the imported [LibraryElement] for the given dependency in the
   * linked library.
   */
  LibraryElement buildImportedLibrary(int dependency);

  /**
   * Return the public namespace of the library.
   */
  Namespace buildPublicNamespace();

  /**
   * Find the entry point of the library.
   */
  FunctionElement findEntryPoint();

  /**
   * Ensure that getters and setters in different units use the same
   * top-level variables.
   */
  void patchTopLevelAccessors();
}

/**
 * A concrete implementation of a [LocalVariableElement].
 */
class LocalVariableElementImpl extends NonParameterVariableElementImpl
    implements LocalVariableElement {
  /**
   * The offset to the beginning of the visible range for this element.
   */
  int _visibleRangeOffset = 0;

  /**
   * The length of the visible range for this element, or `-1` if this element
   * does not have a visible range.
   */
  int _visibleRangeLength = -1;

  /**
   * Initialize a newly created method element to have the given [name] and
   * [offset].
   */
  LocalVariableElementImpl(String name, int offset) : super(name, offset);

  /**
   * Initialize a newly created local variable element to have the given [name].
   */
  LocalVariableElementImpl.forNode(Identifier name) : super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  LocalVariableElementImpl.forSerialized(UnlinkedVariable unlinkedVariable,
      ExecutableElementImpl enclosingExecutable)
      : super.forSerialized(unlinkedVariable, enclosingExecutable);

  /**
   * Initialize using the given serialized information.
   */
  factory LocalVariableElementImpl.forSerializedFactory(
      UnlinkedVariable unlinkedVariable,
      ExecutableElementImpl enclosingExecutable) {
    if (unlinkedVariable.isConst &&
        unlinkedVariable.initializer?.bodyExpr != null) {
      return new ConstLocalVariableElementImpl.forSerialized(
          unlinkedVariable, enclosingExecutable);
    } else {
      return new LocalVariableElementImpl.forSerialized(
          unlinkedVariable, enclosingExecutable);
    }
  }

  @override
  String get identifier {
    String identifier = super.identifier;
    Element enclosing = this.enclosingElement;
    if (enclosing is ExecutableElement) {
      int id = ElementImpl._findElementIndexUsingIdentical(
          enclosing.localVariables, this);
      identifier += "@$id";
    }
    return identifier;
  }

  @override
  bool get isPotentiallyMutatedInClosure => true;

  @override
  bool get isPotentiallyMutatedInScope => true;

  @override
  ElementKind get kind => ElementKind.LOCAL_VARIABLE;

  @override
  SourceRange get visibleRange {
    if (_unlinkedVariable != null) {
      if (_unlinkedVariable.visibleLength == 0) {
        return null;
      }
      return new SourceRange(
          _unlinkedVariable.visibleOffset, _unlinkedVariable.visibleLength);
    }
    if (_visibleRangeLength < 0) {
      return null;
    }
    return new SourceRange(_visibleRangeOffset, _visibleRangeLength);
  }

  @override
  accept(ElementVisitor visitor) => visitor.visitLocalVariableElement(this);

  @override
  void appendTo(StringBuffer buffer) {
    buffer.write(type);
    buffer.write(" ");
    buffer.write(displayName);
  }

  @override
  Declaration computeNode() => getNodeMatching(
      (node) => node is DeclaredIdentifier || node is VariableDeclaration);

  /**
   * Set the visible range for this element to the range starting at the given
   * [offset] with the given [length].
   */
  void setVisibleRange(int offset, int length) {
    assert(_unlinkedVariable == null);
    _visibleRangeOffset = offset;
    _visibleRangeLength = length;
  }
}

/**
 * A concrete implementation of a [MethodElement].
 */
class MethodElementImpl extends ExecutableElementImpl implements MethodElement {
  /**
   * Initialize a newly created method element to have the given [name] at the
   * given [offset].
   */
  MethodElementImpl(String name, int offset) : super(name, offset);

  /**
   * Initialize a newly created method element to have the given [name].
   */
  MethodElementImpl.forNode(Identifier name) : super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  MethodElementImpl.forSerialized(
      UnlinkedExecutable serializedExecutable, ClassElementImpl enclosingClass)
      : super.forSerialized(serializedExecutable, enclosingClass);

  /**
   * Set whether this method is abstract.
   */
  void set abstract(bool isAbstract) {
    assert(serializedExecutable == null);
    setModifier(Modifier.ABSTRACT, isAbstract);
  }

  @override
  List<TypeParameterType> get allEnclosingTypeParameterTypes {
    if (isStatic) {
      return const <TypeParameterType>[];
    }
    return super.allEnclosingTypeParameterTypes;
  }

  @override
  String get displayName {
    String displayName = super.displayName;
    if ("unary-" == displayName) {
      return "-";
    }
    return displayName;
  }

  @override
  ClassElement get enclosingElement => super.enclosingElement as ClassElement;

  @override
  TypeParameterizedElementMixin get enclosingTypeParameterContext =>
      super.enclosingElement as ClassElementImpl;

  @override
  bool get isOperator {
    String name = displayName;
    if (name.isEmpty) {
      return false;
    }
    int first = name.codeUnitAt(0);
    return !((0x61 <= first && first <= 0x7A) ||
        (0x41 <= first && first <= 0x5A) ||
        first == 0x5F ||
        first == 0x24);
  }

  @override
  bool get isStatic {
    if (serializedExecutable != null) {
      return serializedExecutable.isStatic;
    }
    return hasModifier(Modifier.STATIC);
  }

  @override
  ElementKind get kind => ElementKind.METHOD;

  @override
  String get name {
    String name = super.name;
    if (name == '-' && parameters.isEmpty) {
      return 'unary-';
    }
    return super.name;
  }

  /**
   * Set whether this method is static.
   */
  void set static(bool isStatic) {
    assert(serializedExecutable == null);
    setModifier(Modifier.STATIC, isStatic);
  }

  @override
  accept(ElementVisitor visitor) => visitor.visitMethodElement(this);

  @override
  void appendTo(StringBuffer buffer) {
    buffer.write(displayName);
    super.appendTo(buffer);
  }

  @override
  MethodDeclaration computeNode() =>
      getNodeMatching((node) => node is MethodDeclaration);
}

/**
 * The constants for all of the modifiers defined by the Dart language and for a
 * few additional flags that are useful.
 *
 * Clients may not extend, implement or mix-in this class.
 */
class Modifier implements Comparable<Modifier> {
  /**
   * Indicates that the modifier 'abstract' was applied to the element.
   */
  static const Modifier ABSTRACT = const Modifier('ABSTRACT', 0);

  /**
   * Indicates that an executable element has a body marked as being
   * asynchronous.
   */
  static const Modifier ASYNCHRONOUS = const Modifier('ASYNCHRONOUS', 1);

  /**
   * Indicates that the modifier 'const' was applied to the element.
   */
  static const Modifier CONST = const Modifier('CONST', 2);

  /**
   * Indicates that the import element represents a deferred library.
   */
  static const Modifier DEFERRED = const Modifier('DEFERRED', 3);

  /**
   * Indicates that a class element was defined by an enum declaration.
   */
  static const Modifier ENUM = const Modifier('ENUM', 4);

  /**
   * Indicates that a class element was defined by an enum declaration.
   */
  static const Modifier EXTERNAL = const Modifier('EXTERNAL', 5);

  /**
   * Indicates that the modifier 'factory' was applied to the element.
   */
  static const Modifier FACTORY = const Modifier('FACTORY', 6);

  /**
   * Indicates that the modifier 'final' was applied to the element.
   */
  static const Modifier FINAL = const Modifier('FINAL', 7);

  /**
   * Indicates that an executable element has a body marked as being a
   * generator.
   */
  static const Modifier GENERATOR = const Modifier('GENERATOR', 8);

  /**
   * Indicates that the pseudo-modifier 'get' was applied to the element.
   */
  static const Modifier GETTER = const Modifier('GETTER', 9);

  /**
   * A flag used for libraries indicating that the defining compilation unit
   * contains at least one import directive whose URI uses the "dart-ext"
   * scheme.
   */
  static const Modifier HAS_EXT_URI = const Modifier('HAS_EXT_URI', 10);

  /**
   * Indicates that the associated element did not have an explicit type
   * associated with it. If the element is an [ExecutableElement], then the
   * type being referred to is the return type.
   */
  static const Modifier IMPLICIT_TYPE = const Modifier('IMPLICIT_TYPE', 11);

  /**
   * Indicates that a class is a mixin application.
   */
  static const Modifier MIXIN_APPLICATION =
      const Modifier('MIXIN_APPLICATION', 12);

  /**
   * Indicates that a class contains an explicit reference to 'super'.
   */
  static const Modifier REFERENCES_SUPER =
      const Modifier('REFERENCES_SUPER', 13);

  /**
   * Indicates that the pseudo-modifier 'set' was applied to the element.
   */
  static const Modifier SETTER = const Modifier('SETTER', 14);

  /**
   * Indicates that the modifier 'static' was applied to the element.
   */
  static const Modifier STATIC = const Modifier('STATIC', 15);

  /**
   * Indicates that the element does not appear in the source code but was
   * implicitly created. For example, if a class does not define any
   * constructors, an implicit zero-argument constructor will be created and it
   * will be marked as being synthetic.
   */
  static const Modifier SYNTHETIC = const Modifier('SYNTHETIC', 16);

  static const List<Modifier> values = const [
    ABSTRACT,
    ASYNCHRONOUS,
    CONST,
    DEFERRED,
    ENUM,
    EXTERNAL,
    FACTORY,
    FINAL,
    GENERATOR,
    GETTER,
    HAS_EXT_URI,
    IMPLICIT_TYPE,
    MIXIN_APPLICATION,
    REFERENCES_SUPER,
    SETTER,
    STATIC,
    SYNTHETIC
  ];

  /**
   * The name of this modifier.
   */
  final String name;

  /**
   * The ordinal value of the modifier.
   */
  final int ordinal;

  const Modifier(this.name, this.ordinal);

  @override
  int get hashCode => ordinal;

  @override
  int compareTo(Modifier other) => ordinal - other.ordinal;

  @override
  String toString() => name;
}

/**
 * A concrete implementation of a [MultiplyDefinedElement].
 */
class MultiplyDefinedElementImpl implements MultiplyDefinedElement {
  /**
   * The unique integer identifier of this element.
   */
  final int id = ElementImpl._NEXT_ID++;

  /**
   * The analysis context in which the multiply defined elements are defined.
   */
  final AnalysisContext context;

  /**
   * The name of the conflicting elements.
   */
  String _name;

  /**
   * A list containing all of the elements defined in SDK libraries that
   * conflict.
   */
  final List<Element> sdkElements;

  /**
   * A list containing all of the elements defined in non-SDK libraries that
   * conflict.
   */
  final List<Element> nonSdkElements;

  /**
   * Initialize a newly created element in the given [context] to represent a
   * list of conflicting [sdkElements] and [nonSdkElements]. At least one of the
   * lists must contain more than one element.
   */
  MultiplyDefinedElementImpl(
      this.context, this.sdkElements, this.nonSdkElements) {
    if (nonSdkElements.length > 0) {
      _name = nonSdkElements[0].name;
    } else {
      _name = sdkElements[0].name;
    }
  }

  @override
  List<Element> get conflictingElements {
    if (sdkElements.isEmpty) {
      return nonSdkElements;
    } else if (nonSdkElements.isEmpty) {
      return sdkElements;
    }
    List<Element> elements = nonSdkElements.toList();
    elements.addAll(sdkElements);
    return elements;
  }

  @override
  String get displayName => _name;

  @override
  String get documentationComment => null;

  @override
  Element get enclosingElement => null;

  @override
  bool get isDeprecated => false;

  @override
  bool get isFactory => false;

  @override
  bool get isJS => false;

  @override
  bool get isOverride => false;

  @override
  bool get isPrivate {
    String name = displayName;
    if (name == null) {
      return false;
    }
    return Identifier.isPrivateName(name);
  }

  @override
  bool get isProtected => false;

  @override
  bool get isPublic => !isPrivate;

  @override
  bool get isRequired => false;

  @override
  bool get isSynthetic => true;

  @override
  ElementKind get kind => ElementKind.ERROR;

  @override
  LibraryElement get library => null;

  @override
  Source get librarySource => null;

  @override
  ElementLocation get location => null;

  @override
  List<ElementAnnotation> get metadata => const <ElementAnnotation>[];

  @override
  String get name => _name;

  @override
  int get nameLength => displayName != null ? displayName.length : 0;

  @override
  int get nameOffset => -1;

  @override
  Source get source => null;

  @override
  DartType get type => DynamicTypeImpl.instance;

  @override
  CompilationUnit get unit => null;

  @override
  accept(ElementVisitor visitor) => visitor.visitMultiplyDefinedElement(this);

  @override
  String computeDocumentationComment() => null;

  @override
  AstNode computeNode() => null;

  @override
  Element/*=E*/ getAncestor/*<E extends Element >*/(
          Predicate<Element> predicate) =>
      null;

  @override
  String getExtendedDisplayName(String shortName) {
    if (shortName != null) {
      return shortName;
    }
    return displayName;
  }

  @override
  bool isAccessibleIn(LibraryElement library) {
    for (Element element in conflictingElements) {
      if (element.isAccessibleIn(library)) {
        return true;
      }
    }
    return false;
  }

  @override
  String toString() {
    StringBuffer buffer = new StringBuffer();
    bool needsSeparator = false;
    void writeList(List<Element> elements) {
      for (Element element in elements) {
        if (needsSeparator) {
          buffer.write(", ");
        } else {
          needsSeparator = true;
        }
        if (element is ElementImpl) {
          element.appendTo(buffer);
        } else {
          buffer.write(element);
        }
      }
    }

    buffer.write("[");
    writeList(nonSdkElements);
    writeList(sdkElements);
    buffer.write("]");
    return buffer.toString();
  }

  @override
  void visitChildren(ElementVisitor visitor) {
    // There are no children to visit
  }

  /**
   * Return an element in the given [context] that represents the fact that the
   * [firstElement] and [secondElement] conflict. (If the elements are the same,
   * then one of the two will be returned directly.)
   */
  static Element fromElements(
      AnalysisContext context, Element firstElement, Element secondElement) {
    Set<Element> sdkElements = new HashSet<Element>.identity();
    Set<Element> nonSdkElements = new HashSet<Element>.identity();
    void add(Element element) {
      if (element != null) {
        if (element is MultiplyDefinedElementImpl) {
          sdkElements.addAll(element.sdkElements);
          nonSdkElements.addAll(element.nonSdkElements);
        } else if (element.library.isInSdk) {
          sdkElements.add(element);
        } else {
          nonSdkElements.add(element);
        }
      }
    }

    add(firstElement);
    add(secondElement);
    int nonSdkCount = nonSdkElements.length;
    if (nonSdkCount == 0) {
      int sdkCount = sdkElements.length;
      if (sdkCount == 0) {
        return null;
      } else if (sdkCount == 1) {
        return sdkElements.first;
      }
    } else if (nonSdkCount == 1) {
      return nonSdkElements.first;
    }
    return new MultiplyDefinedElementImpl(
        context,
        sdkElements.toList(growable: false),
        nonSdkElements.toList(growable: false));
  }
}

/**
 * A [MethodElementImpl], with the additional information of a list of
 * [ExecutableElement]s from which this element was composed.
 */
class MultiplyInheritedMethodElementImpl extends MethodElementImpl
    implements MultiplyInheritedExecutableElement {
  /**
   * A list the array of executable elements that were used to compose this
   * element.
   */
  List<ExecutableElement> _elements = MethodElement.EMPTY_LIST;

  MultiplyInheritedMethodElementImpl(Identifier name) : super.forNode(name) {
    synthetic = true;
  }

  @override
  List<ExecutableElement> get inheritedElements => _elements;

  void set inheritedElements(List<ExecutableElement> elements) {
    this._elements = elements;
  }
}

/**
 * A [PropertyAccessorElementImpl], with the additional information of a list of
 * [ExecutableElement]s from which this element was composed.
 */
class MultiplyInheritedPropertyAccessorElementImpl
    extends PropertyAccessorElementImpl
    implements MultiplyInheritedExecutableElement {
  /**
   * A list the array of executable elements that were used to compose this
   * element.
   */
  List<ExecutableElement> _elements = PropertyAccessorElement.EMPTY_LIST;

  MultiplyInheritedPropertyAccessorElementImpl(Identifier name)
      : super.forNode(name) {
    synthetic = true;
  }

  @override
  TypeParameterizedElementMixin get enclosingTypeParameterContext => null;

  @override
  List<ExecutableElement> get inheritedElements => _elements;

  void set inheritedElements(List<ExecutableElement> elements) {
    this._elements = elements;
  }
}

/**
 * A [VariableElementImpl], which is not a parameter.
 */
abstract class NonParameterVariableElementImpl extends VariableElementImpl {
  /**
   * The unlinked representation of the variable in the summary.
   */
  final UnlinkedVariable _unlinkedVariable;

  /**
   * Initialize a newly created variable element to have the given [name] and
   * [offset].
   */
  NonParameterVariableElementImpl(String name, int offset)
      : _unlinkedVariable = null,
        super(name, offset);

  /**
   * Initialize a newly created variable element to have the given [name].
   */
  NonParameterVariableElementImpl.forNode(Identifier name)
      : _unlinkedVariable = null,
        super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  NonParameterVariableElementImpl.forSerialized(
      this._unlinkedVariable, ElementImpl enclosingElement)
      : super.forSerialized(enclosingElement);

  @override
  int get codeLength {
    if (_unlinkedVariable != null) {
      return _unlinkedVariable.codeRange?.length;
    }
    return super.codeLength;
  }

  @override
  int get codeOffset {
    if (_unlinkedVariable != null) {
      return _unlinkedVariable.codeRange?.offset;
    }
    return super.codeOffset;
  }

  @override
  void set const3(bool isConst) {
    assert(_unlinkedVariable == null);
    super.const3 = isConst;
  }

  @override
  String get documentationComment {
    if (_unlinkedVariable != null) {
      return _unlinkedVariable?.documentationComment?.text;
    }
    return super.documentationComment;
  }

  @override
  void set final2(bool isFinal) {
    assert(_unlinkedVariable == null);
    super.final2 = isFinal;
  }

  @override
  bool get hasImplicitType {
    if (_unlinkedVariable != null) {
      return _unlinkedVariable.type == null;
    }
    return super.hasImplicitType;
  }

  @override
  void set hasImplicitType(bool hasImplicitType) {
    assert(_unlinkedVariable == null);
    super.hasImplicitType = hasImplicitType;
  }

  @override
  FunctionElement get initializer {
    if (_unlinkedVariable != null && _initializer == null) {
      UnlinkedExecutable unlinkedInitializer = _unlinkedVariable.initializer;
      if (unlinkedInitializer != null) {
        _initializer = new FunctionElementImpl.forSerialized(
            unlinkedInitializer, this)..synthetic = true;
      } else {
        return null;
      }
    }
    return super.initializer;
  }

  /**
   * Set the function representing this variable's initializer to the given
   * [function].
   */
  void set initializer(FunctionElement function) {
    assert(_unlinkedVariable == null);
    super.initializer = function;
  }

  @override
  bool get isConst {
    if (_unlinkedVariable != null) {
      return _unlinkedVariable.isConst;
    }
    return super.isConst;
  }

  @override
  bool get isFinal {
    if (_unlinkedVariable != null) {
      return _unlinkedVariable.isFinal;
    }
    return super.isFinal;
  }

  @override
  List<ElementAnnotation> get metadata {
    if (_unlinkedVariable != null) {
      return _metadata ??=
          _buildAnnotations(enclosingUnit, _unlinkedVariable.annotations);
    }
    return super.metadata;
  }

  @override
  String get name {
    if (_unlinkedVariable != null) {
      return _unlinkedVariable.name;
    }
    return super.name;
  }

  @override
  int get nameOffset {
    if (_unlinkedVariable != null) {
      return _unlinkedVariable.nameOffset;
    }
    return super.nameOffset;
  }

  @override
  DartType get type {
    if (_unlinkedVariable != null && _type == null) {
      _type = enclosingUnit.resynthesizerContext.resolveLinkedType(
              _unlinkedVariable.inferredTypeSlot, typeParameterContext) ??
          enclosingUnit.resynthesizerContext
              .resolveTypeRef(_unlinkedVariable.type, typeParameterContext);
    }
    return super.type;
  }

  void set type(DartType type) {
    assert(_unlinkedVariable == null);
    _type = type;
  }

  /**
   * Subclasses need this getter, see [ConstVariableElement._unlinkedConst].
   */
  UnlinkedConst get _unlinkedConst => _unlinkedVariable?.initializer?.bodyExpr;
}

/**
 * A concrete implementation of a [ParameterElement].
 */
class ParameterElementImpl extends VariableElementImpl
    with ParameterElementMixin
    implements ParameterElement {
  /**
   * The unlinked representation of the parameter in the summary.
   */
  final UnlinkedParam _unlinkedParam;

  /**
   * A list containing all of the parameters defined by this parameter element.
   * There will only be parameters if this parameter is a function typed
   * parameter.
   */
  List<ParameterElement> _parameters = ParameterElement.EMPTY_LIST;

  /**
   * A list containing all of the type parameters defined for this parameter
   * element. There will only be parameters if this parameter is a function
   * typed parameter.
   */
  List<TypeParameterElement> _typeParameters = TypeParameterElement.EMPTY_LIST;

  /**
   * The kind of this parameter.
   */
  ParameterKind _parameterKind;

  /**
   * The Dart code of the default value.
   */
  String _defaultValueCode;

  /**
   * The offset to the beginning of the visible range for this element.
   */
  int _visibleRangeOffset = 0;

  /**
   * The length of the visible range for this element, or `-1` if this element
   * does not have a visible range.
   */
  int _visibleRangeLength = -1;

  bool _inheritsCovariant = false;

  /**
   * Initialize a newly created parameter element to have the given [name] and
   * [nameOffset].
   */
  ParameterElementImpl(String name, int nameOffset)
      : _unlinkedParam = null,
        super(name, nameOffset);

  /**
   * Initialize a newly created parameter element to have the given [name].
   */
  ParameterElementImpl.forNode(Identifier name)
      : _unlinkedParam = null,
        super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  ParameterElementImpl.forSerialized(
      this._unlinkedParam, ElementImpl enclosingElement)
      : super.forSerialized(enclosingElement);

  /**
   * Initialize using the given serialized information.
   */
  factory ParameterElementImpl.forSerializedFactory(
      UnlinkedParam unlinkedParameter, ElementImpl enclosingElement,
      {bool synthetic: false}) {
    ParameterElementImpl element;
    if (unlinkedParameter.isInitializingFormal) {
      if (unlinkedParameter.kind == UnlinkedParamKind.required) {
        element = new FieldFormalParameterElementImpl.forSerialized(
            unlinkedParameter, enclosingElement);
      } else {
        element = new DefaultFieldFormalParameterElementImpl.forSerialized(
            unlinkedParameter, enclosingElement);
      }
    } else {
      if (unlinkedParameter.kind == UnlinkedParamKind.required) {
        element = new ParameterElementImpl.forSerialized(
            unlinkedParameter, enclosingElement);
      } else {
        element = new DefaultParameterElementImpl.forSerialized(
            unlinkedParameter, enclosingElement);
      }
    }
    element.synthetic = synthetic;
    return element;
  }

  /**
   * Creates a synthetic parameter with [name], [type] and [kind].
   */
  factory ParameterElementImpl.synthetic(
      String name, DartType type, ParameterKind kind) {
    ParameterElementImpl element = new ParameterElementImpl(name, -1);
    element.type = type;
    element.synthetic = true;
    element.parameterKind = kind;
    return element;
  }

  @override
  int get codeLength {
    if (_unlinkedParam != null) {
      return _unlinkedParam.codeRange?.length;
    }
    return super.codeLength;
  }

  @override
  int get codeOffset {
    if (_unlinkedParam != null) {
      return _unlinkedParam.codeRange?.offset;
    }
    return super.codeOffset;
  }

  @override
  void set const3(bool isConst) {
    assert(_unlinkedParam == null);
    super.const3 = isConst;
  }

  @override
  String get defaultValueCode {
    if (_unlinkedParam != null) {
      if (_unlinkedParam.initializer?.bodyExpr == null) {
        return null;
      }
      return _unlinkedParam.defaultValueCode;
    }
    return _defaultValueCode;
  }

  /**
   * Set Dart code of the default value.
   */
  void set defaultValueCode(String defaultValueCode) {
    assert(_unlinkedParam == null);
    this._defaultValueCode = StringUtilities.intern(defaultValueCode);
  }

  @override
  void set final2(bool isFinal) {
    assert(_unlinkedParam == null);
    super.final2 = isFinal;
  }

  @override
  bool get hasImplicitType {
    if (_unlinkedParam != null) {
      return _unlinkedParam.type == null;
    }
    return super.hasImplicitType;
  }

  @override
  void set hasImplicitType(bool hasImplicitType) {
    assert(_unlinkedParam == null);
    super.hasImplicitType = hasImplicitType;
  }

  /**
   * True if this parameter inherits from a covariant parameter. This happens
   * when it overrides a method in a supertype that has a corresponding
   * covariant parameter.
   */
  bool get inheritsCovariant {
    if (_unlinkedParam != null) {
      return enclosingUnit.resynthesizerContext
          .inheritsCovariant(_unlinkedParam.inheritsCovariantSlot);
    } else {
      return _inheritsCovariant;
    }
  }

  /**
   * Record whether or not this parameter inherits from a covariant parameter.
   */
  void set inheritsCovariant(bool value) {
    assert(_unlinkedParam == null);
    _inheritsCovariant = value;
  }

  @override
  FunctionElement get initializer {
    if (_unlinkedParam != null && _initializer == null) {
      UnlinkedExecutable unlinkedInitializer = _unlinkedParam.initializer;
      if (unlinkedInitializer != null) {
        _initializer = new FunctionElementImpl.forSerialized(
            unlinkedInitializer, this)..synthetic = true;
      } else {
        return null;
      }
    }
    return super.initializer;
  }

  /**
   * Set the function representing this variable's initializer to the given
   * [function].
   */
  void set initializer(FunctionElement function) {
    assert(_unlinkedParam == null);
    super.initializer = function;
  }

  @override
  bool get isConst {
    if (_unlinkedParam != null) {
      return false;
    }
    return super.isConst;
  }

  @override
  bool get isCovariant {
    if (inheritsCovariant) {
      return true;
    }
    for (ElementAnnotationImpl annotation in metadata) {
      if (annotation.isCovariant) {
        return true;
      }
    }
    return false;
  }

  @override
  bool get isFinal {
    if (_unlinkedParam != null) {
      return false;
    }
    return super.isFinal;
  }

  @override
  bool get isInitializingFormal => false;

  @override
  bool get isPotentiallyMutatedInClosure => true;

  @override
  bool get isPotentiallyMutatedInScope => true;

  @override
  ElementKind get kind => ElementKind.PARAMETER;

  @override
  List<ElementAnnotation> get metadata {
    if (_unlinkedParam != null) {
      return _metadata ??=
          _buildAnnotations(enclosingUnit, _unlinkedParam.annotations);
    }
    return super.metadata;
  }

  @override
  String get name {
    if (_unlinkedParam != null) {
      return _unlinkedParam.name;
    }
    return super.name;
  }

  @override
  int get nameOffset {
    if (_unlinkedParam != null) {
      if (isSynthetic) {
        return -1;
      }
      return _unlinkedParam.nameOffset;
    }
    return super.nameOffset;
  }

  @override
  ParameterKind get parameterKind {
    if (_unlinkedParam != null && _parameterKind == null) {
      switch (_unlinkedParam.kind) {
        case UnlinkedParamKind.named:
          _parameterKind = ParameterKind.NAMED;
          break;
        case UnlinkedParamKind.positional:
          _parameterKind = ParameterKind.POSITIONAL;
          break;
        case UnlinkedParamKind.required:
          _parameterKind = ParameterKind.REQUIRED;
          break;
      }
    }
    return _parameterKind;
  }

  void set parameterKind(ParameterKind parameterKind) {
    assert(_unlinkedParam == null);
    _parameterKind = parameterKind;
  }

  @override
  List<ParameterElement> get parameters {
    _resynthesizeTypeAndParameters();
    return _parameters;
  }

  /**
   * Set the parameters defined by this executable element to the given
   * [parameters].
   */
  void set parameters(List<ParameterElement> parameters) {
    for (ParameterElement parameter in parameters) {
      (parameter as ParameterElementImpl).enclosingElement = this;
    }
    this._parameters = parameters;
  }

  @override
  DartType get type {
    _resynthesizeTypeAndParameters();
    return super.type;
  }

  @override
  List<TypeParameterElement> get typeParameters => _typeParameters;

  /**
   * Set the type parameters defined by this parameter element to the given
   * [typeParameters].
   */
  void set typeParameters(List<TypeParameterElement> typeParameters) {
    for (TypeParameterElement parameter in typeParameters) {
      (parameter as TypeParameterElementImpl).enclosingElement = this;
    }
    this._typeParameters = typeParameters;
  }

  @override
  SourceRange get visibleRange {
    if (_unlinkedParam != null) {
      if (_unlinkedParam.visibleLength == 0) {
        return null;
      }
      return new SourceRange(
          _unlinkedParam.visibleOffset, _unlinkedParam.visibleLength);
    }
    if (_visibleRangeLength < 0) {
      return null;
    }
    return new SourceRange(_visibleRangeOffset, _visibleRangeLength);
  }

  /**
   * Subclasses need this getter, see [ConstVariableElement._unlinkedConst].
   */
  UnlinkedConst get _unlinkedConst => _unlinkedParam?.initializer?.bodyExpr;

  @override
  accept(ElementVisitor visitor) => visitor.visitParameterElement(this);

  @override
  void appendTo(StringBuffer buffer) {
    String left = "";
    String right = "";
    while (true) {
      if (parameterKind == ParameterKind.NAMED) {
        left = "{";
        right = "}";
      } else if (parameterKind == ParameterKind.POSITIONAL) {
        left = "[";
        right = "]";
      } else if (parameterKind == ParameterKind.REQUIRED) {}
      break;
    }
    buffer.write(left);
    appendToWithoutDelimiters(buffer);
    buffer.write(right);
  }

  @override
  FormalParameter computeNode() =>
      getNodeMatching((node) => node is FormalParameter);

  @override
  ElementImpl getChild(String identifier) {
    for (ParameterElement parameter in _parameters) {
      ParameterElementImpl parameterImpl = parameter;
      if (parameterImpl.identifier == identifier) {
        return parameterImpl;
      }
    }
    return null;
  }

  /**
   * Set the visible range for this element to the range starting at the given
   * [offset] with the given [length].
   */
  void setVisibleRange(int offset, int length) {
    assert(_unlinkedParam == null);
    _visibleRangeOffset = offset;
    _visibleRangeLength = length;
  }

  @override
  void visitChildren(ElementVisitor visitor) {
    super.visitChildren(visitor);
    safelyVisitChildren(parameters, visitor);
  }

  /**
   * If this element is resynthesized, and its type and parameters have not
   * been build yet, build them and remember in the corresponding fields.
   */
  void _resynthesizeTypeAndParameters() {
    if (_unlinkedParam != null && _type == null) {
      if (_unlinkedParam.isFunctionTyped) {
        CompilationUnitElementImpl enclosingUnit = this.enclosingUnit;
        FunctionElementImpl parameterTypeElement =
            new FunctionElementImpl_forFunctionTypedParameter(
                enclosingUnit, this);
        if (!isSynthetic) {
          parameterTypeElement.enclosingElement = this;
        }
        List<ParameterElement> subParameters = ParameterElementImpl
            .resynthesizeList(_unlinkedParam.parameters, this,
                synthetic: isSynthetic);
        if (isSynthetic) {
          parameterTypeElement.parameters = subParameters;
        } else {
          _parameters = subParameters;
          parameterTypeElement.shareParameters(subParameters);
        }
        parameterTypeElement.returnType = enclosingUnit.resynthesizerContext
            .resolveTypeRef(_unlinkedParam.type, typeParameterContext);
        FunctionTypeImpl parameterType =
            new FunctionTypeImpl.elementWithNameAndArgs(parameterTypeElement,
                null, typeParameterContext.allTypeParameterTypes, false);
        parameterTypeElement.type = parameterType;
        _type = parameterType;
      } else {
        _type = enclosingUnit.resynthesizerContext.resolveLinkedType(
                _unlinkedParam.inferredTypeSlot, typeParameterContext) ??
            enclosingUnit.resynthesizerContext
                .resolveTypeRef(_unlinkedParam.type, typeParameterContext);
      }
    }
  }

  /**
   * Create and return [ParameterElement]s for the given [unlinkedParameters].
   */
  static List<ParameterElement> resynthesizeList(
      List<UnlinkedParam> unlinkedParameters, ElementImpl enclosingElement,
      {bool synthetic: false}) {
    int length = unlinkedParameters.length;
    if (length != 0) {
      List<ParameterElement> parameters = new List<ParameterElement>(length);
      for (int i = 0; i < length; i++) {
        parameters[i] = new ParameterElementImpl.forSerializedFactory(
            unlinkedParameters[i], enclosingElement,
            synthetic: synthetic);
      }
      return parameters;
    } else {
      return const <ParameterElement>[];
    }
  }
}

/**
 * The parameter of an implicit setter.
 */
class ParameterElementImpl_ofImplicitSetter extends ParameterElementImpl {
  final PropertyAccessorElementImpl_ImplicitSetter setter;

  ParameterElementImpl_ofImplicitSetter(
      PropertyAccessorElementImpl_ImplicitSetter setter)
      : setter = setter,
        super('_${setter.variable.name}', setter.variable.nameOffset) {
    enclosingElement = setter;
    synthetic = true;
    parameterKind = ParameterKind.REQUIRED;
  }

  @override
  bool get isCovariant {
    if (inheritsCovariant) {
      return true;
    }
    for (ElementAnnotationImpl annotation in setter.variable.metadata) {
      if (annotation.isCovariant) {
        return true;
      }
    }
    return false;
  }

  @override
  DartType get type => setter.variable.type;

  @override
  void set type(DartType type) {
    assert(false); // Should never be called.
  }
}

/**
 * A mixin that provides a common implementation for methods defined in
 * [ParameterElement].
 */
abstract class ParameterElementMixin implements ParameterElement {
  @override
  void appendToWithoutDelimiters(StringBuffer buffer) {
    buffer.write(type);
    buffer.write(" ");
    buffer.write(displayName);
    if (defaultValueCode != null) {
      if (parameterKind == ParameterKind.NAMED) {
        buffer.write(": ");
      }
      if (parameterKind == ParameterKind.POSITIONAL) {
        buffer.write(" = ");
      }
      buffer.write(defaultValueCode);
    }
  }
}

/**
 * A concrete implementation of a [PrefixElement].
 */
class PrefixElementImpl extends ElementImpl implements PrefixElement {
  /**
   * The unlinked representation of the import in the summary.
   */
  final UnlinkedImport _unlinkedImport;

  /**
   * Initialize a newly created method element to have the given [name] and
   * [nameOffset].
   */
  PrefixElementImpl(String name, int nameOffset)
      : _unlinkedImport = null,
        super(name, nameOffset);

  /**
   * Initialize a newly created prefix element to have the given [name].
   */
  PrefixElementImpl.forNode(Identifier name)
      : _unlinkedImport = null,
        super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  PrefixElementImpl.forSerialized(
      this._unlinkedImport, LibraryElementImpl enclosingLibrary)
      : super.forSerialized(enclosingLibrary);

  @override
  String get displayName => name;

  @override
  LibraryElement get enclosingElement =>
      super.enclosingElement as LibraryElement;

  @override
  String get identifier => "_${super.identifier}";

  @override
  List<LibraryElement> get importedLibraries => LibraryElement.EMPTY_LIST;

  @override
  ElementKind get kind => ElementKind.PREFIX;

  @override
  String get name {
    if (_unlinkedImport != null) {
      if (_name == null) {
        LibraryElementImpl library = enclosingElement as LibraryElementImpl;
        int prefixId = _unlinkedImport.prefixReference;
        return _name = library._unlinkedDefiningUnit.references[prefixId].name;
      }
    }
    return super.name;
  }

  @override
  int get nameOffset {
    if (_unlinkedImport != null) {
      return _unlinkedImport.prefixOffset;
    }
    return super.nameOffset;
  }

  @override
  accept(ElementVisitor visitor) => visitor.visitPrefixElement(this);

  @override
  void appendTo(StringBuffer buffer) {
    buffer.write("as ");
    super.appendTo(buffer);
  }
}

/**
 * A concrete implementation of a [PropertyAccessorElement].
 */
class PropertyAccessorElementImpl extends ExecutableElementImpl
    implements PropertyAccessorElement {
  /**
   * The variable associated with this accessor.
   */
  PropertyInducingElement variable;

  /**
   * Initialize a newly created property accessor element to have the given
   * [name] and [offset].
   */
  PropertyAccessorElementImpl(String name, int offset) : super(name, offset);

  /**
   * Initialize a newly created property accessor element to have the given
   * [name].
   */
  PropertyAccessorElementImpl.forNode(Identifier name) : super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  PropertyAccessorElementImpl.forSerialized(
      UnlinkedExecutable serializedExecutable, ElementImpl enclosingElement)
      : super.forSerialized(serializedExecutable, enclosingElement);

  /**
   * Initialize a newly created synthetic property accessor element to be
   * associated with the given [variable].
   */
  PropertyAccessorElementImpl.forVariable(PropertyInducingElementImpl variable)
      : super(variable.name, variable.nameOffset) {
    this.variable = variable;
    static = variable.isStatic;
    synthetic = true;
  }

  /**
   * Set whether this accessor is abstract.
   */
  void set abstract(bool isAbstract) {
    assert(serializedExecutable == null);
    setModifier(Modifier.ABSTRACT, isAbstract);
  }

  @override
  List<TypeParameterType> get allEnclosingTypeParameterTypes {
    if (isStatic) {
      return const <TypeParameterType>[];
    }
    return super.allEnclosingTypeParameterTypes;
  }

  @override
  PropertyAccessorElement get correspondingGetter {
    if (isGetter || variable == null) {
      return null;
    }
    return variable.getter;
  }

  @override
  PropertyAccessorElement get correspondingSetter {
    if (isSetter || variable == null) {
      return null;
    }
    return variable.setter;
  }

  @override
  String get displayName {
    if (serializedExecutable != null && isSetter) {
      String name = serializedExecutable.name;
      assert(name.endsWith('='));
      return name.substring(0, name.length - 1);
    }
    return super.displayName;
  }

  @override
  TypeParameterizedElementMixin get enclosingTypeParameterContext {
    return (enclosingElement as ElementImpl).typeParameterContext;
  }

  /**
   * Set whether this accessor is a getter.
   */
  void set getter(bool isGetter) {
    assert(serializedExecutable == null);
    setModifier(Modifier.GETTER, isGetter);
  }

  @override
  String get identifier {
    String name = displayName;
    String suffix = isGetter ? "?" : "=";
    return "$name$suffix";
  }

  @override
  bool get isGetter {
    if (serializedExecutable != null) {
      return serializedExecutable.kind == UnlinkedExecutableKind.getter;
    }
    return hasModifier(Modifier.GETTER);
  }

  @override
  bool get isSetter {
    if (serializedExecutable != null) {
      return serializedExecutable.kind == UnlinkedExecutableKind.setter;
    }
    return hasModifier(Modifier.SETTER);
  }

  @override
  bool get isStatic {
    if (serializedExecutable != null) {
      return serializedExecutable.isStatic ||
          variable is TopLevelVariableElement;
    }
    return hasModifier(Modifier.STATIC);
  }

  @override
  ElementKind get kind {
    if (isGetter) {
      return ElementKind.GETTER;
    }
    return ElementKind.SETTER;
  }

  @override
  String get name {
    if (serializedExecutable != null) {
      return serializedExecutable.name;
    }
    if (isSetter) {
      return "${super.name}=";
    }
    return super.name;
  }

  /**
   * Set whether this accessor is a setter.
   */
  void set setter(bool isSetter) {
    assert(serializedExecutable == null);
    setModifier(Modifier.SETTER, isSetter);
  }

  /**
   * Set whether this accessor is static.
   */
  void set static(bool isStatic) {
    assert(serializedExecutable == null);
    setModifier(Modifier.STATIC, isStatic);
  }

  @override
  accept(ElementVisitor visitor) => visitor.visitPropertyAccessorElement(this);

  @override
  void appendTo(StringBuffer buffer) {
    buffer.write(isGetter ? "get " : "set ");
    buffer.write(variable.displayName);
    super.appendTo(buffer);
  }

  @override
  AstNode computeNode() {
    if (isSynthetic) {
      return null;
    }
    if (enclosingElement is ClassElement) {
      return getNodeMatching((node) => node is MethodDeclaration);
    } else if (enclosingElement is CompilationUnitElement) {
      return getNodeMatching((node) => node is FunctionDeclaration);
    }
    return null;
  }
}

/**
 * Implicit getter for a [PropertyInducingElementImpl].
 */
class PropertyAccessorElementImpl_ImplicitGetter
    extends PropertyAccessorElementImpl {
  /**
   * Create the implicit getter and bind it to the [property].
   */
  PropertyAccessorElementImpl_ImplicitGetter(
      PropertyInducingElementImpl property)
      : super.forVariable(property) {
    property.getter = this;
    enclosingElement = property.enclosingElement;
  }

  @override
  bool get hasImplicitReturnType => variable.hasImplicitType;

  @override
  bool get isGetter => true;

  @override
  DartType get returnType => variable.type;

  @override
  void set returnType(DartType returnType) {
    assert(false); // Should never be called.
  }

  @override
  FunctionType get type {
    return _type ??= new FunctionTypeImpl(this);
  }

  @override
  void set type(FunctionType type) {
    assert(false); // Should never be called.
  }
}

/**
 * Implicit setter for a [PropertyInducingElementImpl].
 */
class PropertyAccessorElementImpl_ImplicitSetter
    extends PropertyAccessorElementImpl {
  /**
   * Create the implicit setter and bind it to the [property].
   */
  PropertyAccessorElementImpl_ImplicitSetter(
      PropertyInducingElementImpl property)
      : super.forVariable(property) {
    property.setter = this;
  }

  @override
  bool get isSetter => true;

  @override
  List<ParameterElement> get parameters {
    return _parameters ??= <ParameterElement>[
      new ParameterElementImpl_ofImplicitSetter(this)
    ];
  }

  @override
  DartType get returnType => VoidTypeImpl.instance;

  @override
  void set returnType(DartType returnType) {
    assert(false); // Should never be called.
  }

  @override
  FunctionType get type {
    return _type ??= new FunctionTypeImpl(this);
  }

  @override
  void set type(FunctionType type) {
    assert(false); // Should never be called.
  }
}

/**
 * A concrete implementation of a [PropertyInducingElement].
 */
abstract class PropertyInducingElementImpl
    extends NonParameterVariableElementImpl implements PropertyInducingElement {
  /**
   * The getter associated with this element.
   */
  PropertyAccessorElement getter;

  /**
   * The setter associated with this element, or `null` if the element is
   * effectively `final` and therefore does not have a setter associated with
   * it.
   */
  PropertyAccessorElement setter;

  /**
   * The propagated type of this variable, or `null` if type propagation has not
   * been performed.
   */
  DartType _propagatedType;

  /**
   * Initialize a newly created synthetic element to have the given [name] and
   * [offset].
   */
  PropertyInducingElementImpl(String name, int offset) : super(name, offset);

  /**
   * Initialize a newly created element to have the given [name].
   */
  PropertyInducingElementImpl.forNode(Identifier name) : super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  PropertyInducingElementImpl.forSerialized(
      UnlinkedVariable unlinkedVariable, ElementImpl enclosingElement)
      : super.forSerialized(unlinkedVariable, enclosingElement);

  @override
  DartType get propagatedType {
    if (_unlinkedVariable != null && _propagatedType == null) {
      _propagatedType = enclosingUnit.resynthesizerContext.resolveLinkedType(
          _unlinkedVariable.propagatedTypeSlot, typeParameterContext);
    }
    return _propagatedType;
  }

  void set propagatedType(DartType propagatedType) {
    assert(_unlinkedVariable == null);
    _propagatedType = propagatedType;
  }
}

/**
 * The context in which elements are resynthesized.
 */
abstract class ResynthesizerContext {
  /**
   * Build [ElementAnnotationImpl] for the given [UnlinkedConst].
   */
  ElementAnnotationImpl buildAnnotation(ElementImpl context, UnlinkedConst uc);

  /**
   * Build [Expression] for the given [UnlinkedConst].
   */
  Expression buildExpression(ElementImpl context, UnlinkedConst uc);

  /**
   * Build explicit top-level property accessors.
   */
  UnitExplicitTopLevelAccessors buildTopLevelAccessors();

  /**
   * Build explicit top-level variables.
   */
  UnitExplicitTopLevelVariables buildTopLevelVariables();

  /**
   * Return `true` if the given parameter [slot] inherits `@covariant` behavior.
   */
  bool inheritsCovariant(int slot);

  /**
   * Return `true` if the given const constructor [slot] is a part of a cycle.
   */
  bool isInConstCycle(int slot);

  /**
   * Resolve an [EntityRef] into a constructor.  If the reference is
   * unresolved, return `null`.
   */
  ConstructorElement resolveConstructorRef(
      TypeParameterizedElementMixin typeParameterContext, EntityRef entry);

  /**
   * Build the appropriate [DartType] object corresponding to a slot id in the
   * [LinkedUnit.types] table.
   */
  DartType resolveLinkedType(
      int slot, TypeParameterizedElementMixin typeParameterContext);

  /**
   * Resolve an [EntityRef] into a type.  If the reference is
   * unresolved, return [DynamicTypeImpl.instance].
   *
   * TODO(paulberry): or should we have a class representing an
   * unresolved type, for consistency with the full element model?
   */
  DartType resolveTypeRef(
      EntityRef type, TypeParameterizedElementMixin typeParameterContext,
      {bool defaultVoid: false, bool instantiateToBoundsAllowed: true});
}

/**
 * A concrete implementation of a [ShowElementCombinator].
 */
class ShowElementCombinatorImpl implements ShowElementCombinator {
  /**
   * The unlinked representation of the combinator in the summary.
   */
  final UnlinkedCombinator _unlinkedCombinator;

  /**
   * The names that are to be made visible in the importing library if they are
   * defined in the imported library.
   */
  List<String> _shownNames;

  /**
   * The offset of the character immediately following the last character of
   * this node.
   */
  int _end = -1;

  /**
   * The offset of the 'show' keyword of this element.
   */
  int _offset = 0;

  ShowElementCombinatorImpl() : _unlinkedCombinator = null;

  /**
   * Initialize using the given serialized information.
   */
  ShowElementCombinatorImpl.forSerialized(this._unlinkedCombinator);

  @override
  int get end {
    if (_unlinkedCombinator != null) {
      return _unlinkedCombinator.end;
    }
    return _end;
  }

  void set end(int end) {
    assert(_unlinkedCombinator == null);
    _end = end;
  }

  @override
  int get offset {
    if (_unlinkedCombinator != null) {
      return _unlinkedCombinator.offset;
    }
    return _offset;
  }

  void set offset(int offset) {
    assert(_unlinkedCombinator == null);
    _offset = offset;
  }

  @override
  List<String> get shownNames {
    if (_unlinkedCombinator != null) {
      _shownNames ??= _unlinkedCombinator.shows.toList(growable: false);
    }
    return _shownNames ?? const <String>[];
  }

  void set shownNames(List<String> shownNames) {
    assert(_unlinkedCombinator == null);
    _shownNames = shownNames;
  }

  @override
  String toString() {
    StringBuffer buffer = new StringBuffer();
    buffer.write("show ");
    int count = shownNames.length;
    for (int i = 0; i < count; i++) {
      if (i > 0) {
        buffer.write(", ");
      }
      buffer.write(shownNames[i]);
    }
    return buffer.toString();
  }
}

/**
 * A concrete implementation of a [TopLevelVariableElement].
 */
class TopLevelVariableElementImpl extends PropertyInducingElementImpl
    implements TopLevelVariableElement {
  /**
   * Initialize a newly created synthetic top-level variable element to have the
   * given [name] and [offset].
   */
  TopLevelVariableElementImpl(String name, int offset) : super(name, offset);

  /**
   * Initialize a newly created top-level variable element to have the given
   * [name].
   */
  TopLevelVariableElementImpl.forNode(Identifier name) : super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  TopLevelVariableElementImpl.forSerialized(
      UnlinkedVariable unlinkedVariable, ElementImpl enclosingElement)
      : super.forSerialized(unlinkedVariable, enclosingElement);

  @override
  bool get isStatic => true;

  @override
  ElementKind get kind => ElementKind.TOP_LEVEL_VARIABLE;

  @override
  accept(ElementVisitor visitor) => visitor.visitTopLevelVariableElement(this);

  @override
  VariableDeclaration computeNode() =>
      getNodeMatching((node) => node is VariableDeclaration);
}

/**
 * A concrete implementation of a [TypeParameterElement].
 */
class TypeParameterElementImpl extends ElementImpl
    implements TypeParameterElement {
  /**
   * The unlinked representation of the type parameter in the summary.
   */
  final UnlinkedTypeParam _unlinkedTypeParam;

  /**
   * The number of type parameters whose scope overlaps this one, and which are
   * declared earlier in the file.
   *
   * TODO(scheglov) make private?
   */
  final int nestingLevel;

  /**
   * The type defined by this type parameter.
   */
  TypeParameterType _type;

  /**
   * The type representing the bound associated with this parameter, or `null`
   * if this parameter does not have an explicit bound.
   */
  DartType _bound;

  /**
   * Initialize a newly created method element to have the given [name] and
   * [offset].
   */
  TypeParameterElementImpl(String name, int offset)
      : _unlinkedTypeParam = null,
        nestingLevel = null,
        super(name, offset);

  /**
   * Initialize a newly created type parameter element to have the given [name].
   */
  TypeParameterElementImpl.forNode(Identifier name)
      : _unlinkedTypeParam = null,
        nestingLevel = null,
        super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  TypeParameterElementImpl.forSerialized(this._unlinkedTypeParam,
      TypeParameterizedElementMixin enclosingElement, this.nestingLevel)
      : super.forSerialized(enclosingElement);

  /**
   * Initialize a newly created synthetic type parameter element to have the
   * given [name], and with [synthetic] set to true.
   */
  TypeParameterElementImpl.synthetic(String name)
      : _unlinkedTypeParam = null,
        nestingLevel = null,
        super(name, -1) {
    synthetic = true;
  }

  DartType get bound {
    if (_unlinkedTypeParam != null) {
      if (_unlinkedTypeParam.bound == null) {
        return null;
      }
      return _bound ??= enclosingUnit.resynthesizerContext.resolveTypeRef(
          _unlinkedTypeParam.bound, enclosingElement,
          instantiateToBoundsAllowed: false);
    }
    return _bound;
  }

  void set bound(DartType bound) {
    assert(_unlinkedTypeParam == null);
    _bound = bound;
  }

  @override
  int get codeLength {
    if (_unlinkedTypeParam != null) {
      return _unlinkedTypeParam.codeRange?.length;
    }
    return super.codeLength;
  }

  @override
  int get codeOffset {
    if (_unlinkedTypeParam != null) {
      return _unlinkedTypeParam.codeRange?.offset;
    }
    return super.codeOffset;
  }

  @override
  String get displayName => name;

  @override
  ElementKind get kind => ElementKind.TYPE_PARAMETER;

  @override
  List<ElementAnnotation> get metadata {
    if (_unlinkedTypeParam != null) {
      return _metadata ??=
          _buildAnnotations(enclosingUnit, _unlinkedTypeParam.annotations);
    }
    return super.metadata;
  }

  @override
  String get name {
    if (_unlinkedTypeParam != null) {
      return _unlinkedTypeParam.name;
    }
    return super.name;
  }

  @override
  int get nameOffset {
    if (_unlinkedTypeParam != null) {
      return _unlinkedTypeParam.nameOffset;
    }
    return super.nameOffset;
  }

  TypeParameterType get type {
    if (_unlinkedTypeParam != null) {
      _type ??= new TypeParameterTypeImpl(this);
    }
    return _type;
  }

  void set type(TypeParameterType type) {
    _type = type;
  }

  @override
  accept(ElementVisitor visitor) => visitor.visitTypeParameterElement(this);

  @override
  void appendTo(StringBuffer buffer) {
    buffer.write(displayName);
    if (bound != null) {
      buffer.write(" extends ");
      buffer.write(bound);
    }
  }
}

/**
 * Mixin representing an element which can have type parameters.
 */
abstract class TypeParameterizedElementMixin
    implements TypeParameterizedElement, ElementImpl {
  int _nestingLevel;
  List<TypeParameterElement> _typeParameterElements;
  List<TypeParameterType> _typeParameterTypes;
  List<TypeParameterType> _allTypeParameterTypes;

  /**
   * Return all type parameter types of the element that encloses element.
   * Not `null`, but might be empty for top-level and static class members.
   */
  List<TypeParameterType> get allEnclosingTypeParameterTypes {
    return enclosingTypeParameterContext?.allTypeParameterTypes ??
        const <TypeParameterType>[];
  }

  /**
   * Return all type parameter types of this element.
   */
  List<TypeParameterType> get allTypeParameterTypes {
    if (_allTypeParameterTypes == null) {
      _allTypeParameterTypes = <TypeParameterType>[];
      // The most logical order would be (enclosing, this).
      // But we have to have it like this to be consistent with (inconsistent
      // by itself) element builder for generic functions.
      _allTypeParameterTypes.addAll(typeParameterTypes);
      _allTypeParameterTypes.addAll(allEnclosingTypeParameterTypes);
    }
    return _allTypeParameterTypes;
  }

  /**
   * Get the type parameter context enclosing this one, if any.
   */
  TypeParameterizedElementMixin get enclosingTypeParameterContext;

  /**
   * The unit in which this element is resynthesized.
   */
  CompilationUnitElementImpl get enclosingUnit;

  /**
   * Find out how many type parameters are in scope in this context.
   */
  int get typeParameterNestingLevel =>
      _nestingLevel ??= unlinkedTypeParams.length +
          (enclosingTypeParameterContext?.typeParameterNestingLevel ?? 0);

  List<TypeParameterElement> get typeParameters {
    if (_typeParameterElements == null) {
      int enclosingNestingLevel =
          enclosingTypeParameterContext?.typeParameterNestingLevel ?? 0;
      int numTypeParameters = unlinkedTypeParams.length;
      _typeParameterElements =
          new List<TypeParameterElement>(numTypeParameters);
      for (int i = 0; i < numTypeParameters; i++) {
        _typeParameterElements[i] = new TypeParameterElementImpl.forSerialized(
            unlinkedTypeParams[i], this, enclosingNestingLevel + i);
      }
    }
    return _typeParameterElements;
  }

  /**
   * Get a list of [TypeParameterType] objects corresponding to the
   * element's type parameters.
   */
  List<TypeParameterType> get typeParameterTypes {
    return _typeParameterTypes ??= typeParameters
        .map((TypeParameterElement e) => e.type)
        .toList(growable: false);
  }

  /**
   * Get the [UnlinkedTypeParam]s representing the type parameters declared by
   * this element.
   *
   * TODO(scheglov) make private after switching linker to Impl
   */
  List<UnlinkedTypeParam> get unlinkedTypeParams;

  /**
   * Convert the given [index] into a type parameter type.
   */
  TypeParameterType getTypeParameterType(int index) {
    List<TypeParameterType> types = typeParameterTypes;
    if (index <= types.length) {
      return types[types.length - index];
    } else if (enclosingTypeParameterContext != null) {
      return enclosingTypeParameterContext
          .getTypeParameterType(index - types.length);
    } else {
      // If we get here, it means that a summary contained a type parameter index
      // that was out of range.
      throw new RangeError('Invalid type parameter index');
    }
  }

  /**
   * Find out if the given [typeParameter] is in scope in this context.
   */
  bool isTypeParameterInScope(TypeParameterElement typeParameter) {
    if (typeParameter.enclosingElement == this) {
      return true;
    } else if (enclosingTypeParameterContext != null) {
      return enclosingTypeParameterContext
          .isTypeParameterInScope(typeParameter);
    } else {
      return false;
    }
  }
}

/**
 * Container with information about explicit top-level property accessors and
 * corresponding implicit top-level variables.
 */
class UnitExplicitTopLevelAccessors {
  final List<PropertyAccessorElementImpl> accessors =
      <PropertyAccessorElementImpl>[];
  final List<TopLevelVariableElementImpl> implicitVariables =
      <TopLevelVariableElementImpl>[];
}

/**
 * Container with information about explicit top-level variables and
 * corresponding implicit top-level property accessors.
 */
class UnitExplicitTopLevelVariables {
  final List<TopLevelVariableElementImpl> variables;
  final List<PropertyAccessorElementImpl> implicitAccessors =
      <PropertyAccessorElementImpl>[];

  UnitExplicitTopLevelVariables(int numberOfVariables)
      : variables = numberOfVariables != 0
            ? new List<TopLevelVariableElementImpl>(numberOfVariables)
            : const <TopLevelVariableElementImpl>[];
}

/**
 * A concrete implementation of a [UriReferencedElement].
 */
abstract class UriReferencedElementImpl extends ElementImpl
    implements UriReferencedElement {
  /**
   * The offset of the URI in the file, or `-1` if this node is synthetic.
   */
  int _uriOffset = -1;

  /**
   * The offset of the character immediately following the last character of
   * this node's URI, or `-1` if this node is synthetic.
   */
  int _uriEnd = -1;

  /**
   * The URI that is specified by this directive.
   */
  String _uri;

  /**
   * Initialize a newly created import element to have the given [name] and
   * [offset]. The offset may be `-1` if the element is synthetic.
   */
  UriReferencedElementImpl(String name, int offset) : super(name, offset);

  /**
   * Initialize using the given serialized information.
   */
  UriReferencedElementImpl.forSerialized(ElementImpl enclosingElement)
      : super.forSerialized(enclosingElement);

  /**
   * Return the URI that is specified by this directive.
   */
  String get uri => _uri;

  /**
   * Set the URI that is specified by this directive to be the given [uri].
   */
  void set uri(String uri) {
    _uri = uri;
  }

  /**
   * Return the offset of the character immediately following the last character
   * of this node's URI, or `-1` if this node is synthetic.
   */
  int get uriEnd => _uriEnd;

  /**
   * Set the offset of the character immediately following the last character of
   * this node's URI to the given [offset].
   */
  void set uriEnd(int offset) {
    _uriEnd = offset;
  }

  /**
   * Return the offset of the URI in the file, or `-1` if this node is synthetic.
   */
  int get uriOffset => _uriOffset;

  /**
   * Set the offset of the URI in the file to the given [offset].
   */
  void set uriOffset(int offset) {
    _uriOffset = offset;
  }

  String _selectUri(
      String defaultUri, List<UnlinkedConfiguration> configurations) {
    for (UnlinkedConfiguration configuration in configurations) {
      if (context.declaredVariables.get(configuration.name) ==
          configuration.value) {
        return configuration.uri;
      }
    }
    return defaultUri;
  }
}

/**
 * A concrete implementation of a [VariableElement].
 */
abstract class VariableElementImpl extends ElementImpl
    implements VariableElement {
  /**
   * The declared type of this variable.
   */
  DartType _type;

  /**
   * A synthetic function representing this variable's initializer, or `null` if
   * this variable does not have an initializer.
   */
  FunctionElement _initializer;

  /**
   * Initialize a newly created variable element to have the given [name] and
   * [offset].
   */
  VariableElementImpl(String name, int offset) : super(name, offset);

  /**
   * Initialize a newly created variable element to have the given [name].
   */
  VariableElementImpl.forNode(Identifier name) : super.forNode(name);

  /**
   * Initialize using the given serialized information.
   */
  VariableElementImpl.forSerialized(ElementImpl enclosingElement)
      : super.forSerialized(enclosingElement);

  /**
   * Set whether this variable is const.
   */
  void set const3(bool isConst) {
    setModifier(Modifier.CONST, isConst);
  }

  /**
   * If this element represents a constant variable, and it has an initializer,
   * a copy of the initializer for the constant.  Otherwise `null`.
   *
   * Note that in correct Dart code, all constant variables must have
   * initializers.  However, analyzer also needs to handle incorrect Dart code,
   * in which case there might be some constant variables that lack
   * initializers.
   */
  Expression get constantInitializer => null;

  @override
  DartObject get constantValue => evaluationResult?.value;

  @override
  String get displayName => name;

  /**
   * Return the result of evaluating this variable's initializer as a
   * compile-time constant expression, or `null` if this variable is not a
   * 'const' variable, if it does not have an initializer, or if the compilation
   * unit containing the variable has not been resolved.
   */
  EvaluationResultImpl get evaluationResult => null;

  /**
   * Set the result of evaluating this variable's initializer as a compile-time
   * constant expression to the given [result].
   */
  void set evaluationResult(EvaluationResultImpl result) {
    throw new StateError(
        "Invalid attempt to set a compile-time constant result");
  }

  /**
   * Set whether this variable is final.
   */
  void set final2(bool isFinal) {
    setModifier(Modifier.FINAL, isFinal);
  }

  @override
  bool get hasImplicitType {
    return hasModifier(Modifier.IMPLICIT_TYPE);
  }

  /**
   * Set whether this variable element has an implicit type.
   */
  void set hasImplicitType(bool hasImplicitType) {
    setModifier(Modifier.IMPLICIT_TYPE, hasImplicitType);
  }

  @override
  FunctionElement get initializer => _initializer;

  /**
   * Set the function representing this variable's initializer to the given
   * [function].
   */
  void set initializer(FunctionElement function) {
    if (function != null) {
      (function as FunctionElementImpl).enclosingElement = this;
    }
    this._initializer = function;
  }

  @override
  bool get isConst {
    return hasModifier(Modifier.CONST);
  }

  @override
  bool get isFinal {
    return hasModifier(Modifier.FINAL);
  }

  @override
  bool get isPotentiallyMutatedInClosure => false;

  @override
  bool get isPotentiallyMutatedInScope => false;

  @override
  bool get isStatic => hasModifier(Modifier.STATIC);

  @override
  DartType get type => _type;

  void set type(DartType type) {
    _type = type;
  }

  @override
  void appendTo(StringBuffer buffer) {
    buffer.write(type);
    buffer.write(" ");
    buffer.write(displayName);
  }

  @override
  DartObject computeConstantValue() => null;

  @override
  void visitChildren(ElementVisitor visitor) {
    super.visitChildren(visitor);
    _initializer?.accept(visitor);
  }
}

/**
 * A visitor that visit all the elements recursively and fill the given [map].
 */
class _BuildOffsetToElementMap extends GeneralizingElementVisitor {
  final Map<int, Element> map;

  _BuildOffsetToElementMap(this.map);

  @override
  void visitElement(Element element) {
    int offset = element.nameOffset;
    if (offset != -1) {
      map[offset] = element;
    }
    super.visitElement(element);
  }
}
