diff --git a/tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs b/tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs index f8a550893..69d1b6455 100644 --- a/tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs +++ b/tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs @@ -565,7 +565,8 @@ public void ObsoletedOSPlatformAttributeSupport () Assert.True (writer.ToString ().Contains ("[global::System.Runtime.Versioning.ObsoletedOSPlatform (\"android25.0\", @\"This is a field deprecated since 25!\")]"), writer.ToString ()); Assert.True (writer.ToString ().Contains ("[global::System.Runtime.Versioning.ObsoletedOSPlatform (\"android25.0\", @\"This is a constructor deprecated since 25!\")]"), writer.ToString ()); Assert.True (writer.ToString ().Contains ("[global::System.Runtime.Versioning.ObsoletedOSPlatform (\"android25.0\", @\"This is a method deprecated since 25!\")]"), writer.ToString ()); - Assert.True (writer.ToString ().Contains ("[global::System.Runtime.Versioning.ObsoletedOSPlatform (\"android25.0\", @\"This is a property getter deprecated since 25! This is a property setter deprecated since 25!\")]"), writer.ToString ()); + Assert.True (writer.ToString ().Contains ("[global::System.Runtime.Versioning.ObsoletedOSPlatform (\"android25.0\", @\"This is a property getter deprecated since 25!\")]"), writer.ToString ()); + Assert.True (writer.ToString ().Contains ("[global::System.Runtime.Versioning.ObsoletedOSPlatform (\"android25.0\", @\"This is a property setter deprecated since 25!\")]"), writer.ToString ()); } [Test] @@ -611,7 +612,152 @@ public void ObsoletedOSPlatformAttributeUnneededSupport () } [Test] - [NonParallelizable] // We are setting a static property on Report + public void ObsoleteGetterOnlyProperty () + { + 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 [Obsolete] on the entire property because the getter is obsolete and there is no setter + Assert.True (StripRegisterAttributes (writer.ToString ()).NormalizeLineEndings ().Contains ("[global::System.Obsolete (@\"deprecated\")]public virtual unsafe int Count".NormalizeLineEndings ()), writer.ToString ()); + + // Ensure we don't write getter attribute + Assert.False (StripRegisterAttributes (writer.ToString ()).NormalizeLineEndings ().Contains ("[global::System.Obsolete(@\"deprecated\")]get"), writer.ToString ()); + } + + [Test] + public void ObsoletePropertyGetter () + { + 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 [Obsolete] on just the property getter since the setter is not obsolete + Assert.True (StripRegisterAttributes (writer.ToString ()).NormalizeLineEndings ().Contains ("[global::System.Obsolete(@\"deprecated\")]get"), writer.ToString ()); + } + + [Test] + public void ObsoletePropertySetter () + { + 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 [Obsolete] on just the property setter since the getter is not obsolete + Assert.True (StripRegisterAttributes (writer.ToString ()).NormalizeLineEndings ().Contains ("[global::System.Obsolete(@\"deprecated\")]set"), writer.ToString ()); + } + + [Test] + public void ObsoleteBothPropertyMethods () + { + 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 [Obsolete] on both property methods because the deprecation messages are different + Assert.True (StripRegisterAttributes (writer.ToString ()).NormalizeLineEndings ().Contains ("[global::System.Obsolete(@\"getter_message\")]get"), writer.ToString ()); + Assert.True (StripRegisterAttributes (writer.ToString ()).NormalizeLineEndings ().Contains ("[global::System.Obsolete(@\"setter_message\")]set"), writer.ToString ()); + } + + [Test] + public void ObsoleteEntireProperty () + { + 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 [Obsolete] on the entire property because the getter and setter are both obsoleted with the same message + Assert.True (StripRegisterAttributes (writer.ToString ()).NormalizeLineEndings ().Contains ("[global::System.Obsolete (@\"deprecated\")]public virtual unsafe int Count".NormalizeLineEndings ()), writer.ToString ()); + + // Ensure we don't write getter/setter attributes + Assert.False (StripRegisterAttributes (writer.ToString ()).NormalizeLineEndings ().Contains ("[global::System.Obsolete(@\"deprecated\")]get"), writer.ToString ()); + Assert.False (StripRegisterAttributes (writer.ToString ()).NormalizeLineEndings ().Contains ("[global::System.Obsolete(@\"deprecated\")]set"), writer.ToString ()); + } + + [Test] + [NonParallelizable] // We are setting a static property on Report public void WarnIfTypeNameMatchesNamespace () { var @class = new TestClass ("Object", "java.myclass.MyClass"); @@ -650,6 +796,20 @@ public void DontWarnIfNestedTypeNameMatchesNamespace () // The warning should not be raised if the nested type matches enclosing namespace Assert.False (sb.ToString ().Contains ("warning BG8403")); } + + static string StripRegisterAttributes (string str) + { + // It is hard to test if the [Obsolete] is on the setter/etc due to the [Register], so remove all [Register]s + // [global::System.Obsolete (@"setter_message")] + // [Register ("setCount", "(I)V", "GetSetCount_IHandler")] + // set { + int index; + + while ((index = str.IndexOf ("[Register", StringComparison.Ordinal)) > -1) + str = str.Substring (0, index) + str.Substring (str.IndexOf (']', index) + 1); + + return str; + } } [TestFixture] diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Property.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Property.cs index 57b4b59a8..a9a51ff74 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Property.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/Property.cs @@ -60,5 +60,32 @@ public void AutoDetectEnumifiedOverrideProperties (AncestorDescendantCache cache } public string ExplicitInterface => Getter?.ExplicitInterface ?? Setter?.ExplicitInterface; + + public bool IsWholePropertyDeprecated { + get { + // If the getter isn't deprecated then the property isn't + if (Getter?.Deprecated is null) + return false; + + // If the getter is deprecated and there is no setter then the property is deprecated + if (Setter is null) + return true; + + // If the setter isn't deprecated then the property isn't + if (Setter.Deprecated is null) + return false; + + // If the getter/setter deprecation messages differ, don't use whole property deprecation + if (Getter.Deprecated != Setter.Deprecated) + return false; + + // If the getter/setter deprecation versions differ, don't use whole property deprecation + if (Getter.DeprecatedSince != Setter.DeprecatedSince) + return false; + + // Getter/Setter deprecation is the same, use whole property deprecation + return true; + } + } } } diff --git a/tools/generator/SourceWriters/BoundProperty.cs b/tools/generator/SourceWriters/BoundProperty.cs index 240b86a95..591d37e91 100644 --- a/tools/generator/SourceWriters/BoundProperty.cs +++ b/tools/generator/SourceWriters/BoundProperty.cs @@ -72,11 +72,18 @@ public BoundProperty (GenBase gen, Property property, CodeGenerationOptions opt, IsOverride = false; } - // Unlike [Register], [Obsolete] cannot be put on property accessors, so we can apply them only under limited condition... - if (property.Getter.Deprecated != null && (property.Setter == null || property.Setter.Deprecated != null)) { - var message = property.Getter.Deprecated.Trim () + (property.Setter != null && property.Setter.Deprecated != property.Getter.Deprecated ? " " + property.Setter.Deprecated.Trim () : null); - var since = property.Getter?.DeprecatedSince ?? property.Setter?.DeprecatedSince; - SourceWriterExtensions.AddObsolete (Attributes, message, opt, deprecatedSince: since); + // Add [Obsolete] or [ObsoletedOSPlatform] + if (property.IsWholePropertyDeprecated) { + // This case applies [Obsolete] to the entire property + SourceWriterExtensions.AddObsolete (Attributes, property.Getter.Deprecated.Trim (), opt, deprecatedSince: property.Getter.DeprecatedSince); + } else { + // This case applies [Obsolete] to just the getter + if (property.Getter?.Deprecated != null) + SourceWriterExtensions.AddObsolete (GetterAttributes, property.Getter.Deprecated.Trim (), opt, deprecatedSince: property.Getter?.DeprecatedSince); + + // This case applies [Obsolete] to just the setter + if (property.Setter?.Deprecated != null) + SourceWriterExtensions.AddObsolete (SetterAttributes, property.Setter.Deprecated.Trim (), opt, deprecatedSince: property.Setter?.DeprecatedSince); } SourceWriterExtensions.AddSupportedOSPlatform (Attributes, property.Getter, opt);