Reenable warnings/hints for method type variables in dart2js

Change-Id: I09662afc8b1a4e8c9fdbfe2e342e80ea58351593
Reviewed-on: https://dart-review.googlesource.com/32060
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Lasse R.H. Nielsen <lrn@google.com>
This commit is contained in:
Johnni Winther 2018-01-08 13:05:24 +01:00 committed by commit-bot@chromium.org
parent a4d2d87238
commit ab636dfbd1
4 changed files with 200 additions and 4 deletions

View file

@ -268,10 +268,6 @@ abstract class ConstantCompilerBase implements ConstantCompiler {
ErroneousElement element = elementType.element;
reporter.reportErrorMessage(
node, element.messageKind, element.messageArguments);
} else {
assert(elementType is MethodTypeVariableType);
// Used to `reportErrorMessage` here, but with Dart 2 upcoming
// very soon we do not emit this message any more.
}
} else {
// We need to throw an exception at runtime.

View file

@ -404,6 +404,8 @@ enum MessageKind {
TYPE_ARGUMENT_COUNT_MISMATCH,
TYPE_VARIABLE_IN_CONSTANT,
TYPE_VARIABLE_WITHIN_STATIC_MEMBER,
TYPE_VARIABLE_FROM_METHOD_NOT_REIFIED,
TYPE_VARIABLE_FROM_METHOD_CONSIDERED_DYNAMIC,
TYPEDEF_FORMAL_WITH_DEFAULT,
UNARY_OPERATOR_BAD_ARITY,
UNBOUND_LABEL,
@ -1195,6 +1197,43 @@ class C<T> {
}
void main() => new C().m(null);
"""
]),
MessageKind.TYPE_VARIABLE_FROM_METHOD_NOT_REIFIED: const MessageTemplate(
MessageKind.TYPE_VARIABLE_FROM_METHOD_NOT_REIFIED,
"Method type variables do not have a runtime value.",
howToFix: "Try using the upper bound of the type variable, "
"or refactor the code to avoid needing this runtime value.",
examples: const [
"""
// Method type variables are not reified, so they cannot be returned.
Type f<T>() => T;
main() => f<int>();
""",
"""
// Method type variables are not reified, so they cannot be tested dynamically.
bool f<T>(Object o) => o is T;
main() => f<int>(42);
"""
]),
MessageKind.TYPE_VARIABLE_FROM_METHOD_CONSIDERED_DYNAMIC:
const MessageTemplate(
MessageKind.TYPE_VARIABLE_FROM_METHOD_CONSIDERED_DYNAMIC,
"Method type variables are treated as `dynamic` in `as` "
"expressions.",
howToFix:
"Try using the upper bound of the type variable, or check "
"that the blind success of the test does not introduce bugs.",
examples: const [
"""
// Method type variables are not reified, so they cannot be tested dynamically.
bool f<T>(Object o) => o as T;
main() => f<int>(42);
"""
]),

View file

@ -1131,6 +1131,13 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
sendStructure = new IsStructure(type);
}
// GENERIC_METHODS: Method type variables are not reified so we must warn
// about the error which will occur at runtime.
if (type is MethodTypeVariableType) {
reporter.reportWarningMessage(
node, MessageKind.TYPE_VARIABLE_FROM_METHOD_NOT_REIFIED);
}
registry.registerTypeUse(new TypeUse.isCheck(type));
registry.registerSendStructure(node, sendStructure);
return const NoneResult();
@ -1145,6 +1152,13 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
ResolutionDartType type =
resolveTypeAnnotation(typeNode, registerCheckedModeCheck: false);
// GENERIC_METHODS: Method type variables are not reified, so we must inform
// the developer about the potentially bug-inducing semantics.
if (type is MethodTypeVariableType) {
reporter.reportHintMessage(
node, MessageKind.TYPE_VARIABLE_FROM_METHOD_CONSIDERED_DYNAMIC);
}
registry.registerTypeUse(new TypeUse.asCast(type));
registry.registerSendStructure(node, new AsStructure(type));
return const NoneResult();
@ -1913,6 +1927,12 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
// TODO(johnniwinther): Clean up registration of elements and selectors
// for this case.
} else {
// GENERIC_METHODS: Method type variables are not reified so we must warn
// about the error which will occur at runtime.
if (element.type is MethodTypeVariableType) {
reporter.reportWarningMessage(
node, MessageKind.TYPE_VARIABLE_FROM_METHOD_NOT_REIFIED);
}
semantics = new StaticAccess.typeParameterTypeLiteral(element);
}

View file

@ -0,0 +1,141 @@
import 'package:async_helper/async_helper.dart';
import 'package:compiler/src/diagnostics/messages.dart';
import 'package:expect/expect.dart';
import '../memory_compiler.dart';
runTest(String code,
{List<MessageKind> expectedWarnings: const <MessageKind>[],
List<MessageKind> expectedHints: const <MessageKind>[]}) async {
print('--test--------------------------------------------------------------');
print(code);
DiagnosticCollector collector = new DiagnosticCollector();
await runCompiler(
memorySourceFiles: {'main.dart': code}, diagnosticHandler: collector);
Expect.equals(0, collector.errors.length, "Unexpected errors.");
Expect.listEquals(
expectedWarnings,
collector.warnings.map((m) => m.messageKind).toList(),
"Unexpected warnings.");
Expect.listEquals(expectedHints,
collector.hints.map((m) => m.messageKind).toList(), "Unexpected hints.");
}
class Test {
final String code;
final List<MessageKind> warnings;
final List<MessageKind> hints;
const Test(this.code,
{this.warnings: const <MessageKind>[],
this.hints: const <MessageKind>[]});
}
const List<Test> tests = const <Test>[
/// Is-test on method type variable in unused static method.
const Test('''
method<T>(T t) => t is T;
main() {}
'''),
/// Is-test on method type variable in used static method.
const Test('''
method<T>(T t) => t is T;
main() => method<int>(0);
''', warnings: const <MessageKind>[
MessageKind.TYPE_VARIABLE_FROM_METHOD_NOT_REIFIED
]),
/// Is-test on method type variable in unused instance method.
const Test('''
class C {
method<T>(T t) => t is T;
}
main() => new C();
'''),
/// Is-test on method type variable in used instance method.
const Test('''
class C {
method<T>(T t) => t is T;
}
main() => new C().method<int>(0);
''', warnings: const <MessageKind>[
MessageKind.TYPE_VARIABLE_FROM_METHOD_NOT_REIFIED
]),
/// As-cast on method type variable in unused static method.
const Test('''
method<T>(T t) => t as T;
main() {}
'''),
/// As-cast on method type variable in used static method.
const Test('''
method<T>(T t) => t as T;
main() => method<int>(0);
''', hints: const <MessageKind>[
MessageKind.TYPE_VARIABLE_FROM_METHOD_CONSIDERED_DYNAMIC
]),
/// As-cast on method type variable in unused instance method.
const Test('''
class C {
method<T>(T t) => t as T;
}
main() => new C();
'''),
/// As-cast on method type variable in used instance method.
const Test('''
class C {
method<T>(T t) => t as T;
}
main() => new C().method<int>(0);
''', hints: const <MessageKind>[
MessageKind.TYPE_VARIABLE_FROM_METHOD_CONSIDERED_DYNAMIC
]),
/// Method type variable literal in unused static method.
const Test('''
method<T>() => T;
main() {}
'''),
/// Method type variable literal in used static method.
const Test('''
method<T>() => T;
main() => method<int>();
''', warnings: const <MessageKind>[
MessageKind.TYPE_VARIABLE_FROM_METHOD_NOT_REIFIED
]),
/// Method type variable literal in unused instance method.
const Test('''
class C {
method<T>() => T;
}
main() => new C();
'''),
/// Method type variable literal in used instance method.
const Test('''
class C {
method<T>() => T;
}
main() => new C().method<int>();
''', warnings: const <MessageKind>[
MessageKind.TYPE_VARIABLE_FROM_METHOD_NOT_REIFIED
]),
];
main() {
asyncTest(() async {
for (Test test in tests) {
await runTest(
test.code,
expectedWarnings: test.warnings,
expectedHints: test.hints,
);
}
});
}