[dart:js_interop] Fix checking of type parameters in interop APIs
Addresses some comments in https://github.com/dart-lang/sdk/issues/54192
- With the addition of `nonTypeVariableBound`, we no longer need a
recursive checker.
- We also should account for `StructuralParameterTypes`.
- The current checks did not check type parameters for functions
converted via `toJS`, so that is handled as well.
- Functions that declare type parameters cannot be called in JS
correctly, so an error is added when users try to convert them.
- Some errors are reworded/modified.
Change-Id: Ic05220883ec4ad8653963acd901d339426cba6c6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/339346
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Srujan Gaddam <srujzs@google.com>
diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
index a93f73b..c65db5b 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -8632,6 +8632,18 @@
r"""Try removing the 'external' keyword or adding a JS interop annotation.""");
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeJsInteropFunctionToJSTypeParameters =
+ messageJsInteropFunctionToJSTypeParameters;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageJsInteropFunctionToJSTypeParameters = const MessageCode(
+ "JsInteropFunctionToJSTypeParameters",
+ problemMessage:
+ r"""Functions converted via `toJS` cannot declare type parameters.""",
+ correctionMessage:
+ r"""Remove the declared type parameters from the function.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<Null> codeJsInteropInvalidStaticClassMemberName =
messageJsInteropInvalidStaticClassMemberName;
@@ -8836,21 +8848,6 @@
correctionMessage: r"""Try replacing this with a normal method.""");
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Null>
- codeJsInteropStaticInteropExternalMemberWithInvalidTypeParameters =
- messageJsInteropStaticInteropExternalMemberWithInvalidTypeParameters;
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const MessageCode
- messageJsInteropStaticInteropExternalMemberWithInvalidTypeParameters =
- const MessageCode(
- "JsInteropStaticInteropExternalMemberWithInvalidTypeParameters",
- problemMessage:
- r"""External static interop members can only use type parameters that extend either a static interop type or one of the 'dart:js_interop' types.""",
- correctionMessage:
- r"""Try adding a valid bound to the type parameters used in this member.""");
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<Null> codeJsInteropStaticInteropGenerativeConstructor =
messageJsInteropStaticInteropGenerativeConstructor;
diff --git a/pkg/_js_interop_checks/lib/js_interop_checks.dart b/pkg/_js_interop_checks/lib/js_interop_checks.dart
index 3005f22..447aa94 100644
--- a/pkg/_js_interop_checks/lib/js_interop_checks.dart
+++ b/pkg/_js_interop_checks/lib/js_interop_checks.dart
@@ -16,13 +16,13 @@
messageJsInteropExternalExtensionMemberOnTypeInvalid,
messageJsInteropExternalExtensionMemberWithStaticDisallowed,
messageJsInteropExternalMemberNotJSAnnotated,
+ messageJsInteropFunctionToJSTypeParameters,
messageJsInteropInvalidStaticClassMemberName,
messageJsInteropNamedParameters,
messageJsInteropNonExternalConstructor,
messageJsInteropNonExternalMember,
messageJsInteropOperatorCannotBeRenamed,
messageJsInteropOperatorsNotSupported,
- messageJsInteropStaticInteropExternalMemberWithInvalidTypeParameters,
messageJsInteropStaticInteropGenerativeConstructor,
messageJsInteropStaticInteropParameterInitializersAreIgnored,
messageJsInteropStaticInteropSyntheticConstructor,
@@ -68,7 +68,6 @@
final Map<String, Class> _nativeClasses;
final JsInteropDiagnosticReporter _reporter;
final StatefulStaticTypeContext _staticTypeContext;
- late _TypeParameterBoundChecker _typeParameterBoundChecker;
bool _classHasJSAnnotation = false;
bool _classHasAnonymousAnnotation = false;
bool _classHasStaticInteropAnnotation = false;
@@ -134,7 +133,6 @@
TypeEnvironment(_coreTypes, hierarchy)) {
_extensionIndex =
ExtensionIndex(_coreTypes, _staticTypeContext.typeEnvironment);
- _typeParameterBoundChecker = _TypeParameterBoundChecker(_extensionIndex);
}
/// Determines if given [member] is an external extension member that needs to
@@ -375,7 +373,6 @@
((hasDartJSInteropAnnotation(annotatable) ||
annotatable is ExtensionTypeDeclaration))) {
// Checks for dart:js_interop APIs only.
- _checkStaticInteropMemberUsesValidTypeParameters(node);
final function = node.function;
_reportProcedureIfNotAllowedType(function.returnType, node);
for (final parameter in function.positionalParameters) {
@@ -697,16 +694,18 @@
/// Checks that [node], which is a call to 'Function.toJS', is called with a
/// valid function type.
void _checkFunctionToJSCall(StaticInvocation node) {
+ void report(Message message) => _reporter.report(
+ message, node.fileOffset, node.name.text.length, node.location?.file);
+
final argument = node.arguments.positional.single;
final functionType = argument.getStaticType(_staticTypeContext);
if (functionType is! FunctionType) {
- _reporter.report(
- templateJsInteropFunctionToJSRequiresStaticType.withArguments(
- functionType, true),
- node.fileOffset,
- node.name.text.length,
- node.location?.file);
+ report(templateJsInteropFunctionToJSRequiresStaticType.withArguments(
+ functionType, true));
} else {
+ if (functionType.typeParameters.isNotEmpty) {
+ report(messageJsInteropFunctionToJSTypeParameters);
+ }
_reportStaticInvocationIfNotAllowedType(functionType.returnType, node);
for (final parameter in functionType.positionalParameters) {
_reportStaticInvocationIfNotAllowedType(parameter, node);
@@ -812,16 +811,6 @@
}
}
- void _checkStaticInteropMemberUsesValidTypeParameters(Procedure node) {
- if (_typeParameterBoundChecker.containsInvalidTypeBound(node)) {
- _reporter.report(
- messageJsInteropStaticInteropExternalMemberWithInvalidTypeParameters,
- node.fileOffset,
- node.name.text.length,
- node.fileUri);
- }
- }
-
/// If [procedure] is a generated procedure that represents a relevant
/// tear-off, return the torn-off member.
///
@@ -894,9 +883,20 @@
// provide JS types equivalents, but likely only if we have implicit
// conversions between Dart types and JS types.
- // Type parameter types are checked elsewhere.
- if (type is VoidType || type is NullType || type is TypeParameterType) {
- return true;
+ if (type is VoidType || type is NullType) return true;
+ if (type is TypeParameterType || type is StructuralParameterType) {
+ final bound = type.nonTypeVariableBound;
+ final isStaticInteropBound =
+ bound is InterfaceType && hasStaticInteropAnnotation(bound.classNode);
+ final isInteropExtensionTypeBound = bound is ExtensionType &&
+ _extensionIndex
+ .isInteropExtensionType(bound.extensionTypeDeclaration);
+ // TODO(srujzs): We may want to support type parameters with primitive
+ // bounds that are themselves allowed e.g. `num`. If so, we should handle
+ // that change in dart2wasm.
+ if (isStaticInteropBound || isInteropExtensionTypeBound) {
+ return true;
+ }
}
if (type is InterfaceType) {
final cls = type.classNode;
@@ -940,55 +940,6 @@
type, node, node.name, node.location?.file);
}
-/// Visitor used to check that all usages of type parameter types of an external
-/// static interop member is a valid static interop type.
-class _TypeParameterBoundChecker extends RecursiveVisitor {
- final ExtensionIndex _extensionIndex;
-
- _TypeParameterBoundChecker(this._extensionIndex);
-
- bool _containsInvalidTypeBound = false;
-
- bool containsInvalidTypeBound(Procedure node) {
- _containsInvalidTypeBound = false;
- final function = node.function;
- for (final param in function.positionalParameters) {
- param.accept(this);
- }
- function.returnType.accept(this);
- return _containsInvalidTypeBound;
- }
-
- @override
- void visitInterfaceType(InterfaceType node) {
- final cls = node.classNode;
- if (hasStaticInteropAnnotation(cls)) return;
- super.visitInterfaceType(node);
- }
-
- @override
- void visitExtensionType(ExtensionType node) {
- if (_extensionIndex.isInteropExtensionType(node.extensionTypeDeclaration)) {
- return;
- }
- super.visitExtensionType(node);
- }
-
- @override
- void visitTypeParameterType(TypeParameterType node) {
- final bound = node.bound;
- if (bound is ExtensionType &&
- !_extensionIndex
- .isInteropExtensionType(bound.extensionTypeDeclaration)) {
- _containsInvalidTypeBound = true;
- }
- if (bound is InterfaceType &&
- !hasStaticInteropAnnotation(bound.classNode)) {
- _containsInvalidTypeBound = true;
- }
- }
-}
-
class JsInteropDiagnosticReporter {
bool hasJsInteropErrors = false;
final DiagnosticReporter<Message, LocatedMessage> _reporter;
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart
index c6e06c3..5330bfc 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart
@@ -4082,7 +4082,7 @@
Message Function(DartType _type, bool isNonNullableByDefault)>(
"JsInteropFunctionToJSRequiresStaticType",
problemMessageTemplate:
- r"""`Function.toJS` requires a statically known function type, but Type '#type' is not a function type, e.g., `void Function()`.""",
+ r"""`Function.toJS` requires a statically known function type, but Type '#type' is not a precise function type, e.g., `void Function()`.""",
correctionMessageTemplate:
r"""Insert an explicit cast to the expected function type.""",
withArguments: _withArgumentsJsInteropFunctionToJSRequiresStaticType);
@@ -4102,7 +4102,7 @@
String type = typeParts.join();
return new Message(codeJsInteropFunctionToJSRequiresStaticType,
problemMessage:
- """`Function.toJS` requires a statically known function type, but Type '${type}' is not a function type, e.g., `void Function()`.""" +
+ """`Function.toJS` requires a statically known function type, but Type '${type}' is not a precise function type, e.g., `void Function()`.""" +
labeler.originMessages,
correctionMessage: """Insert an explicit cast to the expected function type.""",
arguments: {'type': _type});
@@ -4114,7 +4114,7 @@
Message Function(DartType _type, bool isNonNullableByDefault)>(
"JsInteropStaticInteropExternalTypeViolation",
problemMessageTemplate:
- r"""Type '#type' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types.""",
+ r"""Type '#type' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.""",
correctionMessageTemplate: r"""Use one of the valid types instead.""",
withArguments:
_withArgumentsJsInteropStaticInteropExternalTypeViolation);
@@ -4134,7 +4134,7 @@
String type = typeParts.join();
return new Message(codeJsInteropStaticInteropExternalTypeViolation,
problemMessage:
- """Type '${type}' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types.""" +
+ """Type '${type}' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.""" +
labeler.originMessages,
correctionMessage: """Use one of the valid types instead.""",
arguments: {'type': _type});
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 5ffd916..ba7ad60 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -629,6 +629,8 @@
JsInteropExternalMemberNotJSAnnotated/example: Fail # Web compiler specific
JsInteropFunctionToJSRequiresStaticType/analyzerCode: Fail # Web compiler specific
JsInteropFunctionToJSRequiresStaticType/example: Fail # Web compiler specific
+JsInteropFunctionToJSTypeParameters/analyzerCode: Fail # Web compiler specific
+JsInteropFunctionToJSTypeParameters/example: Fail # Web compiler specific
JsInteropInvalidStaticClassMemberName/analyzerCode: Fail
JsInteropInvalidStaticClassMemberName/example: Fail
JsInteropJSClassExtendsDartClass/analyzerCode: Fail # Web compiler specific
@@ -649,8 +651,6 @@
JsInteropOperatorCannotBeRenamed/example: Fail # Web compiler specific
JsInteropOperatorsNotSupported/analyzerCode: Fail # Web compiler specific
JsInteropOperatorsNotSupported/example: Fail # Web compiler specific
-JsInteropStaticInteropExternalMemberWithInvalidTypeParameters/analyzerCode: Fail # Web compiler specific
-JsInteropStaticInteropExternalMemberWithInvalidTypeParameters/example: Fail # Web compiler specific
JsInteropStaticInteropExternalTypeViolation/analyzerCode: Fail # Web compiler specific
JsInteropStaticInteropExternalTypeViolation/example: Fail # Web compiler specific
JsInteropStaticInteropGenerativeConstructor/analyzerCode: Fail # Web compiler specific
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 9611850..0902253 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -5793,6 +5793,14 @@
problemMessage: "Only JS interop members may be 'external'."
correctionMessage: "Try removing the 'external' keyword or adding a JS interop annotation."
+JsInteropFunctionToJSRequiresStaticType:
+ problemMessage: "`Function.toJS` requires a statically known function type, but Type '#type' is not a precise function type, e.g., `void Function()`."
+ correctionMessage: "Insert an explicit cast to the expected function type."
+
+JsInteropFunctionToJSTypeParameters:
+ problemMessage: "Functions converted via `toJS` cannot declare type parameters."
+ correctionMessage: "Remove the declared type parameters from the function."
+
JsInteropInvalidStaticClassMemberName:
problemMessage: "JS interop static class members cannot have '.' in their JS name."
@@ -5832,12 +5840,8 @@
problemMessage: "Class '#name' does not have an `@staticInterop` annotation, but has supertype '#name2', which does."
correctionMessage: "Try marking '#name' as a `@staticInterop` class, or don't inherit '#name2'."
-JsInteropStaticInteropExternalMemberWithInvalidTypeParameters:
- problemMessage: "External static interop members can only use type parameters that extend either a static interop type or one of the 'dart:js_interop' types."
- correctionMessage: "Try adding a valid bound to the type parameters used in this member."
-
JsInteropStaticInteropExternalTypeViolation:
- problemMessage: "Type '#type' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types."
+ problemMessage: "Type '#type' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type."
correctionMessage: "Use one of the valid types instead."
JsInteropStaticInteropGenerativeConstructor:
@@ -5897,10 +5901,6 @@
problemMessage: "Library '#name' is forbidden when strict mode is enabled."
correctionMessage: "Remove the import of a forbidden library."
-JsInteropFunctionToJSRequiresStaticType:
- problemMessage: "`Function.toJS` requires a statically known function type, but Type '#type' is not a function type, e.g., `void Function()`."
- correctionMessage: "Insert an explicit cast to the expected function type."
-
NonNullableInNullAware:
problemMessage: "Operand of null-aware operation '#name' has type '#type' which excludes null."
severity: WARNING
diff --git a/pkg/front_end/test/spell_checking_list_messages.txt b/pkg/front_end/test/spell_checking_list_messages.txt
index e0878a7..2fa697a 100644
--- a/pkg/front_end/test/spell_checking_list_messages.txt
+++ b/pkg/front_end/test/spell_checking_list_messages.txt
@@ -123,6 +123,7 @@
tearoff
this.namedconstructor
this.x
+tojs
trusttypes
type3.#name
u
diff --git a/tests/lib/js/static_interop_test/external_member_type_parameters_static_test.dart b/tests/lib/js/static_interop_test/external_member_type_parameters_static_test.dart
index 3116e41..9270252 100644
--- a/tests/lib/js/static_interop_test/external_member_type_parameters_static_test.dart
+++ b/tests/lib/js/static_interop_test/external_member_type_parameters_static_test.dart
@@ -18,7 +18,7 @@
@JS()
external T invalidTopLevel<T>(T t);
// ^
-// [web] External static interop members can only use type parameters that extend either a static interop type or one of the 'dart:js_interop' types.
+// [web] Type 'T' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
typedef Typedef<T> = T Function();
@@ -29,7 +29,7 @@
class Uninstantiated<W, X extends Instantiated?> {
external factory Uninstantiated(W w);
// ^
- // [web] External static interop members can only use type parameters that extend either a static interop type or one of the 'dart:js_interop' types.
+ // [web] Type 'W' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
external factory Uninstantiated.named(X x);
}
@@ -37,35 +37,35 @@
on Uninstantiated {
external T fieldT;
// ^
- // [web] External static interop members can only use type parameters that extend either a static interop type or one of the 'dart:js_interop' types.
+ // [web] Type 'T' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
external U fieldU;
external V fieldV;
T get getTDart => throw UnimplementedError();
external T get getT;
// ^
- // [web] External static interop members can only use type parameters that extend either a static interop type or one of the 'dart:js_interop' types.
+ // [web] Type 'T' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
external U get getU;
external V get getV;
set setTDart(T t) => throw UnimplementedError();
external set setT(T t);
// ^
- // [web] External static interop members can only use type parameters that extend either a static interop type or one of the 'dart:js_interop' types.
+ // [web] Type 'T' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
external set setU(U u);
external set setV(V v);
T returnTDart() => throw UnimplementedError();
external T returnT();
// ^
- // [web] External static interop members can only use type parameters that extend either a static interop type or one of the 'dart:js_interop' types.
+ // [web] Type 'T' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
external U returnU();
external V returnV();
void consumeTDart(T t) => throw UnimplementedError();
external void consumeT(T t);
// ^
- // [web] External static interop members can only use type parameters that extend either a static interop type or one of the 'dart:js_interop' types.
+ // [web] Type 'T' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
external void consumeU(U u);
external void consumeV(V v);
@@ -80,7 +80,7 @@
W returnWDart<W>() => throw UnimplementedError();
external W returnW<W>();
// ^
- // [web] External static interop members can only use type parameters that extend either a static interop type or one of the 'dart:js_interop' types.
+ // [web] Type 'W' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
external X returnX<X extends JSArray>();
}
@@ -88,41 +88,41 @@
V extends InstantiatedExtensionType>._(JSObject _) {
external UninstantiatedExtensionType(T t);
// ^
- // [web] External static interop members can only use type parameters that extend either a static interop type or one of the 'dart:js_interop' types.
+ // [web] Type 'T' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
external factory UninstantiatedExtensionType.fact(U u);
// Test simple type parameters.
external T fieldT;
// ^
- // [web] External static interop members can only use type parameters that extend either a static interop type or one of the 'dart:js_interop' types.
+ // [web] Type 'T' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
external U fieldU;
external V fieldV;
T get getTDart => throw UnimplementedError();
external T get getT;
// ^
- // [web] External static interop members can only use type parameters that extend either a static interop type or one of the 'dart:js_interop' types.
+ // [web] Type 'T' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
external U get getU;
external V get getV;
set setTDart(T t) => throw UnimplementedError();
external set setT(T t);
// ^
- // [web] External static interop members can only use type parameters that extend either a static interop type or one of the 'dart:js_interop' types.
+ // [web] Type 'T' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
external set setU(U u);
external set setV(V v);
T returnTDart() => throw UnimplementedError();
external T returnT();
// ^
- // [web] External static interop members can only use type parameters that extend either a static interop type or one of the 'dart:js_interop' types.
+ // [web] Type 'T' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
external U returnU();
external V returnV();
void consumeTDart(T t) => throw UnimplementedError();
external void consumeT(T t);
// ^
- // [web] External static interop members can only use type parameters that extend either a static interop type or one of the 'dart:js_interop' types.
+ // [web] Type 'T' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
external void consumeU(U u);
external void consumeV(V v);
@@ -137,7 +137,7 @@
W returnWDart<W>() => throw UnimplementedError();
external W returnW<W>();
// ^
- // [web] External static interop members can only use type parameters that extend either a static interop type or one of the 'dart:js_interop' types.
+ // [web] Type 'W' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
external X returnX<X extends JSArray>();
}
@@ -145,7 +145,7 @@
V extends InstantiatedExtensionType> on UninstantiatedExtensionType<T, U, V> {
external T get extensionGetT;
// ^
- // [web] External static interop members can only use type parameters that extend either a static interop type or one of the 'dart:js_interop' types.
+ // [web] Type 'T' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
external U get extensionGetU;
external V get extensionGetV;
}
diff --git a/tests/lib/js/static_interop_test/strict_mode_test.dart b/tests/lib/js/static_interop_test/strict_mode_test.dart
index e85820d..b5948a6 100644
--- a/tests/lib/js/static_interop_test/strict_mode_test.dart
+++ b/tests/lib/js/static_interop_test/strict_mode_test.dart
@@ -16,41 +16,41 @@
class JSClass {
external factory JSClass(List<int> baz);
// ^
- // [web] Type 'List<int>' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types.
+ // [web] Type 'List<int>' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
external factory JSClass.other(Object blu);
// ^
- // [web] Type 'Object' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types.
+ // [web] Type 'Object' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
external static dynamic foo();
// ^
- // [web] Type 'dynamic' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types.
+ // [web] Type 'dynamic' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
external static Function get fooGet;
// ^
- // [web] Type 'Function' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types.
+ // [web] Type 'Function' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
external static set fooSet(void Function() bar);
// ^
- // [web] Type 'void Function()' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types.
+ // [web] Type 'void Function()' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
}
extension JSClassExtension on JSClass {
external dynamic extFoo();
// ^
- // [web] Type 'dynamic' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types.
+ // [web] Type 'dynamic' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
external JSClass extFoo2(List<Object?> bar);
// ^
- // [web] Type 'List<Object?>' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types.
+ // [web] Type 'List<Object?>' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
external Function get extFooGet;
// ^
- // [web] Type 'Function' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types.
+ // [web] Type 'Function' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
external set extFooSet(void Function() bar);
// ^
- // [web] Type 'void Function()' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types.
+ // [web] Type 'void Function()' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
}
@JS()
@@ -65,20 +65,48 @@
@JS()
external void useStaticInteropExtensionType(ExtensionType foo);
+void declareTypeParameter<T extends JSAny?>() {}
+
+T declareAndUseTypeParameter<T extends JSAny?>(T t) => t;
+
+T declareAndUseInvalidTypeParameter<T>(T t) => t;
+
void main() {
- jsFunctionTest(((double foo) => 4.0.toJS).toJS);
+ ((double foo) => 4.0.toJS).toJS;
- jsFunctionTest(((JSNumber foo) => 4.0).toJS);
+ ((JSNumber foo) => 4.0).toJS;
- jsFunctionTest(((List foo) => 4.0).toJS);
- // ^
- // [web] Type 'List<dynamic>' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types.
+ ((List foo) => 4.0).toJS;
+ // ^
+ // [web] Type 'List<dynamic>' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
- jsFunctionTest(((JSNumber foo) => () {}).toJS);
- // ^
- // [web] Type 'Null Function()' is not a valid type for external `dart:js_interop` APIs. The only valid types are: @staticInterop types, JS types from `dart:js_interop`, void, bool, num, double, int, String, and any extension type that erases to one of these types.
+ ((JSNumber foo) => () {}).toJS;
+ // ^
+ // [web] Type 'Null Function()' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
- jsFunctionTest(((((JSNumber foo) => 4.0) as dynamic) as Function).toJS);
- // ^
- // [web] `Function.toJS` requires a statically known function type, but Type 'Function' is not a function type, e.g., `void Function()`.
-}
\ No newline at end of file
+ ((((JSNumber foo) => 4.0) as dynamic) as Function).toJS;
+ // ^
+ // [web] `Function.toJS` requires a statically known function type, but Type 'Function' is not a precise function type, e.g., `void Function()`.
+
+ void typeParametersTest<T extends JSAny, U extends ExtensionType,
+ V extends JSClass, W, Y>() {
+ ((T t) => t).toJS;
+ ((U u) => u).toJS;
+ ((V v) => v).toJS;
+ ((W w) => w as Y).toJS;
+ // ^
+ // [web] Type 'W' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
+ // [web] Type 'Y' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
+
+ declareTypeParameter.toJS;
+ // ^
+ // [web] Functions converted via `toJS` cannot declare type parameters.
+ declareAndUseTypeParameter.toJS;
+ // ^
+ // [web] Functions converted via `toJS` cannot declare type parameters.
+ declareAndUseInvalidTypeParameter.toJS;
+ // ^
+ // [web] Functions converted via `toJS` cannot declare type parameters.
+ // [web] Type 'T' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
+ }
+}