Skip to content

Commit dceda33

Browse files
jpobstjonpryor
authored andcommitted
[generator] Fix CS0115 error "overriding" default interface method (#489)
Given this Java hierarchy: // Java public interface DefaultMethodsInterface { default int getBar () { return 2; } } public class EmptyOverrideClass implements DefaultMethodsInterface { } public class ImplementedChainOverrideClass extends EmptyOverrideClass { @OverRide public int getBar () { return 100; } } We were improperly binding `ImplementedChainOverrideClass.Bar` as an `override`: // C# binding public partial interface IDefaultMethodsInterface : IJavaPeerable { virtual unsafe int Bar { [Register ("getBar", "()I", "...")] get { /* ... */} } } public partial class EmptyOverrideClass : Java.Lang.Object, IDefaultMethodsInterface { } public partial class ImplementedChainOverrideClass : EmptyOverrideClass { public override unsafe int Bar { /* ... */ } } This results in a CS0115: CS0115: 'ImplementedChainOverrideClass.Bar': no suitable method found to override C# doesn't allow using the `override` keyword with default interface methods; see also [dotnet/csharplang@2757][0]. We need to instead emit `virtual` in this context: public partial class ImplementedChainOverrideClass : EmptyOverrideClass { public virtual unsafe int Bar { /* ... */ } } [0]: dotnet/csharplang#2757
1 parent c62db34 commit dceda33

File tree

3 files changed

+73
-2
lines changed

3 files changed

+73
-2
lines changed

tools/generator/Java.Interop.Tools.Generator.CodeGeneration/CodeGenerator.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1470,8 +1470,13 @@ public void WriteProperty (Property property, GenBase gen, string indent, bool w
14701470
if ((property.Getter ?? property.Setter).IsStatic)
14711471
virtual_override = " static";
14721472
// It should be using AdjustedName instead of Name, but ICharSequence ("Formatted") properties are not caught by this...
1473-
else if (gen.BaseSymbol != null && gen.BaseSymbol.GetPropertyByName (property.Name, true) != null)
1474-
virtual_override = " override";
1473+
else if (gen.BaseSymbol != null) {
1474+
var base_prop = gen.BaseSymbol.GetPropertyByName (property.Name, true);
1475+
1476+
// If the matching base getter we found is a DIM, we do not override it, it should stay virtual
1477+
if (base_prop != null && !base_prop.Getter.IsInterfaceDefaultMethod)
1478+
virtual_override = " override";
1479+
}
14751480

14761481
WriteMethodIdField (property.Getter, indent);
14771482
if (property.Setter != null)

tools/generator/Tests/Unit-Tests/CodeGeneratorTestBase.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.IO;
4+
using System.Linq;
35
using System.Reflection;
46
using System.Text;
7+
using System.Xml.Linq;
58
using MonoDroid.Generation;
69
using NUnit.Framework;
710

@@ -55,5 +58,25 @@ protected string GetTargetedExpected (string testName)
5558

5659
return File.ReadAllText (Path.Combine (root, "Unit-Tests", "CodeGeneratorExpectedResults", target, $"{testName}.txt")).NormalizeLineEndings ();
5760
}
61+
62+
protected List<GenBase> ParseApiDefinition (string xml)
63+
{
64+
var doc = XDocument.Parse (xml);
65+
var gens = new Parser (options).Parse (doc, Enumerable.Empty<string> (), "29", 7);
66+
67+
foreach (var gen in gens)
68+
options.SymbolTable.AddType (gen);
69+
70+
foreach (var gen in gens)
71+
gen.Validate (options, new GenericParameterDefinitionList (), generator.Context);
72+
73+
foreach (var gen in gens)
74+
gen.FillProperties ();
75+
76+
foreach (var gen in gens)
77+
gen.FixupMethodOverrides (options);
78+
79+
return gens;
80+
}
5881
}
5982
}

tools/generator/Tests/Unit-Tests/DefaultInterfaceMethodsTests.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Linq;
23
using MonoDroid.Generation;
34
using NUnit.Framework;
45
using Xamarin.Android.Binder;
@@ -149,6 +150,48 @@ public void WriteSealedOverriddenDefaultMethod ()
149150
Assert.False (writer.ToString ().Contains ("virtual sealed"));
150151
}
151152

153+
[Test]
154+
public void WriteInterfaceRedeclaredChainDefaultMethod ()
155+
{
156+
// Fix a case where a property declared in this hierarchy was generated as "override" instead of "virtual"
157+
// public interface MyInterface { default int getValue () { return 0; } }
158+
// public class MyClass implements MyInterface { }
159+
// public class MySecondClass extends MyClass { @Override public int getValue () { return 1; } }
160+
var gens = ParseApiDefinition (@"<api>
161+
<package name='java.lang' jni-name='java/lang'>
162+
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/EmptyOverrideClass;' />
163+
</package>
164+
<package name='com.xamarin.android' jni-name='com/xamarin/android'>
165+
<interface abstract='true' deprecated='not deprecated' final='false' name='DefaultMethodsInterface' static='false' visibility='public' jni-signature='Lcom/xamarin/android/DefaultMethodsInterface;'>
166+
<method abstract='false' deprecated='not deprecated' final='false' name='foo' jni-signature='()I' bridge='false' native='false' return='int' jni-return='I' static='false' synchronized='false' synthetic='false' visibility='public'></method>
167+
<method abstract='false' deprecated='not deprecated' final='false' name='getBar' jni-signature='()I' bridge='false' native='false' return='int' jni-return='I' static='false' synchronized='false' synthetic='false' visibility='public'></method>
168+
<method abstract='false' deprecated='not deprecated' final='false' name='setBar' jni-signature='(I)V' bridge='false' native='false' return='void' jni-return='V' static='false' synchronized='false' synthetic='false' visibility='public'>
169+
<parameter name='p0' type='int' jni-type='I'></parameter>
170+
</method>
171+
</interface>
172+
<class abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' jni-extends='Ljava/lang/Object;' final='false' name='EmptyOverrideClass' static='false' visibility='public' jni-signature='Lcom/xamarin/android/EmptyOverrideClass;'>
173+
<implements name='com.xamarin.android.DefaultMethodsInterface' name-generic-aware='com.xamarin.android.DefaultMethodsInterface' jni-type='Lcom/xamarin/android/DefaultMethodsInterface;'></implements>
174+
<constructor deprecated='not deprecated' final='false' name='EmptyOverrideClass' jni-signature='()V' bridge='false' static='false' type='com.xamarin.android.EmptyOverrideClass' synthetic='false' visibility='public'></constructor>
175+
</class>
176+
<class abstract='false' deprecated='not deprecated' extends='com.xamarin.android.EmptyOverrideClass' extends-generic-aware='com.xamarin.android.EmptyOverrideClass' jni-extends='Lcom/xamarin/android/EmptyOverrideClass;' final='false' name='ImplementedChainOverrideClass' static='false' visibility='public' jni-signature='Lcom/xamarin/android/ImplementedChainOverrideClass;'>
177+
<constructor deprecated='not deprecated' final='false' name='ImplementedChainOverrideClass' jni-signature='()V' bridge='false' static='false' type='com.xamarin.android.ImplementedChainOverrideClass' synthetic='false' visibility='public'></constructor>
178+
<method abstract='false' deprecated='not deprecated' final='false' name='getBar' jni-signature='()I' bridge='false' native='false' return='int' jni-return='I' static='false' synchronized='false' synthetic='false' visibility='public'></method>
179+
<method abstract='false' deprecated='not deprecated' final='false' name='setBar' jni-signature='(I)V' bridge='false' native='false' return='void' jni-return='V' static='false' synchronized='false' synthetic='false' visibility='public'>
180+
<parameter name='p0' type='int' jni-type='I'></parameter>
181+
</method>
182+
</class>
183+
</package>
184+
</api>");
185+
186+
var klass = (ClassGen)gens.First (g => g.Name == "ImplementedChainOverrideClass");
187+
188+
generator.Context.ContextTypes.Push (klass);
189+
generator.WriteClass (klass, string.Empty, new GenerationInfo (string.Empty, string.Empty, "MyAssembly"));
190+
generator.Context.ContextTypes.Pop ();
191+
192+
Assert.True (writer.ToString ().Contains ("public virtual unsafe int Bar"));
193+
}
194+
152195
[Test]
153196
public void WriteStaticInterfaceMethod ()
154197
{

0 commit comments

Comments
 (0)