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 2ccb2992d06..01dd119547b 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 @@ const MessageCode messageJsInteropEnclosingClassJSAnnotationContext = message: r"""This is the enclosing class."""); // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. -const Code codeJsInteropExternalExtensionMemberNotOnJSClass = - messageJsInteropExternalExtensionMemberNotOnJSClass; +const Code 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 756793097bc..975fca74f55 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 @@ import 'package:_fe_analyzer_shared/src/messages/codes.dart' messageJsInteropAnonymousFactoryPositionalParameters, messageJsInteropEnclosingClassJSAnnotation, messageJsInteropEnclosingClassJSAnnotationContext, - messageJsInteropExternalExtensionMemberNotOnJSClass, + messageJsInteropExternalExtensionMemberOnTypeInvalid, messageJsInteropExternalMemberNotJSAnnotated, messageJsInteropIndexNotSupported, messageJsInteropNamedParameters, @@ -282,13 +282,14 @@ class JsInteropChecks extends RecursiveVisitor { /// [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 @@ class JsInteropChecks extends RecursiveVisitor { /// 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 @@ class JsInteropChecks extends RecursiveVisitor { } 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 ba2bc5527d7..8eb1f8df4e1 100644 --- a/pkg/front_end/messages.status +++ b/pkg/front_end/messages.status @@ -514,8 +514,8 @@ JsInteropDartClassExtendsJSClass/analyzerCode: Fail # Web compiler specific 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 0873cbade0f..e94eac1041e 100644 --- a/pkg/front_end/messages.yaml +++ b/pkg/front_end/messages.yaml @@ -4914,8 +4914,8 @@ JsInteropEnclosingClassJSAnnotationContext: 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 913d20278f0..87c46150619 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 @@ class AnonymousClass { 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 @@ class NonJSClass { extension ExtensionGenericNonJS on GenericNonJSClass { 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 {} +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 0a1c8dbfc07..a455de087ff 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 @@ class AnonymousClass { 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 @@ class NonJSClass { extension ExtensionGenericNonJS on GenericNonJSClass { 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 {} @@ -229,4 +230,24 @@ extension ExtensionPrivateJS on _privateJSClass { @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 1de7e61e2ba..d0fa7dfb674 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 @@ class AnonymousClass { 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 @@ class NonJSClass { extension ExtensionGenericNonJS on GenericNonJSClass { 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 {} +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 3f4c60b73e8..8084a7219db 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 @@ class AnonymousClass { 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 @@ class NonJSClass { extension ExtensionGenericNonJS on GenericNonJSClass { 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 {} @@ -121,6 +125,7 @@ extension ExtensionJS on JSClass { external method(); external static staticMethod(); + external optionalParameterMethod([int a, int b = 0]); @JS('memberAnnotation') external annotatedMethod(); @@ -176,4 +181,19 @@ extension ExtensionPrivateJS on _privateJSClass { @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() {}