diff --git a/src/Compilers/CSharp/Portable/Lowering/Instrumentation/DynamicAnalysisInjector.cs b/src/Compilers/CSharp/Portable/Lowering/Instrumentation/DynamicAnalysisInjector.cs index 85bdacd13cc96..4c35bdbc31279 100644 --- a/src/Compilers/CSharp/Portable/Lowering/Instrumentation/DynamicAnalysisInjector.cs +++ b/src/Compilers/CSharp/Portable/Lowering/Instrumentation/DynamicAnalysisInjector.cs @@ -590,10 +590,8 @@ private static Text.TextSpan SkipAttributes(SyntaxNode syntax) return SkipAttributes(syntax, propertySyntax.AttributeLists, propertySyntax.Modifiers, default(SyntaxToken), propertySyntax.Type); case SyntaxKind.DataPropertyDeclaration: - { - var dataProp = (DataPropertyDeclarationSyntax)syntax; - return SkipAttributes(syntax, dataProp.AttributeLists, dataProp.Modifiers, dataProp.DataKeyword, dataProp.Type); - } + Debug.Assert(false, "data properties cannot contain bodies"); + break; case SyntaxKind.GetAccessorDeclaration: case SyntaxKind.SetAccessorDeclaration: diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs index d6794e6184813..5f0d56abddf4b 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs @@ -174,6 +174,9 @@ public static bool IsInTypeOnlyContext(ExpressionSyntax node) case PropertyDeclaration: return ((PropertyDeclarationSyntax)parent).Type == node; + case DataPropertyDeclaration: + return ((DataPropertyDeclarationSyntax)parent).Type == node; + case DelegateDeclaration: return ((DelegateDeclarationSyntax)parent).ReturnType == node; @@ -254,7 +257,7 @@ public static bool IsInNamespaceOrTypeContext(ExpressionSyntax? node) } /// - /// Is the node the name of a named argument of an invocation, object creation expression, + /// Is the node the name of a named argument of an invocation, object creation expression, /// constructor initializer, or element access, but not an attribute. /// public static bool IsNamedArgumentName(SyntaxNode node) diff --git a/src/Compilers/CSharp/Test/Emit/Emit/DynamicAnalysis/DynamicAnalysisResourceTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/DynamicAnalysis/DynamicAnalysisResourceTests.cs index 0b79f1f5edf3e..f4b18ac2c2bf2 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/DynamicAnalysis/DynamicAnalysisResourceTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/DynamicAnalysis/DynamicAnalysisResourceTests.cs @@ -281,7 +281,7 @@ public static void Main() // Method 0 { Fred(); } - + [Obsolete()] static void Fred() // Method 1 { @@ -341,7 +341,7 @@ ref int BamBam(ref int x) // Method 7 var reader = DynamicAnalysisDataReader.TryCreateFromPE(peReader, ""); VerifyDocuments(reader, reader.Documents, - @"'C:\myproject\doc1.cs' A3-08-94-55-7C-64-8D-C7-61-7A-11-0B-4B-68-2C-3B-51-C3-C4-58 (SHA1)"); + @"'C:\myproject\doc1.cs' A4-02-50-34-9F-CD-91-F7-CB-94-31-74-4C-E6-71-07-8E-8A-F9-DD (SHA1)"); Assert.Equal(15, reader.Methods.Length); @@ -404,7 +404,7 @@ public static void Main() // Method 0 s.GPA = 2.3; Operate(s); } - + static string Operate(Person p) // Method 1 { switch (p) @@ -611,10 +611,13 @@ static C() // Method 4 int Prop1 { get; } = 15; static int Prop2 { get; } = 255; + [Obsolete] + data int Prop3 = 16; } "; - var c = CreateCompilation(Parse(source + InstrumentationHelperSource, @"C:\myproject\doc1.cs")); + var c = CreateCompilation(Parse(source + IsExternalInitTypeDefinition + InstrumentationHelperSource, + @"C:\myproject\doc1.cs", options: TestOptions.RegularPreview)); var peImage = c.EmitToArray(EmitOptions.Default.WithInstrumentationKinds(ImmutableArray.Create(InstrumentationKind.TestCoverage))); var peReader = new PEReader(peImage); @@ -640,6 +643,7 @@ static C() // Method 4 new SpanResult(27, 13, 27, 19, "Init()"), new SpanResult(28, 13, 28, 24, "Init() + 12"), new SpanResult(44, 25, 44, 27, "15"), + new SpanResult(47, 21, 47, 23, "16"), new SpanResult(19, 8, 19, 16, "_z = 12")); VerifySpans(reader, reader.Methods[4], sourceLines, @@ -654,6 +658,7 @@ static C() // Method 4 new SpanResult(27, 13, 27, 19, "Init()"), new SpanResult(28, 13, 28, 24, "Init() + 12"), new SpanResult(44, 25, 44, 27, "15"), + new SpanResult(47, 21, 47, 23, "16"), new SpanResult(36, 8, 36, 15, "_z = x")); VerifySpans(reader, reader.Methods[6], sourceLines, @@ -661,6 +666,7 @@ static C() // Method 4 new SpanResult(27, 13, 27, 19, "Init()"), new SpanResult(28, 13, 28, 24, "Init() + 12"), new SpanResult(44, 25, 44, 27, "15"), + new SpanResult(47, 21, 47, 23, "16"), new SpanResult(41, 8, 41, 19, "_z = a + b")); } diff --git a/src/Compilers/CSharp/Test/Emit/Emit/DynamicAnalysis/DynamicInstrumentationTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/DynamicAnalysis/DynamicInstrumentationTests.cs index 1e18060ffb92a..f34d0ea901e7e 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/DynamicAnalysis/DynamicInstrumentationTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/DynamicAnalysis/DynamicInstrumentationTests.cs @@ -16,6 +16,65 @@ namespace Microsoft.CodeAnalysis.CSharp.DynamicAnalysis.UnitTests { public class DynamicInstrumentationTests : CSharpTestBase { + [Fact] + public void DataPropertyInitializer() + { + string source = @" +using System; +using System.Diagnostics.CodeAnalysis; + +class C +{ + data int P1; + data int P2 = 25; + + public static void Main(string[] args) + { + new C(); + Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload(); + } +} +"; + var verifier = CompileAndVerify(source + InstrumentationHelperSource + IsExternalInitTypeDefinition, + options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); + + // data accessors are implicit, therefore not instrumented + AssertNotInstrumented(verifier, "C.P1.get"); + AssertNotInstrumented(verifier, "C.P1.init"); + + verifier.VerifyIL("C..ctor()", @" +{ + // Code size 71 (0x47) + .maxstack 5 + .locals init (bool[] V_0) + IL_0000: ldsfld ""bool[][] .PayloadRoot0"" + IL_0005: ldtoken ""C..ctor()"" + IL_000a: ldelem.ref + IL_000b: stloc.0 + IL_000c: ldloc.0 + IL_000d: brtrue.s IL_0034 + IL_000f: ldsfld ""System.Guid .MVID"" + IL_0014: ldtoken ""C..ctor()"" + IL_0019: ldtoken Source Document 0 + IL_001e: ldsfld ""bool[][] .PayloadRoot0"" + IL_0023: ldtoken ""C..ctor()"" + IL_0028: ldelema ""bool[]"" + IL_002d: ldc.i4.1 + IL_002e: call ""bool[] Microsoft.CodeAnalysis.Runtime.Instrumentation.CreatePayload(System.Guid, int, int, ref bool[], int)"" + IL_0033: stloc.0 + IL_0034: ldloc.0 + IL_0035: ldc.i4.0 + IL_0036: ldc.i4.1 + IL_0037: stelem.i1 + IL_0038: ldarg.0 + IL_0039: ldc.i4.s 25 + IL_003b: stfld ""int C.k__BackingField"" + IL_0040: ldarg.0 + IL_0041: call ""object..ctor()"" + IL_0046: ret +}"); + } + [Fact] public void HelpersInstrumentation() { @@ -766,7 +825,7 @@ public static void Main(string[] args) // Metho TestMain(); Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload(); } - + static void TestMain() // Method 2 { int x = Count; @@ -873,11 +932,11 @@ public static void Main(string[] args) // Metho TestMain(); Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload(); } - + static void TestMain() // Method 2 { new D().M1(); - } + } } public class D @@ -971,7 +1030,7 @@ public static void Main(string[] args) // Metho TestMain(); Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload(); } - + #line 20 ""File2.cs"" static void TestMain() // Method 2 { @@ -1442,7 +1501,7 @@ static void TestMain() // Method 2 s.GPA = s.Name switch { _ => 2.3 }; // switch expression is not instrumented Operate(s); } - + static string Operate(Person p) // Method 3 { switch (p) @@ -1866,7 +1925,7 @@ File 1 public void IteratorCoverage() { string source = @" -using System; +using System; public class Program { @@ -1879,11 +1938,11 @@ public static void Main(string[] args) // Metho static void TestMain() // Method 2 { foreach (var i in Goo()) - { + { Console.WriteLine(i); - } + } foreach (var i in Goo()) - { + { Console.WriteLine(i); } } @@ -2280,14 +2339,14 @@ public void MissingMethodNeededForAnalysis() string source = @" namespace System { - public class Object { } - public struct Int32 { } - public struct Boolean { } - public class String { } - public class Exception { } - public class ValueType { } - public class Enum { } - public struct Void { } + public class Object { } + public struct Int32 { } + public struct Boolean { } + public class String { } + public class Exception { } + public class ValueType { } + public class Enum { } + public struct Void { } public class Guid { } } @@ -2397,7 +2456,7 @@ class C { [ExcludeFromCodeCoverage] static void M1() { L1(); void L1() { new Action(() => { Console.WriteLine(1); }).Invoke(); } } - + static void M2() { L2(); void L2() { new Action(() => { Console.WriteLine(2); }).Invoke(); } } } "; @@ -2561,16 +2620,16 @@ public void ExcludeFromCodeCoverageAttribute_LocalFunctionsAndLambdas_InAccessor class C { [ExcludeFromCodeCoverage] - int P1 - { - get { L1(); void L1() { Console.WriteLine(1); } return 1; } - set { L2(); void L2() { Console.WriteLine(2); } } + int P1 + { + get { L1(); void L1() { Console.WriteLine(1); } return 1; } + set { L2(); void L2() { Console.WriteLine(2); } } } int P2 - { - get { L3(); void L3() { Console.WriteLine(3); } return 3; } - set { L4(); void L4() { Console.WriteLine(4); } } + { + get { L3(); void L3() { Console.WriteLine(3); } return 3; } + set { L4(); void L4() { Console.WriteLine(4); } } } } "; @@ -2712,10 +2771,10 @@ class C { [ExcludeFromCodeCoverage] int P1 { get => 1; set {} } - + [ExcludeFromCodeCoverage] event Action E1 { add { } remove { } } - + int P2 { get => 1; set {} } event Action E2 { add { } remove { } } } @@ -2842,7 +2901,7 @@ public void TestPartialMethodsWithImplementation() public partial class Class1 { partial void Method1(int x); - public void Method2(int x) + public void Method2(int x) { Console.WriteLine($""Method2: x = {x}""); Method1(x); @@ -2944,7 +3003,7 @@ public void TestPartialMethodsWithoutImplementation() public partial class Class1 { partial void Method1(int x); - public void Method2(int x) + public void Method2(int x) { Console.WriteLine($""Method2: x = {x}""); Method1(x); @@ -3391,7 +3450,7 @@ private static void AssertInstrumented(CompilationVerifier verifier, string qual { string il = verifier.VisualizeIL(qualifiedMethodName); - // Tests using this helper are constructed such that instrumented methods contain a call to CreatePayload, + // Tests using this helper are constructed such that instrumented methods contain a call to CreatePayload, // lambdas a reference to payload bool array. bool instrumented = il.Contains("CreatePayload") || il.Contains("bool[]"); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs index 6ece292273c8e..b7a05b76f9c88 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs @@ -196,8 +196,8 @@ public void GeneratedProperties() Assert.False(y.IsWriteOnly); Assert.False(y.IsImplicitlyDeclared); Assert.Equal(Accessibility.Public, y.DeclaredAccessibility); - Assert.False(x.IsVirtual); - Assert.False(x.IsStatic); + Assert.False(y.IsVirtual); + Assert.False(y.IsStatic); Assert.Equal(c, y.ContainingType); Assert.Equal(c, y.ContainingSymbol); @@ -1736,6 +1736,150 @@ class C ); } + [Fact] + public void DataProperties11() + { + var comp = CreateCompilation(@" +abstract class C +{ + data int P1; + virtual data int P2; + abstract data int P3; +}"); + comp.VerifyDiagnostics(); + var c = comp.GlobalNamespace.GetTypeMember("C"); + + var p1 = (SourcePropertySymbolBase)c.GetProperty("P1"); + Assert.NotNull(p1.GetMethod); + Assert.Equal(MethodKind.PropertyGet, p1.GetMethod.MethodKind); + Assert.Equal(SpecialType.System_Int32, p1.Type.SpecialType); + Assert.False(p1.IsReadOnly); + Assert.False(p1.IsWriteOnly); + Assert.False(p1.IsImplicitlyDeclared); + Assert.Equal(Accessibility.Public, p1.DeclaredAccessibility); + Assert.False(p1.IsVirtual); + Assert.False(p1.IsStatic); + Assert.Equal(c, p1.ContainingType); + Assert.Equal(c, p1.ContainingSymbol); + + var backing = p1.BackingField; + Debug.Assert(backing != null); + Assert.Equal(p1, backing.AssociatedSymbol); + Assert.Equal(c, backing.ContainingSymbol); + Assert.Equal(c, backing.ContainingType); + Assert.True(backing.IsImplicitlyDeclared); + + var getAccessor = p1.GetMethod; + Assert.Equal(p1, getAccessor.AssociatedSymbol); + Assert.True(getAccessor.IsImplicitlyDeclared); + Assert.Equal(c, getAccessor.ContainingSymbol); + Assert.Equal(c, getAccessor.ContainingType); + Assert.Equal(Accessibility.Public, getAccessor.DeclaredAccessibility); + + var setAccessor = p1.SetMethod; + Assert.Equal(p1, setAccessor.AssociatedSymbol); + Assert.True(setAccessor.IsImplicitlyDeclared); + Assert.Equal(c, setAccessor.ContainingSymbol); + Assert.Equal(c, setAccessor.ContainingType); + Assert.Equal(Accessibility.Public, setAccessor.DeclaredAccessibility); + Assert.True(setAccessor.IsInitOnly); + + var p2 = (SourcePropertySymbolBase)c.GetProperty("P2"); + Assert.NotNull(p2.GetMethod); + Assert.Equal(MethodKind.PropertyGet, p2.GetMethod.MethodKind); + Assert.Equal(SpecialType.System_Int32, p2.Type.SpecialType); + Assert.False(p2.IsReadOnly); + Assert.False(p2.IsWriteOnly); + Assert.False(p2.IsImplicitlyDeclared); + Assert.Equal(Accessibility.Public, p2.DeclaredAccessibility); + Assert.True(p2.IsVirtual); + Assert.False(p2.IsStatic); + Assert.Equal(c, p2.ContainingType); + Assert.Equal(c, p2.ContainingSymbol); + + backing = p2.BackingField; + Debug.Assert(backing != null); + Assert.Equal(p2, backing.AssociatedSymbol); + Assert.Equal(c, backing.ContainingSymbol); + Assert.Equal(c, backing.ContainingType); + Assert.True(backing.IsImplicitlyDeclared); + + getAccessor = p2.GetMethod; + Assert.Equal(p2, getAccessor.AssociatedSymbol); + Assert.True(getAccessor.IsImplicitlyDeclared); + Assert.Equal(c, getAccessor.ContainingSymbol); + Assert.Equal(c, getAccessor.ContainingType); + + setAccessor = p2.SetMethod; + Assert.Equal(p2, setAccessor.AssociatedSymbol); + Assert.True(setAccessor.IsImplicitlyDeclared); + Assert.Equal(c, setAccessor.ContainingSymbol); + Assert.Equal(c, setAccessor.ContainingType); + Assert.Equal(Accessibility.Public, setAccessor.DeclaredAccessibility); + Assert.True(setAccessor.IsInitOnly); + + var p3 = (SourcePropertySymbolBase)c.GetProperty("P3"); + Assert.NotNull(p3.GetMethod); + Assert.Equal(MethodKind.PropertyGet, p3.GetMethod.MethodKind); + Assert.Equal(SpecialType.System_Int32, p3.Type.SpecialType); + Assert.False(p3.IsReadOnly); + Assert.False(p3.IsWriteOnly); + Assert.False(p3.IsImplicitlyDeclared); + Assert.Equal(Accessibility.Public, p3.DeclaredAccessibility); + Assert.False(p3.IsVirtual); + Assert.True(p3.IsAbstract); + Assert.False(p3.IsStatic); + Assert.Equal(c, p3.ContainingType); + Assert.Equal(c, p3.ContainingSymbol); + + Assert.Null(p3.BackingField); + + getAccessor = p3.GetMethod; + Assert.True(getAccessor.IsAbstract); + Assert.Equal(p3, getAccessor.AssociatedSymbol); + Assert.True(getAccessor.IsImplicitlyDeclared); + Assert.Equal(c, getAccessor.ContainingSymbol); + Assert.Equal(c, getAccessor.ContainingType); + + setAccessor = p3.SetMethod; + Assert.True(setAccessor.IsAbstract); + Assert.Equal(p3, setAccessor.AssociatedSymbol); + Assert.True(setAccessor.IsImplicitlyDeclared); + Assert.Equal(c, setAccessor.ContainingSymbol); + Assert.Equal(c, setAccessor.ContainingType); + Assert.Equal(Accessibility.Public, setAccessor.DeclaredAccessibility); + Assert.True(setAccessor.IsInitOnly); + } + + [Fact] + public void DataProperties12() + { + var src = @" +class C +{ + data dynamic P; +}"; + var comp = CreateCompilation(src); + comp.MakeTypeMissing(WellKnownType.System_Runtime_CompilerServices_DynamicAttribute); + comp.VerifyDiagnostics( + // (4,10): error CS1980: Cannot define a class or member that utilizes 'dynamic' because the compiler required type 'System.Runtime.CompilerServices.DynamicAttribute' cannot be found. Are you missing a reference? + // data dynamic P; + Diagnostic(ErrorCode.ERR_DynamicAttributeMissing, "dynamic").WithArguments("System.Runtime.CompilerServices.DynamicAttribute").WithLocation(4, 10) + ); + + CompileAndVerify(new[] { src, IsExternalInitTypeDefinition }, + parseOptions: TestOptions.RegularPreview, + symbolValidator: m => + { + var p = m.GlobalNamespace.GetTypeMember("C").GetMember("P"); + var attr = Assert.Single(p.GetAttributes()).AttributeClass!; + Assert.Equal( + "System.Runtime.CompilerServices.DynamicAttribute", + attr.ToTestDisplayString()); + Assert.NotEqual(m.ContainingAssembly, attr.ContainingAssembly); + }); + } + [Fact] public void DataPropertiesInterface() { @@ -1818,6 +1962,27 @@ class C5 : C4 ); } + [Fact] + public void DataPropertiesSealed() + { + var src = @" +abstract class C1 +{ + abstract data int P1; +} +class C2 : C1 +{ + sealed override data int P1; +}"; + + var comp = CreateCompilation(src); + comp.VerifyDiagnostics( + // (8,30): error CS0106: The modifier 'sealed' is not valid for this item + // sealed override data int P1; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "P1").WithArguments("sealed").WithLocation(8, 30) + ); + } + [Fact] public void GenericRecord() {