Skip to content

Commit b274a67

Browse files
authored
[generator] Add support for @RestrictTo (#1094)
Fixes: #1081 Context: dotnet/android-libraries#690 AndroidX has a [`@RestrictTo` annotation][0] that essentially means "this API is implemented as `public`, but we do not consider it to be supported API". (It can be considered as the equivalent of [C# `internal` visibility][1] or of [.NET preview features][2].) This means that Google reserves the right to change such types at any time -- and they have! -- which can in turn break our apps. Because we simply bound the API as `public`, it misled our users into believing that this is a stable API they can rely on. For example, consider dotnet/android-libraries#690, in which [`androidx.appcompat.appcomat-resources`][3] "broke" API between [version 1.5.1][4] and [version 1.6.0][5], because Google [decided to promote classes][6] which had been `@RestrictTo({RestrictTo.Scope.LIBRARY_GROUP_PREFIX})` in version 1.5.1 to become "real" `public` types, and in the process *renamed* those types, e.g. `DrawableWrapper` became `DrawableWrapperCompat`. This renaming broke some of our customers. Version 1.5.1: ![image](https://user-images.githubusercontent.com/179295/216440200-ff4f3bd3-1a86-419d-adff-9460976a6355.png) @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class DrawableWrapper extends Drawable … Version 1.6.0: ![image](https://user-images.githubusercontent.com/179295/216440273-917a74a0-a1b5-4bdc-bb56-8615be89f18e.png) public class DrawableWrapperCompat extends Drawable … The root problem is that we were binding `@RestrictTo` types which never should have been bound. Fixing this involves two steps: (1) detection, and (2) mitigation. Update `class-parse` so that the `@RestrictTo` annotation is now supported. If the `@RestrictTo` annotation is present, then a new `//*/@annotated-visibility` attribute within `api.xml` will contain the [`RestrictTo.Scope` values][7] <class name='DrawableWrapper' … annotated-visibility='LIBRARY_GROUP_PREFIX' /> Update `Xamarin.Android.Tools.ApiXmlAdjuster.dll` so that the new `annotated-visibility` is supported and "passed through". These changes allow `generator` to know that `@RestrictTo` was used and what it was applied to, which brings us to mitigation: We can't *not* continue binding these types; removing these types would be an API break. Instead, *for now*, we will add an `[Obsolete]` attribute to the affected API with a message describing the situation: [Obsolete ( "While this type is 'public', Google considers it internal API and reserves the right to modify or delete it in the future. Use at your own risk.", DiagnosticId = "XAOBS001")[] public partial class DrawableWrapper { } This uses a custom warning code XAOBS001, which allows users to use `$(NoWarn)` to ignore these warnings, if necessary. Additionally, this new "`[Obsolete]` on `@RestrictTo` types" behavior is *off by default*, and only enabled via the new `generator --lang-features=restrict-to-attributes` option. Note that only one `[Obsolete]` is allowed on each type/member, so if the API already has an `[Obsolete]` attribute, e.g. because it is `@Deprecated`, then the XAOBS001 obsolete message will be skipped. TODO: Enable `generator --lang-features=restrict-to-attributes` in .NET Android Binding Projects, with an MSBuild property to disable this option if necessary. TODO: *Eventually* we'll also need a way to *not* bind these types. [0]: https://developer.android.com/reference/androidx/annotation/RestrictTo [1]: https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/internal [2]: https://github.com/dotnet/designs/blob/7d0be161bfb55117543f2833b645e089b646f8ab/accepted/2021/preview-features/preview-features.md [3]: https://maven.google.com/web/index.html?q=androidx.appcompat#androidx.appcompat:appcompat-resources [4]: https://dl.google.com/android/maven2/androidx/appcompat/appcompat-resources/1.5.1/appcompat-resources-1.5.1.aar [5]: https://dl.google.com/android/maven2/androidx/appcompat/appcompat-resources/1.6.0/appcompat-resources-1.6.0.aar [6]: https://android-review.googlesource.com/c/platform/frameworks/support/+/2120177 [7]: https://developer.android.com/reference/androidx/annotation/RestrictTo.Scope
1 parent 554d819 commit b274a67

30 files changed

+250
-16
lines changed

src/Java.Interop.Tools.JavaTypeSystem/Adapters/JavaXmlApiExporter.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ static void SaveTypeParameters (JavaTypeParameters parameters, XmlWriter writer)
158158
}
159159

160160
static void SaveConstructor (JavaConstructorModel ctor, XmlWriter writer)
161-
=> SaveMember (ctor, writer, "constructor", null, null, null, null, null, ctor.DeclaringType.FullName, null, null, null, ctor.Parameters, ctor.IsBridge, null, ctor.IsSynthetic, null);
161+
=> SaveMember (ctor, writer, "constructor", null, null, null, null, null, ctor.DeclaringType.FullName, null, null, null, ctor.Parameters, ctor.IsBridge, null, ctor.IsSynthetic, null, ctor.AnnotatedVisibility);
162162

163163
static void SaveField (JavaFieldModel field, XmlWriter writer)
164164
{
@@ -177,7 +177,8 @@ static void SaveField (JavaFieldModel field, XmlWriter writer)
177177
null,
178178
null,
179179
null,
180-
field.IsNotNull);
180+
field.IsNotNull,
181+
field.AnnotatedVisibility);
181182
}
182183

183184
static void SaveMethod (JavaMethodModel method, XmlWriter writer)
@@ -228,7 +229,8 @@ bool check (JavaMethodModel _) => _.BaseMethod?.DeclaringType?.Visibility == "pu
228229
extBridge: method.IsBridge,
229230
jniReturn: method.ReturnJni,
230231
extSynthetic: method.IsSynthetic,
231-
notNull: method.ReturnNotNull);
232+
notNull: method.ReturnNotNull,
233+
annotatedVisibility: method.AnnotatedVisibility);
232234
}
233235

234236
static string GetVisibleReturnTypeString (JavaMethodModel method)
@@ -277,7 +279,7 @@ static void SaveMember (JavaMemberModel m, XmlWriter writer, string elementName,
277279
string? transient, string? type, string? typeGeneric,
278280
string? value, string? volat,
279281
IEnumerable<JavaParameterModel>? parameters,
280-
bool? extBridge, string? jniReturn, bool? extSynthetic, bool? notNull)
282+
bool? extBridge, string? jniReturn, bool? extSynthetic, bool? notNull, string? annotatedVisibility)
281283
{
282284
// If any of the parameters contain reference to non-public type, it cannot be generated.
283285
// TODO
@@ -310,6 +312,7 @@ static void SaveMember (JavaMemberModel m, XmlWriter writer, string elementName,
310312
writer.WriteAttributeStringIfValue ("type", type);
311313
writer.WriteAttributeStringIfValue ("type-generic-aware", typeGeneric);
312314
writer.WriteAttributeStringIfValue ("value", value);
315+
writer.WriteAttributeStringIfValue ("annotated-visibility", annotatedVisibility);
313316

314317
if (extSynthetic.HasValue)
315318
writer.WriteAttributeString ("synthetic", extSynthetic.Value ? "true" : "false");

src/Java.Interop.Tools.JavaTypeSystem/Adapters/JavaXmlApiImporter.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,8 @@ public static JavaMethodModel ParseMethod (JavaTypeModel type, XElement element)
197197
returnJni: element.XGetAttribute ("jni-return"),
198198
isNative: element.XGetAttributeAsBool ("native"),
199199
isSynchronized: element.XGetAttributeAsBool ("synchronized"),
200-
returnNotNull: element.XGetAttributeAsBool ("return-not-null")
200+
returnNotNull: element.XGetAttributeAsBool ("return-not-null"),
201+
annotatedVisibility: element.XGetAttributeOrNull ("annotated-visibility")
201202
);
202203

203204
if (element.Element ("typeParameters") is XElement tp)
@@ -226,7 +227,8 @@ public static JavaConstructorModel ParseConstructor (JavaTypeModel type, XElemen
226227
deprecated: element.XGetAttribute ("deprecated"),
227228
jniSignature: element.XGetAttribute ("jni-signature"),
228229
isSynthetic: element.XGetAttributeAsBool ("synthetic"),
229-
isBridge: element.XGetAttributeAsBool ("bridge")
230+
isBridge: element.XGetAttributeAsBool ("bridge"),
231+
annotatedVisibility: element.XGetAttributeOrNull ("annotated-visibility")
230232
);
231233

232234
// Yes, constructors in Java can have generic type parameters ¯\_(ツ)_/¯
@@ -262,7 +264,8 @@ public static JavaFieldModel ParseField (JavaTypeModel type, XElement element)
262264
jniSignature: element.XGetAttribute ("jni-signature"),
263265
isTransient: element.XGetAttributeAsBool ("transient"),
264266
isVolatile: element.XGetAttributeAsBool ("volatile"),
265-
isNotNull: element.XGetAttributeAsBool ("not-null")
267+
isNotNull: element.XGetAttributeAsBool ("not-null"),
268+
annotatedVisibility: element.XGetAttributeOrNull ("annotated-visibility")
266269
);
267270

268271
if (element.XGetAttribute ("merge.SourceFile") is string source && source.HasValue ())

src/Java.Interop.Tools.JavaTypeSystem/Adapters/ManagedApiImporter.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,8 @@ static bool ShouldImport (TypeDefinition td)
185185
returnJni: jni_signature.Return.Jni,
186186
isNative: false,
187187
isSynchronized: false,
188-
returnNotNull: false
188+
returnNotNull: false,
189+
annotatedVisibility: null
189190
);
190191

191192
for (var i = 0; i < jni_signature.Parameters.Count; i++)

src/Java.Interop.Tools.JavaTypeSystem/JavaModels/JavaConstructorModel.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ namespace Java.Interop.Tools.JavaTypeSystem.Models
44
{
55
public class JavaConstructorModel : JavaMethodModel
66
{
7-
public JavaConstructorModel (string javaName, string javaVisibility, bool javaStatic, JavaTypeModel javaDeclaringType, string deprecated, string jniSignature, bool isSynthetic, bool isBridge)
8-
: base (javaName, javaVisibility, false, false, javaStatic, "void", javaDeclaringType, deprecated, jniSignature, isSynthetic, isBridge, string.Empty, false, false, false)
7+
public JavaConstructorModel (string javaName, string javaVisibility, bool javaStatic, JavaTypeModel javaDeclaringType, string deprecated, string jniSignature, bool isSynthetic, bool isBridge, string? annotatedVisibility)
8+
: base (javaName, javaVisibility, false, false, javaStatic, "void", javaDeclaringType, deprecated, jniSignature, isSynthetic, isBridge, string.Empty, false, false, false, annotatedVisibility)
99
{
1010
}
1111

src/Java.Interop.Tools.JavaTypeSystem/JavaModels/JavaFieldModel.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ public class JavaFieldModel : JavaMemberModel
1515

1616
public JavaTypeReference? TypeModel { get; private set; }
1717

18-
public JavaFieldModel (string name, string visibility, string type, string typeGeneric, string? value, bool isStatic, JavaTypeModel declaringType, bool isFinal, string deprecated, string jniSignature, bool isTransient, bool isVolatile, bool isNotNull)
19-
: base (name, isStatic, isFinal, visibility, declaringType, deprecated, jniSignature)
18+
public JavaFieldModel (string name, string visibility, string type, string typeGeneric, string? value, bool isStatic, JavaTypeModel declaringType, bool isFinal, string deprecated, string jniSignature, bool isTransient, bool isVolatile, bool isNotNull, string? annotatedVisibility)
19+
: base (name, isStatic, isFinal, visibility, declaringType, deprecated, jniSignature, annotatedVisibility)
2020
{
2121
Type = type;
2222
TypeGeneric = typeGeneric;

src/Java.Interop.Tools.JavaTypeSystem/JavaModels/JavaMemberModel.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ public abstract class JavaMemberModel : IJavaResolvable
1212
public string Visibility { get; }
1313
public string Deprecated { get; }
1414
public string JniSignature { get; }
15+
public string? AnnotatedVisibility { get; }
1516

1617
public Dictionary<string, string> PropertyBag { get; } = new Dictionary<string, string> ();
1718

18-
public JavaMemberModel (string name, bool isStatic, bool isFinal, string visibility, JavaTypeModel declaringType, string deprecated, string jniSignature)
19+
public JavaMemberModel (string name, bool isStatic, bool isFinal, string visibility, JavaTypeModel declaringType, string deprecated, string jniSignature, string? annotatedVisibility)
1920
{
2021
Name = name;
2122
IsStatic = isStatic;
@@ -24,6 +25,7 @@ public JavaMemberModel (string name, bool isStatic, bool isFinal, string visibil
2425
DeclaringType = declaringType;
2526
Deprecated = deprecated;
2627
JniSignature = jniSignature;
28+
AnnotatedVisibility = annotatedVisibility;
2729
}
2830

2931
public abstract void Resolve (JavaTypeCollection types, ICollection<JavaUnresolvableModel> unresolvables);

src/Java.Interop.Tools.JavaTypeSystem/JavaModels/JavaMethodModel.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ public class JavaMethodModel : JavaMemberModel
2222
public List<JavaParameterModel> Parameters { get; } = new List<JavaParameterModel> ();
2323
public List<JavaExceptionModel> Exceptions { get; } = new List<JavaExceptionModel> ();
2424

25-
public JavaMethodModel (string javaName, string javaVisibility, bool javaAbstract, bool javaFinal, bool javaStatic, string javaReturn, JavaTypeModel javaDeclaringType, string deprecated, string jniSignature, bool isSynthetic, bool isBridge, string returnJni, bool isNative, bool isSynchronized, bool returnNotNull)
26-
: base (javaName, javaStatic, javaFinal, javaVisibility, javaDeclaringType, deprecated, jniSignature)
25+
public JavaMethodModel (string javaName, string javaVisibility, bool javaAbstract, bool javaFinal, bool javaStatic, string javaReturn, JavaTypeModel javaDeclaringType, string deprecated, string jniSignature, bool isSynthetic, bool isBridge, string returnJni, bool isNative, bool isSynchronized, bool returnNotNull, string? annotatedVisibility)
26+
: base (javaName, javaStatic, javaFinal, javaVisibility, javaDeclaringType, deprecated, jniSignature, annotatedVisibility)
2727
{
2828
IsAbstract = javaAbstract;
2929
Return = javaReturn;

src/Xamarin.Android.Tools.Bytecode/XmlClassDeclarationBuilder.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public XElement ToXElement ()
4040
GetSourceFile (),
4141
new XAttribute ("static", classFile.IsStatic),
4242
new XAttribute ("visibility", GetVisibility (classFile.Visibility)),
43+
GetAnnotatedVisibility (classFile.Attributes),
4344
GetTypeParmeters (signature == null ? null : signature.TypeParameters),
4445
GetImplementedInterfaces (),
4546
GetConstructors (),
@@ -346,6 +347,7 @@ XElement GetMethod (string element, string name, MethodInfo method, string? retu
346347
new XAttribute ("static", (method.AccessFlags & MethodAccessFlags.Static) != 0),
347348
GetSynchronized (method),
348349
new XAttribute ("visibility", GetVisibility (method.AccessFlags)),
350+
GetAnnotatedVisibility (method.Attributes),
349351
new XAttribute ("bridge", (method.AccessFlags & MethodAccessFlags.Bridge) != 0),
350352
new XAttribute ("synthetic", (method.AccessFlags & MethodAccessFlags.Synthetic) != 0),
351353
new XAttribute ("jni-signature", method.Descriptor),
@@ -427,6 +429,27 @@ IEnumerable<XElement> GetExceptions (MethodInfo method)
427429
}
428430
}
429431

432+
static XAttribute? GetAnnotatedVisibility (AttributeCollection attributes)
433+
{
434+
var annotations = attributes?.OfType<RuntimeInvisibleAnnotationsAttribute> ().FirstOrDefault ()?.Annotations;
435+
436+
if (annotations?.FirstOrDefault (a => a.Type == "Landroidx/annotation/RestrictTo;") is Annotation annotation) {
437+
var annotation_element_values = (annotation.Values.FirstOrDefault ().Value as AnnotationElementArray)?.Values?.OfType<AnnotationElementEnum> ();
438+
439+
if (annotation_element_values is null || !annotation_element_values.Any ())
440+
return null;
441+
442+
var value_string = string.Join (" ", annotation_element_values.Select (v => v.ConstantName).Where (p => p != null));
443+
444+
if (string.IsNullOrWhiteSpace (value_string))
445+
return null;
446+
447+
return new XAttribute ("annotated-visibility", value_string);
448+
}
449+
450+
return null;
451+
}
452+
430453
static XAttribute? GetNotNull (MethodInfo method)
431454
{
432455
var annotations = method.Attributes?.OfType<RuntimeInvisibleAnnotationsAttribute> ().FirstOrDefault ()?.Annotations;
@@ -500,6 +523,7 @@ IEnumerable<XElement> GetFields ()
500523
GetNotNull (field),
501524
GetValue (field),
502525
new XAttribute ("visibility", visibility),
526+
GetAnnotatedVisibility (field.Attributes),
503527
new XAttribute ("volatile", (field.AccessFlags & FieldAccessFlags.Volatile) != 0));
504528
}
505529
}

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

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,144 @@ public void ObsoleteEntireProperty ()
892892
Assert.False (StripRegisterAttributes (writer.ToString ()).NormalizeLineEndings ().Contains ("[global::System.Obsolete(@\"deprecated\")]set"), writer.ToString ());
893893
}
894894

895+
[Test]
896+
public void RestrictToType ()
897+
{
898+
options.UseRestrictToAttributes = true;
899+
900+
var xml = @"<api>
901+
<package name='java.lang' jni-name='java/lang'>
902+
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
903+
</package>
904+
<package name='com.xamarin.android' jni-name='com/xamarin/android'>
905+
<class abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' jni-extends='Ljava/lang/Object;' final='false' name='MyClass' static='false' visibility='public' jni-signature='Lcom/xamarin/android/MyClass;' annotated-visibility='LIBRARY_GROUP_PREFIX' />
906+
</package>
907+
</api>";
908+
909+
var gens = ParseApiDefinition (xml);
910+
var iface = gens.Single (g => g.Name == "MyClass");
911+
912+
generator.Context.ContextTypes.Push (iface);
913+
generator.WriteType (iface, string.Empty, new GenerationInfo ("", "", "MyAssembly"));
914+
generator.Context.ContextTypes.Pop ();
915+
916+
// This should use a special [Obsolete] describing the "internal" nature of this API
917+
Assert.True (writer.ToString ().NormalizeLineEndings ().Contains ("[global::System.Obsolete (\"While this type is 'public', Google considers it internal API and reserves the right to modify or delete it in the future. Use at your own risk.\", DiagnosticId = \"XAOBS001\")]".NormalizeLineEndings ()), writer.ToString ());
918+
}
919+
920+
[Test]
921+
public void RestrictToField ()
922+
{
923+
options.UseRestrictToAttributes = true;
924+
925+
var xml = @"<api>
926+
<package name='java.lang' jni-name='java/lang'>
927+
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
928+
</package>
929+
<package name='com.xamarin.android' jni-name='com/xamarin/android'>
930+
<class abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' jni-extends='Ljava/lang/Object;' final='false' name='MyClass' static='false' visibility='public' jni-signature='Lcom/xamarin/android/MyClass;'>
931+
<field deprecated='not deprecated' name='ACCEPT_HANDOVER' jni-signature='Ljava/lang/String;' transient='false' type='java.lang.String' type-generic-aware='java.lang.String' visibility='public' volatile='false' annotated-visibility='LIBRARY_GROUP_PREFIX'></field>
932+
</class>
933+
</package>
934+
</api>";
935+
936+
var gens = ParseApiDefinition (xml);
937+
var iface = gens.Single (g => g.Name == "MyClass");
938+
939+
generator.Context.ContextTypes.Push (iface);
940+
generator.WriteType (iface, string.Empty, new GenerationInfo ("", "", "MyAssembly"));
941+
generator.Context.ContextTypes.Pop ();
942+
943+
// This should use a special [Obsolete] describing the "internal" nature of this API
944+
Assert.True (writer.ToString ().NormalizeLineEndings ().Contains ("[global::System.Obsolete (\"While this member is 'public', Google considers it internal API and reserves the right to modify or delete it in the future. Use at your own risk.\", DiagnosticId = \"XAOBS001\")]".NormalizeLineEndings ()), writer.ToString ());
945+
}
946+
947+
[Test]
948+
public void RestrictToMethod ()
949+
{
950+
options.UseRestrictToAttributes = true;
951+
952+
var xml = @"<api>
953+
<package name='java.lang' jni-name='java/lang'>
954+
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
955+
</package>
956+
<package name='com.xamarin.android' jni-name='com/xamarin/android'>
957+
<class abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' jni-extends='Ljava/lang/Object;' final='false' name='MyClass' static='false' visibility='public' jni-signature='Lcom/xamarin/android/MyClass;'>
958+
<method abstract='false' final='false' name='countAffectedRows' jni-signature='()I' bridge='false' native='false' return='int' jni-return='I' static='false' synchronized='false' synthetic='false' visibility='public' annotated-visibility='LIBRARY_GROUP_PREFIX'></method>
959+
</class>
960+
</package>
961+
</api>";
962+
963+
var gens = ParseApiDefinition (xml);
964+
var iface = gens.Single (g => g.Name == "MyClass");
965+
966+
generator.Context.ContextTypes.Push (iface);
967+
generator.WriteType (iface, string.Empty, new GenerationInfo ("", "", "MyAssembly"));
968+
generator.Context.ContextTypes.Pop ();
969+
970+
// This should use a special [Obsolete] describing the "internal" nature of this API
971+
Assert.True (writer.ToString ().NormalizeLineEndings ().Contains ("[global::System.Obsolete (\"While this member is 'public', Google considers it internal API and reserves the right to modify or delete it in the future. Use at your own risk.\", DiagnosticId = \"XAOBS001\")]".NormalizeLineEndings ()), writer.ToString ());
972+
}
973+
974+
[Test]
975+
public void RestrictToProperty ()
976+
{
977+
options.UseRestrictToAttributes = true;
978+
979+
var xml = @"<api>
980+
<package name='java.lang' jni-name='java/lang'>
981+
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
982+
</package>
983+
<package name='com.xamarin.android' jni-name='com/xamarin/android'>
984+
<class abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' jni-extends='Ljava/lang/Object;' final='false' name='MyClass' static='false' visibility='public' jni-signature='Lcom/xamarin/android/MyClass;'>
985+
<method abstract='false' deprecated='not deprecated' final='false' name='getCount' jni-signature='()I' bridge='false' native='false' return='int' jni-return='I' static='false' synchronized='false' synthetic='false' visibility='public' annotated-visibility='LIBRARY_GROUP_PREFIX'></method>
986+
<method abstract='false' deprecated='not deprecated' final='false' name='setCount' jni-signature='(I)V' bridge='false' native='false' return='void' jni-return='V' static='false' synchronized='false' synthetic='false' visibility='public' annotated-visibility='LIBRARY_GROUP_PREFIX'>
987+
<parameter name='count' type='int' jni-type='I'></parameter>
988+
</method>
989+
</class>
990+
</package>
991+
</api>";
992+
993+
var gens = ParseApiDefinition (xml);
994+
var iface = gens.Single (g => g.Name == "MyClass");
995+
996+
generator.Context.ContextTypes.Push (iface);
997+
generator.WriteType (iface, string.Empty, new GenerationInfo ("", "", "MyAssembly"));
998+
generator.Context.ContextTypes.Pop ();
999+
1000+
// This should use a special [Obsolete] describing the "internal" nature of this API
1001+
Assert.True (writer.ToString ().NormalizeLineEndings ().Contains ("[global::System.Obsolete (\"While this member is 'public', Google considers it internal API and reserves the right to modify or delete it in the future. Use at your own risk.\", DiagnosticId = \"XAOBS001\")]".NormalizeLineEndings ()), writer.ToString ());
1002+
}
1003+
1004+
[Test]
1005+
public void DoNotWriteObsoleteAndRestrictTo ()
1006+
{
1007+
options.UseRestrictToAttributes = true;
1008+
1009+
var xml = @"<api>
1010+
<package name='java.lang' jni-name='java/lang'>
1011+
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
1012+
</package>
1013+
<package name='com.xamarin.android' jni-name='com/xamarin/android'>
1014+
<class abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' jni-extends='Ljava/lang/Object;' final='false' name='MyClass' static='false' visibility='public' jni-signature='Lcom/xamarin/android/MyClass;'>
1015+
<method deprecated='deprecated' abstract='false' final='false' name='countAffectedRows' jni-signature='()I' bridge='false' native='false' return='int' jni-return='I' static='false' synchronized='false' synthetic='false' visibility='public' annotated-visibility='LIBRARY_GROUP_PREFIX'></method>
1016+
</class>
1017+
</package>
1018+
</api>";
1019+
1020+
var gens = ParseApiDefinition (xml);
1021+
var iface = gens.Single (g => g.Name == "MyClass");
1022+
1023+
generator.Context.ContextTypes.Push (iface);
1024+
generator.WriteType (iface, string.Empty, new GenerationInfo ("", "", "MyAssembly"));
1025+
generator.Context.ContextTypes.Pop ();
1026+
1027+
// This method is both @Deprecated and @RestrictTo. We cannot write 2 [Obsolete] attributes, so
1028+
// only write the deprecated one.
1029+
Assert.True (writer.ToString ().Replace (" (@\"deprecated\")", "").NormalizeLineEndings ().Contains ("[global::System.Obsolete]".NormalizeLineEndings ()), writer.ToString ());
1030+
Assert.False (writer.ToString ().NormalizeLineEndings ().Contains ("[global::System.Obsolete (\"While this member is 'public', Google considers it internal API and reserves the right to modify or delete it in the future. Use at your own risk.\", DiagnosticId = \"XAOBS001\")]".NormalizeLineEndings ()), writer.ToString ());
1031+
}
1032+
8951033
[Test]
8961034
[NonParallelizable] // We are setting a static property on Report
8971035
public void WarnIfTypeNameMatchesNamespace ()

0 commit comments

Comments
 (0)