Skip to content

Commit c5e1b92

Browse files
[generator] Fix UnsupportedOSPlatform for property setters when base has getter-only
Context: dotnet/android#10510 (comment) When a derived class has a property setter with `ApiRemovedSince`, but the base class only has a getter (no setter), clear the setter's `ApiRemovedSince` if the base getter is not removed. Also handle standalone `setXxx` methods that correspond to base class properties. Fixes `CA1416` warning for `ListView.Adapter.set` being incorrectly marked as unsupported on `android15.0`.
1 parent faee0da commit c5e1b92

File tree

2 files changed

+60
-0
lines changed

2 files changed

+60
-0
lines changed

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1563,6 +1563,43 @@ public void UnsupportedOSPlatformIgnoresPropertyOverrides ()
15631563
StringAssert.DoesNotContain ("[global::System.Runtime.Versioning.UnsupportedOSPlatformAttribute (\"android30.0\")]", ifaceActual, "Should not contain UnsupportedOSPlatform on interface property override!");
15641564
}
15651565

1566+
[Test]
1567+
public void UnsupportedOSPlatformIgnoresPropertySetterOverridesWhenBaseHasGetterOnly ()
1568+
{
1569+
// Given:
1570+
// public class AdapterView {
1571+
// public Object getAdapter () { ... }
1572+
// }
1573+
// public class ListView : AdapterView {
1574+
// public Object getAdapter () { ... } // removed-since = 15
1575+
// public void setAdapter (Object value) { ... } // removed-since = 15
1576+
// }
1577+
// We should not write [UnsupportedOSPlatform] on ListView.Adapter.set because the base property (via getter) isn't "removed".
1578+
var xml = @$"<api>
1579+
<package name='java.lang' jni-name='java/lang'>
1580+
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
1581+
</package>
1582+
<package name='android.widget' jni-name='android/widget'>
1583+
<class abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' final='false' name='AdapterView' static='false' visibility='public'>
1584+
<method abstract='false' deprecated='not deprecated' final='false' name='getAdapter' bridge='false' native='false' return='java.lang.Object' static='false' synchronized='false' synthetic='false' visibility='public' />
1585+
</class>
1586+
<class abstract='false' deprecated='not deprecated' extends='android.widget.AdapterView' extends-generic-aware='android.widget.AdapterView' final='false' name='ListView' static='false' visibility='public'>
1587+
<method abstract='false' deprecated='not deprecated' final='false' name='getAdapter' bridge='false' native='false' return='java.lang.Object' static='false' synchronized='false' synthetic='false' visibility='public' removed-since='15' />
1588+
<method abstract='false' deprecated='not deprecated' final='false' name='setAdapter' bridge='false' native='false' return='void' static='false' synchronized='false' synthetic='false' visibility='public' removed-since='15'>
1589+
<parameter name='value' type='java.lang.Object' />
1590+
</method>
1591+
</class>
1592+
</package>
1593+
</api>";
1594+
1595+
var gens = ParseApiDefinition (xml);
1596+
var klass = gens.Single (g => g.Name == "ListView");
1597+
var actual = GetGeneratedTypeOutput (klass);
1598+
1599+
// Neither the getter nor the setter should have [UnsupportedOSPlatform] because the base property (getter) isn't removed
1600+
StringAssert.DoesNotContain ("[global::System.Runtime.Versioning.UnsupportedOSPlatformAttribute (\"android15.0\")]", actual, "Should not contain UnsupportedOSPlatform on property setter when base has getter only!");
1601+
}
1602+
15661603
[Test]
15671604
public void StringPropertyOverride ([Values ("true", "false")] string final)
15681605
{

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,13 +366,32 @@ public void FixupMethodOverrides (CodeGenerationOptions opt)
366366
prop.Setter.ApiRemovedSince = default;
367367
shouldBreak = true;
368368
}
369+
} else if (prop.Setter != null && prop.Setter.ApiRemovedSince > 0 && baseProp.Setter == null && baseProp.Getter != null && baseProp.Getter.ApiRemovedSince == 0) {
370+
// Base has getter-only property; setter in derived should not be marked removed
371+
prop.Setter.ApiRemovedSince = default;
372+
shouldBreak = true;
369373
}
370374
if (shouldBreak) {
371375
break;
372376
}
373377
}
374378
}
375379

380+
// Process standalone setter methods (setXxx) that correspond to base class properties.
381+
// If the base property getter isn't removed, the setter shouldn't be either.
382+
foreach (var m in Methods.Where (m => !m.IsStatic && !m.IsInterfaceDefaultMethod && m.ApiRemovedSince > 0)) {
383+
if (!m.JavaName.StartsWith ("set", StringComparison.Ordinal) || m.Parameters.Count != 1 || m.RetVal.JavaName != "void")
384+
continue;
385+
var propertyName = m.JavaName.Substring (3);
386+
for (var bt = GetBaseGen (opt); bt != null; bt = bt.GetBaseGen (opt)) {
387+
var baseProp = bt.Properties.FirstOrDefault (p => p.Getter?.JavaName == "get" + propertyName);
388+
if (baseProp?.Getter != null && baseProp.Getter.ApiRemovedSince == 0) {
389+
m.ApiRemovedSince = default;
390+
break;
391+
}
392+
}
393+
}
394+
376395
// Process interface inheritance for both regular and default interface methods
377396
if (this is InterfaceGen currentInterface) {
378397
// For interfaces, check all base interfaces (interfaces that this interface implements/extends)
@@ -419,6 +438,10 @@ public void FixupMethodOverrides (CodeGenerationOptions opt)
419438
prop.Setter.ApiRemovedSince = default;
420439
shouldBreak = true;
421440
}
441+
} else if (prop.Setter != null && prop.Setter.ApiRemovedSince > 0 && baseProp.Setter == null && baseProp.Getter != null && baseProp.Getter.ApiRemovedSince == 0) {
442+
// Base has getter-only property; setter in derived should not be marked removed
443+
prop.Setter.ApiRemovedSince = default;
444+
shouldBreak = true;
422445
}
423446
if (shouldBreak) {
424447
break;

0 commit comments

Comments
 (0)