Skip to content

Commit 28d6905

Browse files
authored
[generator] Don't mark a method as [UnsupportedOSPlatform] if it overrides a supported base method. (#1313)
Context: 1cfb4f4 @jonathanpeppers [noted][0]: > MAUI noticed this API says `UnsupportedOSPlatform`: > > // Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065 > // Android.OS.Bundle > [UnsupportedOSPlatform("android21.0")] > [Register("putString", "(Ljava/lang/String;Ljava/lang/String;)V", "")] > public override void PutString(string? key, string? value) { ... } In [API-21][1], `android.os.Bundle.putString()` was *moved* to the new base class [`android.os.BaseBundle`][2]. <class … name="BaseBundle" … merge.SourceFile="..\..\bin\BuildDebug\api\api-21.xml.in"> <method … name="putString" jni-signature="(Ljava/lang/String;Ljava/lang/String;)V" …> <parameter name="key" type="java.lang.String" jni-type="Ljava/lang/String;"></parameter> <parameter name="value" type="java.lang.String" jni-type="Ljava/lang/String;"></parameter> </method> </class> … <class … extends="android.os.BaseBundle" … name="Bundle" …> <method … name="putString" jni-signature="(Ljava/lang/String;Ljava/lang/String;)V" … removed-since="21"> <parameter name="key" type="java.lang.String" jni-type="Ljava/lang/String;"></parameter> <parameter name="value" type="java.lang.String" jni-type="Ljava/lang/String;"></parameter> </method> </class> This movement was detected as an API *removal* by `api-merge`, causing `generator` to emit `[UnsupportedOSPlatform]`: partial class Bundle { [UnsupportedOSPlatform("android21.0")] [Register("putString", "(Ljava/lang/String;Ljava/lang/String;)V", "")] public override void PutString(string? key, string? value) => … } While technically correct that `android.os.Bundle.putString()` was "removed" in API-21, it was really moved to a base class as `android.os.BaseBundle.putString()`. We will continue to generate both methods for binary compatibility, but we should not mark the overriding method as `[UnsupportedOSPlatform]` because the "supported" base method is still present. Fix `generator` to recognize this case and not emit `[UnsupportedOSPlatform]` here. With this change, `Android.OS.Bundle.cs` contains: partial class Bundle { // Metadata.xml XPath method reference: path="/api/package[@name='android.os']/class[@name='Bundle']/method[@name='putString' and count(parameter)=2 and parameter[1][@type='java.lang.String'] and parameter[2][@type='java.lang.String']]" [Register ("putString", "(Ljava/lang/String;Ljava/lang/String;)V", "")] public override unsafe void PutString (string? key, string? value) { ... } } [0]: https://discord.com/channels/732297728826277939/732297837953679412/1342558634390982666 [1]: https://developer.android.com/sdk/api_diff/21/changes [2]: https://developer.android.com/reference/android/os/BaseBundle
1 parent 9dea87d commit 28d6905

File tree

2 files changed

+38
-0
lines changed

2 files changed

+38
-0
lines changed

tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs

+33
Original file line numberDiff line numberDiff line change
@@ -1436,6 +1436,39 @@ public void UnsupportedOSPlatformConstFields ()
14361436
StringAssert.Contains ("[global::System.Runtime.Versioning.UnsupportedOSPlatformAttribute (\"android30.0\")]", builder.ToString (), "Should contain UnsupportedOSPlatform!");
14371437
}
14381438

1439+
1440+
[Test]
1441+
public void UnsupportedOSPlatformIgnoresMethodOverrides ()
1442+
{
1443+
// Given:
1444+
// public class TextView {
1445+
// public Object doThing () { ... }
1446+
// }
1447+
// public class TextView2 : TextView {
1448+
// public Object doThing () { ... } // removed-since = 30
1449+
// }
1450+
// We should not write [UnsupportedOSPlatform] on TextView2.doThing (), because the base method isn't "removed".
1451+
var xml = @$"<api>
1452+
<package name='java.lang' jni-name='java/lang'>
1453+
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
1454+
</package>
1455+
<package name='android.widget' jni-name='android/widget'>
1456+
<class abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' final='false' name='TextView' static='false' visibility='public'>
1457+
<method abstract='false' deprecated='not deprecated' final='false' name='doThing' bridge='false' native='false' return='java.lang.Object' static='false' synchronized='false' synthetic='false' visibility='public' />
1458+
</class>
1459+
<class abstract='false' deprecated='not deprecated' extends='android.widget.TextView' extends-generic-aware='java.lang.Object' final='false' name='TextView2' static='false' visibility='public'>
1460+
<method abstract='false' deprecated='not deprecated' final='false' name='doThing' bridge='false' native='false' return='java.lang.Object' static='false' synchronized='false' synthetic='false' visibility='public' removed-since='30' />
1461+
</class>
1462+
</package>
1463+
</api>";
1464+
1465+
var gens = ParseApiDefinition (xml);
1466+
var klass = gens.Single (g => g.Name == "TextView2");
1467+
var actual = GetGeneratedTypeOutput (klass);
1468+
1469+
StringAssert.DoesNotContain ("[global::System.Runtime.Versioning.UnsupportedOSPlatformAttribute (\"android30.0\")]", actual, "Should contain UnsupportedOSPlatform!");
1470+
}
1471+
14391472
[Test]
14401473
public void StringPropertyOverride ([Values ("true", "false")] string final)
14411474
{

tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBase.cs

+5
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,11 @@ public void FixupMethodOverrides (CodeGenerationOptions opt)
323323
m.DeprecatedSince = bm.DeprecatedSince;
324324
}
325325

326+
// If a "removed" method overrides a "not removed" method, the method was
327+
// likely moved to a base class, so don't mark it as removed.
328+
if (m.ApiRemovedSince > 0 && bm.ApiRemovedSince == 0)
329+
m.ApiRemovedSince = 0;
330+
326331
break;
327332
}
328333
}

0 commit comments

Comments
 (0)