未验证 提交 e85be97b 编写于 作者: A Andy Gocke 提交者: GitHub

Synthesize record properties and ctor (#41011)

The synthesized constructor assigns the properties, if they are
synthesized properties. The properties are synthesized only if no
conflicting member already exists in the class.
上级 ceca715d
......@@ -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 &&
......
......@@ -5942,4 +5942,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_BadRecordDeclaration" xml:space="preserve">
<value>Records must have both a 'data' modifier and parameter list</value>
</data>
<data name="ERR_DuplicateRecordConstructor" xml:space="preserve">
<value>There cannot be a primary constructor and a member constructor with the same parameter types.</value>
</data>
</root>
\ No newline at end of file
......@@ -1741,6 +1741,16 @@ internal static BoundBlock BindMethodBody(MethodSymbol method, TypeCompilationSt
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<BoundStatement>.GetInstance();
ctor.GenerateMethodBodyStatements(factory, stmts, diagnostics);
body = BoundBlock.SynthesizedNoLocals(node, stmts.ToImmutableAndFree());
}
else
{
// synthesized methods should return their bound bodies
......
......@@ -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)
}
......
......@@ -346,7 +346,7 @@ private void SynthesizeClosureEnvironments(ArrayBuilder<ClosureDebugInfo> 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
......
......@@ -277,6 +277,8 @@ private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bo
}
}
#nullable enable
private BoundExpression MakePropertyAssignment(
SyntaxNode syntax,
BoundExpression rewrittenReceiver,
......@@ -292,12 +294,13 @@ private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bo
// 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 VisitAssignmentOperator(BoundAssignmentOperator node, bo
// 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);
......
......@@ -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;
}
......
......@@ -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<Symbol> 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<Symbol> 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<Symbol> members, ArrayBuilder<ImmutableArray<FieldOrPropertyInitializer>> staticInitializers, DiagnosticBag diagnostics)
......@@ -2909,7 +2936,6 @@ private void AddSynthesizedConstructorsIfNecessary(ArrayBuilder<Symbol> members,
{
case DeclarationKind.Class:
case DeclarationKind.Struct:
AddSynthesizedRecordMembersIfNecessary(members, diagnostics);
break;
}
......
......@@ -555,9 +555,9 @@ private void HasBaseInterfaceDeclaringInterface(NamedTypeSymbol baseInterface, N
}
}
}
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)
......
// 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<AttributeListSyntax> 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;
}
}
......@@ -15,7 +15,7 @@
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
internal sealed class SourcePropertySymbol : PropertySymbol, IAttributeTargetSymbol
internal sealed class SourcePropertySymbol : SourceOrRecordPropertySymbol
{
/// <summary>
/// Condensed flags storing useful information about the <see cref="SourcePropertySymbol"/>
......@@ -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<CustomModifier> _refCustomModifiers;
private readonly SourcePropertyAccessorSymbol _getMethod;
private readonly SourcePropertyAccessorSymbol _setMethod;
private readonly SynthesizedBackingFieldSymbol _backingField;
private readonly TypeSymbol _explicitInterfaceType;
private readonly ImmutableArray<PropertySymbol> _explicitInterfaceImplementations;
private readonly Flags _propertyFlags;
......@@ -71,6 +69,7 @@ private enum Flags : byte
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 enum Flags : byte
_propertyFlags |= Flags.IsExplicitInterfaceImplementation;
}
_location = location;
_containingType = containingType;
_syntaxRef = syntax.GetReference();
_refKind = syntax.Type.GetRefKind();
......@@ -192,7 +190,7 @@ private enum Flags : byte
}
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<Location> 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;
/// <summary>
/// Backing field for automatically implemented property, or
/// for a property with an initializer.
/// </summary>
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<AttributeListSyntax> 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<CSharpAttributeData> 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<ParameterSymbol> 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;
}
......
......@@ -14,13 +14,13 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols
/// </summary>
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<AttributeListSyntax> AttributeDeclarationSyntaxList
=> _property.CSharpSyntaxNode.AttributeLists;
=> _property.AttributeDeclarationSyntaxList;
public override Symbol AssociatedSymbol
=> _property;
......
......@@ -126,7 +126,7 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState,
GenerateMethodBodyCore(compilationState, diagnostics);
}
protected override void GenerateMethodBodyStatements(SyntheticBoundNodeFactory factory, ArrayBuilder<BoundStatement> statements, DiagnosticBag diagnostics) => _getConstructorBody(factory, statements, _parameters);
internal override void GenerateMethodBodyStatements(SyntheticBoundNodeFactory factory, ArrayBuilder<BoundStatement> statements, DiagnosticBag diagnostics) => _getConstructorBody(factory, statements, _parameters);
}
}
......@@ -301,7 +301,7 @@ protected void GenerateMethodBodyCore(TypeCompilationState compilationState, Dia
factory.CloseMethod(block);
}
protected virtual void GenerateMethodBodyStatements(SyntheticBoundNodeFactory factory, ArrayBuilder<BoundStatement> statements, DiagnosticBag diagnostics)
internal virtual void GenerateMethodBodyStatements(SyntheticBoundNodeFactory factory, ArrayBuilder<BoundStatement> statements, DiagnosticBag diagnostics)
{
// overridden in a derived class to add extra statements to the body of the generated constructor
}
......
......@@ -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<ParameterSymbol> Parameters { get; }
public SynthesizedRecordConstructor(
NamedTypeSymbol containingType,
SourceMemberContainerTypeSymbol containingType,
Binder parameterBinder,
ParameterListSyntax parameterList,
DiagnosticBag diagnostics)
......@@ -28,5 +29,26 @@ internal sealed class SynthesizedRecordConstructor : SynthesizedInstanceConstruc
allowThis: false,
addRefReadOnlyModifier: false);
}
internal override void GenerateMethodBodyStatements(SyntheticBoundNodeFactory F, ArrayBuilder<BoundStatement> 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)));
}
}
}
}
}
// 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<CustomModifier> RefCustomModifiers => ImmutableArray<CustomModifier>.Empty;
public override ImmutableArray<ParameterSymbol> Parameters => ImmutableArray<ParameterSymbol>.Empty;
public override bool IsIndexer => false;
public override MethodSymbol? SetMethod => null;
public override ImmutableArray<PropertySymbol> ExplicitInterfaceImplementations => ImmutableArray<PropertySymbol>.Empty;
public override Symbol ContainingSymbol => ContainingType;
public override ImmutableArray<Location> Locations => _backingParameter.Locations;
public override ImmutableArray<SyntaxReference> 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<CSharpAttributeData> GetAttributes() => ImmutableArray<CSharpAttributeData>.Empty;
internal override bool HasPointerType => Type.IsPointerType();
public override SyntaxList<AttributeListSyntax> AttributeDeclarationSyntaxList => new SyntaxList<AttributeListSyntax>();
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<TypeWithAnnotations> TypeArgumentsWithAnnotations => ImmutableArray<TypeWithAnnotations>.Empty;
public override ImmutableArray<TypeParameterSymbol> TypeParameters => ImmutableArray<TypeParameterSymbol>.Empty;
public override ImmutableArray<ParameterSymbol> Parameters => _property.Parameters;
public override ImmutableArray<MethodSymbol> ExplicitInterfaceImplementations => ImmutableArray<MethodSymbol>.Empty;
public override ImmutableArray<CustomModifier> RefCustomModifiers => _property.RefCustomModifiers;
public override Symbol AssociatedSymbol => _property;
public override Symbol ContainingSymbol => _property.ContainingSymbol;
public override ImmutableArray<Location> 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<string> ReturnNotNullIfParameterNotNull => ImmutableHashSet<string>.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<string> GetAppliedConditionalSymbols()
=> ImmutableArray<string>.Empty;
internal override IEnumerable<SecurityAttribute> GetSecurityInformation()
=> Array.Empty<SecurityAttribute>();
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))));
}
}
}
}
......@@ -152,6 +152,11 @@
<target state="translated">Pro přístupové objekty vlastnosti i indexeru {0} nelze zadat modifikátory readonly. Místo toho zadejte modifikátor readonly jenom pro vlastnost.</target>
<note />
</trans-unit>
<trans-unit id="ERR_DuplicateRecordConstructor">
<source>There cannot be a primary constructor and a member constructor with the same parameter types.</source>
<target state="new">There cannot be a primary constructor and a member constructor with the same parameter types.</target>
<note />
</trans-unit>
<trans-unit id="ERR_ElseCannotStartStatement">
<source>'else' cannot start a statement.</source>
<target state="translated">Příkaz nemůže začínat na else.</target>
......
......@@ -152,6 +152,11 @@
<target state="translated">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.</target>
<note />
</trans-unit>
<trans-unit id="ERR_DuplicateRecordConstructor">
<source>There cannot be a primary constructor and a member constructor with the same parameter types.</source>
<target state="new">There cannot be a primary constructor and a member constructor with the same parameter types.</target>
<note />
</trans-unit>
<trans-unit id="ERR_ElseCannotStartStatement">
<source>'else' cannot start a statement.</source>
<target state="translated">Eine Anweisung kann nicht mit "else" beginnen.</target>
......
......@@ -152,6 +152,11 @@
<target state="translated">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.</target>
<note />
</trans-unit>
<trans-unit id="ERR_DuplicateRecordConstructor">
<source>There cannot be a primary constructor and a member constructor with the same parameter types.</source>
<target state="new">There cannot be a primary constructor and a member constructor with the same parameter types.</target>
<note />
</trans-unit>
<trans-unit id="ERR_ElseCannotStartStatement">
<source>'else' cannot start a statement.</source>
<target state="translated">“else” no puede iniciar una instrucción.</target>
......
......@@ -152,6 +152,11 @@
<target state="translated">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.</target>
<note />
</trans-unit>
<trans-unit id="ERR_DuplicateRecordConstructor">
<source>There cannot be a primary constructor and a member constructor with the same parameter types.</source>
<target state="new">There cannot be a primary constructor and a member constructor with the same parameter types.</target>
<note />
</trans-unit>
<trans-unit id="ERR_ElseCannotStartStatement">
<source>'else' cannot start a statement.</source>
<target state="translated">'else' ne peut pas démarrer d'instruction.</target>
......
......@@ -152,6 +152,11 @@
<target state="translated">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.</target>
<note />
</trans-unit>
<trans-unit id="ERR_DuplicateRecordConstructor">
<source>There cannot be a primary constructor and a member constructor with the same parameter types.</source>
<target state="new">There cannot be a primary constructor and a member constructor with the same parameter types.</target>
<note />
</trans-unit>
<trans-unit id="ERR_ElseCannotStartStatement">
<source>'else' cannot start a statement.</source>
<target state="translated">Un'istruzione non può iniziare con 'else'.</target>
......
......@@ -152,6 +152,11 @@
<target state="translated">プロパティまたはインデクサー '{0}' の両方のアクセサーで 'readonly' 修飾子を指定することはできません。代わりに、プロパティ自体に 'readonly' 修飾子を指定してください。</target>
<note />
</trans-unit>
<trans-unit id="ERR_DuplicateRecordConstructor">
<source>There cannot be a primary constructor and a member constructor with the same parameter types.</source>
<target state="new">There cannot be a primary constructor and a member constructor with the same parameter types.</target>
<note />
</trans-unit>
<trans-unit id="ERR_ElseCannotStartStatement">
<source>'else' cannot start a statement.</source>
<target state="translated">'else' でステートメントを開始することはできません。</target>
......
......@@ -152,6 +152,11 @@
<target state="translated">'{0}' 속성 또는 인덱서의 두 접근자에 'readonly' 한정자를 지정할 수 없습니다. 대신 속성 자체에 'readonly' 한정자를 지정하세요.</target>
<note />
</trans-unit>
<trans-unit id="ERR_DuplicateRecordConstructor">
<source>There cannot be a primary constructor and a member constructor with the same parameter types.</source>
<target state="new">There cannot be a primary constructor and a member constructor with the same parameter types.</target>
<note />
</trans-unit>
<trans-unit id="ERR_ElseCannotStartStatement">
<source>'else' cannot start a statement.</source>
<target state="translated">'else'로 문을 시작할 수 없습니다.</target>
......
......@@ -152,6 +152,11 @@
<target state="translated">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.</target>
<note />
</trans-unit>
<trans-unit id="ERR_DuplicateRecordConstructor">
<source>There cannot be a primary constructor and a member constructor with the same parameter types.</source>
<target state="new">There cannot be a primary constructor and a member constructor with the same parameter types.</target>
<note />
</trans-unit>
<trans-unit id="ERR_ElseCannotStartStatement">
<source>'else' cannot start a statement.</source>
<target state="translated">Instrukcja nie może rozpoczynać się od elementu „else”.</target>
......
......@@ -152,6 +152,11 @@
<target state="translated">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.</target>
<note />
</trans-unit>
<trans-unit id="ERR_DuplicateRecordConstructor">
<source>There cannot be a primary constructor and a member constructor with the same parameter types.</source>
<target state="new">There cannot be a primary constructor and a member constructor with the same parameter types.</target>
<note />
</trans-unit>
<trans-unit id="ERR_ElseCannotStartStatement">
<source>'else' cannot start a statement.</source>
<target state="translated">'else' não pode iniciar uma instrução.</target>
......
......@@ -152,6 +152,11 @@
<target state="translated">Запрещено указывать модификаторы readonly для обоих методов доступа свойства или индексатора "{0}". Вместо этого укажите модификатор readonly для самого свойства.</target>
<note />
</trans-unit>
<trans-unit id="ERR_DuplicateRecordConstructor">
<source>There cannot be a primary constructor and a member constructor with the same parameter types.</source>
<target state="new">There cannot be a primary constructor and a member constructor with the same parameter types.</target>
<note />
</trans-unit>
<trans-unit id="ERR_ElseCannotStartStatement">
<source>'else' cannot start a statement.</source>
<target state="translated">"else" не может запускать оператор.</target>
......
......@@ -152,6 +152,11 @@
<target state="translated">'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.</target>
<note />
</trans-unit>
<trans-unit id="ERR_DuplicateRecordConstructor">
<source>There cannot be a primary constructor and a member constructor with the same parameter types.</source>
<target state="new">There cannot be a primary constructor and a member constructor with the same parameter types.</target>
<note />
</trans-unit>
<trans-unit id="ERR_ElseCannotStartStatement">
<source>'else' cannot start a statement.</source>
<target state="translated">'else' bir deyim başlatamaz.</target>
......
......@@ -152,6 +152,11 @@
<target state="translated">不能在属性或索引器 "{0}" 的两个访问器上指定 "readonly" 修饰符。而应在属性本身上指定 "readonly" 修饰符。</target>
<note />
</trans-unit>
<trans-unit id="ERR_DuplicateRecordConstructor">
<source>There cannot be a primary constructor and a member constructor with the same parameter types.</source>
<target state="new">There cannot be a primary constructor and a member constructor with the same parameter types.</target>
<note />
</trans-unit>
<trans-unit id="ERR_ElseCannotStartStatement">
<source>'else' cannot start a statement.</source>
<target state="translated">"else" 不能用在语句的开头。</target>
......
......@@ -152,6 +152,11 @@
<target state="translated">在屬性和索引子 '{0}' 的存取子上均無法指定 'readonly' 修飾詞。請改在屬性自身上放置 'readonly' 修飾詞。</target>
<note />
</trans-unit>
<trans-unit id="ERR_DuplicateRecordConstructor">
<source>There cannot be a primary constructor and a member constructor with the same parameter types.</source>
<target state="new">There cannot be a primary constructor and a member constructor with the same parameter types.</target>
<note />
</trans-unit>
<trans-unit id="ERR_ElseCannotStartStatement">
<source>'else' cannot start a statement.</source>
<target state="translated">'else' 無法開始陳述式。</target>
......
......@@ -49,6 +49,8 @@ public void PositionalRecord2()
Add( // C Type parameters
"T"),
Add( // Members
"System.Int32 C<T>.x { get; }",
"T C<T>.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)",
......
......@@ -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 @@ class Point(int x, int y);
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 @@ class Point(int x, int y);
// 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 @@ class Point(int x, int y);
// 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 @@ class Point(int x, int y);
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
......@@ -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 void RecordExistingConstructor01()
}
}
}
[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);
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册