Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow Obsolete Attribute on Accessors #32571

Merged
merged 21 commits into from
Apr 2, 2019

Conversation

YairHalberstadt
Copy link
Contributor

@YairHalberstadt YairHalberstadt commented Jan 17, 2019

See #32472, dotnet/csharplang#2152, https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-02-27.md

This PR makes the following changes:

  1. Allow Obsolete and Deprecated attributes on property accessors in C# 8. They remain invalid on event accessors.

  2. An obsolete or deprecated attribute on any accessor (property or event) will suppress warnings from the usage of obsolete/deprecated symbols in that accessor. For simplicity, this applies across all language versions.

  3. A new error has been added to indicate that deprecated and obsolete attributes cannot be used on event accessors but can be used on property accessors.

  4. The error for using obsolete/deprecated on an attribute pre C# 8 now reads:

                // (5,24): error CS8652: The feature 'obsolete on property accessor' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.

C# already had to be able to consume code which declared an accessor obsolete, as that is perfectly legal both in IL, and in VB. As such no changes have been necessary to allow consumption of obsolete accessors in C#.

In any cases where I made design decisions which I am unsure of, I will leave a review.

@YairHalberstadt YairHalberstadt requested a review from a team as a code owner January 17, 2019 20:59
@jinujoseph jinujoseph added Area-Compilers Community The pull request was submitted by a contributor who is not a Microsoft employee. labels Jan 23, 2019
@YairHalberstadt YairHalberstadt changed the title Allow Obsolete Attribute on Accessors WIP: Allow Obsolete Attribute on Accessors Mar 13, 2019
Forbid Obsolete/Deprecated on event accessors.

Give suitable error messages for Obsolete/Deprected on event accessors, and on property accessors pre-C# 8

Add further tests
…e on property accessor'.

Fix missing using directive in unit test.
@@ -81,7 +76,15 @@ private static ThreeState GetObsoleteContextState(Symbol symbol, bool forceCompl
return state;
}

symbol = symbol.ContainingSymbol;
// For property or event accessors, check the associated property or event next.
if (symbol.IsAccessor())
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This suppresses warnings/errors from the usage of an obsolete symbol in an accessor if the accessor is marked obsolete itself.

We only really need to do this for properties, not for events, but it is simplest to treat them both equally, and it is arguable which way is most correct.

For a similar reason, we change this for all C# versions, not just for C# 8

@@ -1598,7 +1598,7 @@ internal enum ErrorCode
ERR_PossibleAsyncIteratorWithoutYieldOrAwait = 8420,
ERR_StaticLocalFunctionCannotCaptureVariable = 8421,
ERR_StaticLocalFunctionCannotCaptureThis = 8422,

ERR_AttributeNotOnEventAccessor = 8423,
Copy link
Contributor Author

@YairHalberstadt YairHalberstadt Mar 14, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure how new error codes are picked. I am also not sure whether this should go with C# 1 errors, or C# 8, as it's just a different name/message for what is essentially an old error. #Closed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This number (in the C# 8.0 range) is fine.


In reply to: 265474149 [](ancestors = 265474149)

@YairHalberstadt YairHalberstadt changed the title WIP: Allow Obsolete Attribute on Accessors Allow Obsolete Attribute on Accessors Mar 14, 2019
@YairHalberstadt
Copy link
Contributor Author

YairHalberstadt commented Mar 14, 2019

@gafter
This PR is ready for review. #Closed

// CS1667: Attribute '{0}' is not valid on event accessors. It is only valid on '{1}' declarations.
AttributeUsageInfo attributeUsage = arguments.Attribute.AttributeClass.GetAttributeUsageInfo();
arguments.Diagnostics.Add(ErrorCode.ERR_AttributeNotOnEventAccessor, arguments.AttributeSyntaxOpt.Name.Location, description.FullName, attributeUsage.GetValidTargetsErrorArgument());
}
Copy link
Member

@jcouv jcouv Mar 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

} [](start = 20, length = 1)

Feels like we should check the language version in an else branch here. There is no point in giving a language version diagnostic on events. Please add a test too. #Closed

@@ -5968,6 +5991,10 @@ public class TestClass
[Obsolete(""Do not use Prop1"", false)]
public int Prop1 { get; set; }

public int Prop2 { [Obsolete(""Do not use Prop2.Get"")] get; set; }
Copy link
Member

@jcouv jcouv Mar 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you test a scenario with Obsolete on both the property and the accessor? I think that will produce a single diagnostic (which is desirable). #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if one is an error, and one a warning?
Or both an error/warning, but with different messages?
It might be best if we treat it as two separate diagnostics.
We could just ban making both a property and an accessor obsolete, but that still wouldn't help when consuming the attribute from other languages.

Copy link
Member

@jcouv jcouv Mar 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From discussion with @cston two options seem ok:

  • report the worse one
  • report both

Either seems fine, whatever is easiest to implement. #Closed

Copy link
Contributor

@AlekseyTs AlekseyTs Mar 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be reasonable to give priority to the attribute on the accessor. #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current behaviour is to report both. Given that this is both an unlikely scenario, and the correct behaviour is debatable, I think it makes most sense to take the simplest option, and keep the current behaviour.

@@ -6447,38 +6553,50 @@ public int Prop
get { return 1; }
set { }
}
[Obsolete(""Property"", true)]
Copy link
Member

@jcouv jcouv Mar 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Obsolete(""Property"", true)] [](start = 4, length = 30)

is it intentional to have Obsolete on both the property and the setter here? The Obsolete on the setter does not seem to come into play #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like there's an (already existing) bug here in that we don't report diagnostics from an obsolete setter when we use the property in an attribute constructor.

using System;

[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class Att : Attribute
{
     public int Prop { get; [obsolete] set; }
}

[Att(Prop = 1)] // no warning reported here
public class Test
{
}

Although fixing this is a breaking change (as it is possible that people were already consuming such attributes from Visual Basic) I do not think it is an issue to fix as it is not binary breaking, and adding an Obsolete attribute is explicitly trying to break peoples source.

}
}

class Class6
{
int P1
public int P1
{
[Deprecated(""P1.get is deprecated."", DeprecationType.Remove, 1)]
Copy link
Member

@jcouv jcouv Mar 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Deprecated(""P1.get is deprecated."", DeprecationType.Remove, 1)] [](start = 8, length = 66)

Consider adding a test for Deprecated on the setter as well. #Closed

Copy link
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done with review pass (iteration 12).
Sorry for the delay. We've been focused on fixing nullable issues.

}
class E2 : E1
{
public override int Goo { [Obsolete] get; set;}
Copy link
Contributor

@AlekseyTs AlekseyTs Mar 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Obsolete] [](start = 30, length = 10)

Please make sure we have a separate unit-test for consumption of an overridden property, i.e. the Obsolete attribute is on different levels of inheritance hierarchy, including cases when not all accessors are overridden in a derived class #Closed

Copy link
Contributor Author

@YairHalberstadt YairHalberstadt Mar 22, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We completely ignore any obsolete attributes on an overridden member, and only look at the attributes on the base member. This is true for accessors, properties, methods, etc. #Resolved

AlekseyTs
AlekseyTs previously approved these changes Mar 21, 2019
Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done with review pass (iteration 12)

@AlekseyTs AlekseyTs dismissed their stale review March 21, 2019 21:03

Didn't mean to sign-off

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Mar 21, 2019

Done with review pass (iteration 12) #Closed

@jcouv jcouv self-requested a review March 22, 2019 17:40

class Base
{
public virtual int Foo { [Obsolete] get; set;}
Copy link
Member

@jcouv jcouv Mar 22, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Foo [](start = 23, length = 3)

Foo is disallowed in the codebase, as it triggers some word check tools we run. #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope Boo is better than Foo :-)

Copy link
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM Thanks (iteration 16) modulo "Foo"

@YairHalberstadt
Copy link
Contributor Author

@jcouv
Is there anything else that needs to be done here?
Thanks

@jcouv
Copy link
Member

jcouv commented Mar 28, 2019

@AlekseyTs for second review. Thanks

if (namedArgumentNameSymbol.Kind == SymbolKind.Property)
{
var propertySymbol = (PropertySymbol)namedArgumentNameSymbol;
if (propertySymbol.SetMethod != null)
Copy link
Contributor

@AlekseyTs AlekseyTs Mar 29, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

propertySymbol [](start = 20, length = 14)

If the property is overridden, which property do we get here the one from the base, or the one from the derived type? Derived type doesn't have to override all accessors. If we are dealing with an overriding property that overrides only getter, we will fail the null check and will not check the setter for obsoleteness. Please add a test for a scenario like that. #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for spotting that. Fixed.

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Mar 29, 2019

Done with review pass (iteration 17) #Closed

Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM (iteration 21)

@jcouv jcouv merged commit ef24d3f into dotnet:master Apr 2, 2019
@jcouv
Copy link
Member

jcouv commented Apr 2, 2019

Merged for dev16.1 preview2. Thanks @YairHalberstadt!

@gafter
Copy link
Member

gafter commented Apr 2, 2019

Thank you so much for your contribution, @YairHalberstadt!

jonpryor pushed a commit to dotnet/java-interop that referenced this pull request Dec 14, 2022
Fixes: #1033

Context: dotnet/roslyn#32571

When we are converting Java get-method and set-method pairs to
C# properties, we can hit an interesting scenario where a getter may
be `@Deprecated` and the setter is not, or vice versa:

	class Example {
	  public boolean hasOptionsMenu () { ... }

	  @deprecated
	  public void setHasOptionsMenu (boolean hasMenu) { ... }
	}

C# has traditionally not allowed `[Obsolete]` to be placed on just a
`get` or a `set` block; it could only be on the entire property:

	partial class Example {
	  [Obsolete]
	  public bool HasOptionsMenu { get; set; }
	}

This can lead to confusion because using the getter will report an
obsolete warning when it is not obsolete.  Thus, for properties, we
only add `[Obsolete]` in 2 cases:

 1. The `get` is obsolete and there is no `set`
 2. Both the `get` and `set` are obsolete

We have this comment in our code:

	// Unlike [Register], [Obsolete] cannot be put on property accessors, so we can apply them only under limited condition...

However, the C# compiler team has determined that preventing
`[Obsolete]` on property accessors was a bug, and has fixed it in C#8
via dotnet/roslyn#32571.

Update `generator` to support scenarios in which only the Java getter
or setter is marked as `@Deprecated`, by placing `[Obsolete]` on the
appropriate `get` or `set` block:

	partial class Example {
	    public bool HasOptionsMenu {
	        get => …

	        [Obsolete]
	        set =>…
	    }
	}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers Community The pull request was submitted by a contributor who is not a Microsoft employee.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants