[package:js] Allow external extension members for Native classes
Change-Id: I5baf81c3c2b6b3a112327e1aea084a93677bd9b5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/208380
Reviewed-by: Srujan Gaddam <srujzs@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Riley Porter <rileyporter@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 2ccb299..01dd119 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -6237,14 +6237,14 @@
message: r"""This is the enclosing class.""");
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Null> codeJsInteropExternalExtensionMemberNotOnJSClass =
- messageJsInteropExternalExtensionMemberNotOnJSClass;
+const Code<Null> codeJsInteropExternalExtensionMemberOnTypeInvalid =
+ messageJsInteropExternalExtensionMemberOnTypeInvalid;
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const MessageCode messageJsInteropExternalExtensionMemberNotOnJSClass =
- const MessageCode("JsInteropExternalExtensionMemberNotOnJSClass",
+const MessageCode messageJsInteropExternalExtensionMemberOnTypeInvalid =
+ const MessageCode("JsInteropExternalExtensionMemberOnTypeInvalid",
message:
- r"""JS interop class required for 'external' extension members.""",
+ r"""JS interop or Native class required for 'external' extension members.""",
tip:
r"""Try adding a JS interop annotation to the on type class of the extension.""");
diff --git a/pkg/_js_interop_checks/lib/js_interop_checks.dart b/pkg/_js_interop_checks/lib/js_interop_checks.dart
index 7567930..975fca7 100644
--- a/pkg/_js_interop_checks/lib/js_interop_checks.dart
+++ b/pkg/_js_interop_checks/lib/js_interop_checks.dart
@@ -12,7 +12,7 @@
messageJsInteropAnonymousFactoryPositionalParameters,
messageJsInteropEnclosingClassJSAnnotation,
messageJsInteropEnclosingClassJSAnnotationContext,
- messageJsInteropExternalExtensionMemberNotOnJSClass,
+ messageJsInteropExternalExtensionMemberOnTypeInvalid,
messageJsInteropExternalMemberNotJSAnnotated,
messageJsInteropIndexNotSupported,
messageJsInteropNamedParameters,
@@ -282,13 +282,14 @@
/// [member] is `external` and not an allowed `external` usage.
void _checkDisallowedExternal(Member member) {
if (member.isExternal) {
- // TODO(rileyporter): Allow extension members on some Native classes.
if (member.isExtensionMember) {
- _diagnosticsReporter.report(
- messageJsInteropExternalExtensionMemberNotOnJSClass,
- member.fileOffset,
- member.name.text.length,
- member.fileUri);
+ if (!_isNativeExtensionMember(member)) {
+ _diagnosticsReporter.report(
+ messageJsInteropExternalExtensionMemberOnTypeInvalid,
+ member.fileOffset,
+ member.name.text.length,
+ member.fileUri);
+ }
} else if (!hasJSInteropAnnotation(member) &&
!_isAllowedExternalUsage(member)) {
// Member could be JS annotated and not considered a JS interop member
@@ -338,6 +339,18 @@
/// Returns whether given extension [member] is in an extension that is on a
/// JS interop class.
bool _isJSExtensionMember(Member member) {
+ return _checkExtensionMember(member, hasJSInteropAnnotation);
+ }
+
+ /// Returns whether given extension [member] is in an extension on a Native
+ /// class.
+ bool _isNativeExtensionMember(Member member) {
+ return _checkExtensionMember(member, _nativeClasses.containsValue);
+ }
+
+ /// Returns whether given extension [member] is on a class that passses the
+ /// given [validateExtensionClass].
+ bool _checkExtensionMember(Member member, Function validateExtensionClass) {
assert(member.isExtensionMember);
if (_libraryExtensionsIndex == null) {
_libraryExtensionsIndex = {};
@@ -347,6 +360,6 @@
}
var onType = _libraryExtensionsIndex![member.reference]!.onType;
- return onType is InterfaceType && hasJSInteropAnnotation(onType.classNode);
+ return onType is InterfaceType && validateExtensionClass(onType.classNode);
}
}
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index ba2bc55..8eb1f8d 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -514,8 +514,8 @@
JsInteropDartClassExtendsJSClass/example: Fail # Web compiler specific
JsInteropEnclosingClassJSAnnotation/analyzerCode: Fail # Web compiler specific
JsInteropEnclosingClassJSAnnotation/example: Fail # Web compiler specific
-JsInteropExternalExtensionMemberNotOnJSClass/analyzerCode: Fail # Web compiler specific
-JsInteropExternalExtensionMemberNotOnJSClass/example: Fail # Web compiler specific
+JsInteropExternalExtensionMemberOnTypeInvalid/analyzerCode: Fail # Web compiler specific
+JsInteropExternalExtensionMemberOnTypeInvalid/example: Fail # Web compiler specific
JsInteropExternalMemberNotJSAnnotated/analyzerCode: Fail # Web compiler specific
JsInteropExternalMemberNotJSAnnotated/example: Fail # Web compiler specific
JsInteropIndexNotSupported/analyzerCode: Fail # Web compiler specific
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 0873cba..e94eac1 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -4914,8 +4914,8 @@
template: "This is the enclosing class."
severity: CONTEXT
-JsInteropExternalExtensionMemberNotOnJSClass:
- template: "JS interop class required for 'external' extension members."
+JsInteropExternalExtensionMemberOnTypeInvalid:
+ template: "JS interop or Native class required for 'external' extension members."
tip: "Try adding a JS interop annotation to the on type class of the extension."
JsInteropExternalMemberNotJSAnnotated:
diff --git a/tests/lib/js/external_nonjs_static_test.dart b/tests/lib/js/external_nonjs_static_test.dart
index 913d202..87c4615 100644
--- a/tests/lib/js/external_nonjs_static_test.dart
+++ b/tests/lib/js/external_nonjs_static_test.dart
@@ -7,6 +7,7 @@
library external_nonjs_static_test;
+import 'dart:html';
import 'package:js/js.dart';
external var topLevelField;
@@ -101,43 +102,43 @@
extension ExtensionNonJS on NonJSClass {
external var field;
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external final finalField;
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external static var staticField;
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external static final staticFinalField;
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external get getter;
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external set setter(_);
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external static get staticGetter;
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external static set staticSetter(_);
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external method();
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external static staticMethod();
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external optionalParameterMethod([int? a, int b = 0]);
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external overridenMethod();
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
nonExternalMethod() => 1;
static nonExternalStaticMethod() => 2;
@@ -150,9 +151,29 @@
extension ExtensionGenericNonJS<T> on GenericNonJSClass<T> {
external T method();
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
}
class GenericNonJSClass<T> {}
+extension ExtensionNative on HtmlElement {
+ external var field;
+ external final finalField;
+ external static var staticField;
+ external static final staticFinalField;
+
+ external get getter;
+ external set setter(_);
+
+ external static get staticGetter;
+ external static set staticSetter(_);
+
+ external method();
+ external static staticMethod();
+ external optionalParameterMethod([int? a, int b = 0]);
+
+ nonExternalMethod() => 1;
+ static nonExternalStaticMethod() => 2;
+}
+
main() {}
diff --git a/tests/lib/js/external_static_test.dart b/tests/lib/js/external_static_test.dart
index 0a1c8db..a455de0 100644
--- a/tests/lib/js/external_static_test.dart
+++ b/tests/lib/js/external_static_test.dart
@@ -8,6 +8,7 @@
@JS()
library external_static_test;
+import 'dart:html';
import 'package:js/js.dart';
// external top level members ok in @JS() library.
@@ -89,52 +90,52 @@
extension ExtensionNonJS on NonJSClass {
external var field;
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external final finalField;
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external static var staticField;
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external static final staticFinalField;
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external get getter;
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external set setter(_);
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external static get staticGetter;
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external static set staticSetter(_);
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external method();
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external static staticMethod();
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external optionalParameterMethod([int? a, int b = 0]);
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external overridenMethod();
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
@JS('fieldAnnotation')
external var annotatedField;
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
@JS('memberAnnotation')
external annotatedMethod();
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
nonExternalMethod() => 1;
static nonExternalStaticMethod() => 2;
@@ -147,7 +148,7 @@
extension ExtensionGenericNonJS<T> on GenericNonJSClass<T> {
external T method();
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
}
class GenericNonJSClass<T> {}
@@ -229,4 +230,24 @@
@JS()
class _privateJSClass {}
+extension ExtensionNative on HtmlElement {
+ external var field;
+ external final finalField;
+ external static var staticField;
+ external static final staticFinalField;
+
+ external get getter;
+ external set setter(_);
+
+ external static get staticGetter;
+ external static set staticSetter(_);
+
+ external method();
+ external static staticMethod();
+ external optionalParameterMethod([int? a, int b = 0]);
+
+ nonExternalMethod() => 1;
+ static nonExternalStaticMethod() => 2;
+}
+
main() {}
diff --git a/tests/lib_2/js/external_nonjs_static_test.dart b/tests/lib_2/js/external_nonjs_static_test.dart
index 1de7e61..d0fa7df 100644
--- a/tests/lib_2/js/external_nonjs_static_test.dart
+++ b/tests/lib_2/js/external_nonjs_static_test.dart
@@ -9,6 +9,7 @@
library external_nonjs_static_test;
+import 'dart:html';
import 'package:js/js.dart';
external get topLevelGetter;
@@ -75,27 +76,30 @@
extension ExtensionNonJS on NonJSClass {
external get getter;
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external set setter(_);
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external static get staticGetter;
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external static set staticSetter(_);
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external method();
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external static staticMethod();
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
+ external optionalParameterMethod([int a, int b = 0]);
+ // ^
+ // [web] JS interop or Native class required for 'external' extension members.
external overridenMethod();
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
nonExternalMethod() => 1;
static nonExternalStaticMethod() => 2;
@@ -108,9 +112,24 @@
extension ExtensionGenericNonJS<T> on GenericNonJSClass<T> {
external T method();
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
}
class GenericNonJSClass<T> {}
+extension ExtensionNative on HtmlElement {
+ external get getter;
+ external set setter(_);
+
+ external static get staticGetter;
+ external static set staticSetter(_);
+
+ external method();
+ external static staticMethod();
+ external optionalParameterMethod([int a, int b = 0]);
+
+ nonExternalMethod() => 1;
+ static nonExternalStaticMethod() => 2;
+}
+
main() {}
diff --git a/tests/lib_2/js/external_static_test.dart b/tests/lib_2/js/external_static_test.dart
index 3f4c60b7..8084a72 100644
--- a/tests/lib_2/js/external_static_test.dart
+++ b/tests/lib_2/js/external_static_test.dart
@@ -10,6 +10,7 @@
@JS()
library external_static_test;
+import 'dart:html';
import 'package:js/js.dart';
// external top level members ok in @JS() library.
@@ -69,32 +70,35 @@
extension ExtensionNonJS on NonJSClass {
external get getter;
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external set setter(_);
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external static get staticGetter;
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external static set staticSetter(_);
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external method();
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
external static staticMethod();
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
+ external optionalParameterMethod([int a, int b = 0]);
+ // ^
+ // [web] JS interop or Native class required for 'external' extension members.
external overridenMethod();
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
@JS('memberAnnotation')
external annotatedMethod();
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
nonExternalMethod() => 1;
static nonExternalStaticMethod() => 2;
@@ -107,7 +111,7 @@
extension ExtensionGenericNonJS<T> on GenericNonJSClass<T> {
external T method();
// ^
- // [web] JS interop class required for 'external' extension members.
+ // [web] JS interop or Native class required for 'external' extension members.
}
class GenericNonJSClass<T> {}
@@ -121,6 +125,7 @@
external method();
external static staticMethod();
+ external optionalParameterMethod([int a, int b = 0]);
@JS('memberAnnotation')
external annotatedMethod();
@@ -176,4 +181,19 @@
@JS()
class _privateJSClass {}
+extension ExtensionNative on HtmlElement {
+ external get getter;
+ external set setter(_);
+
+ external static get staticGetter;
+ external static set staticSetter(_);
+
+ external method();
+ external static staticMethod();
+ external optionalParameterMethod([int a, int b = 0]);
+
+ nonExternalMethod() => 1;
+ static nonExternalStaticMethod() => 2;
+}
+
main() {}