diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
index 91fcd884dc576..e2b3ed4647928 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
@@ -1582,7 +1582,7 @@ private static bool AccessingAutoPropertyFromConstructor(BoundExpression receive
propertySymbol = propertySymbol.OriginalDefinition;
}
- var sourceProperty = propertySymbol as SourcePropertySymbol;
+ var sourceProperty = propertySymbol as SourceOrRecordPropertySymbol;
var propertyIsStatic = propertySymbol.IsStatic;
return (object)sourceProperty != null &&
diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx
index 4af53ad5a846a..fa51098ddc2db 100644
--- a/src/Compilers/CSharp/Portable/CSharpResources.resx
+++ b/src/Compilers/CSharp/Portable/CSharpResources.resx
@@ -5942,4 +5942,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
Records must have both a 'data' modifier and parameter list
+
+ There cannot be a primary constructor and a member constructor with the same parameter types.
+
\ No newline at end of file
diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
index ced7fa0cfa207..9bbca3c962a5d 100644
--- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
+++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
@@ -1741,6 +1741,16 @@ private static BoundBlock BindMethodBody(MethodSymbol method, TypeCompilationSta
return null;
}
}
+ else if (method is SynthesizedInstanceConstructor ctor)
+ {
+ // Synthesized instance constructors may partially synthesize
+ // their body
+ var node = ctor.GetNonNullSyntaxNode();
+ var factory = new SyntheticBoundNodeFactory(ctor, node, compilationState, diagnostics);
+ var stmts = ArrayBuilder.GetInstance();
+ ctor.GenerateMethodBodyStatements(factory, stmts, diagnostics);
+ body = BoundBlock.SynthesizedNoLocals(node, stmts.ToImmutableAndFree());
+ }
else
{
// synthesized methods should return their bound bodies
diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
index 3d438c78013df..282e70cca400d 100644
--- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
+++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
@@ -1743,6 +1743,7 @@ internal enum ErrorCode
ERR_AmbigBinaryOpsOnUnconstrainedDefault = 8761,
ERR_BadRecordDeclaration = 8770,
+ ERR_DuplicateRecordConstructor = 8771,
// Note: you will need to re-generate compiler code after adding warnings (eng\generate-compiler-code.cmd)
}
diff --git a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs
index 9b2f590686289..57fdf4792e486 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs
@@ -346,7 +346,7 @@ private void SynthesizeClosureEnvironments(ArrayBuilder closur
AddSynthesizedMethod(
frame.Constructor,
FlowAnalysisPass.AppendImplicitReturn(
- MethodCompiler.BindMethodBody(frame.Constructor, CompilationState, null),
+ MethodCompiler.BindMethodBody(frame.Constructor, CompilationState, Diagnostics),
frame.Constructor));
}
@@ -539,7 +539,7 @@ private SynthesizedClosureEnvironment GetStaticFrame(DiagnosticBag diagnostics,
AddSynthesizedMethod(
frame.Constructor,
FlowAnalysisPass.AppendImplicitReturn(
- MethodCompiler.BindMethodBody(frame.Constructor, CompilationState, null),
+ MethodCompiler.BindMethodBody(frame.Constructor, CompilationState, diagnostics),
frame.Constructor));
// add cctor
diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs
index 08b2f545c98e8..341029989ccec 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs
@@ -277,6 +277,8 @@ private BoundExpression MakeStaticAssignmentOperator(
}
}
+#nullable enable
+
private BoundExpression MakePropertyAssignment(
SyntaxNode syntax,
BoundExpression rewrittenReceiver,
@@ -292,12 +294,13 @@ private BoundExpression MakePropertyAssignment(
// Rewrite property assignment into call to setter.
var setMethod = property.GetOwnOrInheritedSetMethod();
- if ((object)setMethod == null)
+ if (setMethod is null)
{
- Debug.Assert((property as SourcePropertySymbol)?.IsAutoProperty == true,
+ var autoProp = (SourceOrRecordPropertySymbol)property;
+ Debug.Assert(autoProp.IsAutoProperty,
"only autoproperties can be assignable without having setters");
- var backingField = (property as SourcePropertySymbol).BackingField;
+ var backingField = autoProp.BackingField;
return _factory.AssignmentExpression(
_factory.Field(rewrittenReceiver, backingField),
rewrittenRight);
@@ -323,7 +326,7 @@ private BoundExpression MakePropertyAssignment(
// Save expression value to a temporary before calling the
// setter, and restore the temporary after the setter, so the
// assignment can be used as an embedded expression.
- TypeSymbol exprType = rewrittenRight.Type;
+ TypeSymbol? exprType = rewrittenRight.Type;
LocalSymbol rhsTemp = _factory.SynthesizedLocal(exprType);
diff --git a/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs b/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs
index 9d786915d71cc..ec6638f89e67d 100644
--- a/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs
@@ -446,8 +446,11 @@ public int GetHashCode(Symbol member)
// CONSIDER: modify hash for constraints?
- hash = Hash.Combine(member.GetMemberArity(), hash);
- hash = Hash.Combine(member.GetParameterCount(), hash);
+ if (member.Kind != SymbolKind.Field)
+ {
+ hash = Hash.Combine(member.GetMemberArity(), hash);
+ hash = Hash.Combine(member.GetParameterCount(), hash);
+ }
}
return hash;
}
diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs
index c0946146c9551..04ca163051751 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs
@@ -2323,6 +2323,7 @@ internal void AddOrWrapTupleMembers(SourceMemberContainerTypeSymbol type)
case TypeKind.Struct:
CheckForStructBadInitializers(builder, diagnostics);
CheckForStructDefaultConstructors(builder.NonTypeNonIndexerMembers, isEnum: false, diagnostics: diagnostics);
+ AddSynthesizedRecordMembersIfNecessary(builder, diagnostics);
AddSynthesizedConstructorsIfNecessary(builder.NonTypeNonIndexerMembers, builder.StaticInitializers, diagnostics);
break;
@@ -2334,6 +2335,7 @@ internal void AddOrWrapTupleMembers(SourceMemberContainerTypeSymbol type)
case TypeKind.Class:
case TypeKind.Interface:
case TypeKind.Submission:
+ AddSynthesizedRecordMembersIfNecessary(builder, diagnostics);
// No additional checking required.
AddSynthesizedConstructorsIfNecessary(builder.NonTypeNonIndexerMembers, builder.StaticInitializers, diagnostics);
break;
@@ -2846,9 +2848,19 @@ private void CheckForStructBadInitializers(MembersAndInitializersBuilder builder
}
}
- private void AddSynthesizedRecordMembersIfNecessary(ArrayBuilder members, DiagnosticBag diagnostics)
+ private void AddSynthesizedRecordMembersIfNecessary(MembersAndInitializersBuilder builder, DiagnosticBag diagnostics)
{
- Debug.Assert(declaration.Kind == DeclarationKind.Class || declaration.Kind == DeclarationKind.Struct);
+ switch (declaration.Kind)
+ {
+ case DeclarationKind.Class:
+ case DeclarationKind.Struct:
+ break;
+
+ default:
+ return;
+ }
+
+ var members = builder.NonTypeNonIndexerMembers;
ParameterListSyntax? paramList = null;
foreach (SingleTypeDeclaration decl in declaration.Declarations)
@@ -2901,6 +2913,21 @@ private void AddSynthesizedRecordMembersIfNecessary(ArrayBuilder members
{
members.Add(ctor);
}
+ else
+ {
+ diagnostics.Add(ErrorCode.ERR_DuplicateRecordConstructor, paramList.Location);
+ }
+
+ foreach (ParameterSymbol param in ctor.Parameters)
+ {
+ var property = new SynthesizedRecordPropertySymbol(this, param);
+ if (!memberSignatures.Contains(property))
+ {
+ members.Add(property);
+ members.Add(property.GetMethod);
+ members.Add(property.BackingField);
+ }
+ }
}
private void AddSynthesizedConstructorsIfNecessary(ArrayBuilder members, ArrayBuilder> staticInitializers, DiagnosticBag diagnostics)
@@ -2909,7 +2936,6 @@ private void AddSynthesizedConstructorsIfNecessary(ArrayBuilder members,
{
case DeclarationKind.Class:
case DeclarationKind.Struct:
- AddSynthesizedRecordMembersIfNecessary(members, diagnostics);
break;
}
diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs
index c8766eda8c632..06fa8e5e100be 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs
@@ -555,9 +555,9 @@ private void CheckMembersAgainstBaseType(
}
}
}
- else
+ else if (property is SourcePropertySymbol sourceProperty)
{
- var isNewProperty = ((SourcePropertySymbol)property).IsNew;
+ var isNewProperty = sourceProperty.IsNew;
CheckNonOverrideMember(property, isNewProperty, property.OverriddenOrHiddenMembers, diagnostics, out suppressAccessors);
if (!suppressAccessors)
diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrRecordPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrRecordPropertySymbol.cs
new file mode 100644
index 0000000000000..da5440291d09f
--- /dev/null
+++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrRecordPropertySymbol.cs
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace Microsoft.CodeAnalysis.CSharp.Symbols
+{
+ internal abstract class SourceOrRecordPropertySymbol : PropertySymbol, IAttributeTargetSymbol
+ {
+ public Location Location { get; }
+
+ public SourceOrRecordPropertySymbol(Location location)
+ {
+ Location = location;
+ }
+
+ internal abstract SynthesizedBackingFieldSymbol BackingField { get; }
+
+ internal abstract bool IsAutoProperty { get; }
+
+ internal abstract bool HasPointerType { get; }
+
+ public abstract SyntaxList AttributeDeclarationSyntaxList { get; }
+
+ protected abstract IAttributeTargetSymbol AttributesOwner { get; }
+
+ protected abstract AttributeLocation AllowedAttributeLocations { get; }
+
+ protected abstract AttributeLocation DefaultAttributeLocation { get; }
+
+ IAttributeTargetSymbol IAttributeTargetSymbol.AttributesOwner => AttributesOwner;
+
+ AttributeLocation IAttributeTargetSymbol.AllowedAttributeLocations => AllowedAttributeLocations;
+
+ AttributeLocation IAttributeTargetSymbol.DefaultAttributeLocation => DefaultAttributeLocation;
+ }
+}
diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs
index 42b82d25e51cc..94bbb570e3bf0 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs
@@ -15,7 +15,7 @@
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
- internal sealed class SourcePropertySymbol : PropertySymbol, IAttributeTargetSymbol
+ internal sealed class SourcePropertySymbol : SourceOrRecordPropertySymbol
{
///
/// Condensed flags storing useful information about the
@@ -36,12 +36,10 @@ private enum Flags : byte
private readonly SourceMemberContainerTypeSymbol _containingType;
private readonly string _name;
private readonly SyntaxReference _syntaxRef;
- private readonly Location _location;
private readonly DeclarationModifiers _modifiers;
private readonly ImmutableArray _refCustomModifiers;
private readonly SourcePropertyAccessorSymbol _getMethod;
private readonly SourcePropertyAccessorSymbol _setMethod;
- private readonly SynthesizedBackingFieldSymbol _backingField;
private readonly TypeSymbol _explicitInterfaceType;
private readonly ImmutableArray _explicitInterfaceImplementations;
private readonly Flags _propertyFlags;
@@ -71,6 +69,7 @@ private SourcePropertySymbol(
string name,
Location location,
DiagnosticBag diagnostics)
+ : base(location)
{
// This has the value that IsIndexer will ultimately have, once we've populated the fields of this object.
bool isIndexer = syntax.Kind() == SyntaxKind.IndexerDeclaration;
@@ -81,7 +80,6 @@ private SourcePropertySymbol(
_propertyFlags |= Flags.IsExplicitInterfaceImplementation;
}
- _location = location;
_containingType = containingType;
_syntaxRef = syntax.GetReference();
_refKind = syntax.Type.GetRefKind();
@@ -192,7 +190,7 @@ private SourcePropertySymbol(
}
string fieldName = GeneratedNames.MakeBackingFieldName(_sourceName);
- _backingField = new SynthesizedBackingFieldSymbol(this,
+ BackingField = new SynthesizedBackingFieldSymbol(this,
fieldName,
isGetterOnly,
this.IsStatic,
@@ -545,7 +543,7 @@ public override TypeWithAnnotations TypeWithAnnotations
}
}
- internal bool HasPointerType
+ internal override bool HasPointerType
{
get
{
@@ -602,22 +600,14 @@ public override NamedTypeSymbol ContainingType
internal override LexicalSortKey GetLexicalSortKey()
{
- return new LexicalSortKey(_location, this.DeclaringCompilation);
+ return new LexicalSortKey(Location, this.DeclaringCompilation);
}
public override ImmutableArray Locations
{
get
{
- return ImmutableArray.Create(_location);
- }
- }
-
- internal Location Location
- {
- get
- {
- return _location;
+ return ImmutableArray.Create(Location);
}
}
@@ -749,17 +739,14 @@ public bool HasSkipLocalsInitAttribute
}
}
- internal bool IsAutoProperty
+ internal override bool IsAutoProperty
=> (_propertyFlags & Flags.IsAutoProperty) != 0;
///
/// Backing field for automatically implemented property, or
/// for a property with an initializer.
///
- internal SynthesizedBackingFieldSymbol BackingField
- {
- get { return _backingField; }
- }
+ internal override SynthesizedBackingFieldSymbol BackingField { get; }
internal override bool MustCallMethodsDirectly
{
@@ -782,6 +769,9 @@ internal BasePropertyDeclarationSyntax CSharpSyntaxNode
}
}
+ public override SyntaxList AttributeDeclarationSyntaxList
+ => CSharpSyntaxNode.AttributeLists;
+
internal SyntaxTree SyntaxTree
{
get
@@ -1153,17 +1143,11 @@ private SynthesizedSealedPropertyAccessor MakeSynthesizedSealedAccessor()
#region Attributes
- IAttributeTargetSymbol IAttributeTargetSymbol.AttributesOwner
- {
- get { return this; }
- }
+ protected override IAttributeTargetSymbol AttributesOwner => this;
- AttributeLocation IAttributeTargetSymbol.DefaultAttributeLocation
- {
- get { return AttributeLocation.Property; }
- }
+ protected override AttributeLocation DefaultAttributeLocation => AttributeLocation.Property;
- AttributeLocation IAttributeTargetSymbol.AllowedAttributeLocations
+ protected override AttributeLocation AllowedAttributeLocations
=> (_propertyFlags & Flags.IsAutoProperty) != 0
? AttributeLocation.Property | AttributeLocation.Field
: AttributeLocation.Property;
@@ -1183,7 +1167,7 @@ private CustomAttributesBag GetAttributesBag()
}
// The property is responsible for completion of the backing field
- _ = _backingField?.GetAttributes();
+ _ = BackingField?.GetAttributes();
if (LoadAndValidateAttributes(OneOrMany.Create(this.CSharpSyntaxNode.AttributeLists), ref _lazyCustomAttributesBag))
{
@@ -1562,7 +1546,7 @@ internal override void ForceComplete(SourceLocation locationOpt, CancellationTok
{
var diagnostics = DiagnosticBag.GetInstance();
var conversions = new TypeConversions(this.ContainingAssembly.CorLibrary);
- this.Type.CheckAllConstraints(DeclaringCompilation, conversions, _location, diagnostics);
+ this.Type.CheckAllConstraints(DeclaringCompilation, conversions, Location, diagnostics);
var type = this.Type;
if (type.IsRestrictedType(ignoreSpanLikeTypes: true))
@@ -1613,15 +1597,15 @@ private TypeWithAnnotations ComputeType(Binder binder, BasePropertyDeclarationSy
{
// "Inconsistent accessibility: indexer return type '{1}' is less accessible than indexer '{0}'"
// "Inconsistent accessibility: property type '{1}' is less accessible than property '{0}'"
- diagnostics.Add((this.IsIndexer ? ErrorCode.ERR_BadVisIndexerReturn : ErrorCode.ERR_BadVisPropertyType), _location, this, type.Type);
+ diagnostics.Add((this.IsIndexer ? ErrorCode.ERR_BadVisIndexerReturn : ErrorCode.ERR_BadVisPropertyType), Location, this, type.Type);
}
- diagnostics.Add(_location, useSiteDiagnostics);
+ diagnostics.Add(Location, useSiteDiagnostics);
if (type.IsVoidType())
{
ErrorCode errorCode = this.IsIndexer ? ErrorCode.ERR_IndexerCantHaveVoidType : ErrorCode.ERR_PropertyCantHaveVoidType;
- diagnostics.Add(errorCode, _location, this);
+ diagnostics.Add(errorCode, Location, this);
}
return type;
@@ -1637,15 +1621,15 @@ private ImmutableArray ComputeParameters(Binder binder, BasePro
{
if (syntax.ExplicitInterfaceSpecifier == null && !this.IsNoMoreVisibleThan(param.Type, ref useSiteDiagnostics))
{
- diagnostics.Add(ErrorCode.ERR_BadVisIndexerParam, _location, this, param.Type);
+ diagnostics.Add(ErrorCode.ERR_BadVisIndexerParam, Location, this, param.Type);
}
else if ((object)_setMethod != null && param.Name == ParameterSymbol.ValueParameterName)
{
- diagnostics.Add(ErrorCode.ERR_DuplicateGeneratedName, param.Locations.FirstOrDefault() ?? _location, param.Name);
+ diagnostics.Add(ErrorCode.ERR_DuplicateGeneratedName, param.Locations.FirstOrDefault() ?? Location, param.Name);
}
}
- diagnostics.Add(_location, useSiteDiagnostics);
+ diagnostics.Add(Location, useSiteDiagnostics);
return parameters;
}
diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs
index e1f05fbcda5ad..0451b39332c17 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs
@@ -14,13 +14,13 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols
///
internal sealed class SynthesizedBackingFieldSymbol : FieldSymbolWithAttributesAndModifiers
{
- private readonly SourcePropertySymbol _property;
+ private readonly SourceOrRecordPropertySymbol _property;
private readonly string _name;
internal bool HasInitializer { get; }
protected override DeclarationModifiers Modifiers { get; }
public SynthesizedBackingFieldSymbol(
- SourcePropertySymbol property,
+ SourceOrRecordPropertySymbol property,
string name,
bool isReadOnly,
bool isStatic,
@@ -45,7 +45,7 @@ internal override Location ErrorLocation
=> _property.Location;
protected override SyntaxList AttributeDeclarationSyntaxList
- => _property.CSharpSyntaxNode.AttributeLists;
+ => _property.AttributeDeclarationSyntaxList;
public override Symbol AssociatedSymbol
=> _property;
diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableAttributeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableAttributeSymbol.cs
index b03bd3bb15b03..2a4580571a19e 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableAttributeSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedNullableAttributeSymbol.cs
@@ -126,7 +126,7 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState,
GenerateMethodBodyCore(compilationState, diagnostics);
}
- protected override void GenerateMethodBodyStatements(SyntheticBoundNodeFactory factory, ArrayBuilder statements, DiagnosticBag diagnostics) => _getConstructorBody(factory, statements, _parameters);
+ internal override void GenerateMethodBodyStatements(SyntheticBoundNodeFactory factory, ArrayBuilder statements, DiagnosticBag diagnostics) => _getConstructorBody(factory, statements, _parameters);
}
}
diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceConstructor.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceConstructor.cs
index a585d72781c6e..4992c3868ed9b 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceConstructor.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceConstructor.cs
@@ -301,7 +301,7 @@ protected void GenerateMethodBodyCore(TypeCompilationState compilationState, Dia
factory.CloseMethod(block);
}
- protected virtual void GenerateMethodBodyStatements(SyntheticBoundNodeFactory factory, ArrayBuilder statements, DiagnosticBag diagnostics)
+ internal virtual void GenerateMethodBodyStatements(SyntheticBoundNodeFactory factory, ArrayBuilder statements, DiagnosticBag diagnostics)
{
// overridden in a derived class to add extra statements to the body of the generated constructor
}
diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedRecordConstructor.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedRecordConstructor.cs
index ce2643d655eb1..d271b2e9ccce1 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedRecordConstructor.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedRecordConstructor.cs
@@ -4,6 +4,7 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.PooledObjects;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
@@ -12,7 +13,7 @@ internal sealed class SynthesizedRecordConstructor : SynthesizedInstanceConstruc
public override ImmutableArray Parameters { get; }
public SynthesizedRecordConstructor(
- NamedTypeSymbol containingType,
+ SourceMemberContainerTypeSymbol containingType,
Binder parameterBinder,
ParameterListSyntax parameterList,
DiagnosticBag diagnostics)
@@ -28,5 +29,26 @@ public SynthesizedRecordConstructor(
allowThis: false,
addRefReadOnlyModifier: false);
}
+
+ internal override void GenerateMethodBodyStatements(SyntheticBoundNodeFactory F, ArrayBuilder statements, DiagnosticBag diagnostics)
+ {
+ // Write assignments to backing fields
+ //
+ // {
+ // this.backingField1 = arg1
+ // ...
+ // this.backingFieldN = argN
+ // }
+ var containing = (SourceMemberContainerTypeSymbol)ContainingType;
+ foreach (var param in Parameters)
+ {
+ var members = containing.GetMembers(param.Name);
+ if (members.Length == 1 && members[0] is SynthesizedRecordPropertySymbol prop)
+ {
+ var field = prop.BackingField;
+ statements.Add(F.Assignment(F.Field(F.This(), field), F.Parameter(param)));
+ }
+ }
+ }
}
}
diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedRecordPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedRecordPropertySymbol.cs
new file mode 100644
index 0000000000000..141d49a885cb1
--- /dev/null
+++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedRecordPropertySymbol.cs
@@ -0,0 +1,208 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Reflection;
+using System.Text;
+using Microsoft.Cci;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Roslyn.Utilities;
+
+namespace Microsoft.CodeAnalysis.CSharp.Symbols
+{
+ internal sealed class SynthesizedRecordPropertySymbol : SourceOrRecordPropertySymbol
+ {
+ private readonly ParameterSymbol _backingParameter;
+ internal override SynthesizedBackingFieldSymbol BackingField { get; }
+ public override MethodSymbol GetMethod { get; }
+ public override NamedTypeSymbol ContainingType { get; }
+
+ public SynthesizedRecordPropertySymbol(
+ NamedTypeSymbol containingType,
+ ParameterSymbol backingParameter)
+ : base(backingParameter.Locations[0])
+ {
+ ContainingType = containingType;
+ _backingParameter = backingParameter;
+ string name = backingParameter.Name;
+ BackingField = new SynthesizedBackingFieldSymbol(
+ this,
+ GeneratedNames.MakeBackingFieldName(name),
+ isReadOnly: true,
+ isStatic: false,
+ hasInitializer: backingParameter.HasExplicitDefaultValue);
+ GetMethod = new GetAccessorSymbol(this, name);
+ }
+
+ internal override bool IsAutoProperty => true;
+
+ public override RefKind RefKind => RefKind.None;
+
+ public override TypeWithAnnotations TypeWithAnnotations => _backingParameter.TypeWithAnnotations;
+
+ public override ImmutableArray RefCustomModifiers => ImmutableArray.Empty;
+
+ public override ImmutableArray Parameters => ImmutableArray.Empty;
+
+ public override bool IsIndexer => false;
+
+ public override MethodSymbol? SetMethod => null;
+
+ public override ImmutableArray ExplicitInterfaceImplementations => ImmutableArray.Empty;
+
+ public override Symbol ContainingSymbol => ContainingType;
+
+ public override ImmutableArray Locations => _backingParameter.Locations;
+
+ public override ImmutableArray DeclaringSyntaxReferences => _backingParameter.DeclaringSyntaxReferences;
+
+ public override Accessibility DeclaredAccessibility => Accessibility.Public;
+
+ public override bool IsStatic => false;
+
+ public override bool IsVirtual => false;
+
+ public override bool IsOverride => false;
+
+ public override bool IsAbstract => false;
+
+ public override bool IsSealed => false;
+
+ public override bool IsExtern => false;
+
+ internal override bool HasSpecialName => false;
+
+ internal override CallingConvention CallingConvention => CallingConvention.HasThis;
+
+ internal override bool MustCallMethodsDirectly => false;
+
+ internal override ObsoleteAttributeData? ObsoleteAttributeData => null;
+
+ public override string Name => _backingParameter.Name;
+
+ protected override IAttributeTargetSymbol AttributesOwner => this;
+
+ protected override AttributeLocation AllowedAttributeLocations => AttributeLocation.None;
+
+ protected override AttributeLocation DefaultAttributeLocation => AttributeLocation.None;
+
+ public override ImmutableArray GetAttributes() => ImmutableArray.Empty;
+
+ internal override bool HasPointerType => Type.IsPointerType();
+
+ public override SyntaxList AttributeDeclarationSyntaxList => new SyntaxList();
+
+ private sealed class GetAccessorSymbol : SynthesizedInstanceMethodSymbol
+ {
+ private readonly SynthesizedRecordPropertySymbol _property;
+
+ public override string Name { get; }
+
+ public GetAccessorSymbol(SynthesizedRecordPropertySymbol property, string paramName)
+ {
+ _property = property;
+ Name = SourcePropertyAccessorSymbol.GetAccessorName(
+ paramName,
+ getNotSet: true,
+ isWinMdOutput: false /* unused for getters */);
+ }
+
+ public override MethodKind MethodKind => MethodKind.PropertyGet;
+
+ public override int Arity => 0;
+
+ public override bool IsExtensionMethod => false;
+
+ public override bool HidesBaseMethodsByName => false;
+
+ public override bool IsVararg => false;
+
+ public override bool ReturnsVoid => false;
+
+ public override bool IsAsync => false;
+
+ public override RefKind RefKind => RefKind.None;
+
+ public override TypeWithAnnotations ReturnTypeWithAnnotations => _property.TypeWithAnnotations;
+
+ public override FlowAnalysisAnnotations ReturnTypeFlowAnalysisAnnotations => FlowAnalysisAnnotations.None;
+
+ public override ImmutableArray TypeArgumentsWithAnnotations => ImmutableArray.Empty;
+
+ public override ImmutableArray TypeParameters => ImmutableArray.Empty;
+
+ public override ImmutableArray Parameters => _property.Parameters;
+
+ public override ImmutableArray ExplicitInterfaceImplementations => ImmutableArray.Empty;
+
+ public override ImmutableArray RefCustomModifiers => _property.RefCustomModifiers;
+
+ public override Symbol AssociatedSymbol => _property;
+
+ public override Symbol ContainingSymbol => _property.ContainingSymbol;
+
+ public override ImmutableArray Locations => _property.Locations;
+
+ public override Accessibility DeclaredAccessibility => _property.DeclaredAccessibility;
+
+ public override bool IsStatic => _property.IsStatic;
+
+ public override bool IsVirtual => _property.IsVirtual;
+
+ public override bool IsOverride => _property.IsOverride;
+
+ public override bool IsAbstract => _property.IsAbstract;
+
+ public override bool IsSealed => _property.IsSealed;
+
+ public override bool IsExtern => _property.IsExtern;
+
+ public override ImmutableHashSet ReturnNotNullIfParameterNotNull => ImmutableHashSet.Empty;
+
+ internal override bool HasSpecialName => _property.HasSpecialName;
+
+ internal override MethodImplAttributes ImplementationAttributes => MethodImplAttributes.Managed;
+
+ internal override bool HasDeclarativeSecurity => false;
+
+ internal override MarshalPseudoCustomAttributeData? ReturnValueMarshallingInformation => null;
+
+ internal override bool RequiresSecurityObject => false;
+
+ internal override CallingConvention CallingConvention => CallingConvention.HasThis;
+
+ internal override bool GenerateDebugInfo => false;
+
+ public override DllImportData? GetDllImportData() => null;
+
+ internal override ImmutableArray GetAppliedConditionalSymbols()
+ => ImmutableArray.Empty;
+
+ internal override IEnumerable GetSecurityInformation()
+ => Array.Empty();
+
+ internal override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false) => false;
+
+ internal override bool IsMetadataVirtual(bool ignoreInterfaceImplementationChanges = false) => false;
+
+ internal override bool SynthesizesLoweredBoundBody => true;
+
+ internal override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics)
+ {
+ // Method body:
+ //
+ // {
+ // return this.<>backingField;
+ // }
+
+ var F = new SyntheticBoundNodeFactory(this, this.GetNonNullSyntaxNode(), compilationState, diagnostics);
+
+ F.CurrentFunction = this;
+ F.CloseMethod(F.Block(F.Return(F.Field(F.This(), _property.BackingField))));
+ }
+ }
+ }
+}
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
index 32a355cf523c5..2d174390d4f46 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
@@ -152,6 +152,11 @@
Pro přístupové objekty vlastnosti i indexeru {0} nelze zadat modifikátory readonly. Místo toho zadejte modifikátor readonly jenom pro vlastnost.
+
+
+ There cannot be a primary constructor and a member constructor with the same parameter types.
+
+
Příkaz nemůže začínat na else.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
index 5c38de835bf45..059b03296f619 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
@@ -152,6 +152,11 @@
readonly-Modifizierer können nicht für beide Accessoren der Eigenschaft oder des Indexers "{0}" angegeben werden. Legen Sie stattdessen einen readonly-Modifizierer für die Eigenschaft selbst fest.
+
+
+ There cannot be a primary constructor and a member constructor with the same parameter types.
+
+
Eine Anweisung kann nicht mit "else" beginnen.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
index 86ae17fd696f2..96f474d75835d 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
@@ -152,6 +152,11 @@
No se pueden especificar modificadores "readonly" en ambos descriptores de acceso de la propiedad o del indizador "{0}". En su lugar, coloque un modificador "readonly" en la propiedad.
+
+
+ There cannot be a primary constructor and a member constructor with the same parameter types.
+
+
“else” no puede iniciar una instrucción.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
index f0b21615ba4de..554701573a9d9 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
@@ -152,6 +152,11 @@
Impossible de spécifier des modificateurs 'readonly' sur les deux accesseurs de la propriété ou de l'indexeur '{0}'. À la place, mettez un modificateur 'readonly' sur la propriété elle-même.
+
+
+ There cannot be a primary constructor and a member constructor with the same parameter types.
+
+
'else' ne peut pas démarrer d'instruction.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
index 64d75dcc07de0..b1c4235e28d0d 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
@@ -152,6 +152,11 @@
Non è possibile specificare i modificatori 'readonly' in entrambe le funzioni di accesso della proprietà o dell'indicizzatore '{0}'. Inserire invece un modificatore 'readonly' nella proprietà stessa.
+
+
+ There cannot be a primary constructor and a member constructor with the same parameter types.
+
+
Un'istruzione non può iniziare con 'else'.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
index 50a29b0aa7061..2a7b975d1a58a 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
@@ -152,6 +152,11 @@
プロパティまたはインデクサー '{0}' の両方のアクセサーで 'readonly' 修飾子を指定することはできません。代わりに、プロパティ自体に 'readonly' 修飾子を指定してください。
+
+
+ There cannot be a primary constructor and a member constructor with the same parameter types.
+
+
'else' でステートメントを開始することはできません。
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
index 596da44396626..8871a1f24ec37 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
@@ -152,6 +152,11 @@
'{0}' 속성 또는 인덱서의 두 접근자에 'readonly' 한정자를 지정할 수 없습니다. 대신 속성 자체에 'readonly' 한정자를 지정하세요.
+
+
+ There cannot be a primary constructor and a member constructor with the same parameter types.
+
+
'else'로 문을 시작할 수 없습니다.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
index 9ca29e308cad4..453b2b4cad75a 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
@@ -152,6 +152,11 @@
Nie można określić modyfikatorów „readonly” dla obu metod dostępu właściwości lub indeksatora „{0}”. Zamiast tego dodaj modyfikator „readonly” do samej właściwości.
+
+
+ There cannot be a primary constructor and a member constructor with the same parameter types.
+
+
Instrukcja nie może rozpoczynać się od elementu „else”.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
index e823f50cd8abf..690bdf5c4145b 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
@@ -152,6 +152,11 @@
Não é possível especificar modificadores 'readonly' em ambos os acessadores de propriedade ou de indexador '{0}'. Nesse caso, coloque um modificador 'readonly' na própria propriedade.
+
+
+ There cannot be a primary constructor and a member constructor with the same parameter types.
+
+
'else' não pode iniciar uma instrução.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
index 6d1d5508d9e9d..406357ae91402 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
@@ -152,6 +152,11 @@
Запрещено указывать модификаторы readonly для обоих методов доступа свойства или индексатора "{0}". Вместо этого укажите модификатор readonly для самого свойства.
+
+
+ There cannot be a primary constructor and a member constructor with the same parameter types.
+
+
"else" не может запускать оператор.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
index ce79ded7e9302..bd3233b105908 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
@@ -152,6 +152,11 @@
'readonly' değiştiricileri, '{0}' özelliğinin veya dizin oluşturucusunun her iki erişimcisinde de belirtilemez. Bunun yerine özelliğin kendisine bir 'readonly' değiştiricisi koyun.
+
+
+ There cannot be a primary constructor and a member constructor with the same parameter types.
+
+
'else' bir deyim başlatamaz.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
index f86538360bee7..ad754953dc9e5 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
@@ -152,6 +152,11 @@
不能在属性或索引器 "{0}" 的两个访问器上指定 "readonly" 修饰符。而应在属性本身上指定 "readonly" 修饰符。
+
+
+ There cannot be a primary constructor and a member constructor with the same parameter types.
+
+
"else" 不能用在语句的开头。
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
index 01ead305f4923..cfdfd26784adc 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
@@ -152,6 +152,11 @@
在屬性和索引子 '{0}' 的存取子上均無法指定 'readonly' 修飾詞。請改在屬性自身上放置 'readonly' 修飾詞。
+
+
+ There cannot be a primary constructor and a member constructor with the same parameter types.
+
+
'else' 無法開始陳述式。
diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LookupPositionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LookupPositionTests.cs
index a7114c5a86dd9..2ca27eed87e60 100644
--- a/src/Compilers/CSharp/Test/Semantic/Semantics/LookupPositionTests.cs
+++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LookupPositionTests.cs
@@ -49,6 +49,8 @@ public void PositionalRecord2()
Add( // C Type parameters
"T"),
Add( // Members
+ "System.Int32 C.x { get; }",
+ "T C.t { get; }",
"System.Boolean System.Object.Equals(System.Object obj)",
"System.Boolean System.Object.Equals(System.Object objA, System.Object objB)",
"System.Boolean System.Object.ReferenceEquals(System.Object objA, System.Object objB)",
diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs
index 4966e4aa03e61..f448670a31d96 100644
--- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs
+++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs
@@ -8,6 +8,12 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics
{
public class RecordTests : CompilingTestBase
{
+ private static CSharpCompilation CreateCompilation(CSharpTestSource source)
+ => CSharpTestBase.CreateCompilation(source, parseOptions: TestOptions.RegularPreview);
+
+ private CompilationVerifier CompileAndVerify(CSharpTestSource src, string expectedOutput)
+ => base.CompileAndVerify(src, expectedOutput: expectedOutput, parseOptions: TestOptions.RegularPreview);
+
[Fact]
public void RecordLanguageVersion()
{
@@ -20,7 +26,7 @@ data class Point { }
var src3 = @"
data class Point(int x, int y);
";
- var comp = CreateCompilation(src1);
+ var comp = CreateCompilation(src1, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (2,12): error CS8652: The feature 'records' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// class Point(int x, int y);
@@ -32,7 +38,7 @@ data class Point { }
// class Point(int x, int y);
Diagnostic(ErrorCode.ERR_FeatureInPreview, ";").WithArguments("records").WithLocation(2, 26)
);
- comp = CreateCompilation(src2);
+ comp = CreateCompilation(src2, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (2,1): error CS8652: The feature 'records' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// data class Point { }
@@ -41,7 +47,7 @@ data class Point { }
// data class Point { }
Diagnostic(ErrorCode.ERR_BadRecordDeclaration, "Point").WithLocation(2, 12)
);
- comp = CreateCompilation(src3);
+ comp = CreateCompilation(src3, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (2,1): error CS8652: The feature 'records' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// data class Point(int x, int y);
@@ -54,20 +60,177 @@ data class Point { }
Diagnostic(ErrorCode.ERR_FeatureInPreview, ";").WithArguments("records").WithLocation(2, 31)
);
- comp = CreateCompilation(src1, parseOptions: TestOptions.RegularPreview);
+ comp = CreateCompilation(src1);
comp.VerifyDiagnostics(
// (2,12): error CS8761: Records must have both a 'data' modifier and parameter list
// class Point(int x, int y);
Diagnostic(ErrorCode.ERR_BadRecordDeclaration, "(int x, int y)").WithLocation(2, 12)
);
- comp = CreateCompilation(src2, parseOptions: TestOptions.RegularPreview);
+ comp = CreateCompilation(src2);
comp.VerifyDiagnostics(
// (2,12): error CS8761: Records must have both a 'data' modifier and parameter list
// data class Point { }
Diagnostic(ErrorCode.ERR_BadRecordDeclaration, "Point").WithLocation(2, 12)
);
- comp = CreateCompilation(src3, parseOptions: TestOptions.RegularPreview);
+ comp = CreateCompilation(src3);
comp.VerifyDiagnostics();
}
+
+ [Fact]
+ public void RecordProperties_01()
+ {
+ var src = @"
+using System;
+data class C(int X, int Y)
+{
+ public static void Main()
+ {
+ var c = new C(1, 2);
+ Console.WriteLine(c.X);
+ Console.WriteLine(c.Y);
+ }
+}";
+ CompileAndVerify(src, expectedOutput: @"
+1
+2");
+ }
+
+ [Fact]
+ public void RecordProperties_02()
+ {
+ var src = @"
+using System;
+data class C(int X, int Y)
+{
+ public C(int a, int b)
+ {
+ }
+
+ public static void Main()
+ {
+ var c = new C(1, 2);
+ Console.WriteLine(c.X);
+ Console.WriteLine(c.Y);
+ }
+}";
+ var comp = CreateCompilation(src);
+ comp.VerifyDiagnostics(
+ // (3,13): error CS8762: There cannot be a primary constructor and a member constructor with the same parameter types.
+ // data class C(int X, int Y)
+ Diagnostic(ErrorCode.ERR_DuplicateRecordConstructor, "(int X, int Y)").WithLocation(3, 13)
+ );
+ }
+
+ [Fact]
+ public void RecordProperties_03()
+ {
+ var src = @"
+using System;
+data class C(int X, int Y)
+{
+ public int X { get; }
+
+ public static void Main()
+ {
+ var c = new C(1, 2);
+ Console.WriteLine(c.X);
+ Console.WriteLine(c.Y);
+ }
+}";
+ CompileAndVerify(src, expectedOutput: @"
+0
+2");
+ }
+
+ [Fact]
+ public void RecordProperties_04()
+ {
+ var src = @"
+using System;
+data class C(int X, int Y)
+{
+ public int X { get; } = 3;
+
+ public static void Main()
+ {
+ var c = new C(1, 2);
+ Console.WriteLine(c.X);
+ Console.WriteLine(c.Y);
+ }
+}";
+ CompileAndVerify(src, expectedOutput: @"
+3
+2");
+ }
+
+ [Fact]
+ public void RecordProperties_05()
+ {
+ var src = @"
+data class C(int X, int X)
+{
+}";
+ var comp = CreateCompilation(src);
+ comp.VerifyDiagnostics(
+ // (2,25): error CS0100: The parameter name 'X' is a duplicate
+ // data class C(int X, int X)
+ Diagnostic(ErrorCode.ERR_DuplicateParamName, "X").WithArguments("X").WithLocation(2, 25),
+ // (2,25): error CS0102: The type 'C' already contains a definition for 'X'
+ // data class C(int X, int X)
+ Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "X").WithArguments("C", "X").WithLocation(2, 25)
+ );
+ }
+
+ [Fact]
+ public void RecordProperties_06()
+ {
+ var src = @"
+data class C(int X)
+{
+ public void get_X() {}
+}";
+ var comp = CreateCompilation(src);
+ comp.VerifyDiagnostics(
+ // (2,18): error CS0082: Type 'C' already reserves a member called 'get_X' with the same parameter types
+ // data class C(int X)
+ Diagnostic(ErrorCode.ERR_MemberReserved, "X").WithArguments("get_X", "C").WithLocation(2, 18)
+ );
+ }
+
+ [Fact]
+ public void RecordProperties_07()
+ {
+ var comp = CreateCompilation(@"
+data class C1(object P, object get_P);
+data class C2(object get_P, object P);");
+ comp.VerifyDiagnostics(
+ // (2,22): error CS0102: The type 'C1' already contains a definition for 'get_P'
+ // data class C1(object P, object get_P);
+ Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P").WithArguments("C1", "get_P").WithLocation(2, 22),
+ // (3,36): error CS0102: The type 'C2' already contains a definition for 'get_P'
+ // data class C2(object get_P, object P);
+ Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P").WithArguments("C2", "get_P").WithLocation(3, 36)
+ );
+ }
+
+ [Fact]
+ public void RecordProperties_08()
+ {
+ var comp = CreateCompilation(@"
+data class C1(object O1)
+{
+ public object O1 { get; } = O1;
+ public object O2 { get; } = O1;
+}");
+ // PROTOTYPE: primary ctor parameters not currently in scope
+ comp.VerifyDiagnostics(
+ // (4,33): error CS0236: A field initializer cannot reference the non-static field, method, or property 'C1.O1'
+ // public object O1 { get; } = O1;
+ Diagnostic(ErrorCode.ERR_FieldInitRefNonstatic, "O1").WithArguments("C1.O1").WithLocation(4, 33),
+ // (5,33): error CS0236: A field initializer cannot reference the non-static field, method, or property 'C1.O1'
+ // public object O2 { get; } = O1;
+ Diagnostic(ErrorCode.ERR_FieldInitRefNonstatic, "O1").WithArguments("C1.O1").WithLocation(5, 33)
+ );
+ }
}
}
\ No newline at end of file
diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs
index f1e0644b01bff..a7d758566ed83 100644
--- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs
+++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs
@@ -60,7 +60,11 @@ public C(int a, string b)
{
}
}");
- comp.VerifyDiagnostics();
+ comp.VerifyDiagnostics(
+ // (2,13): error CS8762: There cannot be a primary constructor and a member constructor with the same parameter types.
+ // data class C(int x, string y)
+ Diagnostic(ErrorCode.ERR_DuplicateRecordConstructor, "(int x, string y)").WithLocation(2, 13)
+ );
var c = comp.GlobalNamespace.GetTypeMember("C");
var ctor = c.GetMethod(".ctor");
Assert.Equal(2, ctor.ParameterCount);
@@ -110,5 +114,55 @@ public C(int a, int b) // overload
}
}
}
+
+ [Fact]
+ public void GeneratedProperties()
+ {
+ var comp = CreateCompilation("data class C(int x, int y);");
+ comp.VerifyDiagnostics();
+ var c = comp.GlobalNamespace.GetTypeMember("C");
+
+ var x = (SourceOrRecordPropertySymbol)c.GetProperty("x");
+ Assert.NotNull(x.GetMethod);
+ Assert.Equal(MethodKind.PropertyGet, x.GetMethod.MethodKind);
+ Assert.Equal(SpecialType.System_Int32, x.Type.SpecialType);
+ Assert.True(x.IsReadOnly);
+ Assert.Equal(Accessibility.Public, x.DeclaredAccessibility);
+ Assert.False(x.IsVirtual);
+ Assert.False(x.IsStatic);
+ Assert.Equal(c, x.ContainingType);
+ Assert.Equal(c, x.ContainingSymbol);
+
+ var backing = x.BackingField;
+ Assert.Equal(x, backing.AssociatedSymbol);
+ Assert.Equal(c, backing.ContainingSymbol);
+ Assert.Equal(c, backing.ContainingType);
+
+ var getAccessor = x.GetMethod;
+ Assert.Equal(x, getAccessor.AssociatedSymbol);
+ Assert.Equal(c, getAccessor.ContainingSymbol);
+ Assert.Equal(c, getAccessor.ContainingType);
+
+ var y = (SourceOrRecordPropertySymbol)c.GetProperty("y");
+ Assert.NotNull(y.GetMethod);
+ Assert.Equal(MethodKind.PropertyGet, y.GetMethod.MethodKind);
+ Assert.Equal(SpecialType.System_Int32, y.Type.SpecialType);
+ Assert.True(y.IsReadOnly);
+ Assert.Equal(Accessibility.Public, y.DeclaredAccessibility);
+ Assert.False(x.IsVirtual);
+ Assert.False(x.IsStatic);
+ Assert.Equal(c, y.ContainingType);
+ Assert.Equal(c, y.ContainingSymbol);
+
+ backing = y.BackingField;
+ Assert.Equal(y, backing.AssociatedSymbol);
+ Assert.Equal(c, backing.ContainingSymbol);
+ Assert.Equal(c, backing.ContainingType);
+
+ getAccessor = y.GetMethod;
+ Assert.Equal(y, getAccessor.AssociatedSymbol);
+ Assert.Equal(c, getAccessor.ContainingSymbol);
+ Assert.Equal(c, getAccessor.ContainingType);
+ }
}
}