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