Compare types between analyzer and front_end.
Change-Id: I8d0c0eba60a2c2a1fe5a3e4e0c72249941d252d6
Reviewed-on: https://dart-review.googlesource.com/72880
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
diff --git a/pkg/analyzer_fe_comparison/lib/src/analyzer.dart b/pkg/analyzer_fe_comparison/lib/src/analyzer.dart
index b0aff01..207ede3 100644
--- a/pkg/analyzer_fe_comparison/lib/src/analyzer.dart
+++ b/pkg/analyzer_fe_comparison/lib/src/analyzer.dart
@@ -7,6 +7,9 @@
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/src/generated/resolver.dart';
import 'package:analyzer/src/generated/source.dart' show SourceKind;
import 'package:analyzer_fe_comparison/src/comparison_node.dart';
@@ -20,6 +23,7 @@
}
var context = contexts[0];
var session = context.currentSession;
+ var typeProvider = await session.typeProvider;
var uriConverter = session.uriConverter;
var contextRoot = context.contextRoot;
var libraryNodes = <ComparisonNode>[];
@@ -35,7 +39,8 @@
for (var compilationUnit in libraryElement.units) {
var unitResult =
await session.getResolvedAst(compilationUnit.source.fullName);
- _AnalyzerVisitor(childNodes)._visitList(unitResult.unit.declarations);
+ _AnalyzerVisitor(typeProvider, childNodes)
+ ._visitList(unitResult.unit.declarations);
}
libraryNodes.add(ComparisonNode.sorted(importUri.toString(), childNodes));
}
@@ -48,22 +53,40 @@
///
/// Results are accumulated into [_resultNodes].
class _AnalyzerVisitor extends UnifyingAstVisitor<void> {
+ final TypeProvider _typeProvider;
+
final List<ComparisonNode> _resultNodes;
- _AnalyzerVisitor(this._resultNodes);
+ _AnalyzerVisitor(this._typeProvider, this._resultNodes);
@override
void visitClassDeclaration(ClassDeclaration node) {
var children = <ComparisonNode>[];
- _AnalyzerVisitor(children)._visitList(node.members);
+ var visitor = _AnalyzerVisitor(_typeProvider, children);
+ visitor._visitTypeParameters(node.declaredElement.typeParameters);
+ if (node.declaredElement.supertype != null) {
+ children.add(_translateType('Extends: ', node.declaredElement.supertype));
+ }
+ for (int i = 0; i < node.declaredElement.mixins.length; i++) {
+ children
+ .add(_translateType('Mixin $i: ', node.declaredElement.mixins[i]));
+ }
+ for (int i = 0; i < node.declaredElement.interfaces.length; i++) {
+ children.add(_translateType(
+ 'Implements $i: ', node.declaredElement.interfaces[i]));
+ }
+ visitor._visitList(node.members);
_resultNodes
.add(ComparisonNode.sorted('Class ${node.name.name}', children));
}
@override
void visitConstructorDeclaration(ConstructorDeclaration node) {
- _resultNodes
- .add(ComparisonNode('Constructor ${node.name?.name ?? '(unnamed)'}'));
+ var children = <ComparisonNode>[];
+ var visitor = _AnalyzerVisitor(_typeProvider, children);
+ visitor._visitParameters(node.parameters);
+ _resultNodes.add(ComparisonNode.sorted(
+ 'Constructor ${node.name?.name ?? '(unnamed)'}', children));
}
@override
@@ -91,12 +114,24 @@
// Kernel calls top level functions "methods".
kind = 'Method';
}
- _resultNodes.add(ComparisonNode('$kind ${node.name.name}'));
+ var children = <ComparisonNode>[];
+ var visitor = _AnalyzerVisitor(_typeProvider, children);
+ visitor._visitTypeParameters(node.declaredElement.typeParameters);
+ visitor._visitParameters(node.functionExpression.parameters);
+ children
+ .add(_translateType('Return type: ', node.declaredElement.returnType));
+ _resultNodes
+ .add(ComparisonNode.sorted('$kind ${node.name.name}', children));
}
@override
void visitFunctionTypeAlias(FunctionTypeAlias node) {
- _resultNodes.add(ComparisonNode('Typedef ${node.name.name}'));
+ _visitTypedef(node);
+ }
+
+ @override
+ void visitGenericTypeAlias(GenericTypeAlias node) {
+ _visitTypedef(node);
}
@override
@@ -111,7 +146,14 @@
} else {
kind = 'Method';
}
- _resultNodes.add(ComparisonNode('$kind ${node.name.name}'));
+ var children = <ComparisonNode>[];
+ var visitor = _AnalyzerVisitor(_typeProvider, children);
+ visitor._visitTypeParameters(node.declaredElement.typeParameters);
+ visitor._visitParameters(node.parameters);
+ children
+ .add(_translateType('Return type: ', node.declaredElement.returnType));
+ _resultNodes
+ .add(ComparisonNode.sorted('$kind ${node.name.name}', children));
}
@override
@@ -127,16 +169,98 @@
@override
void visitVariableDeclarationList(VariableDeclarationList node) {
for (var variableDeclaration in node.variables) {
+ var children = <ComparisonNode>[];
+ children.add(
+ _translateType('Type: ', variableDeclaration.declaredElement.type));
// Kernel calls both fields and top level variable declarations "fields".
- _resultNodes
- .add(ComparisonNode('Field ${variableDeclaration.name.name}'));
+ _resultNodes.add(ComparisonNode.sorted(
+ 'Field ${variableDeclaration.name.name}', children));
}
}
+ /// Converts the analyzer representation of a type into a ComparisonNode.
+ ComparisonNode _translateType(String prefix, DartType type) {
+ if (type is InterfaceType) {
+ var children = <ComparisonNode>[];
+ children
+ .add(ComparisonNode('Library: ${type.element.librarySource.uri}'));
+ for (int i = 0; i < type.typeArguments.length; i++) {
+ children.add(_translateType('Type arg $i: ', type.typeArguments[i]));
+ }
+ return ComparisonNode('${prefix}InterfaceType ${type.name}', children);
+ }
+ if (type is TypeParameterType) {
+ // TODO(paulberry): disambiguate if needed.
+ return ComparisonNode('${prefix}TypeParameterType: ${type.name}');
+ }
+ if (type.isDynamic) {
+ return ComparisonNode('${prefix}Dynamic');
+ }
+ if (type.isVoid) {
+ return ComparisonNode('${prefix}Void');
+ }
+ if (type is FunctionType) {
+ var children = <ComparisonNode>[];
+ var visitor = _AnalyzerVisitor(_typeProvider, children);
+ visitor._visitTypeParameters(type.typeFormals);
+ int positionalParameterIndex = 0;
+ for (var parameterElement in type.parameters) {
+ var kind = parameterElement.isNotOptional
+ ? 'Required'
+ : parameterElement.isOptionalPositional ? 'Optional' : 'Named';
+ var name = parameterElement.isNamed
+ ? parameterElement.name
+ : '${positionalParameterIndex++}';
+ children.add(
+ _translateType('$kind parameter $name: ', parameterElement.type));
+ }
+ return ComparisonNode.sorted('${prefix}FunctionType', children);
+ }
+ throw new UnimplementedError('_translateType: ${type.runtimeType}');
+ }
+
/// Visits all the nodes in [nodes].
void _visitList(List<AstNode> nodes) {
for (var astNode in nodes) {
astNode.accept(this);
}
}
+
+ void _visitParameters(FormalParameterList parameters) {
+ var children = <ComparisonNode>[];
+ // Note: parameters == null for getters
+ if (parameters != null) {
+ for (var parameter in parameters.parameters) {
+ var element = parameter.declaredElement;
+ var kind = element.isNotOptional
+ ? 'Required'
+ : element.isOptionalPositional ? 'Optional' : 'Named';
+ var parameterChildren = <ComparisonNode>[];
+ parameterChildren.add(_translateType('Type: ', element.type));
+ children.add(
+ ComparisonNode.sorted('$kind: ${element.name}', parameterChildren));
+ }
+ }
+ _resultNodes.add(ComparisonNode('Parameters', children));
+ }
+
+ void _visitTypedef(TypeAlias node) {
+ var children = <ComparisonNode>[];
+ var visitor = _AnalyzerVisitor(_typeProvider, children);
+ GenericTypeAliasElement element = node.declaredElement;
+ visitor._visitTypeParameters(element.typeParameters);
+ children.add(_translateType('Type: ', element.function.type));
+ _resultNodes
+ .add(ComparisonNode.sorted('Typedef ${node.name.name}', children));
+ }
+
+ void _visitTypeParameters(List<TypeParameterElement> typeParameters) {
+ for (int i = 0; i < typeParameters.length; i++) {
+ _resultNodes.add(ComparisonNode(
+ 'Type parameter $i: ${typeParameters[i].name}', [
+ _translateType(
+ 'Bound: ', typeParameters[i].bound ?? _typeProvider.objectType)
+ ]));
+ }
+ }
}
diff --git a/pkg/analyzer_fe_comparison/lib/src/kernel.dart b/pkg/analyzer_fe_comparison/lib/src/kernel.dart
index 638447c..935dd2c 100644
--- a/pkg/analyzer_fe_comparison/lib/src/kernel.dart
+++ b/pkg/analyzer_fe_comparison/lib/src/kernel.dart
@@ -29,26 +29,36 @@
..embedSourceText = false;
var component = await kernelForComponent(inputs, compilerOptions);
- return component.accept(new _KernelVisitor(inputs.toSet()));
+ var libraryNodes = <ComparisonNode>[];
+ var visitor = _KernelVisitor(libraryNodes);
+ for (var library in component.libraries) {
+ if (inputs.contains(library.importUri)) {
+ library.accept(visitor);
+ }
+ }
+ return ComparisonNode.sorted('Component', libraryNodes);
}
/// Visitor for serializing a kernel representation of a program into
/// ComparisonNodes.
-class _KernelVisitor extends TreeVisitor<ComparisonNode> {
- final Set<Uri> _inputs;
+///
+/// Results are accumulated into [_resultNodes].
+class _KernelVisitor extends TreeVisitor<void> {
+ final List<ComparisonNode> _resultNodes;
- _KernelVisitor(this._inputs);
+ _KernelVisitor(this._resultNodes);
@override
- ComparisonNode defaultTreeNode(TreeNode node) {
+ void defaultTreeNode(TreeNode node) {
throw new UnimplementedError('KernelVisitor: ${node.runtimeType}');
}
@override
- ComparisonNode visitClass(Class class_) {
+ void visitClass(Class class_) {
if (class_.isAnonymousMixin) return null;
var kind = class_.isEnum ? 'Enum' : 'Class';
var children = <ComparisonNode>[];
+ var visitor = _KernelVisitor(children);
if (class_.isEnum) {
for (var field in class_.fields) {
if (!field.isStatic) continue;
@@ -57,56 +67,99 @@
children.add(ComparisonNode('EnumValue ${field.name.name}'));
}
} else {
- _visitList(class_.fields, children);
- _visitList(class_.constructors, children);
- _visitList(class_.procedures, children);
+ visitor._visitTypeParameters(class_.typeParameters);
+ if (class_.supertype != null) {
+ var declaredSupertype = class_.supertype.asInterfaceType;
+ var mixedInTypes = <DartType>[];
+ while (declaredSupertype.classNode.isAnonymousMixin) {
+ // Since we're walking from the class to its declared supertype, we
+ // encounter the mixins in the reverse order that they were declared,
+ // so we have to use [List.insert] to add them to [mixedInTypes].
+ mixedInTypes.insert(
+ 0, declaredSupertype.classNode.mixedInType.asInterfaceType);
+ declaredSupertype =
+ declaredSupertype.classNode.supertype.asInterfaceType;
+ }
+ children.add(_TypeVisitor.translate('Extends: ', declaredSupertype));
+ for (int i = 0; i < mixedInTypes.length; i++) {
+ children.add(_TypeVisitor.translate('Mixin $i: ', mixedInTypes[i]));
+ }
+ }
+ for (int i = 0; i < class_.implementedTypes.length; i++) {
+ children.add(_TypeVisitor.translate(
+ 'Implements $i: ', class_.implementedTypes[i].asInterfaceType));
+ }
+ visitor._visitList(class_.fields);
+ visitor._visitList(class_.constructors);
+ visitor._visitList(class_.procedures);
}
// TODO(paulberry): handle more fields from Class
- return ComparisonNode.sorted('$kind ${class_.name}', children);
+ _resultNodes.add(ComparisonNode.sorted('$kind ${class_.name}', children));
}
@override
- ComparisonNode visitComponent(Component component) {
- var children = <ComparisonNode>[];
- _visitList(component.libraries, children);
- return ComparisonNode.sorted('Component', children);
- }
-
- @override
- ComparisonNode visitConstructor(Constructor constructor) {
+ void visitConstructor(Constructor constructor) {
if (constructor.isSynthetic) return null;
var name = constructor.name.name;
if (name.isEmpty) {
name = '(unnamed)';
}
- // TODO(paulberry): handle fields from Constructor
- return ComparisonNode('Constructor $name');
+ var children = <ComparisonNode>[];
+ var visitor = _KernelVisitor(children);
+ constructor.function.accept(visitor);
+ // TODO(paulberry): handle more fields from Constructor
+ _resultNodes.add(ComparisonNode.sorted('Constructor $name', children));
}
@override
- ComparisonNode visitField(Field field) {
+ void visitField(Field field) {
if (field.name.name == '_redirecting#') return null;
- // TODO(paulberry): handle fields from Field
- return ComparisonNode('Field ${field.name.name}');
+ var children = <ComparisonNode>[];
+ children.add(_TypeVisitor.translate('Type: ', field.type));
+ // TODO(paulberry): handle more fields from Field
+ _resultNodes
+ .add(ComparisonNode.sorted('Field ${field.name.name}', children));
}
@override
- ComparisonNode visitLibrary(Library library) {
- if (!_inputs.contains(library.importUri)) return null;
+ void visitFunctionNode(FunctionNode node) {
+ var parent = node.parent;
+ if (!(parent is Constructor || parent is Procedure && parent.isFactory)) {
+ _visitTypeParameters(node.typeParameters);
+ _resultNodes
+ .add(_TypeVisitor.translate('Return type: ', node.returnType));
+ }
+ var parameterChildren = <ComparisonNode>[];
+ var parameterVisitor = _KernelVisitor(parameterChildren);
+ for (int i = 0; i < node.positionalParameters.length; i++) {
+ parameterVisitor._visitParameter(node.positionalParameters[i],
+ i < node.requiredParameterCount ? 'Required' : 'Optional');
+ }
+ for (int i = 0; i < node.namedParameters.length; i++) {
+ parameterVisitor._visitParameter(node.namedParameters[i], 'Named');
+ }
+ _resultNodes.add(ComparisonNode('Parameters', parameterChildren));
+ // TODO(paulberry): handle more fields from FunctionNode
+ }
+
+ @override
+ void visitLibrary(Library library) {
var children = <ComparisonNode>[];
if (library.name != null) {
children.add(ComparisonNode('name=${library.name}'));
}
- _visitList(library.typedefs, children);
- _visitList(library.classes, children);
- _visitList(library.procedures, children);
- _visitList(library.fields, children);
+ var visitor = _KernelVisitor(children);
+ visitor._visitList(library.typedefs);
+ visitor._visitList(library.classes);
+ visitor._visitList(library.procedures);
+ visitor._visitList(library.fields);
// TODO(paulberry): handle more fields from Library
- return ComparisonNode.sorted(library.importUri.toString(), children);
+ _resultNodes
+ .add(ComparisonNode.sorted(library.importUri.toString(), children));
}
@override
- ComparisonNode visitProcedure(Procedure procedure) {
+ void visitProcedure(Procedure procedure) {
if (procedure.isForwardingStub) return null;
// TODO(paulberry): add an annotation to the ComparisonNode when the
// procedure is a factory.
@@ -117,24 +170,108 @@
if (name.isEmpty) {
name = '(unnamed)';
}
- // TODO(paulberry): handle fields from Procedure
- return ComparisonNode('$kind $name');
+ var children = <ComparisonNode>[];
+ var visitor = _KernelVisitor(children);
+ procedure.function.accept(visitor);
+ // TODO(paulberry): handle more fields from Procedure
+ _resultNodes.add(ComparisonNode.sorted('$kind $name', children));
}
@override
- ComparisonNode visitTypedef(Typedef typedef) {
- // TODO(paulberry): handle fields from Typedef
- return ComparisonNode('Typedef ${typedef.name}');
+ void visitTypedef(Typedef typedef) {
+ var children = <ComparisonNode>[];
+ var visitor = _KernelVisitor(children);
+ visitor._visitTypeParameters(typedef.typeParameters);
+ children.add(_TypeVisitor.translate('Type: ', typedef.type));
+ // TODO(paulberry): handle more fields from Typedef
+ _resultNodes
+ .add(ComparisonNode.sorted('Typedef ${typedef.name}', children));
}
- /// Transforms all the nodes in [src] to [ComparisonNode]s, and adds those
- /// with non-null results to [dst].
- void _visitList(List<TreeNode> src, List<ComparisonNode> dst) {
- for (var item in src) {
- ComparisonNode result = item.accept(this);
- if (result != null) {
- dst.add(result);
- }
+ /// Visits all the nodes in [nodes].
+ void _visitList(List<TreeNode> nodes) {
+ for (var node in nodes) {
+ node.accept(this);
}
}
+
+ void _visitParameter(VariableDeclaration parameter, String kind) {
+ var children = <ComparisonNode>[];
+ children.add(_TypeVisitor.translate('Type: ', parameter.type));
+ // TODO(paulberry): handle more fields from VariableDeclaration
+ _resultNodes
+ .add(ComparisonNode.sorted('$kind: ${parameter.name}', children));
+ }
+
+ void _visitTypeParameters(List<TypeParameter> typeParameters) {
+ for (int i = 0; i < typeParameters.length; i++) {
+ _resultNodes.add(ComparisonNode(
+ 'Type parameter $i: ${typeParameters[i].name}',
+ [_TypeVisitor.translate('Bound: ', typeParameters[i].bound)]));
+ }
+ }
+}
+
+/// Visitor for serializing a kernel representation of a type into
+/// ComparisonNodes.
+class _TypeVisitor extends DartTypeVisitor<ComparisonNode> {
+ /// Text to prepend to the node text.
+ String _prefix;
+
+ _TypeVisitor(this._prefix);
+
+ @override
+ ComparisonNode defaultDartType(DartType node) {
+ throw new UnimplementedError('_TypeVisitor: ${node.runtimeType}');
+ }
+
+ @override
+ ComparisonNode visitDynamicType(DynamicType node) {
+ return ComparisonNode('${_prefix}Dynamic');
+ }
+
+ @override
+ ComparisonNode visitFunctionType(FunctionType node) {
+ var children = <ComparisonNode>[];
+ var visitor = _KernelVisitor(children);
+ visitor._visitTypeParameters(node.typeParameters);
+ for (int i = 0; i < node.positionalParameters.length; i++) {
+ var kind = i < node.requiredParameterCount ? 'Required' : 'Optional';
+ children
+ .add(translate('$kind parameter $i: ', node.positionalParameters[i]));
+ }
+ for (var namedType in node.namedParameters) {
+ children
+ .add(translate('Named parameter ${namedType.name}', namedType.type));
+ }
+ return ComparisonNode.sorted('${_prefix}FunctionType', children);
+ }
+
+ @override
+ ComparisonNode visitInterfaceType(InterfaceType node) {
+ var children = <ComparisonNode>[];
+ children.add(ComparisonNode(
+ 'Library: ${node.classNode.enclosingLibrary.importUri}'));
+ for (int i = 0; i < node.typeArguments.length; i++) {
+ children.add(translate('Type arg $i: ', node.typeArguments[i]));
+ }
+ return ComparisonNode(
+ '${_prefix}InterfaceType ${node.classNode.name}', children);
+ }
+
+ @override
+ ComparisonNode visitTypeParameterType(TypeParameterType node) {
+ // TODO(paulberry): disambiguate if needed.
+ return ComparisonNode(
+ '${_prefix}TypeParameterType: ${node.parameter.name}');
+ }
+
+ @override
+ ComparisonNode visitVoidType(VoidType node) {
+ return ComparisonNode('${_prefix}Void');
+ }
+
+ static ComparisonNode translate(String prefix, DartType type) {
+ return type.accept(new _TypeVisitor(prefix));
+ }
}