-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Conversation
…bsolete works as expected.
…e accessor declared obsolete.
…warnings from an obsolete return type
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()) |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gafter |
// 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()); | ||
} |
There was a problem hiding this comment.
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; } |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.cs
Show resolved
Hide resolved
@@ -6447,38 +6553,50 @@ public int Prop | |||
get { return 1; } | |||
set { } | |||
} | |||
[Obsolete(""Property"", true)] |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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)] |
There was a problem hiding this comment.
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
There was a problem hiding this 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;} |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this 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)
Done with review pass (iteration 12) #Closed |
…s, and Don't report 2 diagnostics for obsolete on an event in C# 7.
|
||
class Base | ||
{ | ||
public virtual int Foo { [Obsolete] get; set;} |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 :-)
There was a problem hiding this 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"
@jcouv |
@AlekseyTs for second review. Thanks |
if (namedArgumentNameSymbol.Kind == SymbolKind.Property) | ||
{ | ||
var propertySymbol = (PropertySymbol)namedArgumentNameSymbol; | ||
if (propertySymbol.SetMethod != null) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
Done with review pass (iteration 17) #Closed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM (iteration 21)
Merged for dev16.1 preview2. Thanks @YairHalberstadt! |
Thank you so much for your contribution, @YairHalberstadt! |
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 =>… } }
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:
Allow Obsolete and Deprecated attributes on property accessors in C# 8. They remain invalid on event accessors.
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.
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.
The error for using obsolete/deprecated on an attribute pre C# 8 now reads:
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.