Skip to content

Commit f2ddeba

Browse files
authored
Check modifiers on record positional members (#45898)
1 parent 421bf4b commit f2ddeba

File tree

4 files changed

+245
-11
lines changed

4 files changed

+245
-11
lines changed

src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,14 @@ internal override bool IsExpressionBodied
158158
}
159159
}
160160

161+
protected override bool AllowRefOrOut
162+
{
163+
get
164+
{
165+
return true;
166+
}
167+
}
168+
161169
protected override bool IsWithinExpressionOrBlockBody(int position, out int offset)
162170
{
163171
ConstructorDeclarationSyntax ctorSyntax = GetSyntax();

src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ protected sealed override void MethodChecks(DiagnosticBag diagnostics)
4848
SyntaxToken arglistToken;
4949
_lazyParameters = ParameterHelpers.MakeParameters(
5050
signatureBinder, this, parameterList, out arglistToken,
51-
allowRefOrOut: true,
51+
allowRefOrOut: AllowRefOrOut,
5252
allowThis: false,
5353
addRefReadOnlyModifier: false,
5454
diagnostics: diagnostics);
@@ -72,6 +72,8 @@ protected sealed override void MethodChecks(DiagnosticBag diagnostics)
7272

7373
#nullable enable
7474
protected abstract ParameterListSyntax GetParameterList();
75+
76+
protected abstract bool AllowRefOrOut { get; }
7577
#nullable restore
7678

7779
internal sealed override void AfterAddingTypeMembersChecks(ConversionsBase conversions, DiagnosticBag diagnostics)

src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordConstructor.cs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,8 @@
44

55
#nullable enable
66

7-
using System.Collections.Immutable;
87
using System.Diagnostics;
98
using Microsoft.CodeAnalysis.CSharp.Syntax;
10-
using Microsoft.CodeAnalysis.PooledObjects;
11-
using Roslyn.Utilities;
129

1310
namespace Microsoft.CodeAnalysis.CSharp.Symbols
1411
{
@@ -36,13 +33,9 @@ internal RecordDeclarationSyntax GetSyntax()
3633
return GetSyntax().PrimaryConstructorBaseType;
3734
}
3835

39-
internal override bool IsExpressionBodied
40-
{
41-
get
42-
{
43-
return false;
44-
}
45-
}
36+
protected override bool AllowRefOrOut => false;
37+
38+
internal override bool IsExpressionBodied => false;
4639

4740
protected override bool IsWithinExpressionOrBlockBody(int position, out int offset)
4841
{

src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15864,6 +15864,237 @@ record R(int P1, int* P2, delegate*<int> P3);";
1586415864
Assert.True(p.HasPointerType);
1586515865
}
1586615866

15867+
[Fact, WorkItem(45008, "https://github.com/dotnet/roslyn/issues/45008")]
15868+
public void PositionalMemberModifiers_RefOrOut()
15869+
{
15870+
var src = @"
15871+
record R(ref int P1, out int P2);
15872+
";
15873+
15874+
var comp = CreateCompilation(src);
15875+
comp.VerifyEmitDiagnostics(
15876+
// (2,9): error CS0177: The out parameter 'P2' must be assigned to before control leaves the current method
15877+
// record R(ref int P1, out int P2);
15878+
Diagnostic(ErrorCode.ERR_ParamUnassigned, "(ref int P1, out int P2)").WithArguments("P2").WithLocation(2, 9),
15879+
// (2,10): error CS0631: ref and out are not valid in this context
15880+
// record R(ref int P1, out int P2);
15881+
Diagnostic(ErrorCode.ERR_IllegalRefParam, "ref").WithLocation(2, 10),
15882+
// (2,22): error CS0631: ref and out are not valid in this context
15883+
// record R(ref int P1, out int P2);
15884+
Diagnostic(ErrorCode.ERR_IllegalRefParam, "out").WithLocation(2, 22)
15885+
);
15886+
}
15887+
15888+
[Fact, WorkItem(45008, "https://github.com/dotnet/roslyn/issues/45008")]
15889+
public void PositionalMemberModifiers_RefOrOut_WithBase()
15890+
{
15891+
var src = @"
15892+
record Base(int I);
15893+
record R(ref int P1, out int P2) : Base(P2 = 1);
15894+
";
15895+
15896+
var comp = CreateCompilation(src);
15897+
comp.VerifyEmitDiagnostics(
15898+
// (3,10): error CS0631: ref and out are not valid in this context
15899+
// record R(ref int P1, out int P2) : Base(P2 = 1);
15900+
Diagnostic(ErrorCode.ERR_IllegalRefParam, "ref").WithLocation(3, 10),
15901+
// (3,22): error CS0631: ref and out are not valid in this context
15902+
// record R(ref int P1, out int P2) : Base(P2 = 1);
15903+
Diagnostic(ErrorCode.ERR_IllegalRefParam, "out").WithLocation(3, 22)
15904+
);
15905+
}
15906+
15907+
[Fact, WorkItem(45008, "https://github.com/dotnet/roslyn/issues/45008")]
15908+
public void PositionalMemberModifiers_In()
15909+
{
15910+
var src = @"
15911+
record R(in int P1);
15912+
15913+
public class C
15914+
{
15915+
public static void Main()
15916+
{
15917+
var r = new R(42);
15918+
int i = 43;
15919+
var r2 = new R(in i);
15920+
System.Console.Write((r.P1, r2.P1));
15921+
}
15922+
}
15923+
";
15924+
15925+
var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, parseOptions: TestOptions.RegularPreview, options: TestOptions.DebugExe);
15926+
comp.VerifyDiagnostics();
15927+
CompileAndVerify(comp, expectedOutput: "(42, 43)", verify: Verification.Skipped /* init-only */);
15928+
15929+
var actualMembers = comp.GetMember<NamedTypeSymbol>("R").Constructors.ToTestDisplayStrings();
15930+
var expectedMembers = new[]
15931+
{
15932+
"R..ctor(in System.Int32 P1)",
15933+
"R..ctor(R )"
15934+
};
15935+
AssertEx.Equal(expectedMembers, actualMembers);
15936+
}
15937+
15938+
[Fact, WorkItem(45008, "https://github.com/dotnet/roslyn/issues/45008")]
15939+
public void PositionalMemberModifiers_This()
15940+
{
15941+
var src = @"
15942+
record R(this int i);
15943+
";
15944+
15945+
var comp = CreateCompilation(src);
15946+
comp.VerifyEmitDiagnostics(
15947+
// (2,10): error CS0027: Keyword 'this' is not available in the current context
15948+
// record R(this int i);
15949+
Diagnostic(ErrorCode.ERR_ThisInBadContext, "this").WithLocation(2, 10)
15950+
);
15951+
}
15952+
15953+
[Fact, WorkItem(45008, "https://github.com/dotnet/roslyn/issues/45008")]
15954+
public void PositionalMemberModifiers_Params()
15955+
{
15956+
var src = @"
15957+
record R(params int[] Array);
15958+
15959+
public class C
15960+
{
15961+
public static void Main()
15962+
{
15963+
var r = new R(42, 43);
15964+
var r2 = new R(new[] { 44, 45 });
15965+
System.Console.Write((r.Array[0], r.Array[1], r2.Array[0], r2.Array[1]));
15966+
}
15967+
}
15968+
";
15969+
15970+
var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, parseOptions: TestOptions.RegularPreview, options: TestOptions.DebugExe);
15971+
comp.VerifyDiagnostics();
15972+
CompileAndVerify(comp, expectedOutput: "(42, 43, 44, 45)", verify: Verification.Skipped /* init-only */);
15973+
15974+
var actualMembers = comp.GetMember<NamedTypeSymbol>("R").Constructors.ToTestDisplayStrings();
15975+
var expectedMembers = new[]
15976+
{
15977+
"R..ctor(params System.Int32[] Array)",
15978+
"R..ctor(R )"
15979+
};
15980+
AssertEx.Equal(expectedMembers, actualMembers);
15981+
}
15982+
15983+
[Fact, WorkItem(45008, "https://github.com/dotnet/roslyn/issues/45008")]
15984+
public void PositionalMemberDefaultValue()
15985+
{
15986+
var src = @"
15987+
record R(int P = 42)
15988+
{
15989+
public static void Main()
15990+
{
15991+
var r = new R();
15992+
System.Console.Write(r.P);
15993+
}
15994+
}
15995+
";
15996+
15997+
var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, parseOptions: TestOptions.RegularPreview, options: TestOptions.DebugExe);
15998+
comp.VerifyDiagnostics();
15999+
CompileAndVerify(comp, expectedOutput: "42", verify: Verification.Skipped /* init-only */);
16000+
}
16001+
16002+
[Fact, WorkItem(45008, "https://github.com/dotnet/roslyn/issues/45008")]
16003+
public void PositionalMemberDefaultValue_AndPropertyWithInitializer()
16004+
{
16005+
var src = @"
16006+
record R(int P = 1)
16007+
{
16008+
public int P { get; init; } = 42;
16009+
16010+
public static void Main()
16011+
{
16012+
var r = new R();
16013+
System.Console.Write(r.P);
16014+
}
16015+
}
16016+
";
16017+
var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, parseOptions: TestOptions.RegularPreview, options: TestOptions.DebugExe);
16018+
comp.VerifyDiagnostics();
16019+
var verifier = CompileAndVerify(comp, expectedOutput: "42", verify: Verification.Skipped /* init-only */);
16020+
16021+
verifier.VerifyIL("R..ctor(int)", @"
16022+
{
16023+
// Code size 16 (0x10)
16024+
.maxstack 2
16025+
IL_0000: ldarg.0
16026+
IL_0001: ldc.i4.s 42
16027+
IL_0003: stfld ""int R.<P>k__BackingField""
16028+
IL_0008: ldarg.0
16029+
IL_0009: call ""object..ctor()""
16030+
IL_000e: nop
16031+
IL_000f: ret
16032+
}");
16033+
}
16034+
16035+
[Fact, WorkItem(45008, "https://github.com/dotnet/roslyn/issues/45008")]
16036+
public void PositionalMemberDefaultValue_AndPropertyWithoutInitializer()
16037+
{
16038+
var src = @"
16039+
record R(int P = 42)
16040+
{
16041+
public int P { get; init; }
16042+
16043+
public static void Main()
16044+
{
16045+
var r = new R();
16046+
System.Console.Write(r.P);
16047+
}
16048+
}
16049+
";
16050+
var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, parseOptions: TestOptions.RegularPreview, options: TestOptions.DebugExe);
16051+
comp.VerifyDiagnostics();
16052+
var verifier = CompileAndVerify(comp, expectedOutput: "0", verify: Verification.Skipped /* init-only */);
16053+
16054+
verifier.VerifyIL("R..ctor(int)", @"
16055+
{
16056+
// Code size 8 (0x8)
16057+
.maxstack 1
16058+
IL_0000: ldarg.0
16059+
IL_0001: call ""object..ctor()""
16060+
IL_0006: nop
16061+
IL_0007: ret
16062+
}");
16063+
}
16064+
16065+
[Fact, WorkItem(45008, "https://github.com/dotnet/roslyn/issues/45008")]
16066+
public void PositionalMemberDefaultValue_AndPropertyWithInitializer_CopyingParameter()
16067+
{
16068+
var src = @"
16069+
record R(int P = 42)
16070+
{
16071+
public int P { get; init; } = P;
16072+
16073+
public static void Main()
16074+
{
16075+
var r = new R();
16076+
System.Console.Write(r.P);
16077+
}
16078+
}
16079+
";
16080+
var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, parseOptions: TestOptions.RegularPreview, options: TestOptions.DebugExe);
16081+
comp.VerifyDiagnostics();
16082+
var verifier = CompileAndVerify(comp, expectedOutput: "42", verify: Verification.Skipped /* init-only */);
16083+
16084+
verifier.VerifyIL("R..ctor(int)", @"
16085+
{
16086+
// Code size 15 (0xf)
16087+
.maxstack 2
16088+
IL_0000: ldarg.0
16089+
IL_0001: ldarg.1
16090+
IL_0002: stfld ""int R.<P>k__BackingField""
16091+
IL_0007: ldarg.0
16092+
IL_0008: call ""object..ctor()""
16093+
IL_000d: nop
16094+
IL_000e: ret
16095+
}");
16096+
}
16097+
1586716098
[Fact]
1586816099
public void AttributesOnPrimaryConstructorParameters_01()
1586916100
{

0 commit comments

Comments
 (0)