diff --git a/src/Java.Interop.Tools.JavaTypeSystem/Adapters/JavaXmlApiExporter.cs b/src/Java.Interop.Tools.JavaTypeSystem/Adapters/JavaXmlApiExporter.cs index d1b7975fd..694f84cbe 100644 --- a/src/Java.Interop.Tools.JavaTypeSystem/Adapters/JavaXmlApiExporter.cs +++ b/src/Java.Interop.Tools.JavaTypeSystem/Adapters/JavaXmlApiExporter.cs @@ -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) { @@ -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) @@ -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) @@ -277,7 +279,7 @@ static void SaveMember (JavaMemberModel m, XmlWriter writer, string elementName, string? transient, string? type, string? typeGeneric, string? value, string? volat, IEnumerable? 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 @@ -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"); diff --git a/src/Java.Interop.Tools.JavaTypeSystem/Adapters/JavaXmlApiImporter.cs b/src/Java.Interop.Tools.JavaTypeSystem/Adapters/JavaXmlApiImporter.cs index 033d836bc..77c8bfd7a 100644 --- a/src/Java.Interop.Tools.JavaTypeSystem/Adapters/JavaXmlApiImporter.cs +++ b/src/Java.Interop.Tools.JavaTypeSystem/Adapters/JavaXmlApiImporter.cs @@ -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) @@ -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 ¯\_(ツ)_/¯ @@ -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 ()) diff --git a/src/Java.Interop.Tools.JavaTypeSystem/Adapters/ManagedApiImporter.cs b/src/Java.Interop.Tools.JavaTypeSystem/Adapters/ManagedApiImporter.cs index aa7dfc541..c1a6b4d7a 100644 --- a/src/Java.Interop.Tools.JavaTypeSystem/Adapters/ManagedApiImporter.cs +++ b/src/Java.Interop.Tools.JavaTypeSystem/Adapters/ManagedApiImporter.cs @@ -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++) diff --git a/src/Java.Interop.Tools.JavaTypeSystem/JavaModels/JavaConstructorModel.cs b/src/Java.Interop.Tools.JavaTypeSystem/JavaModels/JavaConstructorModel.cs index 09726a36e..a9141ec2d 100644 --- a/src/Java.Interop.Tools.JavaTypeSystem/JavaModels/JavaConstructorModel.cs +++ b/src/Java.Interop.Tools.JavaTypeSystem/JavaModels/JavaConstructorModel.cs @@ -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) { } diff --git a/src/Java.Interop.Tools.JavaTypeSystem/JavaModels/JavaFieldModel.cs b/src/Java.Interop.Tools.JavaTypeSystem/JavaModels/JavaFieldModel.cs index 21f29800f..2ba84f2da 100644 --- a/src/Java.Interop.Tools.JavaTypeSystem/JavaModels/JavaFieldModel.cs +++ b/src/Java.Interop.Tools.JavaTypeSystem/JavaModels/JavaFieldModel.cs @@ -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; diff --git a/src/Java.Interop.Tools.JavaTypeSystem/JavaModels/JavaMemberModel.cs b/src/Java.Interop.Tools.JavaTypeSystem/JavaModels/JavaMemberModel.cs index a50a1b5d6..d4a6fed1a 100644 --- a/src/Java.Interop.Tools.JavaTypeSystem/JavaModels/JavaMemberModel.cs +++ b/src/Java.Interop.Tools.JavaTypeSystem/JavaModels/JavaMemberModel.cs @@ -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 PropertyBag { get; } = new Dictionary (); - 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; @@ -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 unresolvables); diff --git a/src/Java.Interop.Tools.JavaTypeSystem/JavaModels/JavaMethodModel.cs b/src/Java.Interop.Tools.JavaTypeSystem/JavaModels/JavaMethodModel.cs index 52695d46e..9e2a142b3 100644 --- a/src/Java.Interop.Tools.JavaTypeSystem/JavaModels/JavaMethodModel.cs +++ b/src/Java.Interop.Tools.JavaTypeSystem/JavaModels/JavaMethodModel.cs @@ -22,8 +22,8 @@ public class JavaMethodModel : JavaMemberModel public List Parameters { get; } = new List (); public List Exceptions { get; } = new List (); - 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; diff --git a/src/Xamarin.Android.Tools.Bytecode/XmlClassDeclarationBuilder.cs b/src/Xamarin.Android.Tools.Bytecode/XmlClassDeclarationBuilder.cs index 6fa05991b..ef0c8a381 100644 --- a/src/Xamarin.Android.Tools.Bytecode/XmlClassDeclarationBuilder.cs +++ b/src/Xamarin.Android.Tools.Bytecode/XmlClassDeclarationBuilder.cs @@ -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 (), @@ -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), @@ -427,6 +429,27 @@ IEnumerable GetExceptions (MethodInfo method) } } + static XAttribute? GetAnnotatedVisibility (AttributeCollection attributes) + { + var annotations = attributes?.OfType ().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 (); + + 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 ().FirstOrDefault ()?.Annotations; @@ -500,6 +523,7 @@ IEnumerable GetFields () GetNotNull (field), GetValue (field), new XAttribute ("visibility", visibility), + GetAnnotatedVisibility (field.Attributes), new XAttribute ("volatile", (field.AccessFlags & FieldAccessFlags.Volatile) != 0)); } } diff --git a/tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs b/tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs index 21e11258f..a32492f0c 100644 --- a/tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs +++ b/tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs @@ -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 = @" + + + + + + + "; + + 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 = @" + + + + + + + + + "; + + 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 = @" + + + + + + + + + "; + + 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 = @" + + + + + + + + + + + + "; + + 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 = @" + + + + + + + + + "; + + 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 () diff --git a/tools/generator/CodeGenerationOptions.cs b/tools/generator/CodeGenerationOptions.cs index 68eb6d0b8..4cf2293e1 100644 --- a/tools/generator/CodeGenerationOptions.cs +++ b/tools/generator/CodeGenerationOptions.cs @@ -67,6 +67,7 @@ public SymbolTable SymbolTable { public bool SupportNullableReferenceTypes { get; set; } public bool UseShallowReferencedTypes { get; set; } public bool UseObsoletedOSPlatformAttributes { get; set; } + public bool UseRestrictToAttributes { get; set; } public bool RemoveConstSugar => BuildingCoreAssembly; bool? buildingCoreAssembly; diff --git a/tools/generator/CodeGenerator.cs b/tools/generator/CodeGenerator.cs index 150e93382..da7ff8d31 100644 --- a/tools/generator/CodeGenerator.cs +++ b/tools/generator/CodeGenerator.cs @@ -84,6 +84,7 @@ static void Run (CodeGeneratorOptions options, DirectoryAssemblyResolver resolve SupportNestedInterfaceTypes = options.SupportNestedInterfaceTypes, SupportNullableReferenceTypes = options.SupportNullableReferenceTypes, UseObsoletedOSPlatformAttributes = options.UseObsoletedOSPlatformAttributes, + UseRestrictToAttributes = options.UseRestrictToAttributes, }; var resolverCache = new TypeDefinitionCache (); diff --git a/tools/generator/CodeGeneratorOptions.cs b/tools/generator/CodeGeneratorOptions.cs index 08395d154..f8753ed04 100644 --- a/tools/generator/CodeGeneratorOptions.cs +++ b/tools/generator/CodeGeneratorOptions.cs @@ -52,6 +52,7 @@ public CodeGeneratorOptions () public bool SupportDefaultInterfaceMethods { get; set; } public bool SupportNestedInterfaceTypes { get; set; } public bool SupportNullableReferenceTypes { get; set; } + public bool UseRestrictToAttributes { get; set; } public bool UseLegacyJavaResolver { get; set; } public bool UseObsoletedOSPlatformAttributes { get; set; } @@ -103,13 +104,14 @@ public static CodeGeneratorOptions Parse (string[] args) "SDK Platform {VERSION}/API level.", v => opts.ApiLevel = v }, { "lang-features=", - "For internal use. (Flags: interface-constants,default-interface-methods,nested-interface-types,nullable-reference-types,obsoleted-platform-attributes)", + "For internal use. (Flags: interface-constants,default-interface-methods,nested-interface-types,nullable-reference-types,obsoleted-platform-attributes,restrict-to-attributes)", v => { opts.SupportInterfaceConstants = v?.Contains ("interface-constants") == true; opts.SupportDefaultInterfaceMethods = v?.Contains ("default-interface-methods") == true; opts.SupportNestedInterfaceTypes = v?.Contains ("nested-interface-types") == true; opts.SupportNullableReferenceTypes = v?.Contains ("nullable-reference-types") == true; opts.UseObsoletedOSPlatformAttributes = v?.Contains ("obsoleted-platform-attributes") == true; + opts.UseRestrictToAttributes = v?.Contains ("restrict-to-attributes") == true; }}, { "preserve-enums", "For internal use.", diff --git a/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs b/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs index 51a8239fe..cd248a514 100644 --- a/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs +++ b/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs @@ -157,6 +157,7 @@ public static ClassGen CreateClass (XElement pkg, XElement elem, CodeGenerationO public static Ctor CreateCtor (GenBase declaringType, XElement elem, CodeGenerationOptions options = null) { var ctor = new Ctor (declaringType) { + AnnotatedVisibility = elem.XGetAttribute ("annotated-visibility"), ApiAvailableSince = declaringType.ApiAvailableSince, CustomAttributes = elem.XGetAttribute ("customAttributes"), Deprecated = elem.Deprecated (), @@ -208,6 +209,7 @@ public static Ctor CreateCtor (GenBase declaringType, XElement elem, CodeGenerat public static Field CreateField (GenBase declaringType, XElement elem, CodeGenerationOptions options = null) { var field = new Field { + AnnotatedVisibility = elem.XGetAttribute ("annotated-visibility"), ApiAvailableSince = declaringType.ApiAvailableSince, DeprecatedComment = elem.XGetAttribute ("deprecated"), DeprecatedSince = elem.XGetAttributeAsIntOrNull ("deprecated-since"), @@ -252,6 +254,7 @@ public static Field CreateField (GenBase declaringType, XElement elem, CodeGener public static GenBaseSupport CreateGenBaseSupport (XElement pkg, XElement elem, CodeGenerationOptions opt, bool isInterface) { var support = new GenBaseSupport { + AnnotatedVisibility = elem.XGetAttribute ("annotated-visibility"), DeprecatedSince = elem.XGetAttributeAsIntOrNull ("deprecated-since"), IsAcw = true, IsDeprecated = elem.XGetAttribute ("deprecated") != "not deprecated", @@ -360,6 +363,7 @@ public static InterfaceGen CreateInterface (XElement pkg, XElement elem, CodeGen public static Method CreateMethod (GenBase declaringType, XElement elem, CodeGenerationOptions options = null) { var method = new Method (declaringType) { + AnnotatedVisibility = elem.XGetAttribute ("annotated-visibility"), ApiAvailableSince = declaringType.ApiAvailableSince, ArgsType = elem.Attribute ("argsType")?.Value, CustomAttributes = elem.XGetAttribute ("customAttributes"), diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Field.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Field.cs index 6f38240c7..8477fab1d 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Field.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Field.cs @@ -6,6 +6,7 @@ namespace MonoDroid.Generation { public class Field : ApiVersionsSupport.IApiAvailability, ISourceLineInfo { + public string AnnotatedVisibility { get; set; } public string Annotation { get; set; } public int ApiAvailableSince { get; set; } public string DeprecatedComment { get; set; } diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBase.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBase.cs index 5331bbf2b..713d45e3b 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBase.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBase.cs @@ -178,6 +178,8 @@ IEnumerable Ancestors () yield return g; } + public string AnnotatedVisibility => support.AnnotatedVisibility; + // not: not currently assembly qualified, but it uses needed // Type.GetType() conventions such as '/' for nested types. public string AssemblyQualifiedName => string.IsNullOrWhiteSpace (Namespace) diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBaseSupport.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBaseSupport.cs index 75258cb6d..a3a1f530a 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBaseSupport.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBaseSupport.cs @@ -4,6 +4,7 @@ namespace MonoDroid.Generation { public class GenBaseSupport { + public string AnnotatedVisibility { get; set; } public bool IsAcw { get; set; } public bool IsDeprecated { get; set; } public string DeprecatedComment { get; set; } diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/MethodBase.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/MethodBase.cs index 09fb19072..714f38392 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/MethodBase.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/MethodBase.cs @@ -13,6 +13,7 @@ protected MethodBase (GenBase declaringType) DeclaringType = declaringType; } + public string AnnotatedVisibility { get; set; } public string Annotation { get; internal set; } public int ApiAvailableSince { get; set; } public string AssemblyName { get; set; } diff --git a/tools/generator/SourceWriters/Attributes/RestrictToAttr.cs b/tools/generator/SourceWriters/Attributes/RestrictToAttr.cs new file mode 100644 index 000000000..b634c0f14 --- /dev/null +++ b/tools/generator/SourceWriters/Attributes/RestrictToAttr.cs @@ -0,0 +1,20 @@ +using System; +using Xamarin.SourceWriter; + +namespace generator.SourceWriters +{ + public class RestrictToAttr : AttributeWriter + { + bool is_type; + + public RestrictToAttr (bool isType) + { + is_type = isType; + } + + public override void WriteAttribute (CodeWriter writer) + { + writer.WriteLine ($"[global::System.Obsolete (\"While this {(is_type ? "type" : "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\")]"); + } + } +} diff --git a/tools/generator/SourceWriters/BoundClass.cs b/tools/generator/SourceWriters/BoundClass.cs index 3acb86c85..399c3c5df 100644 --- a/tools/generator/SourceWriters/BoundClass.cs +++ b/tools/generator/SourceWriters/BoundClass.cs @@ -45,6 +45,7 @@ public BoundClass (ClassGen klass, CodeGenerationOptions opt, CodeGeneratorConte Comments.Add ($"// Metadata.xml XPath class reference: path=\"{klass.MetadataXPathReference}\""); SourceWriterExtensions.AddObsolete (Attributes, klass.DeprecatedComment, opt, forceDeprecate: klass.IsDeprecated, deprecatedSince: klass.DeprecatedSince); + SourceWriterExtensions.AddRestrictToWarning (Attributes, klass.AnnotatedVisibility, true, opt); SourceWriterExtensions.AddSupportedOSPlatform (Attributes, klass, opt); diff --git a/tools/generator/SourceWriters/BoundConstructor.cs b/tools/generator/SourceWriters/BoundConstructor.cs index 9440abf0a..d10e99ec3 100644 --- a/tools/generator/SourceWriters/BoundConstructor.cs +++ b/tools/generator/SourceWriters/BoundConstructor.cs @@ -34,6 +34,7 @@ public BoundConstructor (ClassGen klass, Ctor constructor, bool useBase, CodeGen } SourceWriterExtensions.AddObsolete (Attributes, constructor.Deprecated, opt, deprecatedSince: constructor.DeprecatedSince); + SourceWriterExtensions.AddRestrictToWarning (Attributes, constructor.AnnotatedVisibility, false, opt); if (constructor.CustomAttributes != null) Attributes.Add (new CustomAttr (constructor.CustomAttributes)); diff --git a/tools/generator/SourceWriters/BoundField.cs b/tools/generator/SourceWriters/BoundField.cs index 259bdbc94..0b8e28a44 100644 --- a/tools/generator/SourceWriters/BoundField.cs +++ b/tools/generator/SourceWriters/BoundField.cs @@ -33,6 +33,7 @@ public BoundField (GenBase type, Field field, CodeGenerationOptions opt) SourceWriterExtensions.AddSupportedOSPlatform (Attributes, field, opt); SourceWriterExtensions.AddObsolete (Attributes, field.DeprecatedComment, opt, field.IsDeprecated, isError: field.IsDeprecatedError, deprecatedSince: field.DeprecatedSince); + SourceWriterExtensions.AddRestrictToWarning (Attributes, field.AnnotatedVisibility, false, opt); if (field.Annotation.HasValue ()) Attributes.Add (new CustomAttr (field.Annotation)); diff --git a/tools/generator/SourceWriters/BoundFieldAsProperty.cs b/tools/generator/SourceWriters/BoundFieldAsProperty.cs index 9277a1828..c7f2560cd 100644 --- a/tools/generator/SourceWriters/BoundFieldAsProperty.cs +++ b/tools/generator/SourceWriters/BoundFieldAsProperty.cs @@ -48,6 +48,7 @@ public BoundFieldAsProperty (GenBase type, Field field, CodeGenerationOptions op } SourceWriterExtensions.AddObsolete (Attributes, field.DeprecatedComment, opt, field.IsDeprecated, isError: field.IsDeprecatedError, deprecatedSince: field.DeprecatedSince); + SourceWriterExtensions.AddRestrictToWarning (Attributes, field.AnnotatedVisibility, false, opt); SetVisibility (field.Visibility); UseExplicitPrivateKeyword = true; diff --git a/tools/generator/SourceWriters/BoundInterface.cs b/tools/generator/SourceWriters/BoundInterface.cs index 5ca02cf8f..96192b027 100644 --- a/tools/generator/SourceWriters/BoundInterface.cs +++ b/tools/generator/SourceWriters/BoundInterface.cs @@ -44,6 +44,7 @@ public BoundInterface (InterfaceGen iface, CodeGenerationOptions opt, CodeGenera Comments.Add ($"// Metadata.xml XPath interface reference: path=\"{iface.MetadataXPathReference}\""); SourceWriterExtensions.AddObsolete (Attributes, iface.DeprecatedComment, opt, iface.IsDeprecated, deprecatedSince: iface.DeprecatedSince); + SourceWriterExtensions.AddRestrictToWarning (Attributes, iface.AnnotatedVisibility, true, opt); if (!iface.IsConstSugar (opt)) { var signature = string.IsNullOrWhiteSpace (iface.Namespace) diff --git a/tools/generator/SourceWriters/BoundInterfaceMethodDeclaration.cs b/tools/generator/SourceWriters/BoundInterfaceMethodDeclaration.cs index 502efac0e..01f8493de 100644 --- a/tools/generator/SourceWriters/BoundInterfaceMethodDeclaration.cs +++ b/tools/generator/SourceWriters/BoundInterfaceMethodDeclaration.cs @@ -35,6 +35,7 @@ public BoundInterfaceMethodDeclaration (Method method, string adapter, CodeGener Comments.Add ($"// Metadata.xml XPath method reference: path=\"{method.GetMetadataXPathReference (method.DeclaringType)}\""); SourceWriterExtensions.AddObsolete (Attributes, method.Deprecated, opt, deprecatedSince: method.DeprecatedSince); + SourceWriterExtensions.AddRestrictToWarning (Attributes, method.AnnotatedVisibility, false, opt); if (method.IsReturnEnumified) Attributes.Add (new GeneratedEnumAttr (true)); diff --git a/tools/generator/SourceWriters/BoundMethod.cs b/tools/generator/SourceWriters/BoundMethod.cs index 2146dd436..747191bce 100644 --- a/tools/generator/SourceWriters/BoundMethod.cs +++ b/tools/generator/SourceWriters/BoundMethod.cs @@ -75,6 +75,7 @@ public BoundMethod (GenBase type, Method method, CodeGenerationOptions opt, bool Comments.Add ($"// Metadata.xml XPath method reference: path=\"{method.GetMetadataXPathReference (method.DeclaringType)}\""); SourceWriterExtensions.AddObsolete (Attributes, method.Deprecated, opt, deprecatedSince: method.DeprecatedSince); + SourceWriterExtensions.AddRestrictToWarning (Attributes, method.AnnotatedVisibility, false, opt); if (method.IsReturnEnumified) Attributes.Add (new GeneratedEnumAttr (true)); diff --git a/tools/generator/SourceWriters/BoundMethodAbstractDeclaration.cs b/tools/generator/SourceWriters/BoundMethodAbstractDeclaration.cs index 13ae7cb04..95db23991 100644 --- a/tools/generator/SourceWriters/BoundMethodAbstractDeclaration.cs +++ b/tools/generator/SourceWriters/BoundMethodAbstractDeclaration.cs @@ -57,6 +57,7 @@ public BoundMethodAbstractDeclaration (GenBase gen, Method method, CodeGeneratio Comments.Add ($"// Metadata.xml XPath method reference: path=\"{method.GetMetadataXPathReference (method.DeclaringType)}\""); SourceWriterExtensions.AddObsolete (Attributes, method.Deprecated, opt, deprecatedSince: method.DeprecatedSince); + SourceWriterExtensions.AddRestrictToWarning (Attributes, method.AnnotatedVisibility, false, opt); SourceWriterExtensions.AddSupportedOSPlatform (Attributes, method, opt); diff --git a/tools/generator/SourceWriters/BoundMethodExtensionStringOverload.cs b/tools/generator/SourceWriters/BoundMethodExtensionStringOverload.cs index 4ad132b19..527dfdd2e 100644 --- a/tools/generator/SourceWriters/BoundMethodExtensionStringOverload.cs +++ b/tools/generator/SourceWriters/BoundMethodExtensionStringOverload.cs @@ -27,6 +27,7 @@ public BoundMethodExtensionStringOverload (Method method, CodeGenerationOptions ReturnType = new TypeReferenceWriter (opt.GetTypeReferenceName (method.RetVal).Replace ("Java.Lang.ICharSequence", "string").Replace ("global::string", "string")); SourceWriterExtensions.AddObsolete (Attributes, method.Deprecated, opt, deprecatedSince: method.DeprecatedSince); + SourceWriterExtensions.AddRestrictToWarning (Attributes, method.AnnotatedVisibility, false, opt); SourceWriterExtensions.AddSupportedOSPlatform (Attributes, method, opt); diff --git a/tools/generator/SourceWriters/BoundMethodStringOverload.cs b/tools/generator/SourceWriters/BoundMethodStringOverload.cs index e60f9acb6..a6d73d7fb 100644 --- a/tools/generator/SourceWriters/BoundMethodStringOverload.cs +++ b/tools/generator/SourceWriters/BoundMethodStringOverload.cs @@ -25,6 +25,7 @@ public BoundMethodStringOverload (Method method, CodeGenerationOptions opt) ReturnType = new TypeReferenceWriter (opt.GetTypeReferenceName (method.RetVal).Replace ("Java.Lang.ICharSequence", "string").Replace ("global::string", "string")); SourceWriterExtensions.AddObsolete (Attributes, method.Deprecated, opt, deprecatedSince: method.DeprecatedSince); + SourceWriterExtensions.AddRestrictToWarning (Attributes, method.AnnotatedVisibility, false, opt); SourceWriterExtensions.AddSupportedOSPlatform (Attributes, method, opt); diff --git a/tools/generator/SourceWriters/BoundProperty.cs b/tools/generator/SourceWriters/BoundProperty.cs index 591d37e91..41b48d88e 100644 --- a/tools/generator/SourceWriters/BoundProperty.cs +++ b/tools/generator/SourceWriters/BoundProperty.cs @@ -86,6 +86,9 @@ public BoundProperty (GenBase gen, Property property, CodeGenerationOptions opt, SourceWriterExtensions.AddObsolete (SetterAttributes, property.Setter.Deprecated.Trim (), opt, deprecatedSince: property.Setter?.DeprecatedSince); } + SourceWriterExtensions.AddRestrictToWarning (GetterAttributes, property.Getter.AnnotatedVisibility, false, opt); + SourceWriterExtensions.AddRestrictToWarning (SetterAttributes, property.Setter?.AnnotatedVisibility, false, opt); + SourceWriterExtensions.AddSupportedOSPlatform (Attributes, property.Getter, opt); SourceWriterExtensions.AddMethodCustomAttributes (GetterAttributes, property.Getter); diff --git a/tools/generator/SourceWriters/Extensions/SourceWriterExtensions.cs b/tools/generator/SourceWriters/Extensions/SourceWriterExtensions.cs index f79d8e871..1bc713ebd 100644 --- a/tools/generator/SourceWriters/Extensions/SourceWriterExtensions.cs +++ b/tools/generator/SourceWriters/Extensions/SourceWriterExtensions.cs @@ -341,6 +341,23 @@ static bool AddObsoletedOSPlatformAttribute (List attributes, s return true; } + public static void AddRestrictToWarning (List attributes, string scope, bool isType, CodeGenerationOptions opt) + { + if (!opt.UseRestrictToAttributes) + return; + + // Empty scope means there is no @RestrictTo annotation + if (!scope.HasValue ()) + return; + + // We can't add 2 [Obsolete] attributes, we'll let a "real" obsolete message take precedence + if (attributes.OfType ().Any ()) + return; + + // We consider all scopes to be "internal" API we need to warn about + attributes.Add (new RestrictToAttr (isType)); + } + public static void WriteMethodInvokerBody (CodeWriter writer, Method method, CodeGenerationOptions opt, string contextThis) { writer.WriteLine ($"if ({method.EscapedIdName} == IntPtr.Zero)");