Skip to content

Commit

Permalink
[generator] Add support for @RestrictTo (#1094)
Browse files Browse the repository at this point in the history
Fixes: #1081

Context: xamarin/AndroidX#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 xamarin/AndroidX#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
  • Loading branch information
jpobst authored Apr 20, 2023
1 parent 554d819 commit b274a67
Show file tree
Hide file tree
Showing 30 changed files with 250 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ static void SaveTypeParameters (JavaTypeParameters parameters, XmlWriter writer)
}

static void SaveConstructor (JavaConstructorModel ctor, XmlWriter writer)
=> SaveMember (ctor, writer, "constructor", null, null, null, null, null, ctor.DeclaringType.FullName, null, null, null, ctor.Parameters, ctor.IsBridge, null, ctor.IsSynthetic, null);
=> 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);

static void SaveField (JavaFieldModel field, XmlWriter writer)
{
Expand All @@ -177,7 +177,8 @@ static void SaveField (JavaFieldModel field, XmlWriter writer)
null,
null,
null,
field.IsNotNull);
field.IsNotNull,
field.AnnotatedVisibility);
}

static void SaveMethod (JavaMethodModel method, XmlWriter writer)
Expand Down Expand Up @@ -228,7 +229,8 @@ bool check (JavaMethodModel _) => _.BaseMethod?.DeclaringType?.Visibility == "pu
extBridge: method.IsBridge,
jniReturn: method.ReturnJni,
extSynthetic: method.IsSynthetic,
notNull: method.ReturnNotNull);
notNull: method.ReturnNotNull,
annotatedVisibility: method.AnnotatedVisibility);
}

static string GetVisibleReturnTypeString (JavaMethodModel method)
Expand Down Expand Up @@ -277,7 +279,7 @@ static void SaveMember (JavaMemberModel m, XmlWriter writer, string elementName,
string? transient, string? type, string? typeGeneric,
string? value, string? volat,
IEnumerable<JavaParameterModel>? parameters,
bool? extBridge, string? jniReturn, bool? extSynthetic, bool? notNull)
bool? extBridge, string? jniReturn, bool? extSynthetic, bool? notNull, string? annotatedVisibility)
{
// If any of the parameters contain reference to non-public type, it cannot be generated.
// TODO
Expand Down Expand Up @@ -310,6 +312,7 @@ static void SaveMember (JavaMemberModel m, XmlWriter writer, string elementName,
writer.WriteAttributeStringIfValue ("type", type);
writer.WriteAttributeStringIfValue ("type-generic-aware", typeGeneric);
writer.WriteAttributeStringIfValue ("value", value);
writer.WriteAttributeStringIfValue ("annotated-visibility", annotatedVisibility);

if (extSynthetic.HasValue)
writer.WriteAttributeString ("synthetic", extSynthetic.Value ? "true" : "false");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,8 @@ public static JavaMethodModel ParseMethod (JavaTypeModel type, XElement element)
returnJni: element.XGetAttribute ("jni-return"),
isNative: element.XGetAttributeAsBool ("native"),
isSynchronized: element.XGetAttributeAsBool ("synchronized"),
returnNotNull: element.XGetAttributeAsBool ("return-not-null")
returnNotNull: element.XGetAttributeAsBool ("return-not-null"),
annotatedVisibility: element.XGetAttributeOrNull ("annotated-visibility")
);

if (element.Element ("typeParameters") is XElement tp)
Expand Down Expand Up @@ -226,7 +227,8 @@ public static JavaConstructorModel ParseConstructor (JavaTypeModel type, XElemen
deprecated: element.XGetAttribute ("deprecated"),
jniSignature: element.XGetAttribute ("jni-signature"),
isSynthetic: element.XGetAttributeAsBool ("synthetic"),
isBridge: element.XGetAttributeAsBool ("bridge")
isBridge: element.XGetAttributeAsBool ("bridge"),
annotatedVisibility: element.XGetAttributeOrNull ("annotated-visibility")
);

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

if (element.XGetAttribute ("merge.SourceFile") is string source && source.HasValue ())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ static bool ShouldImport (TypeDefinition td)
returnJni: jni_signature.Return.Jni,
isNative: false,
isSynchronized: false,
returnNotNull: false
returnNotNull: false,
annotatedVisibility: null
);

for (var i = 0; i < jni_signature.Parameters.Count; i++)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ namespace Java.Interop.Tools.JavaTypeSystem.Models
{
public class JavaConstructorModel : JavaMethodModel
{
public JavaConstructorModel (string javaName, string javaVisibility, bool javaStatic, JavaTypeModel javaDeclaringType, string deprecated, string jniSignature, bool isSynthetic, bool isBridge)
: base (javaName, javaVisibility, false, false, javaStatic, "void", javaDeclaringType, deprecated, jniSignature, isSynthetic, isBridge, string.Empty, false, false, false)
public JavaConstructorModel (string javaName, string javaVisibility, bool javaStatic, JavaTypeModel javaDeclaringType, string deprecated, string jniSignature, bool isSynthetic, bool isBridge, string? annotatedVisibility)
: base (javaName, javaVisibility, false, false, javaStatic, "void", javaDeclaringType, deprecated, jniSignature, isSynthetic, isBridge, string.Empty, false, false, false, annotatedVisibility)
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ public class JavaFieldModel : JavaMemberModel

public JavaTypeReference? TypeModel { get; private set; }

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)
: base (name, isStatic, isFinal, visibility, declaringType, deprecated, jniSignature)
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)
: base (name, isStatic, isFinal, visibility, declaringType, deprecated, jniSignature, annotatedVisibility)
{
Type = type;
TypeGeneric = typeGeneric;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ public abstract class JavaMemberModel : IJavaResolvable
public string Visibility { get; }
public string Deprecated { get; }
public string JniSignature { get; }
public string? AnnotatedVisibility { get; }

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

public JavaMemberModel (string name, bool isStatic, bool isFinal, string visibility, JavaTypeModel declaringType, string deprecated, string jniSignature)
public JavaMemberModel (string name, bool isStatic, bool isFinal, string visibility, JavaTypeModel declaringType, string deprecated, string jniSignature, string? annotatedVisibility)
{
Name = name;
IsStatic = isStatic;
Expand All @@ -24,6 +25,7 @@ public JavaMemberModel (string name, bool isStatic, bool isFinal, string visibil
DeclaringType = declaringType;
Deprecated = deprecated;
JniSignature = jniSignature;
AnnotatedVisibility = annotatedVisibility;
}

public abstract void Resolve (JavaTypeCollection types, ICollection<JavaUnresolvableModel> unresolvables);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ public class JavaMethodModel : JavaMemberModel
public List<JavaParameterModel> Parameters { get; } = new List<JavaParameterModel> ();
public List<JavaExceptionModel> Exceptions { get; } = new List<JavaExceptionModel> ();

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)
: base (javaName, javaStatic, javaFinal, javaVisibility, javaDeclaringType, deprecated, jniSignature)
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)
: base (javaName, javaStatic, javaFinal, javaVisibility, javaDeclaringType, deprecated, jniSignature, annotatedVisibility)
{
IsAbstract = javaAbstract;
Return = javaReturn;
Expand Down
24 changes: 24 additions & 0 deletions src/Xamarin.Android.Tools.Bytecode/XmlClassDeclarationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public XElement ToXElement ()
GetSourceFile (),
new XAttribute ("static", classFile.IsStatic),
new XAttribute ("visibility", GetVisibility (classFile.Visibility)),
GetAnnotatedVisibility (classFile.Attributes),
GetTypeParmeters (signature == null ? null : signature.TypeParameters),
GetImplementedInterfaces (),
GetConstructors (),
Expand Down Expand Up @@ -346,6 +347,7 @@ XElement GetMethod (string element, string name, MethodInfo method, string? retu
new XAttribute ("static", (method.AccessFlags & MethodAccessFlags.Static) != 0),
GetSynchronized (method),
new XAttribute ("visibility", GetVisibility (method.AccessFlags)),
GetAnnotatedVisibility (method.Attributes),
new XAttribute ("bridge", (method.AccessFlags & MethodAccessFlags.Bridge) != 0),
new XAttribute ("synthetic", (method.AccessFlags & MethodAccessFlags.Synthetic) != 0),
new XAttribute ("jni-signature", method.Descriptor),
Expand Down Expand Up @@ -427,6 +429,27 @@ IEnumerable<XElement> GetExceptions (MethodInfo method)
}
}

static XAttribute? GetAnnotatedVisibility (AttributeCollection attributes)
{
var annotations = attributes?.OfType<RuntimeInvisibleAnnotationsAttribute> ().FirstOrDefault ()?.Annotations;

if (annotations?.FirstOrDefault (a => a.Type == "Landroidx/annotation/RestrictTo;") is Annotation annotation) {
var annotation_element_values = (annotation.Values.FirstOrDefault ().Value as AnnotationElementArray)?.Values?.OfType<AnnotationElementEnum> ();

if (annotation_element_values is null || !annotation_element_values.Any ())
return null;

var value_string = string.Join (" ", annotation_element_values.Select (v => v.ConstantName).Where (p => p != null));

if (string.IsNullOrWhiteSpace (value_string))
return null;

return new XAttribute ("annotated-visibility", value_string);
}

return null;
}

static XAttribute? GetNotNull (MethodInfo method)
{
var annotations = method.Attributes?.OfType<RuntimeInvisibleAnnotationsAttribute> ().FirstOrDefault ()?.Annotations;
Expand Down Expand Up @@ -500,6 +523,7 @@ IEnumerable<XElement> GetFields ()
GetNotNull (field),
GetValue (field),
new XAttribute ("visibility", visibility),
GetAnnotatedVisibility (field.Attributes),
new XAttribute ("volatile", (field.AccessFlags & FieldAccessFlags.Volatile) != 0));
}
}
Expand Down
138 changes: 138 additions & 0 deletions tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,144 @@ public void ObsoleteEntireProperty ()
Assert.False (StripRegisterAttributes (writer.ToString ()).NormalizeLineEndings ().Contains ("[global::System.Obsolete(@\"deprecated\")]set"), writer.ToString ());
}

[Test]
public void RestrictToType ()
{
options.UseRestrictToAttributes = true;

var xml = @"<api>
<package name='java.lang' jni-name='java/lang'>
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
</package>
<package name='com.xamarin.android' jni-name='com/xamarin/android'>
<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' />
</package>
</api>";

var gens = ParseApiDefinition (xml);
var iface = gens.Single (g => g.Name == "MyClass");

generator.Context.ContextTypes.Push (iface);
generator.WriteType (iface, string.Empty, new GenerationInfo ("", "", "MyAssembly"));
generator.Context.ContextTypes.Pop ();

// This should use a special [Obsolete] describing the "internal" nature of this API
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 ());
}

[Test]
public void RestrictToField ()
{
options.UseRestrictToAttributes = true;

var xml = @"<api>
<package name='java.lang' jni-name='java/lang'>
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
</package>
<package name='com.xamarin.android' jni-name='com/xamarin/android'>
<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;'>
<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>
</class>
</package>
</api>";

var gens = ParseApiDefinition (xml);
var iface = gens.Single (g => g.Name == "MyClass");

generator.Context.ContextTypes.Push (iface);
generator.WriteType (iface, string.Empty, new GenerationInfo ("", "", "MyAssembly"));
generator.Context.ContextTypes.Pop ();

// This should use a special [Obsolete] describing the "internal" nature of this API
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 ());
}

[Test]
public void RestrictToMethod ()
{
options.UseRestrictToAttributes = true;

var xml = @"<api>
<package name='java.lang' jni-name='java/lang'>
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
</package>
<package name='com.xamarin.android' jni-name='com/xamarin/android'>
<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;'>
<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>
</class>
</package>
</api>";

var gens = ParseApiDefinition (xml);
var iface = gens.Single (g => g.Name == "MyClass");

generator.Context.ContextTypes.Push (iface);
generator.WriteType (iface, string.Empty, new GenerationInfo ("", "", "MyAssembly"));
generator.Context.ContextTypes.Pop ();

// This should use a special [Obsolete] describing the "internal" nature of this API
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 ());
}

[Test]
public void RestrictToProperty ()
{
options.UseRestrictToAttributes = true;

var xml = @"<api>
<package name='java.lang' jni-name='java/lang'>
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
</package>
<package name='com.xamarin.android' jni-name='com/xamarin/android'>
<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;'>
<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>
<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'>
<parameter name='count' type='int' jni-type='I'></parameter>
</method>
</class>
</package>
</api>";

var gens = ParseApiDefinition (xml);
var iface = gens.Single (g => g.Name == "MyClass");

generator.Context.ContextTypes.Push (iface);
generator.WriteType (iface, string.Empty, new GenerationInfo ("", "", "MyAssembly"));
generator.Context.ContextTypes.Pop ();

// This should use a special [Obsolete] describing the "internal" nature of this API
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 ());
}

[Test]
public void DoNotWriteObsoleteAndRestrictTo ()
{
options.UseRestrictToAttributes = true;

var xml = @"<api>
<package name='java.lang' jni-name='java/lang'>
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
</package>
<package name='com.xamarin.android' jni-name='com/xamarin/android'>
<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;'>
<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>
</class>
</package>
</api>";

var gens = ParseApiDefinition (xml);
var iface = gens.Single (g => g.Name == "MyClass");

generator.Context.ContextTypes.Push (iface);
generator.WriteType (iface, string.Empty, new GenerationInfo ("", "", "MyAssembly"));
generator.Context.ContextTypes.Pop ();

// This method is both @Deprecated and @RestrictTo. We cannot write 2 [Obsolete] attributes, so
// only write the deprecated one.
Assert.True (writer.ToString ().Replace (" (@\"deprecated\")", "").NormalizeLineEndings ().Contains ("[global::System.Obsolete]".NormalizeLineEndings ()), writer.ToString ());
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 ());
}

[Test]
[NonParallelizable] // We are setting a static property on Report
public void WarnIfTypeNameMatchesNamespace ()
Expand Down
Loading

0 comments on commit b274a67

Please sign in to comment.