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;
+ }
+}