Skip to content

Commit

Permalink
Support applying attributes to properties and fields that are created…
Browse files Browse the repository at this point in the history
… to back primary constructor parameters in records.

Closes dotnet#44691.
  • Loading branch information
AlekseyTs committed Jul 11, 2020
1 parent 918f670 commit 58233a7
Show file tree
Hide file tree
Showing 13 changed files with 843 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,22 @@ private static OverriddenOrHiddenMembersResult MakeOverriddenOrHiddenMembersWork
return MakeInterfaceOverriddenOrHiddenMembers(member, memberIsFromSomeCompilation);
}

Symbol bestMatch = null;
ArrayBuilder<Symbol> hiddenBuilder = null;
ArrayBuilder<Symbol> hiddenBuilder;
ImmutableArray<Symbol> overriddenMembers;
ImmutableArray<Symbol> runtimeOverriddenMembers;
FindOverriddenOrHiddenMembers(member, containingType, memberIsFromSomeCompilation, out hiddenBuilder, out overriddenMembers, out runtimeOverriddenMembers);

ImmutableArray<Symbol> hiddenMembers = hiddenBuilder == null ? ImmutableArray<Symbol>.Empty : hiddenBuilder.ToImmutableAndFree();
return OverriddenOrHiddenMembersResult.Create(overriddenMembers, hiddenMembers, runtimeOverriddenMembers);
}

private static void FindOverriddenOrHiddenMembers(Symbol member, NamedTypeSymbol containingType, bool memberIsFromSomeCompilation,
out ArrayBuilder<Symbol> hiddenBuilder,
out ImmutableArray<Symbol> overriddenMembers,
out ImmutableArray<Symbol> runtimeOverriddenMembers)
{
Symbol bestMatch = null;
hiddenBuilder = null;
for (NamedTypeSymbol currType = containingType.BaseTypeNoUseSiteDiagnostics;
(object)currType != null && (object)bestMatch == null && hiddenBuilder == null;
currType = currType.BaseTypeNoUseSiteDiagnostics)
Expand All @@ -149,12 +162,19 @@ private static OverriddenOrHiddenMembersResult MakeOverriddenOrHiddenMembersWork

// Based on bestMatch, find other methods that will be overridden, hidden, or runtime overridden
// (in bestMatch.ContainingType).
ImmutableArray<Symbol> overriddenMembers;
ImmutableArray<Symbol> runtimeOverriddenMembers;
FindRelatedMembers(member.IsOverride, memberIsFromSomeCompilation, member.Kind, bestMatch, out overriddenMembers, out runtimeOverriddenMembers, ref hiddenBuilder);
}

ImmutableArray<Symbol> hiddenMembers = hiddenBuilder == null ? ImmutableArray<Symbol>.Empty : hiddenBuilder.ToImmutableAndFree();
return OverriddenOrHiddenMembersResult.Create(overriddenMembers, hiddenMembers, runtimeOverriddenMembers);
public static Symbol FindFirstHiddenMemberIfAny(Symbol member, bool memberIsFromSomeCompilation)
{
ArrayBuilder<Symbol> hiddenBuilder;
FindOverriddenOrHiddenMembers(member, member.ContainingType, memberIsFromSomeCompilation, out hiddenBuilder,
overriddenMembers: out _, runtimeOverriddenMembers: out _);

Symbol result = hiddenBuilder?.FirstOrDefault();
hiddenBuilder?.Free();

return result;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public SignatureOnlyPropertySymbol(

public override bool IsVirtual { get { throw ExceptionUtilities.Unreachable; } }

public override bool IsOverride { get { throw ExceptionUtilities.Unreachable; } }
public override bool IsOverride => false;

public override bool IsAbstract { get { throw ExceptionUtilities.Unreachable; } }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,18 @@ public override string MetadataName

AttributeLocation IAttributeTargetSymbol.DefaultAttributeLocation => AttributeLocation.Parameter;

AttributeLocation IAttributeTargetSymbol.AllowedAttributeLocations => AttributeLocation.Parameter;
AttributeLocation IAttributeTargetSymbol.AllowedAttributeLocations
{
get
{
if (SynthesizedRecordPropertySymbol.HaveCorrespondingSynthesizedRecordPropertySymbol(this))
{
return AttributeLocation.Parameter | AttributeLocation.Property | AttributeLocation.Field;
}

return AttributeLocation.Parameter;
}
}

/// <summary>
/// Symbol to copy bound attributes from, or null if the attributes are not shared among multiple source parameter symbols.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3079,16 +3079,24 @@ ImmutableArray<PropertySymbol> addProperties(ImmutableArray<ParameterSymbol> rec
{
bool isInherited = false;
var syntax = param.GetNonNullSyntaxNode();
var property = new SynthesizedRecordPropertySymbol(this, syntax, param, isOverride: false, diagnostics);
if (!memberSignatures.TryGetValue(property, out var existingMember))

var targetProperty = new SignatureOnlyPropertySymbol(param.Name,
this,
ImmutableArray<ParameterSymbol>.Empty,
RefKind.None,
param.TypeWithAnnotations,
ImmutableArray<CustomModifier>.Empty,
isStatic: false,
ImmutableArray<PropertySymbol>.Empty);

if (!memberSignatures.TryGetValue(targetProperty, out var existingMember))
{
Debug.Assert(property.OverriddenOrHiddenMembers.OverriddenMembers.Length == 0); // property is not virtual and should not have overrides
existingMember = property.OverriddenOrHiddenMembers.HiddenMembers.FirstOrDefault();
existingMember = OverriddenOrHiddenMembersHelpers.FindFirstHiddenMemberIfAny(targetProperty, memberIsFromSomeCompilation: true);
isInherited = true;
}
if (existingMember is null)
{
addProperty(property);
addProperty(new SynthesizedRecordPropertySymbol(this, syntax, param, isOverride: false, diagnostics));
}
else if (existingMember is PropertySymbol { IsStatic: false, GetMethod: { } } prop
&& prop.TypeWithAnnotations.Equals(param.TypeWithAnnotations, TypeCompareKind.AllIgnoreOptions))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ private static bool HasInitializer(SyntaxNode syntax)
public override SyntaxList<AttributeListSyntax> AttributeDeclarationSyntaxList
=> ((BasePropertyDeclarationSyntax)CSharpSyntaxNode).AttributeLists;

public override IAttributeTargetSymbol AttributesOwner => this;

private static void GetAccessorDeclarations(
CSharpSyntaxNode syntaxNode,
DiagnosticBag diagnostics,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -705,8 +705,6 @@ internal CSharpSyntaxNode CSharpSyntaxNode
}
}

public abstract SyntaxList<AttributeListSyntax> AttributeDeclarationSyntaxList { get; }

internal SyntaxTree SyntaxTree
{
get
Expand Down Expand Up @@ -980,7 +978,11 @@ private SynthesizedSealedPropertyAccessor MakeSynthesizedSealedAccessor()

#region Attributes

IAttributeTargetSymbol IAttributeTargetSymbol.AttributesOwner => this;
public abstract SyntaxList<AttributeListSyntax> AttributeDeclarationSyntaxList { get; }

public abstract IAttributeTargetSymbol AttributesOwner { get; }

IAttributeTargetSymbol IAttributeTargetSymbol.AttributesOwner => AttributesOwner;

AttributeLocation IAttributeTargetSymbol.DefaultAttributeLocation => AttributeLocation.Property;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,12 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState,
{
var F = new SyntheticBoundNodeFactory(this, ContainingType.GetNonNullSyntaxNode(), compilationState, diagnostics);

var members = ContainingType.GetMembers(WellKnownMemberNames.InstanceConstructorName);
var members = ContainingType.InstanceConstructors;
foreach (var member in members)
{
var ctor = (MethodSymbol)member;
if (ctor.ParameterCount == 1 &&
ctor.Parameters[0].Type.Equals(ContainingType, TypeCompareKind.ConsiderEverything))
if (ctor.ParameterCount == 1 && ctor.Parameters[0].RefKind == RefKind.None &&
ctor.Parameters[0].Type.Equals(ContainingType, TypeCompareKind.AllIgnoreOptions))
{
F.CloseMethod(F.Return(F.New(ctor, F.This())));
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
internal sealed class SynthesizedRecordEqualityContractProperty : SourcePropertySymbolBase, IAttributeTargetSymbol
internal sealed class SynthesizedRecordEqualityContractProperty : SourcePropertySymbolBase
{
internal const string PropertyName = "EqualityContract";

Expand Down Expand Up @@ -52,21 +52,17 @@ public SynthesizedRecordEqualityContractProperty(

public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences => ImmutableArray<SyntaxReference>.Empty;

IAttributeTargetSymbol IAttributeTargetSymbol.AttributesOwner => this;

AttributeLocation IAttributeTargetSymbol.AllowedAttributeLocations => AttributeLocation.None;
public override SyntaxList<AttributeListSyntax> AttributeDeclarationSyntaxList
=> new SyntaxList<AttributeListSyntax>();

AttributeLocation IAttributeTargetSymbol.DefaultAttributeLocation => AttributeLocation.None;
public override IAttributeTargetSymbol AttributesOwner => this;

protected override Location TypeLocation
=> ContainingType.Locations[0];

protected override SyntaxTokenList GetModifierTokens(SyntaxNode syntax)
=> new SyntaxTokenList();

public override SyntaxList<AttributeListSyntax> AttributeDeclarationSyntaxList
=> new SyntaxList<AttributeListSyntax>();

protected override void CheckForBlockAndExpressionBody(CSharpSyntaxNode syntax, DiagnosticBag diagnostics)
{
// Nothing to do here
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@

namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
internal sealed class SynthesizedRecordPropertySymbol : SourcePropertySymbolBase, IAttributeTargetSymbol
internal sealed class SynthesizedRecordPropertySymbol : SourcePropertySymbolBase
{
public ParameterSymbol BackingParameter { get; }
public SourceParameterSymbol BackingParameter { get; }

public SynthesizedRecordPropertySymbol(
SourceMemberContainerTypeSymbol containingType,
Expand Down Expand Up @@ -42,14 +42,11 @@ public SynthesizedRecordPropertySymbol(
hasParameters: false,
diagnostics)
{
BackingParameter = backingParameter;
BackingParameter = (SourceParameterSymbol)backingParameter;
}

IAttributeTargetSymbol IAttributeTargetSymbol.AttributesOwner => this;

AttributeLocation IAttributeTargetSymbol.AllowedAttributeLocations => AttributeLocation.None;

AttributeLocation IAttributeTargetSymbol.DefaultAttributeLocation => AttributeLocation.None;
public override IAttributeTargetSymbol AttributesOwner => BackingParameter as IAttributeTargetSymbol ?? this;

protected override Location TypeLocation
=> ((ParameterSyntax)CSharpSyntaxNode).Type!.Location;
Expand All @@ -58,7 +55,7 @@ protected override SyntaxTokenList GetModifierTokens(SyntaxNode syntax)
=> new SyntaxTokenList();

public override SyntaxList<AttributeListSyntax> AttributeDeclarationSyntaxList
=> new SyntaxList<AttributeListSyntax>();
=> BackingParameter.AttributeDeclarationList;

protected override void CheckForBlockAndExpressionBody(CSharpSyntaxNode syntax, DiagnosticBag diagnostics)
{
Expand Down Expand Up @@ -112,5 +109,10 @@ protected override TypeWithAnnotations ComputeType(Binder? binder, SyntaxNode sy
protected override bool HasPointerTypeSyntactically
// Since we already bound the type, don't bother looking at syntax
=> TypeWithAnnotations.DefaultType.IsPointerOrFunctionPointer();

public static bool HaveCorrespondingSynthesizedRecordPropertySymbol(SourceParameterSymbol parameter)
{
return parameter.ContainingType.GetMembersUnordered().Any((s, parameter) => (s as SynthesizedRecordPropertySymbol)?.BackingParameter == (object)parameter, parameter);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public SynthesizedBackingFieldSymbol(
}

protected override IAttributeTargetSymbol AttributeOwner
=> _property;
=> _property.AttributesOwner;

internal override Location ErrorLocation
=> _property.Location;
Expand Down
Loading

0 comments on commit 58233a7

Please sign in to comment.