Initial CL for fine-grained dependencies - Name, Symbol, Library and declared symbols.

R=brianwilkerson@google.com, paulberry@google.com

Change-Id: I0f33b24f3f8caf13c9b498e7e1d403a5093aae63
Reviewed-on: https://dart-review.googlesource.com/c/85729
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/analysis/dependency/library_builder.dart b/pkg/analyzer/lib/src/dart/analysis/dependency/library_builder.dart
new file mode 100644
index 0000000..5acbdc4
--- /dev/null
+++ b/pkg/analyzer/lib/src/dart/analysis/dependency/library_builder.dart
@@ -0,0 +1,667 @@
+// Copyright (c) 2018, 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.
+
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/src/dart/analysis/dependency/node.dart';
+import 'package:analyzer/src/dart/ast/token.dart';
+import 'package:analyzer/src/summary/api_signature.dart';
+
+/// Build [Library] that describes nodes and dependencies of the library
+/// with the given [uri] and [units].
+///
+/// If the [units] are just parsed, then only token signatures and referenced
+/// names of nodes can be computed. If the [units] are fully resolved, then
+/// also class member references can be recorded.
+Library buildLibrary(
+  Uri uri,
+  List<CompilationUnit> units,
+  ReferenceCollector referenceCollector,
+) {
+  return _LibraryBuilder(uri, units, referenceCollector).build();
+}
+
+/// The `show` or `hide` namespace combinator.
+class Combinator {
+  final bool isShow;
+  final List<String> names;
+
+  Combinator(this.isShow, this.names);
+
+  @override
+  String toString() {
+    if (isShow) {
+      return 'show ' + names.join(', ');
+    } else {
+      return 'hide ' + names.join(', ');
+    }
+  }
+}
+
+/// The `export` directive.
+class Export {
+  /// The absolute URI of the exported library.
+  final Uri uri;
+
+  /// The list of namespace combinators to apply, not `null`.
+  final List<Combinator> combinators;
+
+  Export(this.uri, this.combinators);
+
+  @override
+  String toString() {
+    return 'Export(uri: $uri, combinators: $combinators)';
+  }
+}
+
+/// The `import` directive.
+class Import {
+  /// The absolute URI of the imported library.
+  final Uri uri;
+
+  /// The import prefix, or `null` if not specified.
+  final String prefix;
+
+  /// The list of namespace combinators to apply, not `null`.
+  final List<Combinator> combinators;
+
+  Import(this.uri, this.prefix, this.combinators);
+
+  @override
+  String toString() {
+    return 'Import(uri: $uri, prefix: $prefix, combinators: $combinators)';
+  }
+}
+
+/// The collection of imports, exports, and top-level nodes.
+class Library {
+  /// The absolute URI of the library.
+  final Uri uri;
+
+  /// The list of imports in this library.
+  final List<Import> imports;
+
+  /// The list of exports in this library.
+  final List<Export> exports;
+
+  /// The list of libraries that correspond to the [imports].
+  List<Library> importedLibraries;
+
+  /// The list of top-level nodes defined in the library.
+  ///
+  /// This list is sorted.
+  final List<DependencyNode> declaredNodes;
+
+  /// The map of [declaredNodes], used for fast search.
+  /// TODO(scheglov) consider using binary search instead.
+  final Map<DependencyName, DependencyNode> declaredNodeMap = {};
+
+  /// The list of nodes exported from this library, either using `export`
+  /// directives, or declared in this library.
+  ///
+  /// This list is sorted.
+  List<DependencyNode> exportedNodes;
+
+  /// The map of nodes that are visible in the library, either imported,
+  /// or declared in this library.
+  ///
+  /// TODO(scheglov) support for imports with prefixes
+  Map<String, DependencyNode> libraryScope;
+
+  Library(this.uri, this.imports, this.exports, this.declaredNodes) {
+    for (var node in declaredNodes) {
+      declaredNodeMap[node.name] = node;
+    }
+  }
+
+  @override
+  String toString() => '$uri';
+}
+
+/// The interface for a class that collects information about external nodes
+/// referenced by a node.
+///
+/// The workflow for using it is that the library builder creates a new
+/// instance, fills it with names of import prefixes using [addImportPrefix].
+/// Then for each node defined in the library, methods `appendXyz` called
+/// zero or more times to record references to external names to record API or
+/// implementation dependencies. When all dependencies of a node are appended,
+/// [finish] is invoked to construct the full [DependencyNodeDependencies].
+/// TODO(scheglov) In following CLs we will provide single implementation.
+abstract class ReferenceCollector {
+  final Uri libraryUri;
+
+  ReferenceCollector(this.libraryUri);
+
+  /// Record that the [name] is a name of an import prefix.
+  ///
+  /// So, when we see code like `prefix.foo` we know that `foo` should be
+  /// resolved in the import scope that corresponds to `prefix` (unless the
+  /// name `prefix` is shadowed by a local declaration).
+  void addImportPrefix(String name);
+
+  /// Collect external nodes referenced from the given [node].
+  void appendExpression(Expression node);
+
+  /// Collect external nodes referenced from the given [node].
+  void appendFormalParameters(FormalParameterList node);
+
+  /// Collect external nodes referenced from the given [node].
+  void appendFunctionBody(FunctionBody node);
+
+  /// Collect external nodes referenced from the given [node].
+  void appendTypeAnnotation(TypeAnnotation node);
+
+  /// Construct and return a new [DependencyNodeDependencies] with the given
+  /// [tokenSignature] and all recorded references to external nodes. Clear
+  /// data structures with recorded references and be ready to start recording
+  /// references for a new node.
+  DependencyNodeDependencies finish(List<int> tokenSignature);
+}
+
+class _LibraryBuilder {
+  /// The URI of the library.
+  final Uri uri;
+
+  /// The units of the library, parsed or fully resolved.
+  final List<CompilationUnit> units;
+
+  /// The instance of the referenced names, class members collector.
+  final ReferenceCollector referenceCollector;
+
+  /// The list of imports in the library.
+  final List<Import> imports = [];
+
+  /// The list of exports in the library.
+  final List<Export> exports = [];
+
+  /// The top-level nodes declared in the library.
+  final List<DependencyNode> declaredNodes = [];
+
+  _LibraryBuilder(this.uri, this.units, this.referenceCollector);
+
+  Library build() {
+    _addImports();
+    _addExports();
+
+    // TODO(scheglov) import prefixes are shadowed by class members
+
+    for (var unit in units) {
+      _addUnit(unit);
+    }
+    declaredNodes.sort(DependencyNode.compare);
+
+    return Library(uri, imports, exports, declaredNodes);
+  }
+
+  void _addClassOrMixin(ClassOrMixinDeclaration node) {
+    var hasConstConstructor = node.members.any(
+      (m) => m is ConstructorDeclaration && m.constKeyword != null,
+    );
+
+    List<DependencyNode> classTypeParameters;
+    if (node.typeParameters != null) {
+      classTypeParameters = <DependencyNode>[];
+      for (var typeParameter in node.typeParameters.typeParameters) {
+        classTypeParameters.add(DependencyNode(
+          DependencyName(uri, typeParameter.name.name),
+          DependencyNodeKind.TYPE_PARAMETER,
+          _computeApiDependencies(
+            _computeNodeTokenSignature(typeParameter),
+            typeParameter,
+          ),
+          DependencyNodeDependencies.none,
+        ));
+      }
+      classTypeParameters.sort(DependencyNode.compare);
+    }
+
+    var classMembers = <DependencyNode>[];
+    var hasConstructor = false;
+    for (var member in node.members) {
+      if (member is ConstructorDeclaration) {
+        hasConstructor = true;
+        _addConstructor(classMembers, member);
+      } else if (member is FieldDeclaration) {
+        _addVariables(
+          classMembers,
+          member.metadata,
+          member.fields,
+          hasConstConstructor,
+        );
+      } else if (member is MethodDeclaration) {
+        _addMethod(classMembers, member);
+      } else {
+        throw UnimplementedError('(${member.runtimeType}) $member');
+      }
+    }
+
+    if (!hasConstructor && node is ClassDeclaration) {
+      classMembers.add(DependencyNode(
+        DependencyName(uri, ''),
+        DependencyNodeKind.CONSTRUCTOR,
+        DependencyNodeDependencies.none,
+        DependencyNodeDependencies.none,
+      ));
+    }
+
+    var classTokenSignature = _computeTokenSignature(
+      node.beginToken,
+      node.leftBracket,
+    );
+    // TODO(scheglov) add library URI
+
+    var classNode = DependencyNode(
+      DependencyName(uri, node.name.name),
+      node is MixinDeclaration
+          ? DependencyNodeKind.MIXIN
+          : DependencyNodeKind.CLASS,
+      _computeApiDependencies(classTokenSignature, node),
+      DependencyNodeDependencies.none,
+      classTypeParameters: classTypeParameters,
+    );
+
+    classMembers.sort(DependencyNode.compare);
+    classNode.setClassMembers(classMembers);
+
+    declaredNodes.add(classNode);
+  }
+
+  void _addClassTypeAlias(ClassTypeAlias node) {
+    var tokenSignature = _computeNodeTokenSignature(node);
+    declaredNodes.add(DependencyNode(
+      DependencyName(uri, node.name.name),
+      DependencyNodeKind.CLASS_TYPE_ALIAS,
+      _computeApiDependencies(tokenSignature, node),
+      DependencyNodeDependencies.none,
+    ));
+  }
+
+  void _addConstructor(
+      List<DependencyNode> classMembers, ConstructorDeclaration node) {
+    var signature = ApiSignature();
+    _appendMetadataTokens(signature, node.metadata);
+    _appendFormalParametersTokens(signature, node.parameters);
+    var tokenSignature = signature.toByteList();
+
+    classMembers.add(DependencyNode(
+      DependencyName(uri, node.name?.name ?? ''),
+      DependencyNodeKind.CONSTRUCTOR,
+      _computeApiDependencies(tokenSignature, node),
+      DependencyNodeDependencies.none,
+    ));
+  }
+
+  void _addEnum(EnumDeclaration node) {
+    var classMembers = <DependencyNode>[];
+    for (var constant in node.constants) {
+      classMembers.add(DependencyNode(
+        DependencyName(uri, constant.name.name),
+        DependencyNodeKind.GETTER,
+        DependencyNodeDependencies.none,
+        DependencyNodeDependencies.none,
+      ));
+    }
+    classMembers.add(DependencyNode(
+      DependencyName(uri, 'index'),
+      DependencyNodeKind.GETTER,
+      DependencyNodeDependencies.none,
+      DependencyNodeDependencies.none,
+    ));
+    classMembers.add(DependencyNode(
+      DependencyName(uri, 'values'),
+      DependencyNodeKind.GETTER,
+      DependencyNodeDependencies.none,
+      DependencyNodeDependencies.none,
+    ));
+    classMembers.sort(DependencyNode.compare);
+
+    var enumNode = DependencyNode(
+      DependencyName(uri, node.name.name),
+      DependencyNodeKind.ENUM,
+      DependencyNodeDependencies.none,
+      DependencyNodeDependencies.none,
+    );
+    enumNode.setClassMembers(classMembers);
+
+    declaredNodes.add(enumNode);
+  }
+
+  /// Fill [exports] with information about exports.
+  void _addExports() {
+    for (var directive in units.first.directives) {
+      if (directive is ExportDirective) {
+        var refUri = directive.uri.stringValue;
+        var importUri = uri.resolve(refUri);
+        var combinators = _getCombinators(directive);
+        exports.add(Export(importUri, combinators));
+      }
+    }
+  }
+
+  void _addFunction(FunctionDeclaration node) {
+    var functionExpression = node.functionExpression;
+
+    var signature = ApiSignature();
+    _appendMetadataTokens(signature, node.metadata);
+    _appendNodeTokens(signature, node.returnType);
+    _appendNodeTokens(signature, functionExpression.typeParameters);
+    _appendFormalParametersTokens(signature, functionExpression.parameters);
+    var tokenSignature = signature.toByteList();
+
+    var rawName = node.name.name;
+    var name = DependencyName(uri, node.isSetter ? '$rawName=' : rawName);
+
+    DependencyNodeKind kind;
+    if (node.isGetter) {
+      kind = DependencyNodeKind.GETTER;
+    } else if (node.isSetter) {
+      kind = DependencyNodeKind.SETTER;
+    } else {
+      kind = DependencyNodeKind.FUNCTION;
+    }
+
+    referenceCollector.appendTypeAnnotation(node.returnType);
+    referenceCollector.appendFormalParameters(
+      node.functionExpression.parameters,
+    );
+    var api = referenceCollector.finish(tokenSignature);
+
+    var bodyNode = node.functionExpression.body;
+    var implTokenSignature = _computeNodeTokenSignature(bodyNode);
+    referenceCollector.appendFunctionBody(bodyNode);
+    var impl = referenceCollector.finish(implTokenSignature);
+
+    declaredNodes.add(DependencyNode(name, kind, api, impl));
+  }
+
+  void _addFunctionTypeAlias(FunctionTypeAlias node) {
+    var signature = ApiSignature();
+    _appendMetadataTokens(signature, node.metadata);
+    _appendNodeTokens(signature, node.typeParameters);
+    _appendNodeTokens(signature, node.returnType);
+    _appendFormalParametersTokens(signature, node.parameters);
+    var tokenSignature = signature.toByteList();
+
+    declaredNodes.add(DependencyNode(
+      DependencyName(uri, node.name.name),
+      DependencyNodeKind.FUNCTION_TYPE_ALIAS,
+      _computeApiDependencies(tokenSignature, node),
+      DependencyNodeDependencies.none,
+    ));
+  }
+
+  void _addGenericTypeAlias(GenericTypeAlias node) {
+    var functionType = node.functionType;
+
+    var signature = ApiSignature();
+    _appendMetadataTokens(signature, node.metadata);
+    _appendNodeTokens(signature, node.typeParameters);
+    _appendNodeTokens(signature, functionType.returnType);
+    _appendNodeTokens(signature, functionType.typeParameters);
+    _appendFormalParametersTokens(signature, functionType.parameters);
+    var tokenSignature = signature.toByteList();
+
+    declaredNodes.add(DependencyNode(
+      DependencyName(uri, node.name.name),
+      DependencyNodeKind.GENERIC_TYPE_ALIAS,
+      _computeApiDependencies(tokenSignature, node),
+      DependencyNodeDependencies.none,
+    ));
+  }
+
+  /// Fill [imports] with information about imports.
+  void _addImports() {
+    var hasDartCoreImport = false;
+    for (var directive in units.first.directives) {
+      if (directive is ImportDirective) {
+        var refUri = directive.uri.stringValue;
+        var importUri = uri.resolve(refUri);
+
+        if (importUri.toString() == 'dart:core') {
+          hasDartCoreImport = true;
+        }
+
+        var combinators = _getCombinators(directive);
+
+        imports.add(Import(importUri, directive.prefix?.name, combinators));
+
+        if (directive.prefix != null) {
+          referenceCollector.addImportPrefix(directive.prefix.name);
+        }
+      }
+    }
+
+    if (!hasDartCoreImport) {
+      imports.add(Import(Uri.parse('dart:core'), null, []));
+    }
+  }
+
+  void _addMethod(List<DependencyNode> classMembers, MethodDeclaration node) {
+    var signature = ApiSignature();
+    _appendMetadataTokens(signature, node.metadata);
+    _appendNodeTokens(signature, node.returnType);
+    _appendNodeTokens(signature, node.typeParameters);
+    _appendFormalParametersTokens(signature, node.parameters);
+    var tokenSignature = signature.toByteList();
+
+    DependencyNodeKind kind;
+    if (node.isGetter) {
+      kind = DependencyNodeKind.GETTER;
+    } else if (node.isSetter) {
+      kind = DependencyNodeKind.SETTER;
+    } else {
+      kind = DependencyNodeKind.METHOD;
+    }
+
+    referenceCollector.appendTypeAnnotation(node.returnType);
+    referenceCollector.appendFormalParameters(node.parameters);
+    var api = referenceCollector.finish(tokenSignature);
+
+    var implTokenSignature = _computeNodeTokenSignature(node.body);
+    referenceCollector.appendFunctionBody(node.body);
+    var impl = referenceCollector.finish(implTokenSignature);
+
+    classMembers.add(
+        DependencyNode(DependencyName(uri, node.name.name), kind, api, impl));
+  }
+
+  void _addUnit(CompilationUnit unit) {
+    for (var declaration in unit.declarations) {
+      if (declaration is ClassOrMixinDeclaration) {
+        _addClassOrMixin(declaration);
+      } else if (declaration is ClassTypeAlias) {
+        _addClassTypeAlias(declaration);
+      } else if (declaration is EnumDeclaration) {
+        _addEnum(declaration);
+      } else if (declaration is FunctionDeclaration) {
+        _addFunction(declaration);
+      } else if (declaration is FunctionTypeAlias) {
+        _addFunctionTypeAlias(declaration);
+      } else if (declaration is GenericTypeAlias) {
+        _addGenericTypeAlias(declaration);
+      } else if (declaration is TopLevelVariableDeclaration) {
+        _addVariables(
+          declaredNodes,
+          declaration.metadata,
+          declaration.variables,
+          false,
+        );
+      } else {
+        throw UnimplementedError('(${declaration.runtimeType}) $declaration');
+      }
+    }
+  }
+
+  void _addVariables(
+      List<DependencyNode> variableNodes,
+      List<Annotation> metadata,
+      VariableDeclarationList variables,
+      bool appendInitializerToApi) {
+    if (variables.isConst || variables.type == null) {
+      appendInitializerToApi = true;
+    }
+
+    for (var variable in variables.variables) {
+      var signature = ApiSignature();
+      signature.addInt(variables.isConst ? 1 : 0); // const flag
+      _appendMetadataTokens(signature, metadata);
+
+      _appendNodeTokens(signature, variables.type);
+      referenceCollector.appendTypeAnnotation(variables.type);
+
+      if (appendInitializerToApi) {
+        _appendNodeTokens(signature, variable.initializer);
+        referenceCollector.appendExpression(variable.initializer);
+      }
+
+      var tokenSignature = signature.toByteList();
+      var api = referenceCollector.finish(tokenSignature);
+
+      var rawName = variable.name.name;
+      variableNodes.add(
+        DependencyNode(
+          DependencyName(uri, rawName),
+          DependencyNodeKind.GETTER,
+          api,
+          DependencyNodeDependencies.none,
+        ),
+      );
+      if (!variables.isConst && !variables.isFinal) {
+        variableNodes.add(
+          DependencyNode(
+            DependencyName(uri, '$rawName='),
+            DependencyNodeKind.SETTER,
+            api,
+            DependencyNodeDependencies.none,
+          ),
+        );
+      }
+    }
+  }
+
+  /// Append tokens of the given [parameters] to the [signature].
+  static void _appendFormalParametersTokens(
+      ApiSignature signature, FormalParameterList parameters) {
+    if (parameters == null) return;
+
+    for (var parameter in parameters.parameters) {
+      if (parameter.isRequired) {
+        signature.addInt(1);
+      } else if (parameter.isOptionalPositional) {
+        signature.addInt(2);
+      } else {
+        signature.addInt(3);
+      }
+
+      // If a simple not named parameter, we don't need its name.
+      // We should be careful to include also annotations.
+      if (parameter is SimpleFormalParameter && parameter.type != null) {
+        _appendTokens(
+          signature,
+          parameter.beginToken,
+          parameter.type.endToken,
+        );
+        continue;
+      }
+
+      // We don't know anything better than adding the whole parameter.
+      _appendNodeTokens(signature, parameter);
+    }
+  }
+
+  static void _appendMetadataTokens(
+      ApiSignature signature, List<Annotation> metadata) {
+    if (metadata != null) {
+      for (var annotation in metadata) {
+        _appendNodeTokens(signature, annotation);
+      }
+    }
+  }
+
+  /// Append tokens of the given [node] to the [signature].
+  static void _appendNodeTokens(ApiSignature signature, AstNode node) {
+    if (node != null) {
+      _appendTokens(signature, node.beginToken, node.endToken);
+    }
+  }
+
+  /// Append tokens from [begin] to [end] (both including) to the [signature].
+  static void _appendTokens(ApiSignature signature, Token begin, Token end) {
+    if (begin is CommentToken) {
+      begin = (begin as CommentToken).parent;
+    }
+
+    Token token = begin;
+    while (token != null) {
+      signature.addString(token.lexeme);
+
+      if (token == end) {
+        break;
+      }
+
+      var nextToken = token.next;
+      if (nextToken == token) {
+        break;
+      }
+
+      token = nextToken;
+    }
+  }
+
+  /// TODO(scheglov) Replace all uses with [referenceCollector].
+  static DependencyNodeDependencies _computeApiDependencies(
+      List<int> tokenSignature, AstNode node,
+      [AstNode node2]) {
+    List<String> importPrefixes = [];
+    List<String> unprefixedReferencedNames = [];
+    List<List<String>> importPrefixedReferencedNames = [];
+    return DependencyNodeDependencies(
+      tokenSignature,
+      unprefixedReferencedNames,
+      importPrefixes,
+      importPrefixedReferencedNames,
+      const [],
+    );
+  }
+
+  /// Return the signature for all tokens of the [node].
+  static List<int> _computeNodeTokenSignature(AstNode node) {
+    if (node == null) {
+      return const <int>[];
+    }
+    return _computeTokenSignature(node.beginToken, node.endToken);
+  }
+
+  /// Return the signature for tokens from [begin] to [end] (both including).
+  static List<int> _computeTokenSignature(Token begin, Token end) {
+    var signature = ApiSignature();
+    _appendTokens(signature, begin, end);
+    return signature.toByteList();
+  }
+
+  /// Return [Combinator]s for the given import or export [directive].
+  static List<Combinator> _getCombinators(NamespaceDirective directive) {
+    var combinators = <Combinator>[];
+    for (var combinator in directive.combinators) {
+      if (combinator is ShowCombinator) {
+        combinators.add(
+          Combinator(
+            true,
+            combinator.shownNames.map((id) => id.name).toList(),
+          ),
+        );
+      }
+      if (combinator is HideCombinator) {
+        combinators.add(
+          Combinator(
+            false,
+            combinator.hiddenNames.map((id) => id.name).toList(),
+          ),
+        );
+      }
+    }
+    return combinators;
+  }
+}
diff --git a/pkg/analyzer/lib/src/dart/analysis/dependency/node.dart b/pkg/analyzer/lib/src/dart/analysis/dependency/node.dart
new file mode 100644
index 0000000..e3de954
--- /dev/null
+++ b/pkg/analyzer/lib/src/dart/analysis/dependency/node.dart
@@ -0,0 +1,248 @@
+// Copyright (c) 2018, 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.
+
+import 'dart:typed_data';
+
+import 'package:analyzer/src/generated/utilities_general.dart';
+import 'package:convert/convert.dart';
+
+/// The reference to a class member.
+class ClassMemberReference {
+  /// The target class name.
+  ///
+  /// This is different from the class that actually turned out to define
+  /// the referenced member at the time of recording this reference. So, we
+  /// will notice added overrides in the target class, or anywhere in between.
+  final DependencyName target;
+
+  /// Whether the explicit `super` was used as the target.
+  /// The [target] is the enclosing class.
+  final bool targetSuper;
+
+  /// The name referenced in the [target].
+  final String name;
+
+  @override
+  final int hashCode;
+
+  ClassMemberReference(this.target, this.targetSuper, this.name)
+      : hashCode = JenkinsSmiHash.hash2(target.hashCode, name.hashCode);
+
+  @override
+  bool operator ==(other) {
+    return other is ClassMemberReference &&
+        other.target == target &&
+        other.targetSuper == targetSuper &&
+        other.name == name;
+  }
+
+  @override
+  String toString() {
+    return '($target, $name, super: $targetSuper)';
+  }
+
+  static int compare(ClassMemberReference first, ClassMemberReference second) {
+    var result = DependencyName.compare(first.target, second.target);
+    if (result != 0) return result;
+
+    if (first.targetSuper && !second.targetSuper) return -1;
+    if (!first.targetSuper && second.targetSuper) return 1;
+
+    return first.name.compareTo(second.name);
+  }
+}
+
+/// A name qualified by a library URI.
+class DependencyName {
+  /// The URI of the defining library.
+  /// Not `null`.
+  final Uri libraryUri;
+
+  /// The name of this name object.
+  /// If the name starts with `_`, then the name is private.
+  /// Names of setters end with `=`.
+  final String name;
+
+  /// Whether this name is private, and its [name] starts with `_`.
+  final bool isPrivate;
+
+  /// The cached, pre-computed hash code.
+  @override
+  final int hashCode;
+
+  factory DependencyName(Uri libraryUri, String name) {
+    var isPrivate = name.startsWith('_');
+    var hashCode = JenkinsSmiHash.hash2(libraryUri.hashCode, name.hashCode);
+    return DependencyName._internal(libraryUri, name, isPrivate, hashCode);
+  }
+
+  DependencyName._internal(
+      this.libraryUri, this.name, this.isPrivate, this.hashCode);
+
+  @override
+  bool operator ==(Object other) {
+    return other is DependencyName &&
+        other.hashCode == hashCode &&
+        name == other.name &&
+        libraryUri == other.libraryUri;
+  }
+
+  /// Whether this name us accessible for the library with the given
+  /// [libraryUri], i.e. when the name is public, or is defined in a library
+  /// with the same URI.
+  bool isAccessibleFor(Uri libraryUri) {
+    return !isPrivate || this.libraryUri == libraryUri;
+  }
+
+  @override
+  String toString() => '$libraryUri::$name';
+
+  /// Compare given names by their raw names.
+  ///
+  /// This method should be used only for sorting, it does not follow the
+  /// complete semantics of [==] and [hashCode].
+  static int compare(DependencyName first, DependencyName second) {
+    return first.name.compareTo(second.name);
+  }
+}
+
+/// A dependency node - anything that has a name, and can be referenced.
+class DependencyNode {
+  /// The API or implementation signature used in [DependencyNodeDependencies]
+  /// as a marker that this node is changed, explicitly because its token
+  /// signature changed, or implicitly - because it references a changed node.
+  static final changedSignature = Uint8List.fromList([0xDE, 0xAD, 0xBE, 0xEF]);
+
+  final DependencyName name;
+  final DependencyNodeKind kind;
+
+  /// Dependencies that affect the API of the node, so affect API or
+  /// implementation dependencies of the nodes that use this node.
+  final DependencyNodeDependencies api;
+
+  /// Additional (to the [api]) dependencies that affect only the
+  /// "implementation" of the node, e.g. the body of a method, but are not
+  /// visible outside of the node, and so don't affect any other nodes.
+  final DependencyNodeDependencies impl;
+
+  /// If the node is a class member, the node of the enclosing class.
+  /// Otherwise `null`.
+  final DependencyNode enclosingClass;
+
+  /// If the node is a class, the nodes of its type parameters.
+  /// Otherwise `null`.
+  final List<DependencyNode> classTypeParameters;
+
+  /// If the node is a class, the sorted list of members in this class.
+  /// Otherwise `null`.
+  List<DependencyNode> classMembers;
+
+  DependencyNode(
+    this.name,
+    this.kind,
+    this.api,
+    this.impl, {
+    this.enclosingClass,
+    this.classTypeParameters,
+  });
+
+  /// Return the node that can be referenced by the given [name] from the
+  /// library with the given [libraryUri].
+  DependencyNode getClassMember(Uri libraryUri, String name) {
+    // TODO(scheglov) The list is sorted, use this fact to search faster.
+    // TODO(scheglov) Collect superclass members here or outside.
+    for (var i = 0; i < classMembers.length; ++i) {
+      var member = classMembers[i];
+      var memberName = member.name;
+      if (memberName.name == name && memberName.isAccessibleFor(libraryUri)) {
+        return member;
+      }
+    }
+    return null;
+  }
+
+  /// Set new class members for this class.
+  void setClassMembers(List<DependencyNode> newClassMembers) {
+    classMembers = newClassMembers;
+  }
+
+  @override
+  String toString() {
+    if (enclosingClass != null) {
+      return '$enclosingClass::${name.name}';
+    }
+    return name.toString();
+  }
+
+  /// Compare given nodes by their names.
+  static int compare(DependencyNode first, DependencyNode second) {
+    return DependencyName.compare(first.name, second.name);
+  }
+}
+
+/// The dependencies of the API or implementation portion of a node.
+class DependencyNodeDependencies {
+  static final none = DependencyNodeDependencies([], [], [], [], []);
+
+  /// The token signature of this portion of the node. It depends on all
+  /// tokens that might affect the node API or implementation resolution.
+  final List<int> tokenSignature;
+
+  /// The names that appear unprefixed in this portion of the node, and are
+  /// not defined locally in the node. Locally defined names themselves
+  /// depend on some non-local nodes, which will also recorded here.
+  ///
+  /// This list is sorted.
+  final List<String> unprefixedReferencedNames;
+
+  /// The names of import prefixes used to reference names in this node.
+  ///
+  /// This list is sorted.
+  final List<String> importPrefixes;
+
+  /// The names referenced by this node with the import prefix at the
+  /// corresponding index in [importPrefixes].
+  ///
+  /// This list is sorted.
+  final List<List<String>> importPrefixedReferencedNames;
+
+  /// The class members referenced in this portion of the node.
+  ///
+  /// This list is sorted.
+  final List<ClassMemberReference> classMemberReferences;
+
+  /// All referenced nodes, computed from [unprefixedReferencedNames],
+  /// [importPrefixedReferencedNames], and [classMemberReferences].
+  List<DependencyNode> referencedNodes;
+
+  /// The transitive signature of this portion of the node, computed using
+  /// the [tokenSignature] of this node, and API signatures of the
+  /// [referencedNodes].
+  List<int> transitiveSignature;
+
+  DependencyNodeDependencies(
+      this.tokenSignature,
+      this.unprefixedReferencedNames,
+      this.importPrefixes,
+      this.importPrefixedReferencedNames,
+      this.classMemberReferences);
+
+  String get tokenSignatureHex => hex.encode(tokenSignature);
+}
+
+/// Kinds of nodes.
+enum DependencyNodeKind {
+  CLASS,
+  CLASS_TYPE_ALIAS,
+  CONSTRUCTOR,
+  ENUM,
+  FUNCTION,
+  FUNCTION_TYPE_ALIAS,
+  GENERIC_TYPE_ALIAS,
+  GETTER,
+  METHOD,
+  MIXIN,
+  SETTER,
+  TYPE_PARAMETER,
+}
diff --git a/pkg/analyzer/test/src/dart/analysis/dependency/base.dart b/pkg/analyzer/test/src/dart/analysis/dependency/base.dart
new file mode 100644
index 0000000..8df8a047
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/analysis/dependency/base.dart
@@ -0,0 +1,184 @@
+// Copyright (c) 2018, 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.
+
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/analysis/dependency/library_builder.dart';
+import 'package:analyzer/src/dart/analysis/dependency/node.dart';
+import 'package:meta/meta.dart';
+import 'package:test/test.dart';
+
+import '../../resolution/driver_resolution.dart';
+
+class BaseDependencyTest extends DriverResolutionTest {
+//  DependencyTracker tracker;
+  String a;
+  String b;
+  String c;
+  Uri aUri;
+  Uri bUri;
+  Uri cUri;
+
+  bool hasDartCore = false;
+
+  void assertNodes(
+    List<DependencyNode> actualNodes,
+    List<ExpectedNode> expectedNodes,
+  ) {
+    expect(actualNodes, hasLength(expectedNodes.length));
+    for (var expectedNode in expectedNodes) {
+      var topNode = _getNode(
+        actualNodes,
+        uri: expectedNode.uri,
+        name: expectedNode.name,
+        kind: expectedNode.kind,
+      );
+
+      if (expectedNode.classMembers != null) {
+        assertNodes(topNode.classMembers, expectedNode.classMembers);
+      } else {
+        expect(topNode.classMembers, isNull);
+      }
+
+      if (expectedNode.classTypeParameters != null) {
+        assertNodes(
+          topNode.classTypeParameters,
+          expectedNode.classTypeParameters,
+        );
+      } else {
+        expect(topNode.classTypeParameters, isNull);
+      }
+    }
+  }
+
+  Future<Library> buildTestLibrary(String path, String content) async {
+//    if (!hasDartCore) {
+//      hasDartCore = true;
+//      await _addLibraryByUri('dart:core');
+//      await _addLibraryByUri('dart:async');
+//      await _addLibraryByUri('dart:math');
+//      await _addLibraryByUri('dart:_internal');
+//    }
+
+    newFile(path, content: content);
+    driver.changeFile(path);
+
+    var units = await _resolveLibrary(path);
+    var uri = units.first.declaredElement.source.uri;
+
+    return buildLibrary(uri, units, _ReferenceCollector());
+
+//    tracker.addLibrary(uri, units);
+//
+//    var library = tracker.libraries[uri];
+//    expect(library, isNotNull);
+//
+//    return library;
+  }
+
+  DependencyNode getNode(Library library,
+      {@required Uri uri,
+      @required String name,
+      DependencyNodeKind kind,
+      String memberOf,
+      String typeParameterOf}) {
+    var nodes = library.declaredNodes;
+    if (memberOf != null) {
+      var class_ = _getNode(nodes, uri: aUri, name: memberOf);
+      expect(class_.kind,
+          anyOf(DependencyNodeKind.CLASS, DependencyNodeKind.MIXIN));
+      nodes = class_.classMembers;
+    } else if (typeParameterOf != null) {
+      var class_ = _getNode(nodes, uri: aUri, name: typeParameterOf);
+      expect(class_.kind,
+          anyOf(DependencyNodeKind.CLASS, DependencyNodeKind.MIXIN));
+      nodes = class_.classTypeParameters;
+    }
+    return _getNode(nodes, uri: aUri, name: name, kind: kind);
+  }
+
+  @override
+  void setUp() {
+    super.setUp();
+//    var logger = PerformanceLog(null);
+//    tracker = DependencyTracker(logger);
+    a = convertPath('/test/lib/a.dart');
+    b = convertPath('/test/lib/b.dart');
+    c = convertPath('/test/lib/c.dart');
+    aUri = Uri.parse('package:test/a.dart');
+    bUri = Uri.parse('package:test/b.dart');
+    cUri = Uri.parse('package:test/c.dart');
+  }
+
+//  Future _addLibraryByUri(String uri) async {
+//    var path = driver.sourceFactory.forUri(uri).fullName;
+//    var unitResult = await driver.getUnitElement(path);
+//
+//    var signature = ApiSignature();
+//    signature.addString(unitResult.signature);
+//    var signatureBytes = signature.toByteList();
+//
+//    tracker.addLibraryElement(unitResult.element.library, signatureBytes);
+//  }
+
+  DependencyNode _getNode(List<DependencyNode> nodes,
+      {@required Uri uri, @required String name, DependencyNodeKind kind}) {
+    var nameObj = DependencyName(uri, name);
+    for (var node in nodes) {
+      if (node.name == nameObj) {
+        if (kind != null && node.kind != kind) {
+          fail('Expected $kind "$name", found ${node.kind}');
+        }
+        return node;
+      }
+    }
+    fail('Expected to find $uri::$name in:\n    ${nodes.join('\n    ')}');
+  }
+
+  Future<List<CompilationUnit>> _resolveLibrary(String libraryPath) async {
+    var resolvedLibrary = await driver.getResolvedLibrary(libraryPath);
+    return resolvedLibrary.units.map((ru) => ru.unit).toList();
+  }
+}
+
+class ExpectedNode {
+  final Uri uri;
+  final String name;
+  final DependencyNodeKind kind;
+  final List<ExpectedNode> classMembers;
+  final List<ExpectedNode> classTypeParameters;
+
+  ExpectedNode(
+    this.uri,
+    this.name,
+    this.kind, {
+    this.classMembers,
+    this.classTypeParameters,
+  });
+}
+
+/// TODO(scheglov) remove it once we get actual implementation
+class _ReferenceCollector implements ReferenceCollector {
+  @override
+  Uri get libraryUri => null;
+
+  @override
+  void addImportPrefix(String name) {}
+
+  @override
+  void appendExpression(Expression node) {}
+
+  @override
+  void appendFormalParameters(FormalParameterList formalParameterList) {}
+
+  @override
+  void appendFunctionBody(FunctionBody node) {}
+
+  @override
+  void appendTypeAnnotation(TypeAnnotation node) {}
+
+  @override
+  DependencyNodeDependencies finish(List<int> tokenSignature) {
+    return DependencyNodeDependencies(tokenSignature, [], [], [], []);
+  }
+}
diff --git a/pkg/analyzer/test/src/dart/analysis/dependency/declared_nodes_test.dart b/pkg/analyzer/test/src/dart/analysis/dependency/declared_nodes_test.dart
new file mode 100644
index 0000000..fec1f34
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/analysis/dependency/declared_nodes_test.dart
@@ -0,0 +1,1709 @@
+// Copyright (c) 2018, 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.
+
+import 'package:analyzer/src/dart/analysis/dependency/library_builder.dart'
+    hide buildLibrary;
+import 'package:analyzer/src/dart/analysis/dependency/node.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'base.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(DeclaredNodesTest);
+  });
+}
+
+@reflectiveTest
+class DeclaredNodesTest extends BaseDependencyTest {
+  test_class_constructor() async {
+    var library = await buildTestLibrary(a, r'''
+class C {
+  C();
+  C.named();
+}
+''');
+    assertNodes(library.declaredNodes, [
+      ExpectedNode(
+        aUri,
+        'C',
+        DependencyNodeKind.CLASS,
+        classMembers: [
+          ExpectedNode(aUri, '', DependencyNodeKind.CONSTRUCTOR),
+          ExpectedNode(aUri, 'named', DependencyNodeKind.CONSTRUCTOR),
+        ],
+      ),
+    ]);
+  }
+
+  test_class_constructor_api_tokens_notSame_annotation() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.CONSTRUCTOR,
+      'class X {  X.foo();  }',
+      'class X {  @deprecated X.foo();  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_constructor_api_tokens_notSame_parameter_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.CONSTRUCTOR,
+      'class X {  X.foo();  }',
+      'class X {  X.foo(int a);  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_constructor_api_tokens_notSame_parameter_name_edit_named() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.CONSTRUCTOR,
+      'class X {  X.foo({int a});  }',
+      'class X {  X.foo({int b});  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_constructor_api_tokens_notSame_parameter_type() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.CONSTRUCTOR,
+      'class X {  X.foo(int a);  }',
+      'class X {  X.foo(double a);  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_constructor_api_tokens_same_body() async {
+    await _assertApiTokenSignatureSame(
+      'foo',
+      DependencyNodeKind.CONSTRUCTOR,
+      'class X {  X.foo() { print(1); }  }',
+      'class X {  X.foo() { print(2); }  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_constructor_api_tokens_same_body_add() async {
+    await _assertApiTokenSignatureSame(
+      'foo',
+      DependencyNodeKind.CONSTRUCTOR,
+      'class X {  X.foo();  }',
+      'class X {  X.foo() {}  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_constructor_api_tokens_same_parameter_name_edit_required() async {
+    await _assertApiTokenSignatureSame(
+      'foo',
+      DependencyNodeKind.CONSTRUCTOR,
+      'class X {  X.foo(int a);  }',
+      'class X {  X.foo(int b);  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_constructor_default() async {
+    var library = await buildTestLibrary(a, r'''
+class C {}
+''');
+    assertNodes(library.declaredNodes, [
+      ExpectedNode(
+        aUri,
+        'C',
+        DependencyNodeKind.CLASS,
+        classMembers: [
+          ExpectedNode(aUri, '', DependencyNodeKind.CONSTRUCTOR),
+        ],
+      ),
+    ]);
+  }
+
+  test_class_field() async {
+    var library = await buildTestLibrary(a, r'''
+class C {
+  int a = 1;
+  int b = 2, c = 3;
+}
+''');
+    assertNodes(library.declaredNodes, [
+      ExpectedNode(
+        aUri,
+        'C',
+        DependencyNodeKind.CLASS,
+        classMembers: [
+          ExpectedNode(aUri, '', DependencyNodeKind.CONSTRUCTOR),
+          ExpectedNode(aUri, 'a', DependencyNodeKind.GETTER),
+          ExpectedNode(aUri, 'b', DependencyNodeKind.GETTER),
+          ExpectedNode(aUri, 'c', DependencyNodeKind.GETTER),
+          ExpectedNode(aUri, 'a=', DependencyNodeKind.SETTER),
+          ExpectedNode(aUri, 'b=', DependencyNodeKind.SETTER),
+          ExpectedNode(aUri, 'c=', DependencyNodeKind.SETTER),
+        ],
+      ),
+    ]);
+  }
+
+  test_class_field_api_tokens_notSame_annotation() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'class X {  int foo = 0;  }',
+      'class X {  @deprecated int foo = 0;  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_field_api_tokens_notSame_const() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'class X {  int foo = 0;  }',
+      'class X {  const int foo = 0;  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_field_api_tokens_same_final() async {
+    await _assertApiTokenSignatureSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'class X {  int foo = 0;  }',
+      'class X {  final int foo = 0;  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_field_api_tokens_typed_notSame_type() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'class X {  int foo = 0;  }',
+      'class X {  num foo = 1;  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_field_api_tokens_typed_same_initializer() async {
+    await _assertApiTokenSignatureSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'class X {  int foo = 0;  }',
+      'class X {  int foo = 1;  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_field_api_tokens_untyped_notSame_initializer() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'class X {  var foo = 0;  }',
+      'class X {  var foo = 1.0;  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_field_const() async {
+    var library = await buildTestLibrary(a, r'''
+class X {
+  const foo = 1;
+  const bar = 2;
+}
+''');
+    assertNodes(library.declaredNodes, [
+      ExpectedNode(
+        aUri,
+        'X',
+        DependencyNodeKind.CLASS,
+        classMembers: [
+          ExpectedNode(aUri, '', DependencyNodeKind.CONSTRUCTOR),
+          ExpectedNode(aUri, 'foo', DependencyNodeKind.GETTER),
+          ExpectedNode(aUri, 'bar', DependencyNodeKind.GETTER),
+        ],
+      ),
+    ]);
+  }
+
+  test_class_field_const_api_tokens_typed_notSame_initializer() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'class X {  const int foo = 0;  }',
+      'class X {  const int foo = 1;  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_field_final() async {
+    var library = await buildTestLibrary(a, r'''
+class X {
+  final foo = 1;
+  final bar = 2;
+}
+''');
+    assertNodes(library.declaredNodes, [
+      ExpectedNode(
+        aUri,
+        'X',
+        DependencyNodeKind.CLASS,
+        classMembers: [
+          ExpectedNode(aUri, '', DependencyNodeKind.CONSTRUCTOR),
+          ExpectedNode(aUri, 'foo', DependencyNodeKind.GETTER),
+          ExpectedNode(aUri, 'bar', DependencyNodeKind.GETTER),
+        ],
+      ),
+    ]);
+  }
+
+  test_class_field_final_api_tokens_typed_notSame_initializer_constClass() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'class X {  final int foo = 0;  const X();  }',
+      'class X {  final int foo = 1;  const X();  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_field_final_api_tokens_typed_same_initializer() async {
+    await _assertApiTokenSignatureSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'class X {  final int foo = 0;  }',
+      'class X {  final int foo = 1;  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_field_final_api_tokens_untyped_notSame_initializer() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'class X {  final foo = 0;  }',
+      'class X {  final foo = 1.0;  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_getter() async {
+    var library = await buildTestLibrary(a, r'''
+class C {
+  int get foo => 0;
+  int get bar => 0;
+}
+''');
+    assertNodes(library.declaredNodes, [
+      ExpectedNode(
+        aUri,
+        'C',
+        DependencyNodeKind.CLASS,
+        classMembers: [
+          ExpectedNode(aUri, '', DependencyNodeKind.CONSTRUCTOR),
+          ExpectedNode(aUri, 'foo', DependencyNodeKind.GETTER),
+          ExpectedNode(aUri, 'bar', DependencyNodeKind.GETTER),
+        ],
+      ),
+    ]);
+  }
+
+  test_class_getter_api_tokens_notSame_returnType() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'class X {  int get foo => null;  }',
+      'class X {  double get foo => null;  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_method() async {
+    var library = await buildTestLibrary(a, r'''
+class C {
+  void foo() {}
+  void bar() {}
+}
+''');
+    assertNodes(library.declaredNodes, [
+      ExpectedNode(
+        aUri,
+        'C',
+        DependencyNodeKind.CLASS,
+        classMembers: [
+          ExpectedNode(aUri, '', DependencyNodeKind.CONSTRUCTOR),
+          ExpectedNode(aUri, 'foo', DependencyNodeKind.METHOD),
+          ExpectedNode(aUri, 'bar', DependencyNodeKind.METHOD),
+        ],
+      ),
+    ]);
+  }
+
+  test_class_method_api_tokens_notSame_annotation() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.METHOD,
+      'class X {  void foo() {}  }',
+      'class X {  @deprecated void foo() {}  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_method_api_tokens_notSame_parameter_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.METHOD,
+      'class X {  void foo() {}  }',
+      'class X {  void foo(int a) {}  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_method_api_tokens_notSame_parameter_name_edit_named() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.METHOD,
+      'class X {  void foo({int a}) {}  }',
+      'class X {  void foo({int b}) {}  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_method_api_tokens_notSame_parameter_type() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.METHOD,
+      'class X {  void foo(int a) {}  }',
+      'class X {  void foo(double a) {}  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_method_api_tokens_notSame_returnType() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.METHOD,
+      'class X {  int foo() {}  }',
+      'class X {  double foo() {}  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_method_api_tokens_notSame_typeParameter_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.METHOD,
+      'class X {  void foo() {}  }',
+      'class X {  void foo<T>() {}  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_method_api_tokens_notSame_typeParameter_bound_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.METHOD,
+      'class X {  void foo<T>() {}  }',
+      'class X {  void foo<T extends num>() {}  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_method_api_tokens_same_async_add() async {
+    await _assertApiTokenSignatureSame(
+      'foo',
+      DependencyNodeKind.METHOD,
+      'class X {  foo() {}  }',
+      'class X {  foo() async {}  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_method_api_tokens_same_body() async {
+    await _assertApiTokenSignatureSame(
+      'foo',
+      DependencyNodeKind.METHOD,
+      'class X {  void foo() { print(1); }  }',
+      'class X {  void foo() { print(2); }  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_method_api_tokens_same_parameter_name_edit_required() async {
+    await _assertApiTokenSignatureSame(
+      'foo',
+      DependencyNodeKind.METHOD,
+      'class X {  void foo(int a) {}  }',
+      'class X {  void foo(int b) {}  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_setter() async {
+    var library = await buildTestLibrary(a, r'''
+class C {
+  set foo(_) {}
+  set bar(_) {}
+}
+''');
+    assertNodes(library.declaredNodes, [
+      ExpectedNode(
+        aUri,
+        'C',
+        DependencyNodeKind.CLASS,
+        classMembers: [
+          ExpectedNode(aUri, '', DependencyNodeKind.CONSTRUCTOR),
+          ExpectedNode(aUri, 'foo', DependencyNodeKind.SETTER),
+          ExpectedNode(aUri, 'bar', DependencyNodeKind.SETTER),
+        ],
+      ),
+    ]);
+  }
+
+  test_class_setter_api_tokens_notSame_returnType() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.SETTER,
+      'class X {  set foo(int a) {}  }',
+      'class X {  set foo(double a) {}  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_setter_api_tokens_same_parameter_name() async {
+    await _assertApiTokenSignatureSame(
+      'foo',
+      DependencyNodeKind.SETTER,
+      'class X {  set foo(int a) {}  }',
+      'class X {  set foo(int b) {}  }',
+      memberOf: 'X',
+    );
+  }
+
+  test_class_typeParameter() async {
+    var library = await buildTestLibrary(a, r'''
+class A<T> {}
+class B<T, U> {}
+''');
+    assertNodes(library.declaredNodes, [
+      ExpectedNode(
+        aUri,
+        'A',
+        DependencyNodeKind.CLASS,
+        classMembers: [
+          ExpectedNode(aUri, '', DependencyNodeKind.CONSTRUCTOR),
+        ],
+        classTypeParameters: [
+          ExpectedNode(aUri, 'T', DependencyNodeKind.TYPE_PARAMETER),
+        ],
+      ),
+      ExpectedNode(
+        aUri,
+        'B',
+        DependencyNodeKind.CLASS,
+        classMembers: [
+          ExpectedNode(aUri, '', DependencyNodeKind.CONSTRUCTOR),
+        ],
+        classTypeParameters: [
+          ExpectedNode(aUri, 'T', DependencyNodeKind.TYPE_PARAMETER),
+          ExpectedNode(aUri, 'U', DependencyNodeKind.TYPE_PARAMETER),
+        ],
+      ),
+    ]);
+  }
+
+  test_class_typeParameter_api_tokens_notSame_annotation() async {
+    await _assertApiTokenSignatureNotSame(
+      'T',
+      DependencyNodeKind.TYPE_PARAMETER,
+      'class X<T> {}',
+      'class X<@deprecate T> {}',
+      typeParameterOf: 'X',
+    );
+  }
+
+  test_class_typeParameter_api_tokens_notSame_bound_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'T',
+      DependencyNodeKind.TYPE_PARAMETER,
+      'class X<T> {}',
+      'class X<T extends num> {}',
+      typeParameterOf: 'X',
+    );
+  }
+
+  test_class_typeParameter_api_tokens_notSame_bound_edit() async {
+    await _assertApiTokenSignatureNotSame(
+      'T',
+      DependencyNodeKind.TYPE_PARAMETER,
+      'class X<T extends num> {}',
+      'class X<T extends int> {}',
+      typeParameterOf: 'X',
+    );
+  }
+
+  test_library_export() async {
+    var library = await buildTestLibrary(a, r'''
+export 'dart:math';
+export 'package:aaa/aaa.dart';
+export 'package:bbb/bbb.dart' show b1, b2 hide b3;
+''');
+    _assertExports(library, [
+      Export(Uri.parse('dart:math'), []),
+      Export(Uri.parse('package:aaa/aaa.dart'), []),
+      Export(Uri.parse('package:bbb/bbb.dart'), [
+        Combinator(true, ['b1', 'b2']),
+        Combinator(false, ['b3']),
+      ]),
+    ]);
+  }
+
+  test_library_import() async {
+    var library = await buildTestLibrary(a, r'''
+import 'dart:math';
+import 'package:aaa/aaa.dart';
+import 'package:bbb/bbb.dart' as b;
+import 'package:ccc/ccc.dart' show c1, c2 hide c3;
+''');
+    _assertImports(library, [
+      Import(Uri.parse('dart:math'), null, []),
+      Import(Uri.parse('package:aaa/aaa.dart'), null, []),
+      Import(Uri.parse('package:bbb/bbb.dart'), 'b', []),
+      Import(Uri.parse('package:ccc/ccc.dart'), null, [
+        Combinator(true, ['c1', 'c2']),
+        Combinator(false, ['c3']),
+      ]),
+      Import(Uri.parse('dart:core'), null, []),
+    ]);
+  }
+
+  test_library_import_core_explicit() async {
+    var library = await buildTestLibrary(a, r'''
+import 'dart:core' hide List;
+''');
+    _assertImports(library, [
+      Import(Uri.parse('dart:core'), null, [
+        Combinator(false, ['List']),
+      ]),
+    ]);
+  }
+
+  test_library_import_core_implicit() async {
+    var library = await buildTestLibrary(a, '');
+    _assertImports(library, [
+      Import(Uri.parse('dart:core'), null, []),
+    ]);
+  }
+
+  test_unit_class() async {
+    var library = await buildTestLibrary(a, r'''
+class Foo {}
+class Bar {}
+''');
+    assertNodes(library.declaredNodes, [
+      ExpectedNode(aUri, 'Foo', DependencyNodeKind.CLASS, classMembers: [
+        ExpectedNode(aUri, '', DependencyNodeKind.CONSTRUCTOR),
+      ]),
+      ExpectedNode(aUri, 'Bar', DependencyNodeKind.CLASS, classMembers: [
+        ExpectedNode(aUri, '', DependencyNodeKind.CONSTRUCTOR),
+      ]),
+    ]);
+  }
+
+  test_unit_class_api_tokens_notSame_annotation() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS,
+      'class X {}',
+      '@deprecated class X {}',
+    );
+  }
+
+  test_unit_class_api_tokens_notSame_extends_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS,
+      'class X {}',
+      'class X extends A {}',
+    );
+  }
+
+  test_unit_class_api_tokens_notSame_extends_edit() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS,
+      'class X extends A {}',
+      'class X extends B {}',
+    );
+  }
+
+  test_unit_class_api_tokens_notSame_extends_replace() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS,
+      'class X extends A {}',
+      'class X implements A {}',
+    );
+  }
+
+  test_unit_class_api_tokens_notSame_extends_typeArgument() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS,
+      'class X extends A {}',
+      'class X extends A<int> {}',
+    );
+  }
+
+  test_unit_class_api_tokens_notSame_implements_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS,
+      'class X {}',
+      'class X implements A {}',
+    );
+  }
+
+  test_unit_class_api_tokens_notSame_implements_edit() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS,
+      'class X implements A {}',
+      'class X implements B {}',
+    );
+  }
+
+  test_unit_class_api_tokens_notSame_implements_remove() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS,
+      'class X implements A {}',
+      'class X {}',
+    );
+  }
+
+  test_unit_class_api_tokens_notSame_implements_remove2() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS,
+      'class X implements A, B {}',
+      'class X implements B {}',
+    );
+  }
+
+  test_unit_class_api_tokens_notSame_implements_typeArgument() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS,
+      'class X implements A {}',
+      'class X implements A<int> {}',
+    );
+  }
+
+  test_unit_class_api_tokens_notSame_typeParameter_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS,
+      'class X {}',
+      'class X<T> {}',
+    );
+  }
+
+  test_unit_class_api_tokens_notSame_typeParameter_add2() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS,
+      'class X<T> {}',
+      'class X<T, U> {}',
+    );
+  }
+
+  test_unit_class_api_tokens_notSame_typeParameter_bound_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS,
+      'class X<T> {}',
+      'class X<T extends num> {}',
+    );
+  }
+
+  test_unit_class_api_tokens_notSame_with_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS,
+      'class X extends A {}',
+      'class X extends A with B {}',
+    );
+  }
+
+  test_unit_class_api_tokens_notSame_with_edit() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS,
+      'class X extends A with B {}',
+      'class X extends A with C {}',
+    );
+  }
+
+  test_unit_class_api_tokens_notSame_with_typeArgument() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS,
+      'class X extends A with B {}',
+      'class X extends A with B<int> {}',
+    );
+  }
+
+  test_unit_class_api_tokens_same_body() async {
+    await _assertApiTokenSignatureSame(
+      'X',
+      DependencyNodeKind.CLASS,
+      'class X {  }',
+      'class X { void foo() {} }',
+    );
+  }
+
+  test_unit_classTypeAlias() async {
+    var library = await buildTestLibrary(a, r'''
+class X = Object with M;
+''');
+    assertNodes(library.declaredNodes, [
+      ExpectedNode(aUri, 'X', DependencyNodeKind.CLASS_TYPE_ALIAS),
+    ]);
+  }
+
+  test_unit_classTypeAlias_api_tokens_notSame_implements_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS_TYPE_ALIAS,
+      'class X = A with M;',
+      'class X = A with M implements I;',
+    );
+  }
+
+  test_unit_classTypeAlias_api_tokens_notSame_implements_edit() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS_TYPE_ALIAS,
+      'class X = A with M implements I;',
+      'class X = A with M implements J;',
+    );
+  }
+
+  test_unit_classTypeAlias_api_tokens_notSame_implements_remove() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS_TYPE_ALIAS,
+      'class X = A with M implements I;',
+      'class X = A with M;',
+    );
+  }
+
+  test_unit_classTypeAlias_api_tokens_notSame_implements_typeArgument() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS_TYPE_ALIAS,
+      'class X = A with M implements I;',
+      'class X = A with M implements I<int>;',
+    );
+  }
+
+  test_unit_classTypeAlias_api_tokens_notSame_super() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS_TYPE_ALIAS,
+      'class X = A with M;',
+      'class X = B with M;',
+    );
+  }
+
+  test_unit_classTypeAlias_api_tokens_notSame_super_typeArgument() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS_TYPE_ALIAS,
+      'class X = A with M;',
+      'class X = A<int> with M;',
+    );
+  }
+
+  test_unit_classTypeAlias_api_tokens_notSame_typeParameter_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS_TYPE_ALIAS,
+      'class X = A with M;',
+      'class X<T> = A with M;',
+    );
+  }
+
+  test_unit_classTypeAlias_api_tokens_notSame_typeParameter_add2() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS_TYPE_ALIAS,
+      'class X<T> = A with M;',
+      'class X<T, U> = A with M;',
+    );
+  }
+
+  test_unit_classTypeAlias_api_tokens_notSame_typeParameter_bound_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS_TYPE_ALIAS,
+      'class X<T> = A with M;',
+      'class X<T extends num> = A with M;',
+    );
+  }
+
+  test_unit_classTypeAlias_api_tokens_notSame_with_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS_TYPE_ALIAS,
+      'class X = A with M;',
+      'class X = A with M, N;',
+    );
+  }
+
+  test_unit_classTypeAlias_api_tokens_notSame_with_edit() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS_TYPE_ALIAS,
+      'class X = A with M;',
+      'class X = A with N;',
+    );
+  }
+
+  test_unit_classTypeAlias_api_tokens_notSame_with_typeArgument() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.CLASS_TYPE_ALIAS,
+      'class X = A with M;',
+      'class X = A with M<int>;',
+    );
+  }
+
+  test_unit_enumDeclaration() async {
+    var library = await buildTestLibrary(a, r'''
+enum Foo {a, b, c}
+enum Bar {d, e, f}
+''');
+    assertNodes(library.declaredNodes, [
+      ExpectedNode(
+        aUri,
+        'Foo',
+        DependencyNodeKind.ENUM,
+        classMembers: [
+          ExpectedNode(aUri, 'a', DependencyNodeKind.GETTER),
+          ExpectedNode(aUri, 'b', DependencyNodeKind.GETTER),
+          ExpectedNode(aUri, 'c', DependencyNodeKind.GETTER),
+          ExpectedNode(aUri, 'index', DependencyNodeKind.GETTER),
+          ExpectedNode(aUri, 'values', DependencyNodeKind.GETTER),
+        ],
+      ),
+      ExpectedNode(
+        aUri,
+        'Bar',
+        DependencyNodeKind.ENUM,
+        classMembers: [
+          ExpectedNode(aUri, 'd', DependencyNodeKind.GETTER),
+          ExpectedNode(aUri, 'e', DependencyNodeKind.GETTER),
+          ExpectedNode(aUri, 'f', DependencyNodeKind.GETTER),
+          ExpectedNode(aUri, 'index', DependencyNodeKind.GETTER),
+          ExpectedNode(aUri, 'values', DependencyNodeKind.GETTER),
+        ],
+      ),
+    ]);
+  }
+
+  test_unit_function() async {
+    var library = await buildTestLibrary(a, r'''
+void foo() {}
+void bar() {}
+''');
+    assertNodes(library.declaredNodes, [
+      ExpectedNode(aUri, 'foo', DependencyNodeKind.FUNCTION),
+      ExpectedNode(aUri, 'bar', DependencyNodeKind.FUNCTION),
+    ]);
+  }
+
+  test_unit_function_api_tokens_notSame_annotation() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.FUNCTION,
+      'void foo() {}',
+      '@deprecated void foo() {}',
+    );
+  }
+
+  test_unit_function_api_tokens_notSame_parameter_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.FUNCTION,
+      'void foo() {}',
+      'void foo(int a) {}',
+    );
+  }
+
+  test_unit_function_api_tokens_notSame_parameter_name_edit_named() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.FUNCTION,
+      'void foo({int a}) {}',
+      'void foo({int b}) {}',
+    );
+  }
+
+  test_unit_function_api_tokens_notSame_parameter_type() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.FUNCTION,
+      'void foo(int a) {}',
+      'void foo(double a) {}',
+    );
+  }
+
+  test_unit_function_api_tokens_notSame_returnType() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.FUNCTION,
+      'int foo() {}',
+      'num foo() {}',
+    );
+  }
+
+  test_unit_function_api_tokens_notSame_typeParameter_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.FUNCTION,
+      'void foo() {}',
+      'void foo<T>() {}',
+    );
+  }
+
+  test_unit_function_api_tokens_notSame_typeParameter_bound_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.FUNCTION,
+      'void foo<T>() {}',
+      'void foo<T extends num>() {}',
+    );
+  }
+
+  test_unit_function_api_tokens_same_async_add() async {
+    await _assertApiTokenSignatureSame(
+      'foo',
+      DependencyNodeKind.FUNCTION,
+      'foo() {}',
+      'foo() async {}',
+    );
+  }
+
+  test_unit_function_api_tokens_same_body() async {
+    await _assertApiTokenSignatureSame(
+      'foo',
+      DependencyNodeKind.FUNCTION,
+      'void foo() { print(1); }',
+      'void foo() { print(2); }',
+    );
+  }
+
+  test_unit_function_api_tokens_same_parameter_name_edit_required() async {
+    await _assertApiTokenSignatureSame(
+      'foo',
+      DependencyNodeKind.FUNCTION,
+      'void foo(int a) {}',
+      'void foo(int b) {}',
+    );
+  }
+
+  test_unit_function_api_tokens_same_syncStar_add() async {
+    await _assertApiTokenSignatureSame(
+      'foo',
+      DependencyNodeKind.FUNCTION,
+      'foo() {}',
+      'foo() sync* {}',
+    );
+  }
+
+  test_unit_functionTypeAlias() async {
+    var library = await buildTestLibrary(a, r'''
+typedef void Foo();
+typedef void Bar();
+''');
+    assertNodes(library.declaredNodes, [
+      ExpectedNode(aUri, 'Foo', DependencyNodeKind.FUNCTION_TYPE_ALIAS),
+      ExpectedNode(aUri, 'Bar', DependencyNodeKind.FUNCTION_TYPE_ALIAS),
+    ]);
+  }
+
+  test_unit_functionTypeAlias_api_tokens_notSame_annotation() async {
+    await _assertApiTokenSignatureNotSame(
+      'Foo',
+      DependencyNodeKind.FUNCTION_TYPE_ALIAS,
+      'typedef void Foo();',
+      '@deprecated typedef void Foo();',
+    );
+  }
+
+  test_unit_functionTypeAlias_api_tokens_notSame_parameter_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'Foo',
+      DependencyNodeKind.FUNCTION_TYPE_ALIAS,
+      'typedef void Foo();',
+      'typedef void Foo(int a);',
+    );
+  }
+
+  test_unit_functionTypeAlias_api_tokens_notSame_parameter_name_edit_named() async {
+    await _assertApiTokenSignatureNotSame(
+      'Foo',
+      DependencyNodeKind.FUNCTION_TYPE_ALIAS,
+      'typedef void Foo({int a});',
+      'typedef void Foo({int b});',
+    );
+  }
+
+  test_unit_functionTypeAlias_api_tokens_notSame_parameter_type() async {
+    await _assertApiTokenSignatureNotSame(
+      'Foo',
+      DependencyNodeKind.FUNCTION_TYPE_ALIAS,
+      'typedef void Foo(int a);',
+      'typedef void Foo(double a);',
+    );
+  }
+
+  test_unit_functionTypeAlias_api_tokens_notSame_returnType() async {
+    await _assertApiTokenSignatureNotSame(
+      'Foo',
+      DependencyNodeKind.FUNCTION_TYPE_ALIAS,
+      'typedef int Foo();',
+      'typedef num Foo();',
+    );
+  }
+
+  test_unit_functionTypeAlias_api_tokens_notSame_typeParameter_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'Foo',
+      DependencyNodeKind.FUNCTION_TYPE_ALIAS,
+      'typedef void Foo();',
+      'typedef void Foo<T>();',
+    );
+  }
+
+  test_unit_functionTypeAlias_api_tokens_notSame_typeParameter_bound_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'Foo',
+      DependencyNodeKind.FUNCTION_TYPE_ALIAS,
+      'typedef void Foo<T>();',
+      'typedef void Foo<T extends num>();',
+    );
+  }
+
+  test_unit_functionTypeAlias_api_tokens_same_comment() async {
+    await _assertApiTokenSignatureSame(
+      'Foo',
+      DependencyNodeKind.FUNCTION_TYPE_ALIAS,
+      'typedef int Foo();',
+      '/* text */ typedef int Foo();',
+    );
+  }
+
+  test_unit_functionTypeAlias_api_tokens_same_parameter_name_edit_required() async {
+    await _assertApiTokenSignatureSame(
+      'Foo',
+      DependencyNodeKind.FUNCTION_TYPE_ALIAS,
+      'typedef void Foo(int a);',
+      'typedef void Foo(int b);',
+    );
+  }
+
+  test_unit_genericTypeAlias() async {
+    var library = await buildTestLibrary(a, r'''
+typedef Foo = void Function();
+typedef Bar = void Function();
+''');
+    assertNodes(library.declaredNodes, [
+      ExpectedNode(aUri, 'Foo', DependencyNodeKind.GENERIC_TYPE_ALIAS),
+      ExpectedNode(aUri, 'Bar', DependencyNodeKind.GENERIC_TYPE_ALIAS),
+    ]);
+  }
+
+  test_unit_genericTypeAlias_api_tokens_notSame_annotation() async {
+    await _assertApiTokenSignatureNotSame(
+      'Foo',
+      DependencyNodeKind.GENERIC_TYPE_ALIAS,
+      'typedef Foo = void Function();',
+      '@deprecated typedef Foo = void Function();',
+    );
+  }
+
+  test_unit_genericTypeAlias_api_tokens_notSame_parameter_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'Foo',
+      DependencyNodeKind.GENERIC_TYPE_ALIAS,
+      'typedef Foo = void Function();',
+      'typedef Foo = void Function(int);',
+    );
+  }
+
+  test_unit_genericTypeAlias_api_tokens_notSame_parameter_kind() async {
+    await _assertApiTokenSignatureNotSame(
+      'Foo',
+      DependencyNodeKind.GENERIC_TYPE_ALIAS,
+      'typedef Foo = void Function(int a);',
+      'typedef Foo = void Function([int a]);',
+    );
+  }
+
+  test_unit_genericTypeAlias_api_tokens_notSame_parameter_name_add_positional() async {
+    await _assertApiTokenSignatureNotSame(
+      'Foo',
+      DependencyNodeKind.GENERIC_TYPE_ALIAS,
+      'typedef Foo = void Function([int]);',
+      'typedef Foo = void Function([int a]);',
+    );
+  }
+
+  test_unit_genericTypeAlias_api_tokens_notSame_parameter_name_edit_named() async {
+    await _assertApiTokenSignatureNotSame(
+      'Foo',
+      DependencyNodeKind.GENERIC_TYPE_ALIAS,
+      'typedef Foo = void Function({int a});',
+      'typedef Foo = void Function({int b});',
+    );
+  }
+
+  test_unit_genericTypeAlias_api_tokens_notSame_parameter_name_edit_positional() async {
+    await _assertApiTokenSignatureNotSame(
+      'Foo',
+      DependencyNodeKind.GENERIC_TYPE_ALIAS,
+      'typedef Foo = void Function([int]);',
+      'typedef Foo = void Function([int a]);',
+    );
+  }
+
+  test_unit_genericTypeAlias_api_tokens_notSame_returnType() async {
+    await _assertApiTokenSignatureNotSame(
+      'Foo',
+      DependencyNodeKind.GENERIC_TYPE_ALIAS,
+      'typedef Foo = int Function();',
+      'typedef Foo = double Function();',
+    );
+  }
+
+  test_unit_genericTypeAlias_api_tokens_notSame_typeParameter2_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'Foo',
+      DependencyNodeKind.GENERIC_TYPE_ALIAS,
+      'typedef Foo = void Function();',
+      'typedef Foo = void Function<T>();',
+    );
+  }
+
+  test_unit_genericTypeAlias_api_tokens_notSame_typeParameter2_bound_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'Foo',
+      DependencyNodeKind.GENERIC_TYPE_ALIAS,
+      'typedef Foo = void Function<T>();',
+      'typedef Foo = void Function<T extends num>();',
+    );
+  }
+
+  test_unit_genericTypeAlias_api_tokens_notSame_typeParameter_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'Foo',
+      DependencyNodeKind.GENERIC_TYPE_ALIAS,
+      'typedef Foo = void Function();',
+      'typedef Foo<T> = void Function();',
+    );
+  }
+
+  test_unit_genericTypeAlias_api_tokens_notSame_typeParameter_bound_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'Foo',
+      DependencyNodeKind.GENERIC_TYPE_ALIAS,
+      'typedef Foo<T> = void Function();',
+      'typedef Foo<T extends num> = void Function();',
+    );
+  }
+
+  test_unit_genericTypeAlias_api_tokens_same_parameter_name_add_required() async {
+    await _assertApiTokenSignatureSame(
+      'Foo',
+      DependencyNodeKind.GENERIC_TYPE_ALIAS,
+      'typedef Foo = void Function(int);',
+      'typedef Foo = void Function(int a);',
+    );
+  }
+
+  test_unit_genericTypeAlias_api_tokens_same_parameter_name_edit_required() async {
+    await _assertApiTokenSignatureSame(
+      'Foo',
+      DependencyNodeKind.GENERIC_TYPE_ALIAS,
+      'typedef Foo = void Function(int a);',
+      'typedef Foo = void Function(int b);',
+    );
+  }
+
+  test_unit_getter() async {
+    var library = await buildTestLibrary(a, r'''
+int get foo => 0;
+int get bar => 0;
+''');
+    assertNodes(library.declaredNodes, [
+      ExpectedNode(aUri, 'foo', DependencyNodeKind.GETTER),
+      ExpectedNode(aUri, 'bar', DependencyNodeKind.GETTER),
+    ]);
+  }
+
+  test_unit_getter_api_tokens_notSame_annotation() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'int get foo => 0;',
+      '@deprecated int get foo => 0;',
+    );
+  }
+
+  test_unit_getter_api_tokens_notSame_returnType() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'int get foo => 0;',
+      'num get foo => 0;',
+    );
+  }
+
+  test_unit_getter_api_tokens_same_body() async {
+    await _assertApiTokenSignatureSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'int get foo => 0;',
+      'int get foo => 1;',
+    );
+  }
+
+  test_unit_mixin() async {
+    var library = await buildTestLibrary(a, r'''
+mixin Foo {}
+mixin Bar {}
+''');
+    assertNodes(library.declaredNodes, [
+      ExpectedNode(aUri, 'Foo', DependencyNodeKind.MIXIN,
+          classMembers: const []),
+      ExpectedNode(aUri, 'Bar', DependencyNodeKind.MIXIN,
+          classMembers: const []),
+    ]);
+  }
+
+  test_unit_mixin_api_tokens_notSame_annotation() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.MIXIN,
+      'mixin X {}',
+      '@deprecated mixin X {}',
+    );
+  }
+
+  test_unit_mixin_api_tokens_notSame_implements_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.MIXIN,
+      'mixin X {}',
+      'mixin X implements A {}',
+    );
+  }
+
+  test_unit_mixin_api_tokens_notSame_implements_edit() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.MIXIN,
+      'mixin X implements A {}',
+      'mixin X implements B {}',
+    );
+  }
+
+  test_unit_mixin_api_tokens_notSame_implements_remove() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.MIXIN,
+      'mixin X implements A {}',
+      'mixin X {}',
+    );
+  }
+
+  test_unit_mixin_api_tokens_notSame_implements_remove2() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.MIXIN,
+      'mixin X implements A, B {}',
+      'mixin X implements B {}',
+    );
+  }
+
+  test_unit_mixin_api_tokens_notSame_implements_typeArgument() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.MIXIN,
+      'mixin X implements A {}',
+      'mixin X implements A<int> {}',
+    );
+  }
+
+  test_unit_mixin_api_tokens_notSame_on_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.MIXIN,
+      'mixin X {}',
+      'mixin X on A {}',
+    );
+  }
+
+  test_unit_mixin_api_tokens_notSame_on_add2() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.MIXIN,
+      'mixin X on A {}',
+      'mixin X on A, B {}',
+    );
+  }
+
+  test_unit_mixin_api_tokens_notSame_on_edit() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.MIXIN,
+      'mixin X on A {}',
+      'mixin X on B {}',
+    );
+  }
+
+  test_unit_mixin_api_tokens_notSame_on_replace() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.MIXIN,
+      'mixin X on A {}',
+      'mixin X implements A {}',
+    );
+  }
+
+  test_unit_mixin_api_tokens_notSame_on_typeArgument() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.MIXIN,
+      'mixin X on A {}',
+      'mixin X on A<int> {}',
+    );
+  }
+
+  test_unit_mixin_api_tokens_notSame_typeParameter_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.MIXIN,
+      'mixin X {}',
+      'mixin X<T> {}',
+    );
+  }
+
+  test_unit_mixin_api_tokens_notSame_typeParameter_add2() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.MIXIN,
+      'mixin X<T> {}',
+      'mixin X<T, U> {}',
+    );
+  }
+
+  test_unit_mixin_api_tokens_notSame_typeParameter_bound_add() async {
+    await _assertApiTokenSignatureNotSame(
+      'X',
+      DependencyNodeKind.MIXIN,
+      'mixin X<T> {}',
+      'mixin X<T extends num> {}',
+    );
+  }
+
+  test_unit_mixin_api_tokens_same_body() async {
+    await _assertApiTokenSignatureSame(
+      'X',
+      DependencyNodeKind.MIXIN,
+      'mixin X {  }',
+      'mixin X { void foo() {} }',
+    );
+  }
+
+  test_unit_setter() async {
+    var library = await buildTestLibrary(a, r'''
+void set foo(_) {}
+void set bar(_) {}
+''');
+    assertNodes(library.declaredNodes, [
+      ExpectedNode(aUri, 'foo=', DependencyNodeKind.SETTER),
+      ExpectedNode(aUri, 'bar=', DependencyNodeKind.SETTER),
+    ]);
+  }
+
+  test_unit_setter_api_tokens_notSame_annotation() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo=',
+      DependencyNodeKind.SETTER,
+      'set foo(int a) {}',
+      '@deprecated set foo(int a) {}',
+    );
+  }
+
+  test_unit_setter_api_tokens_notSame_parameter_type() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo=',
+      DependencyNodeKind.SETTER,
+      'set foo(int a) {}',
+      'set foo(num a) {}',
+    );
+  }
+
+  test_unit_setter_api_tokens_same_body() async {
+    await _assertApiTokenSignatureSame(
+      'foo=',
+      DependencyNodeKind.SETTER,
+      'set foo(int a) { print(0); }',
+      'set foo(int a) { print(1); }',
+    );
+  }
+
+  test_unit_variable() async {
+    var library = await buildTestLibrary(a, r'''
+int a = 1;
+int b = 2, c = 3;
+''');
+    assertNodes(library.declaredNodes, [
+      ExpectedNode(aUri, 'a', DependencyNodeKind.GETTER),
+      ExpectedNode(aUri, 'b', DependencyNodeKind.GETTER),
+      ExpectedNode(aUri, 'c', DependencyNodeKind.GETTER),
+      ExpectedNode(aUri, 'a=', DependencyNodeKind.SETTER),
+      ExpectedNode(aUri, 'b=', DependencyNodeKind.SETTER),
+      ExpectedNode(aUri, 'c=', DependencyNodeKind.SETTER),
+    ]);
+  }
+
+  test_unit_variable_api_tokens_notSame_annotation() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'int foo = 0;',
+      '@deprecated int foo = 0;',
+    );
+  }
+
+  test_unit_variable_api_tokens_notSame_const() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'int foo = 0;',
+      'const int foo = 0;',
+    );
+  }
+
+  test_unit_variable_api_tokens_same_final() async {
+    await _assertApiTokenSignatureSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'int foo = 0;',
+      'final int foo = 0;',
+    );
+  }
+
+  test_unit_variable_api_tokens_typed_notSame_type() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'int foo = 0;',
+      'num foo = 1;',
+    );
+  }
+
+  test_unit_variable_api_tokens_typed_same_initializer() async {
+    await _assertApiTokenSignatureSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'int foo = 0;',
+      'int foo = 1;',
+    );
+  }
+
+  test_unit_variable_api_tokens_untyped_notSame_initializer() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'var foo = 0;',
+      'var foo = 1.0;',
+    );
+  }
+
+  test_unit_variable_const() async {
+    var library = await buildTestLibrary(a, r'''
+const foo = 1;
+const bar = 2;
+''');
+    assertNodes(library.declaredNodes, [
+      ExpectedNode(aUri, 'foo', DependencyNodeKind.GETTER),
+      ExpectedNode(aUri, 'bar', DependencyNodeKind.GETTER),
+    ]);
+  }
+
+  test_unit_variable_const_api_tokens_typed_notSame_initializer() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'const int foo = 0;',
+      'const int foo = 1;',
+    );
+  }
+
+  test_unit_variable_final() async {
+    var library = await buildTestLibrary(a, r'''
+final foo = 1;
+final bar = 2;
+''');
+    assertNodes(library.declaredNodes, [
+      ExpectedNode(aUri, 'foo', DependencyNodeKind.GETTER),
+      ExpectedNode(aUri, 'bar', DependencyNodeKind.GETTER),
+    ]);
+  }
+
+  test_unit_variable_final_api_tokens_typed_same_initializer() async {
+    await _assertApiTokenSignatureSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'final int foo = 0;',
+      'final int foo = 1;',
+    );
+  }
+
+  test_unit_variable_final_api_tokens_untyped_notSame_initializer() async {
+    await _assertApiTokenSignatureNotSame(
+      'foo',
+      DependencyNodeKind.GETTER,
+      'final foo = 0;',
+      'final foo = 1.0;',
+    );
+  }
+
+  test_unit_variable_final_withoutValue() async {
+    var library = await buildTestLibrary(a, r'''
+final foo;
+final bar;
+''');
+    assertNodes(library.declaredNodes, [
+      ExpectedNode(aUri, 'foo', DependencyNodeKind.GETTER),
+      ExpectedNode(aUri, 'bar', DependencyNodeKind.GETTER),
+    ]);
+  }
+
+  Future<void> _assertApiTokenSignatureNotSame(
+      String name, DependencyNodeKind kind, String codeBefore, String codeAfter,
+      {String memberOf, String typeParameterOf}) async {
+    DependencyNode getNodeA(Library library) {
+      return getNode(
+        library,
+        uri: aUri,
+        name: name,
+        kind: kind,
+        memberOf: memberOf,
+        typeParameterOf: typeParameterOf,
+      );
+    }
+
+    var libraryBefore = await buildTestLibrary(a, codeBefore);
+    var nodeBefore = getNodeA(libraryBefore);
+
+    var libraryAfter = await buildTestLibrary(a, codeAfter);
+    var nodeAfter = getNodeA(libraryAfter);
+
+    expect(
+      nodeAfter.api.tokenSignatureHex,
+      isNot(nodeBefore.api.tokenSignatureHex),
+    );
+  }
+
+  Future<void> _assertApiTokenSignatureSame(
+      String name, DependencyNodeKind kind, String codeBefore, String codeAfter,
+      {String memberOf, String typeParameterOf}) async {
+    DependencyNode getNodeA(Library library) {
+      return getNode(
+        library,
+        uri: aUri,
+        name: name,
+        kind: kind,
+        memberOf: memberOf,
+        typeParameterOf: typeParameterOf,
+      );
+    }
+
+    var libraryBefore = await buildTestLibrary(a, codeBefore);
+    var nodeBefore = getNodeA(libraryBefore);
+
+    var libraryAfter = await buildTestLibrary(a, codeAfter);
+    var nodeAfter = getNodeA(libraryAfter);
+
+    expect(
+      nodeAfter.api.tokenSignatureHex,
+      nodeBefore.api.tokenSignatureHex,
+    );
+  }
+
+  static void _assertExports(Library library, List<Export> expectedExports) {
+    var actualExports = library.exports;
+    expect(actualExports, hasLength(expectedExports.length));
+    for (var i = 0; i < actualExports.length; ++i) {
+      var actual = actualExports[i];
+      var expected = expectedExports[i];
+      if (actual.uri != expected.uri ||
+          !_equalCombinators(actual.combinators, expected.combinators)) {
+        fail('Expected: $expected\nActual: $actual');
+      }
+    }
+  }
+
+  static void _assertImports(Library library, List<Import> expectedImports) {
+    var actualImports = library.imports;
+    expect(actualImports, hasLength(expectedImports.length));
+    for (var i = 0; i < actualImports.length; ++i) {
+      var actual = actualImports[i];
+      var expected = expectedImports[i];
+      if (actual.uri != expected.uri ||
+          actual.prefix != expected.prefix ||
+          !_equalCombinators(actual.combinators, expected.combinators)) {
+        fail('Expected: $expected\nActual: $actual');
+      }
+    }
+  }
+
+  static bool _equalCombinators(List<Combinator> actualCombinators,
+      List<Combinator> expectedCombinators) {
+    if (actualCombinators.length != expectedCombinators.length) {
+      return false;
+    }
+
+    for (var i = 0; i < actualCombinators.length; i++) {
+      var actualCombinator = actualCombinators[i];
+      var expectedCombinator = expectedCombinators[i];
+      if (actualCombinator.isShow != expectedCombinator.isShow) {
+        return false;
+      }
+
+      var actualNames = actualCombinator.names;
+      var expectedNames = expectedCombinator.names;
+      if (actualNames.length != expectedNames.length) {
+        return false;
+      }
+      for (var j = 0; j < actualNames.length; j++) {
+        if (actualNames[j] != expectedNames[j]) {
+          return false;
+        }
+      }
+    }
+
+    return true;
+  }
+}