Improve the feedback for unimplemented portions of the migration tooling
Change-Id: I1d4fd85f15c16e74e4dfdf5a618de2a7f4f3ec9a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106680
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
Reviewed-by: Dan Rubel <danrubel@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/nnbd_migration/lib/src/graph_builder.dart b/pkg/nnbd_migration/lib/src/graph_builder.dart
index 4ced40b..0bd79a9 100644
--- a/pkg/nnbd_migration/lib/src/graph_builder.dart
+++ b/pkg/nnbd_migration/lib/src/graph_builder.dart
@@ -145,7 +145,8 @@
@override
DecoratedType visitAsExpression(AsExpression node) {
- throw new UnimplementedError('TODO(brianwilkerson)');
+ // TODO(brianwilkerson)
+ _unimplemented(node, 'AsExpression');
}
@override
@@ -166,7 +167,8 @@
@override
DecoratedType visitAssignmentExpression(AssignmentExpression node) {
if (node.operator.type != TokenType.EQ) {
- throw UnimplementedError('TODO(paulberry)');
+ // TODO(paulberry)
+ _unimplemented(node, 'Assignment with operator ${node.operator.lexeme}');
}
var leftType = node.leftHandSide.accept(this);
var conditionalNode = _lastConditionalNode;
@@ -227,8 +229,9 @@
calleeType.positionalParameters[0], node.rightOperand);
return calleeType.returnType;
default:
- assert(false); // TODO(paulberry)
- return null;
+ // TODO(paulberry)
+ _unimplemented(
+ node, 'Binary expression with operator ${node.operator.lexeme}');
}
}
@@ -314,13 +317,15 @@
@override
DecoratedType visitFunctionExpression(FunctionExpression node) {
- throw new UnimplementedError('TODO(brianwilkerson)');
+ // TODO(brianwilkerson)
+ _unimplemented(node, 'FunctionExpression');
}
@override
DecoratedType visitFunctionExpressionInvocation(
FunctionExpressionInvocation node) {
- throw UnimplementedError('TODO(brianwilkerson)');
+ // TODO(brianwilkerson)
+ _unimplemented(node, 'FunctionExpressionInvocation');
}
@override
@@ -369,7 +374,8 @@
}
var callee = node.staticElement;
if (callee == null) {
- throw new UnimplementedError('TODO(paulberry)');
+ // TODO(paulberry)
+ _unimplemented(node, 'Index expression with no static type');
}
var calleeType = getOrComputeElementType(callee, targetType: targetType);
// TODO(paulberry): substitute if necessary
@@ -389,7 +395,8 @@
if (callee.enclosingElement.typeParameters.isNotEmpty) {
// If the class has type parameters then we might need to substitute the
// appropriate type arguments.
- throw UnimplementedError('TODO(brianwilkerson)');
+ // TODO(brianwilkerson)
+ _unimplemented(node, 'Instance creation expression with type arguments');
}
_handleInvocationArguments(node.argumentList, calleeType);
return calleeType.returnType;
@@ -406,9 +413,11 @@
if (type is NamedType && type.typeArguments != null) {
// TODO(brianwilkerson) Figure out what constraints we need to add to
// allow the tool to decide whether to make the type arguments nullable.
- throw new UnimplementedError('TODO(brianwilkerson)');
+ // TODO(brianwilkerson)
+ _unimplemented(node, 'Is expression with type arguments');
} else if (type is GenericFunctionType) {
- throw new UnimplementedError('TODO(brianwilkerson)');
+ // TODO(brianwilkerson)
+ _unimplemented(node, 'Is expression with GenericFunctionType');
}
node.visitChildren(this);
return DecoratedType(node.staticType, _graph.never);
@@ -421,7 +430,8 @@
// TODO(brianwilkerson) We might want to create a fake node in the graph
// to represent the type argument so that we can still create edges from
// the elements to it.
- throw new UnimplementedError('TODO(brianwilkerson)');
+ // TODO(brianwilkerson)
+ _unimplemented(node, 'List literal with no type arguments');
} else {
var typeArgumentType = _variables.decoratedTypeAnnotation(
_source, node.typeArguments.arguments[0]);
@@ -431,7 +441,8 @@
} else {
// Handle spread and control flow elements.
element.accept(this);
- throw new UnimplementedError('TODO(brianwilkerson)');
+ // TODO(brianwilkerson)
+ _unimplemented(node, 'Spread or control flow element');
}
}
return DecoratedType(listType, _graph.never,
@@ -469,7 +480,8 @@
}
var callee = node.methodName.staticElement;
if (callee == null) {
- throw UnimplementedError('TODO(paulberry)');
+ // TODO(paulberry)
+ _unimplemented(node, 'Unresolved method name');
}
var calleeType = getOrComputeElementType(callee, targetType: targetType);
// TODO(paulberry): substitute if necessary
@@ -518,13 +530,15 @@
@override
DecoratedType visitPostfixExpression(PostfixExpression node) {
- throw new UnimplementedError('TODO(brianwilkerson)');
+ // TODO(brianwilkerson)
+ _unimplemented(node, 'PostfixExpression');
}
@override
DecoratedType visitPrefixedIdentifier(PrefixedIdentifier node) {
if (node.prefix.staticElement is ImportElement) {
- throw new UnimplementedError('TODO(paulberry)');
+ // TODO(paulberry)
+ _unimplemented(node, 'PrefixedIdentifier with a prefix');
} else {
return _handlePropertyAccess(node, node.prefix, node.identifier);
}
@@ -538,7 +552,8 @@
return _nonNullableBoolType;
}
// TODO(brianwilkerson) The remaining cases are invocations.
- throw new UnimplementedError('TODO(brianwilkerson)');
+ _unimplemented(
+ node, 'Prefix expression with operator ${node.operator.lexeme}');
}
@override
@@ -565,7 +580,8 @@
// TODO(brianwilkerson) We might want to create fake nodes in the graph to
// represent the type arguments so that we can still create edges from
// the elements to them.
- throw new UnimplementedError('TODO(brianwilkerson)');
+ // TODO(brianwilkerson)
+ _unimplemented(node, 'Set or map literal with no type arguments');
} else if (typeArguments.length == 1) {
var elementType =
_variables.decoratedTypeAnnotation(_source, typeArguments[0]);
@@ -575,7 +591,8 @@
} else {
// Handle spread and control flow elements.
element.accept(this);
- throw new UnimplementedError('TODO(brianwilkerson)');
+ // TODO(brianwilkerson)
+ _unimplemented(node, 'Spread or control flow element');
}
}
return DecoratedType(listType, _graph.never,
@@ -592,13 +609,16 @@
} else {
// Handle spread and control flow elements.
element.accept(this);
- throw new UnimplementedError('TODO(brianwilkerson)');
+ // TODO(brianwilkerson)
+ _unimplemented(node, 'Spread or control flow element');
}
}
return DecoratedType(listType, _graph.never,
typeArguments: [keyType, valueType]);
} else {
- throw new UnimplementedError('TODO(brianwilkerson)');
+ // TODO(brianwilkerson)
+ _unimplemented(
+ node, 'Set or map literal with more than two type arguments');
}
}
@@ -612,7 +632,8 @@
return _nonNullableTypeType;
} else {
// TODO(paulberry)
- throw new UnimplementedError('${staticElement.runtimeType}');
+ _unimplemented(node,
+ 'Simple identifier with a static element of type ${staticElement.runtimeType}');
}
}
@@ -685,7 +706,8 @@
var destinationType = getOrComputeElementType(node.declaredElement);
var initializer = node.initializer;
if (initializer == null) {
- throw UnimplementedError('TODO(paulberry)');
+ // TODO(paulberry)
+ _unimplemented(node, 'Variable declaration with no initializer');
} else {
_handleAssignment(destinationType, initializer);
}
@@ -769,13 +791,15 @@
var name = expression.name.label.name;
var parameterType = calleeType.namedParameters[name];
if (parameterType == null) {
- throw UnimplementedError('TODO(paulberry)');
+ // TODO(paulberry)
+ _unimplemented(expression, 'Missing type for named parameter');
}
_handleAssignment(parameterType, expression.expression);
suppliedNamedParameters.add(name);
} else {
if (calleeType.positionalParameters.length <= i) {
- throw UnimplementedError('TODO(paulberry)');
+ // TODO(paulberry)
+ _unimplemented(argumentList, 'Missing positional parameter at $i');
}
_handleAssignment(calleeType.positionalParameters[i++], expression);
}
@@ -800,7 +824,8 @@
}
var callee = propertyName.staticElement;
if (callee == null) {
- throw new UnimplementedError('TODO(paulberry)');
+ // TODO(paulberry)
+ _unimplemented(node, 'Unresolved property access');
}
var calleeType = getOrComputeElementType(callee, targetType: targetType);
// TODO(paulberry): substitute if necessary
@@ -837,7 +862,9 @@
case TokenType.QUESTION_PERIOD:
return true;
default:
- throw new UnimplementedError('TODO(paulberry)');
+ // TODO(paulberry)
+ _unimplemented(
+ expression, 'Conditional expression with operator ${token.lexeme}');
}
}
@@ -865,6 +892,21 @@
return false;
}
+ @alwaysThrows
+ void _unimplemented(AstNode node, String message) {
+ CompilationUnit unit = node.root as CompilationUnit;
+ StringBuffer buffer = StringBuffer();
+ buffer.write(message);
+ buffer.write(' in "');
+ buffer.write(node.toSource());
+ buffer.write('" on line ');
+ buffer.write(unit.lineInfo.getLocation(node.offset).lineNumber);
+ buffer.write(' of "');
+ buffer.write(unit.declaredElement.source.fullName);
+ buffer.write('"');
+ throw UnimplementedError(buffer.toString());
+ }
+
void _unionDecoratedTypes(
DecoratedType x, DecoratedType y, EdgeOrigin origin) {
_graph.union(x.node, y.node, origin);
@@ -876,7 +918,8 @@
y.positionalParameters.isNotEmpty ||
x.namedParameters.isNotEmpty ||
y.namedParameters.isNotEmpty) {
- throw UnimplementedError('TODO(paulberry): _unionDecoratedTypes($x, $y)');
+ // TODO(paulberry)
+ throw UnimplementedError('_unionDecoratedTypes($x, $y, $origin)');
}
}
}
diff --git a/pkg/nnbd_migration/lib/src/node_builder.dart b/pkg/nnbd_migration/lib/src/node_builder.dart
index aa833d7..202fe0a 100644
--- a/pkg/nnbd_migration/lib/src/node_builder.dart
+++ b/pkg/nnbd_migration/lib/src/node_builder.dart
@@ -10,6 +10,7 @@
import 'package:analyzer/src/generated/resolver.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:front_end/src/scanner/token.dart';
+import 'package:meta/meta.dart';
import 'package:nnbd_migration/nnbd_migration.dart';
import 'package:nnbd_migration/src/conditional_discard.dart';
import 'package:nnbd_migration/src/decorated_type.dart';
@@ -69,7 +70,8 @@
if (node.factoryKeyword != null) {
// Factory constructors can return null, but we don't want to propagate a
// null type if we can prove that null is never returned.
- throw UnimplementedError('TODO(brianwilkerson)');
+ // TODO(brianwilkerson)
+ _unimplemented(node, 'Declaration of a factory constructor');
}
_handleExecutableDeclaration(
node.declaredElement, null, node.parameters, node.body, node);
@@ -93,7 +95,8 @@
@override
DecoratedType visitFieldFormalParameter(FieldFormalParameter node) {
- throw new UnimplementedError('TODO(brianwilkerson)');
+ // TODO(brianwilkerson)
+ _unimplemented(node, 'FieldFormalParameter');
}
@override
@@ -117,13 +120,15 @@
@override
DecoratedType visitFunctionTypeAlias(FunctionTypeAlias node) {
- throw new UnimplementedError('TODO(brianwilkerson)');
+ // TODO(brianwilkerson)
+ _unimplemented(node, 'FunctionTypeAlias');
}
@override
DecoratedType visitFunctionTypedFormalParameter(
FunctionTypedFormalParameter node) {
- throw new UnimplementedError('TODO(brianwilkerson)');
+ // TODO(brianwilkerson)
+ _unimplemented(node, 'FunctionTypedFormalParameter');
}
@override
@@ -197,7 +202,8 @@
if (node is GenericFunctionType) {
returnType = decorateType(node.returnType, node);
if (node.typeParameters != null) {
- throw UnimplementedError('TODO(paulberry)');
+ // TODO(paulberry)
+ _unimplemented(node, 'Generic function type with type parameters');
}
positionalParameters = <DecoratedType>[];
namedParameters = <String, DecoratedType>{};
@@ -281,7 +287,9 @@
typeArguments:
type.typeArguments.map(_decorateImplicitTypeArgument).toList());
}
- throw UnimplementedError('TODO(paulberry): ${type.runtimeType}');
+ // TODO(paulberry)
+ throw UnimplementedError(
+ '_decorateImplicitTypeArgument(${type.runtimeType})');
}
/// Common handling of function and method declarations.
@@ -298,11 +306,15 @@
if (declaredElement.isFactory) {
// Factory constructors can return null, but we don't want to propagate
// a null type if we can prove that null is never returned.
- throw UnimplementedError('TODO(brianwilkerson)');
+ // TODO(brianwilkerson)
+ _unimplemented(
+ parameters.parent, 'Declaration of a factory constructor');
}
if (declaredElement.enclosingElement.typeParameters.isNotEmpty) {
// Need to decorate the type parameters appropriately.
- throw new UnimplementedError('TODO(paulberry,brianwilkerson)');
+ // TODO(paulberry,brianwilkerson)
+ _unimplemented(parameters.parent,
+ 'Declaration of a constructor with type parameters');
}
decoratedReturnType = new DecoratedType(
declaredElement.enclosingElement.type, _graph.never);
@@ -327,6 +339,21 @@
}
_variables.recordDecoratedElementType(declaredElement, functionType);
}
+
+ @alwaysThrows
+ void _unimplemented(AstNode node, String message) {
+ CompilationUnit unit = node.root as CompilationUnit;
+ StringBuffer buffer = StringBuffer();
+ buffer.write(message);
+ buffer.write(' in "');
+ buffer.write(node.toSource());
+ buffer.write('" on line ');
+ buffer.write(unit.lineInfo.getLocation(node.offset).lineNumber);
+ buffer.write(' of "');
+ buffer.write(unit.declaredElement.source.fullName);
+ buffer.write('"');
+ throw UnimplementedError(buffer.toString());
+ }
}
/// Repository of constraint variables and decorated types corresponding to the