From 0af9dcf7eb7452e8b0482ee8f952dadeed63ad47 Mon Sep 17 00:00:00 2001 From: yair halberstadt Date: Fri, 18 Jan 2019 09:01:26 +0000 Subject: [PATCH] Add further unit tests, and fix bug with suppression of obolete inside accessor declared obsolete. --- .../Symbols/ObsoleteAttributeHelpers.cs | 17 +- .../AttributeTests_WellKnownAttributes.cs | 188 +++++++++++++++--- 2 files changed, 166 insertions(+), 39 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/ObsoleteAttributeHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/ObsoleteAttributeHelpers.cs index 9afb5890dd6..708f55bec99 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ObsoleteAttributeHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ObsoleteAttributeHelpers.cs @@ -55,12 +55,7 @@ private static ThreeState GetObsoleteContextState(Symbol symbol, bool forceCompl { while ((object)symbol != null) { - // For property or event accessors, check the associated property or event instead. - if (symbol.IsAccessor()) - { - symbol = ((MethodSymbol)symbol).AssociatedSymbol; - } - else if (symbol.Kind == SymbolKind.Field) + if (symbol.Kind == SymbolKind.Field) { // If this is the backing field of an event, look at the event instead. var associatedSymbol = ((FieldSymbol)symbol).AssociatedSymbol; @@ -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()) + { + symbol = ((MethodSymbol)symbol).AssociatedSymbol; + } + else + { + symbol = symbol.ContainingSymbol; + } } return ThreeState.False; diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.cs index 5f7e06cb21f..94c6c0dca45 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.cs @@ -5881,7 +5881,7 @@ public static class TestExtension Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "gt.event1").WithArguments("GenericTest.event1", "Do not use this event").WithLocation(45, 9), // (121,28): warning CS0067: The event 'GenericTest.event1' is never used // public event Action event1; - Diagnostic(ErrorCode.WRN_UnreferencedEvent, "event1").WithArguments("GenericTest.event1").WithLocation(121, 28) + Diagnostic(ErrorCode.WRN_UnreferencedEvent, "event1").WithArguments("GenericTest.event1").WithLocation(121, 28)); } [Fact] @@ -5985,6 +5985,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; } + + public int Prop3 { get; [Obsolete(""Do not use Prop3.Get"", true)] set; } + [Obsolete(""Do not use field1"", true)] public TestClass field1; @@ -6010,37 +6014,47 @@ public static void Main() c = c.field1; c.event1(); c.event1 += () => {}; + c.Prop2 = 42; + i = c.Prop2; + c.Prop3 = 42; + i = c.Prop3; } } "; CreateCompilation(source, new[] { peReference }).VerifyDiagnostics( - // (4,29): warning CS0612: 'TestClass1' is obsolete - // public static void goo1(TestClass1 c) {} - Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "TestClass1").WithArguments("TestClass1"), // (5,29): warning CS0618: 'TestClass2' is obsolete: 'TestClass2 is obsolete' // public static void goo2(TestClass2 c) {} - Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "TestClass2").WithArguments("TestClass2", "TestClass2 is obsolete"), + Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "TestClass2").WithArguments("TestClass2", "TestClass2 is obsolete").WithLocation(5, 29), // (6,29): error CS0619: 'TestClass3' is obsolete: 'Do not use TestClass3' // public static void goo3(TestClass3 c) {} - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "TestClass3").WithArguments("TestClass3", "Do not use TestClass3"), + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "TestClass3").WithArguments("TestClass3", "Do not use TestClass3").WithLocation(6, 29), // (7,29): warning CS0618: 'TestClass4' is obsolete: 'TestClass4 is obsolete' // public static void goo4(TestClass4 c) {} - Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "TestClass4").WithArguments("TestClass4", "TestClass4 is obsolete"), + Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "TestClass4").WithArguments("TestClass4", "TestClass4 is obsolete").WithLocation(7, 29), + // (4,29): warning CS0612: 'TestClass1' is obsolete + // public static void goo1(TestClass1 c) {} + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "TestClass1").WithArguments("TestClass1").WithLocation(4, 29), // (12,9): warning CS0618: 'TestClass.TestMethod()' is obsolete: 'Do not use TestMethod' // c.TestMethod(); - Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "c.TestMethod()").WithArguments("TestClass.TestMethod()", "Do not use TestMethod"), + Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "c.TestMethod()").WithArguments("TestClass.TestMethod()", "Do not use TestMethod").WithLocation(12, 9), // (13,17): warning CS0618: 'TestClass.Prop1' is obsolete: 'Do not use Prop1' // var i = c.Prop1; - Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "c.Prop1").WithArguments("TestClass.Prop1", "Do not use Prop1"), + Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "c.Prop1").WithArguments("TestClass.Prop1", "Do not use Prop1").WithLocation(13, 17), // (14,13): error CS0619: 'TestClass.field1' is obsolete: 'Do not use field1' // c = c.field1; - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "c.field1").WithArguments("TestClass.field1", "Do not use field1"), + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "c.field1").WithArguments("TestClass.field1", "Do not use field1").WithLocation(14, 13), // (15,9): error CS0619: 'TestClass.event1' is obsolete: 'Do not use event' // c.event1(); - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "c.event1").WithArguments("TestClass.event1", "Do not use event"), + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "c.event1").WithArguments("TestClass.event1", "Do not use event").WithLocation(15, 9), // (16,9): error CS0619: 'TestClass.event1' is obsolete: 'Do not use event' // c.event1 += () => {}; - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "c.event1").WithArguments("TestClass.event1", "Do not use event")); + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "c.event1").WithArguments("TestClass.event1", "Do not use event").WithLocation(16, 9), + // (18,13): warning CS0618: 'TestClass.Prop2.get' is obsolete: 'Do not use Prop2.Get' + // i = c.Prop2; + Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "c.Prop2").WithArguments("TestClass.Prop2.get", "Do not use Prop2.Get").WithLocation(18, 13), + // (19,9): error CS0619: 'TestClass.Prop3.set' is obsolete: 'Do not use Prop3.Get' + // c.Prop3 = 42; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "c.Prop3").WithArguments("TestClass.Prop3.set", "Do not use Prop3.Get").WithLocation(19, 9)); } [Fact] @@ -6103,20 +6117,82 @@ class D6 : D5 [Obsolete] public override void goo() {} } + +class E1 +{ + public virtual int Goo {get; set;} +} +class E2 : E1 +{ + public override int Goo { [Obsolete] get; set;} +} +class E3 : E1 +{ + public new int Goo { [Obsolete] get; set;} +} +class E4 : E1 +{ + public override int Goo {get; set;} +} +class E5 : E4 +{ + public override int Goo { [Obsolete] get; set;} +} +class E6 : E5 +{ + public override int Goo {get; set;} +} + +class F1 +{ + public virtual int Goo { [Obsolete] get; set;} +} +class F2 : F1 +{ + public override int Goo {get; set;} +} +class F3 : F1 +{ + public new int Goo {get; set;} +} +class F4 : F1 +{ + public override int Goo { [Obsolete] get; set;} +} +class F5 : F4 +{ + public override int Goo {get; set;} +} +class F6 : F5 +{ + public override int Goo { [Obsolete] get; set;} +} "; CreateCompilation(source).VerifyDiagnostics( // (10,26): warning CS0809: Obsolete member 'C2.goo()' overrides non-obsolete member 'C1.goo()' // public override void goo() {} - Diagnostic(ErrorCode.WRN_ObsoleteOverridingNonObsolete, "goo").WithArguments("C2.goo()", "C1.goo()"), - // (24,26): warning CS0809: Obsolete member 'C5.goo()' overrides non-obsolete member 'C1.goo()' + Diagnostic(ErrorCode.WRN_ObsoleteOverridingNonObsolete, "goo").WithArguments("C2.goo()", "C1.goo()").WithLocation(10, 26), + // (90,30): warning CS0672: Member 'F2.Goo.get' overrides obsolete member 'F1.Goo.get'. Add the Obsolete attribute to 'F2.Goo.get'. + // public override int Goo {get; set;} + Diagnostic(ErrorCode.WRN_NonObsoleteOverridingObsolete, "get").WithArguments("F2.Goo.get", "F1.Goo.get").WithLocation(90, 30), + // (77,42): warning CS0809: Obsolete member 'E5.Goo.get' overrides non-obsolete member 'E1.Goo.get' + // public override int Goo { [Obsolete] get; set;} + Diagnostic(ErrorCode.WRN_ObsoleteOverridingNonObsolete, "get").WithArguments("E5.Goo.get", "E1.Goo.get").WithLocation(77, 42), + // (51,26): warning CS0672: Member 'D5.goo()' overrides obsolete member 'D1.goo()'. Add the Obsolete attribute to 'D5.goo()'. // public override void goo() {} - Diagnostic(ErrorCode.WRN_ObsoleteOverridingNonObsolete, "goo").WithArguments("C5.goo()", "C1.goo()"), + Diagnostic(ErrorCode.WRN_NonObsoleteOverridingObsolete, "goo").WithArguments("D5.goo()", "D1.goo()").WithLocation(51, 26), // (38,26): warning CS0672: Member 'D2.goo()' overrides obsolete member 'D1.goo()'. Add the Obsolete attribute to 'D2.goo()'. // public override void goo() {} - Diagnostic(ErrorCode.WRN_NonObsoleteOverridingObsolete, "goo").WithArguments("D2.goo()", "D1.goo()"), - // (51,26): warning CS0672: Member 'D5.goo()' overrides obsolete member 'D1.goo()'. Add the Obsolete attribute to 'D5.goo()'. + Diagnostic(ErrorCode.WRN_NonObsoleteOverridingObsolete, "goo").WithArguments("D2.goo()", "D1.goo()").WithLocation(38, 26), + // (24,26): warning CS0809: Obsolete member 'C5.goo()' overrides non-obsolete member 'C1.goo()' // public override void goo() {} - Diagnostic(ErrorCode.WRN_NonObsoleteOverridingObsolete, "goo").WithArguments("D5.goo()", "D1.goo()")); + Diagnostic(ErrorCode.WRN_ObsoleteOverridingNonObsolete, "goo").WithArguments("C5.goo()", "C1.goo()").WithLocation(24, 26), + // (102,30): warning CS0672: Member 'F5.Goo.get' overrides obsolete member 'F1.Goo.get'. Add the Obsolete attribute to 'F5.Goo.get'. + // public override int Goo {get; set;} + Diagnostic(ErrorCode.WRN_NonObsoleteOverridingObsolete, "get").WithArguments("F5.Goo.get", "F1.Goo.get").WithLocation(102, 30), + // (65,42): warning CS0809: Obsolete member 'E2.Goo.get' overrides non-obsolete member 'E1.Goo.get' + // public override int Goo { [Obsolete] get; set;} + Diagnostic(ErrorCode.WRN_ObsoleteOverridingNonObsolete, "get").WithArguments("E2.Goo.get", "E1.Goo.get").WithLocation(65, 42)); } [Fact] @@ -6311,7 +6387,9 @@ public class Test event Action someEvent; [Obsolete] - public static SomeType someProp { get; set; } + public static SomeType someProp { get => new SomeType(); set {} } + + public static string someProp2 { [Obsolete] get => new SomeType().ToString(); } [Obsolete] SomeType this[int x] { get { SomeType y = new SomeType(); return y; } } @@ -6464,6 +6542,11 @@ public int Prop get { return 1; } set { } } + [Obsolete(""Property"", true)] + public int Prop2 + { + get ; [Obsolete] set; + } [Obsolete(""Field"", true)] public int Field; } @@ -6471,6 +6554,7 @@ public int Prop [Att] [Att(Field = 1)] [Att(Prop = 1)] +[Att(Prop2 = 1)] public class Test { [Att()] @@ -6478,24 +6562,30 @@ public class Test } "; CreateCompilation(source).VerifyDiagnostics( - // (20,6): error CS0619: 'Att.Field' is obsolete: 'Field' + // (24,2): error CS0619: 'Att.Att()' is obsolete: 'Constructor' + // [Att] + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "Att").WithArguments("Att.Att()", "Constructor").WithLocation(24, 2), + // (25,6): error CS0619: 'Att.Field' is obsolete: 'Field' // [Att(Field = 1)] - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "Field = 1").WithArguments("Att.Field", "Field"), - // (20,2): error CS0619: 'Att.Att()' is obsolete: 'Constructor' + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "Field = 1").WithArguments("Att.Field", "Field").WithLocation(25, 6), + // (25,2): error CS0619: 'Att.Att()' is obsolete: 'Constructor' // [Att(Field = 1)] - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "Att(Field = 1)").WithArguments("Att.Att()", "Constructor"), - // (21,6): error CS0619: 'Att.Prop' is obsolete: 'Property' + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "Att(Field = 1)").WithArguments("Att.Att()", "Constructor").WithLocation(25, 2), + // (26,6): error CS0619: 'Att.Prop' is obsolete: 'Property' // [Att(Prop = 1)] - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "Prop = 1").WithArguments("Att.Prop", "Property"), - // (21,2): error CS0619: 'Att.Att()' is obsolete: 'Constructor' + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "Prop = 1").WithArguments("Att.Prop", "Property").WithLocation(26, 6), + // (26,2): error CS0619: 'Att.Att()' is obsolete: 'Constructor' // [Att(Prop = 1)] - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "Att(Prop = 1)").WithArguments("Att.Att()", "Constructor"), - // (24,6): error CS0619: 'Att.Att()' is obsolete: 'Constructor' + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "Att(Prop = 1)").WithArguments("Att.Att()", "Constructor").WithLocation(26, 2), + // (27,6): error CS0619: 'Att.Prop2' is obsolete: 'Property' + // [Att(Prop2 = 1)] + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "Prop2 = 1").WithArguments("Att.Prop2", "Property").WithLocation(27, 6), + // (27,2): error CS0619: 'Att.Att()' is obsolete: 'Constructor' + // [Att(Prop2 = 1)] + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "Att(Prop2 = 1)").WithArguments("Att.Att()", "Constructor").WithLocation(27, 2), + // (30,6): error CS0619: 'Att.Att()' is obsolete: 'Constructor' // [Att()] - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "Att()").WithArguments("Att.Att()", "Constructor"), - // (19,2): error CS0619: 'Att.Att()' is obsolete: 'Constructor' - // [Att] - Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "Att").WithArguments("Att.Att()", "Constructor")); + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "Att()").WithArguments("Att.Att()", "Constructor").WithLocation(30, 6)); } [Fact] @@ -8987,5 +9077,39 @@ void M(in int x) // error CS0616: 'IsReadOnlyAttribute' is not an attribute class Diagnostic(ErrorCode.ERR_NotAnAttributeClass).WithArguments("System.Runtime.CompilerServices.IsReadOnlyAttribute").WithLocation(1, 1)); } + + [Fact] + public void TestObsoleteOnPropertyAccessorUsedInNameofAndXmlDocComment() + { + var code = @" +using System; +/// +/// +/// +class C +{ + const string str = nameof(Prop); + + public int Prop { [Obsolete] get; [Obsolete] set; } +} +"; + + CreateCompilation(code).VerifyDiagnostics().VerifyEmitDiagnostics(); + } + + [Fact] + public void TestObsoleteOnPropertyAndAccessors() + { + var code = @" +using System; +class C +{ + [Obsolete] + public int Prop { [Obsolete] get; [Obsolete] set; } +} +"; + + CreateCompilation(code).VerifyDiagnostics().VerifyEmitDiagnostics(); + } } } -- GitLab