Skip to content

Commit

Permalink
Implement FindReferences for deconstructions (#22934)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcouv authored Nov 9, 2017
1 parent 85ca06e commit ee5a91f
Show file tree
Hide file tree
Showing 31 changed files with 768 additions and 37 deletions.
6 changes: 3 additions & 3 deletions src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ private bool MakeDeconstructionConversion(
conversion = Conversion.Deconstruction;

// Figure out the deconstruct method (if one is required) and determine the types we get from the RHS at this level
var deconstructInfo = default(DeconstructionInfo);
var deconstructMethod = default(DeconstructMethodInfo);
if (type.IsTupleType)
{
// tuple literal such as `(1, 2)`, `(null, null)`, `(x.P, y.M())`
Expand All @@ -266,7 +266,7 @@ private bool MakeDeconstructionConversion(
return false;
}

deconstructInfo = new DeconstructionInfo(deconstructInvocation, inputPlaceholder, outPlaceholders);
deconstructMethod = new DeconstructMethodInfo(deconstructInvocation, inputPlaceholder, outPlaceholders);

tupleOrDeconstructedTypes = outPlaceholders.SelectAsArray(p => p.Type);
SetInferredTypes(variables, tupleOrDeconstructedTypes, diagnostics);
Expand Down Expand Up @@ -305,7 +305,7 @@ private bool MakeDeconstructionConversion(
nestedConversions.Add(nestedConversion);
}

conversion = new Conversion(ConversionKind.Deconstruction, deconstructInfo, nestedConversions.ToImmutableAndFree());
conversion = new Conversion(ConversionKind.Deconstruction, deconstructMethod, nestedConversions.ToImmutableAndFree());

return !hasErrors;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@ internal bool IsArrayIndex

private class DeconstructionUncommonData : UncommonData
{
internal DeconstructionUncommonData(DeconstructionInfo deconstructionInfoOpt, ImmutableArray<Conversion> nestedConversions)
: base(false, false, default(UserDefinedConversionResult), null, nestedConversions)
internal DeconstructionUncommonData(DeconstructMethodInfo deconstructMethodInfoOpt, ImmutableArray<Conversion> nestedConversions)
: base(isExtensionMethod: false, isArrayIndex: false, conversionResult: default, conversionMethod: null, nestedConversions)
{
Debug.Assert(!nestedConversions.IsDefaultOrEmpty);
DeconstructionInfo = deconstructionInfoOpt;
DeconstructMethodInfo = deconstructMethodInfoOpt;
}

readonly internal DeconstructionInfo DeconstructionInfo;
readonly internal DeconstructMethodInfo DeconstructMethodInfo;
}

private Conversion(
Expand Down Expand Up @@ -131,12 +131,12 @@ internal Conversion(ConversionKind kind, ImmutableArray<Conversion> nestedConver
nestedConversions: nestedConversions);
}

internal Conversion(ConversionKind kind, DeconstructionInfo deconstructionInfo, ImmutableArray<Conversion> nestedConversions)
internal Conversion(ConversionKind kind, DeconstructMethodInfo deconstructMethodInfo, ImmutableArray<Conversion> nestedConversions)
{
Debug.Assert(kind == ConversionKind.Deconstruction);

this._kind = kind;
_uncommonData = new DeconstructionUncommonData(deconstructionInfo, nestedConversions);
_uncommonData = new DeconstructionUncommonData(deconstructMethodInfo, nestedConversions);
}

internal Conversion SetConversionMethod(MethodSymbol conversionMethod)
Expand Down Expand Up @@ -353,18 +353,24 @@ internal MethodSymbol Method
UserDefinedConversionAnalysis analysis = conversionResult.Results[conversionResult.Best];
return analysis.Operator;
}

if (uncommonData is DeconstructionUncommonData deconstruction
&& deconstruction.DeconstructMethodInfo.Invocation is BoundCall call)
{
return call.Method;
}
}

return null;
}
}

internal DeconstructionInfo DeconstructionInfo
internal DeconstructMethodInfo DeconstructionInfo
{
get
{
var uncommonData = (DeconstructionUncommonData)_uncommonData;
return uncommonData == null ? default(DeconstructionInfo) : uncommonData.DeconstructionInfo;
return uncommonData == null ? default(DeconstructMethodInfo) : uncommonData.DeconstructMethodInfo;
}
}

Expand Down Expand Up @@ -973,9 +979,9 @@ TreeDumperNode Dump(Conversion self)
}

/// <summary>Stores all the information from binding for calling a Deconstruct method.</summary>
internal struct DeconstructionInfo
internal struct DeconstructMethodInfo
{
internal DeconstructionInfo(BoundExpression invocation, BoundDeconstructValuePlaceholder inputPlaceholder,
internal DeconstructMethodInfo(BoundExpression invocation, BoundDeconstructValuePlaceholder inputPlaceholder,
ImmutableArray<BoundDeconstructValuePlaceholder> outputPlaceholders)
{
(Invocation, InputPlaceholder, OutputPlaceholders) = (invocation, inputPlaceholder, outputPlaceholders);
Expand All @@ -984,6 +990,6 @@ internal DeconstructionInfo(BoundExpression invocation, BoundDeconstructValuePla
readonly internal BoundExpression Invocation;
readonly internal BoundDeconstructValuePlaceholder InputPlaceholder;
readonly internal ImmutableArray<BoundDeconstructValuePlaceholder> OutputPlaceholders;
internal bool IsDefault => (object)Invocation == null;
internal bool IsDefault => Invocation is null;
}
}
10 changes: 10 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,16 @@ public static ForEachStatementInfo GetForEachStatementInfo(this SemanticModel se
}
}

public static DeconstructionInfo GetDeconstructionInfo(this SemanticModel semanticModel, AssignmentExpressionSyntax assignment)
{
return semanticModel is CSharpSemanticModel csmodel ? csmodel.GetDeconstructionInfo(assignment) : default;
}

public static DeconstructionInfo GetDeconstructionInfo(this SemanticModel semanticModel, ForEachVariableStatementSyntax @foreach)
{
return semanticModel is CSharpSemanticModel csmodel ? csmodel.GetDeconstructionInfo(@foreach) : default;
}

public static AwaitExpressionInfo GetAwaitExpressionInfo(this SemanticModel semanticModel, AwaitExpressionSyntax awaitExpression)
{
var csmodel = semanticModel as CSharpSemanticModel;
Expand Down
12 changes: 12 additions & 0 deletions src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4402,6 +4402,18 @@ private static ImmutableArray<Symbol> CreateReducedExtensionMethodIfPossible(Bou
/// <param name="node">The node.</param>
public abstract ForEachStatementInfo GetForEachStatementInfo(CommonForEachStatementSyntax node);

/// <summary>
/// Gets deconstruction assignment info.
/// </summary>
/// <param name="node">The node.</param>
public abstract DeconstructionInfo GetDeconstructionInfo(AssignmentExpressionSyntax node);

/// <summary>
/// Gets deconstruction foreach info.
/// </summary>
/// <param name="node">The node.</param>
public abstract DeconstructionInfo GetDeconstructionInfo(ForEachVariableStatementSyntax node);

/// <summary>
/// Gets await expression info.
/// </summary>
Expand Down
75 changes: 75 additions & 0 deletions src/Compilers/CSharp/Portable/Compilation/DeconstructionInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// 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 System.Collections.Immutable;

namespace Microsoft.CodeAnalysis.CSharp
{
// Deconstructions are represented internally as a tree of conversions, but
// since they are not conversions from the language perspective we use a wrapper to
// abstract the public API from the implementation.

/// <summary>
/// The representation of a deconstruction as a tree of Deconstruct methods and conversions.
/// Methods only appear in non-terminal nodes. All terminal nodes have a Conversion.
///
/// Here's an example:
/// A deconstruction like `(int x1, (long x2, long x3)) = deconstructable1` with
/// `Deconstructable1.Deconstruct(out int y1, out Deconstructable2 y2)` and
/// `Deconstructable2.Deconstruct(out int z1, out int z2)` is represented as 5 DeconstructionInfo nodes.
///
/// The top-level node has a <see cref="Method"/> (Deconstructable1.Deconstruct), no <see cref="Conversion"/>, but has two <see cref="Nested"/> nodes.
/// Its first nested node has no <see cref="Method"/>, but has a <see cref="Conversion"/> (Identity).
/// Its second nested node has a <see cref="Method"/> (Deconstructable2.Deconstruct), no <see cref="Conversion"/>, and two <see cref="Nested"/> nodes.
/// Those last two nested nodes have no <see cref="Method"/>, but each have a <see cref="Conversion"/> (ImplicitNumeric, from int to long).
/// </summary>
public struct DeconstructionInfo
{
private readonly Conversion _conversion;

/// <summary>
/// The Deconstruct method (if any) for this non-terminal position in the deconstruction tree.
/// </summary>
public IMethodSymbol Method
{
get
{
return _conversion.Kind == ConversionKind.Deconstruction
? _conversion.Method
: null;
}
}

/// <summary>
/// The conversion for a terminal position in the deconstruction tree.
/// </summary>
public Conversion? Conversion
{
get
{
return _conversion.Kind == ConversionKind.Deconstruction
? null
: (Conversion?)_conversion;
}
}

/// <summary>
/// The children for this deconstruction node.
/// </summary>
public ImmutableArray<DeconstructionInfo> Nested
{
get
{
var underlyingConversions = _conversion.UnderlyingConversions;

return underlyingConversions.IsDefault
? ImmutableArray<DeconstructionInfo>.Empty
: underlyingConversions.SelectAsArray(c => new DeconstructionInfo(c));
}
}

internal DeconstructionInfo(Conversion conversion)
{
_conversion = conversion;
}
}
}
28 changes: 28 additions & 0 deletions src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,34 @@ public override ForEachStatementInfo GetForEachStatementInfo(CommonForEachStatem
enumeratorInfoOpt.CurrentConversion);
}

public override DeconstructionInfo GetDeconstructionInfo(AssignmentExpressionSyntax node)
{
var boundDeconstruction = GetUpperBoundNode(node) as BoundDeconstructionAssignmentOperator;
if (boundDeconstruction is null)
{
return default;
}

var boundConversion = boundDeconstruction.Right;
Debug.Assert(boundConversion != null || boundDeconstruction.HasAnyErrors);

return new DeconstructionInfo(boundConversion.Conversion);
}

public override DeconstructionInfo GetDeconstructionInfo(ForEachVariableStatementSyntax node)
{
var boundForEach = (BoundForEachStatement)GetUpperBoundNode(node);
if (boundForEach is null)
{
return default;
}

var boundDeconstruction = boundForEach.DeconstructionOpt;
Debug.Assert(boundDeconstruction != null || boundForEach.HasAnyErrors);

return new DeconstructionInfo(boundDeconstruction.DeconstructionAssignment.Right.Conversion);
}

private BoundQueryClause GetBoundQueryClause(CSharpSyntaxNode node)
{
CheckSyntaxNode(node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2170,5 +2170,17 @@ public override ForEachStatementInfo GetForEachStatementInfo(CommonForEachStatem
MemberSemanticModel memberModel = GetMemberModel(node);
return memberModel == null ? default(ForEachStatementInfo) : memberModel.GetForEachStatementInfo(node);
}

public override DeconstructionInfo GetDeconstructionInfo(AssignmentExpressionSyntax node)
{
MemberSemanticModel memberModel = GetMemberModel(node);
return memberModel?.GetDeconstructionInfo(node) ?? default;
}

public override DeconstructionInfo GetDeconstructionInfo(ForEachVariableStatementSyntax node)
{
MemberSemanticModel memberModel = GetMemberModel(node);
return memberModel?.GetDeconstructionInfo(node) ?? default;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ private BoundExpression EvaluateConversionToTemp(BoundExpression expression, Con
return EvaluateSideEffectingArgumentToTemp(evalConversion, effects, ref temps);
}

private ImmutableArray<BoundExpression> InvokeDeconstructMethod(DeconstructionInfo deconstruction, BoundExpression target,
private ImmutableArray<BoundExpression> InvokeDeconstructMethod(DeconstructMethodInfo deconstruction, BoundExpression target,
ArrayBuilder<BoundExpression> effects, ref ArrayBuilder<LocalSymbol> temps)
{
AddPlaceholderReplacement(deconstruction.InputPlaceholder, target);
Expand Down
10 changes: 8 additions & 2 deletions src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
*REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax.Update(Microsoft.CodeAnalysis.CSharp.Syntax.NameColonSyntax nameColon, Microsoft.CodeAnalysis.SyntaxToken refOrOutKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression) -> Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax
*REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax.WithRefKindKeyword(Microsoft.CodeAnalysis.SyntaxToken refOrOutKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax
*REMOVED*static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.Argument(Microsoft.CodeAnalysis.CSharp.Syntax.NameColonSyntax nameColon, Microsoft.CodeAnalysis.SyntaxToken refOrOutKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression) -> Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax
Microsoft.CodeAnalysis.CSharp.Conversion.IsStackAlloc.get -> bool
Microsoft.CodeAnalysis.CSharp.Conversion.ToCommonConversion() -> Microsoft.CodeAnalysis.Operations.CommonConversion
Microsoft.CodeAnalysis.CSharp.DeconstructionInfo
Microsoft.CodeAnalysis.CSharp.DeconstructionInfo.Conversion.get -> Microsoft.CodeAnalysis.CSharp.Conversion?
Microsoft.CodeAnalysis.CSharp.DeconstructionInfo.Method.get -> Microsoft.CodeAnalysis.IMethodSymbol
Microsoft.CodeAnalysis.CSharp.DeconstructionInfo.Nested.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.CSharp.DeconstructionInfo>
Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp7_2 = 702 -> Microsoft.CodeAnalysis.CSharp.LanguageVersion
static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetConversion(this Microsoft.CodeAnalysis.Operations.IConversionOperation conversionExpression) -> Microsoft.CodeAnalysis.CSharp.Conversion
Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax.RefKindKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken
Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax.Update(Microsoft.CodeAnalysis.CSharp.Syntax.NameColonSyntax nameColon, Microsoft.CodeAnalysis.SyntaxToken refKindKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression) -> Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax.WithRefKindKeyword(Microsoft.CodeAnalysis.SyntaxToken refKindKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.RefTypeSyntax.ReadOnlyKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken
Microsoft.CodeAnalysis.CSharp.Syntax.RefTypeSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken refKeyword, Microsoft.CodeAnalysis.SyntaxToken readOnlyKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type) -> Microsoft.CodeAnalysis.CSharp.Syntax.RefTypeSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.RefTypeSyntax.WithReadOnlyKeyword(Microsoft.CodeAnalysis.SyntaxToken readOnlyKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.RefTypeSyntax
static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetConversion(this Microsoft.CodeAnalysis.Operations.IConversionOperation conversionExpression) -> Microsoft.CodeAnalysis.CSharp.Conversion
static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeconstructionInfo(this Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.AssignmentExpressionSyntax assignment) -> Microsoft.CodeAnalysis.CSharp.DeconstructionInfo
static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeconstructionInfo(this Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.ForEachVariableStatementSyntax foreach) -> Microsoft.CodeAnalysis.CSharp.DeconstructionInfo
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.Argument(Microsoft.CodeAnalysis.CSharp.Syntax.NameColonSyntax nameColon, Microsoft.CodeAnalysis.SyntaxToken refKindKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression) -> Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.RefType(Microsoft.CodeAnalysis.SyntaxToken refKeyword, Microsoft.CodeAnalysis.SyntaxToken readOnlyKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type) -> Microsoft.CodeAnalysis.CSharp.Syntax.RefTypeSyntax
Microsoft.CodeAnalysis.CSharp.Conversion.IsStackAlloc.get -> bool
Loading

0 comments on commit ee5a91f

Please sign in to comment.