Skip to content

Commit

Permalink
Implement GetSymbolInfo for the identifier in a property pattern.
Browse files Browse the repository at this point in the history
  • Loading branch information
gafter committed May 25, 2018
1 parent 895c4ee commit 607639b
Show file tree
Hide file tree
Showing 8 changed files with 430 additions and 30 deletions.
36 changes: 17 additions & 19 deletions src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs
Original file line number Diff line number Diff line change
Expand Up @@ -554,29 +554,27 @@ private ImmutableArray<BoundSubpattern> BindDeconstructionPatternClause(
}
for (int i = 0; i < node.Subpatterns.Count; i++)
{
var subPattern = node.Subpatterns[i];
var subpatternSyntax = node.Subpatterns[i];
bool isError = i >= elementTypes.Length;
TypeSymbol elementType = isError ? CreateErrorType() : elementTypes[i];
FieldSymbol foundField = null;
if (subPattern.NameColon != null)
if (subpatternSyntax.NameColon != null)
{
string name = subPattern.NameColon.Name.Identifier.ValueText;
foundField = CheckIsTupleElement(subPattern.NameColon.Name, (NamedTypeSymbol)declType, name, i, diagnostics);
string name = subpatternSyntax.NameColon.Name.Identifier.ValueText;
foundField = CheckIsTupleElement(subpatternSyntax.NameColon.Name, (NamedTypeSymbol)declType, name, i, diagnostics);
}

BoundSubpattern boundSubpattern = new BoundSubpattern(
subPattern,
subpatternSyntax,
foundField,
BindPattern(subPattern.Pattern, elementType, isError, diagnostics));
BindPattern(subpatternSyntax.Pattern, elementType, isError, diagnostics));
patterns.Add(boundSubpattern);
}
}
else
{
// It is not a tuple type. Seek an appropriate Deconstruct method.
var inputPlaceholder = new BoundImplicitReceiver(node, declType); // A fake receiver expression to permit us to reuse binding logic
// PROTOTYPE(patterns2): Can we include element names node.Subpatterns[i].NameColon?.Name in the AnalyzedArguments
// used in MakeDeconstructInvocationExpression so they are used to disambiguate? LDM needs to reconcile with deconstruction.
BoundExpression deconstruct = MakeDeconstructInvocationExpression(
node.Subpatterns.Count, inputPlaceholder, node, diagnostics, outPlaceholders: out ImmutableArray<BoundDeconstructValuePlaceholder> outPlaceholders);
deconstructMethod = deconstruct.ExpressionSymbol as MethodSymbol;
Expand Down Expand Up @@ -628,7 +626,7 @@ private ImmutableArray<BoundSubpattern> BindDeconstructionPatternClause(
/// <summary>
/// Check that the given name designates a tuple element at the given index, and return that element.
/// </summary>
private FieldSymbol CheckIsTupleElement(SyntaxNode node, NamedTypeSymbol tupleType, string name, int tupleIndex, DiagnosticBag diagnostics)
private static FieldSymbol CheckIsTupleElement(SyntaxNode node, NamedTypeSymbol tupleType, string name, int tupleIndex, DiagnosticBag diagnostics)
{
FieldSymbol foundElement = null;
foreach (var symbol in tupleType.GetMembers(name))
Expand Down Expand Up @@ -702,10 +700,11 @@ private BoundPattern BindVarDesignation(VarPatternSyntax node, VariableDesignati
}
for (int i = 0; i < tupleDesignation.Variables.Count; i++)
{
var variable = tupleDesignation.Variables[i];
bool isError = i >= elementTypes.Length;
TypeSymbol elementType = isError ? CreateErrorType() : elementTypes[i];
BoundPattern boundSubpattern = BindVarDesignation(node, tupleDesignation.Variables[i], elementType, isError, diagnostics);
subPatterns.Add(new BoundSubpattern(node, symbol: null, boundSubpattern));
BoundPattern pattern = BindVarDesignation(node, variable, elementType, isError, diagnostics);
subPatterns.Add(new BoundSubpattern(variable, symbol: null, pattern));
}
}
else
Expand All @@ -719,10 +718,11 @@ private BoundPattern BindVarDesignation(VarPatternSyntax node, VariableDesignati

for (int i = 0; i < tupleDesignation.Variables.Count; i++)
{
var variable = tupleDesignation.Variables[i];
bool isError = outPlaceholders.IsDefaultOrEmpty || i >= outPlaceholders.Length;
TypeSymbol elementType = isError ? CreateErrorType() : outPlaceholders[i].Type;
BoundPattern pattern = BindVarDesignation(node, tupleDesignation.Variables[i], elementType, isError, diagnostics);
subPatterns.Add(new BoundSubpattern(node, symbol: null, pattern));
BoundPattern pattern = BindVarDesignation(node, variable, elementType, isError, diagnostics);
subPatterns.Add(new BoundSubpattern(variable, symbol: null, pattern));
}

// PROTOTYPE(patterns2): If no Deconstruct method is found, try casting to `ITuple`.
Expand Down Expand Up @@ -768,7 +768,7 @@ ImmutableArray<BoundSubpattern> BindPropertyPatternClause(
}

BoundPattern boundPattern = BindPattern(pattern, memberType, hasErrors, diagnostics);
builder.Add(new BoundSubpattern(pattern, member, boundPattern));
builder.Add(new BoundSubpattern(p, member, boundPattern));
}

return builder.ToImmutableAndFree();
Expand All @@ -782,13 +782,13 @@ private Symbol LookupMemberForPropertyPattern(
if (inputType.IsErrorType() || hasErrors || symbol == (object)null)
{
memberType = CreateErrorType();
return null;
}
else
{
memberType = symbol.GetTypeOrReturnType();
return symbol;
}

return symbol;
}

private Symbol BindPropertyPatternMember(
Expand Down Expand Up @@ -819,7 +819,6 @@ private Symbol BindPropertyPatternMember(
(BoundPropertyGroup)boundMember, mustHaveAllOptionalParameters: true, diagnostics: diagnostics);
}

LookupResultKind resultKind = boundMember.ResultKind;
hasErrors |= boundMember.HasAnyErrors || implicitReceiver.HasAnyErrors;

switch (boundMember.Kind)
Expand Down Expand Up @@ -854,14 +853,13 @@ private Symbol BindPropertyPatternMember(
}

hasErrors = true;
return null;
return boundMember.ExpressionSymbol;
}

if (hasErrors || !CheckValueKind(node: memberName.Parent, expr: boundMember, valueKind: BindValueKind.RValue,
checkingReceiver: false, diagnostics: diagnostics))
{
hasErrors = true;
return null;
}

return boundMember.ExpressionSymbol;
Expand Down
29 changes: 24 additions & 5 deletions src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1756,6 +1756,14 @@ internal SymbolInfo GetSymbolInfoForNode(
BoundNode boundNodeForSyntacticParent,
Binder binderOpt)
{
LookupResultKind resultKind;
ImmutableArray<Symbol> symbols;

if (lowestBoundNode is BoundSubpattern subpattern)
{
return GetSymbolInfoForSubpattern(subpattern);
}

if (!(lowestBoundNode is BoundExpression boundExpr))
{
return SymbolInfo.None;
Expand All @@ -1768,10 +1776,7 @@ internal SymbolInfo GetSymbolInfoForNode(

// Get symbols and result kind from the lowest and highest nodes associated with the
// syntax node.
LookupResultKind resultKind;
bool isDynamic;
ImmutableArray<Symbol> unusedMemberGroup;
var symbols = GetSemanticSymbols(boundExpr, boundNodeForSyntacticParent, binderOpt, options, out isDynamic, out resultKind, out unusedMemberGroup);
symbols = GetSemanticSymbols(boundExpr, boundNodeForSyntacticParent, binderOpt, options, out bool isDynamic, out resultKind, out ImmutableArray<Symbol> unusedMemberGroup);

if (highestBoundNode is BoundExpression highestBoundExpr)
{
Expand Down Expand Up @@ -1844,6 +1849,16 @@ internal SymbolInfo GetSymbolInfoForNode(
return SymbolInfoFactory.Create(symbols, resultKind, isDynamic);
}

private SymbolInfo GetSymbolInfoForSubpattern(BoundSubpattern subpattern)
{
if (subpattern.Symbol?.OriginalDefinition is ErrorTypeSymbol originalErrorType)
{
return new SymbolInfo(null, originalErrorType.CandidateSymbols.CastArray<ISymbol>(), originalErrorType.ResultKind.ToCandidateReason());
}

return new SymbolInfo(subpattern.Symbol, CandidateReason.None);
}

private static void AddUnwrappingErrorTypes(ArrayBuilder<Symbol> builder, Symbol s)
{
var originalErrorSymbol = s.OriginalDefinition as ErrorTypeSymbol;
Expand Down Expand Up @@ -4101,10 +4116,14 @@ private SymbolInfo GetNamedArgumentSymbolInfo(IdentifierNameSyntax identifierNam
return (object)tupleElement == null ? SymbolInfo.None : new SymbolInfo(tupleElement, ImmutableArray<ISymbol>.Empty, CandidateReason.None);
}

if (parent3.IsKind(SyntaxKind.PropertyPatternClause) || parent3.IsKind(SyntaxKind.DeconstructionPatternClause))
{
return GetSymbolInfoWorker(identifierNameSyntax, SymbolInfoOptions.DefaultOptions, cancellationToken);
}

CSharpSyntaxNode containingInvocation = parent3.Parent;
SymbolInfo containingInvocationInfo = GetSymbolInfoWorker(containingInvocation, SymbolInfoOptions.PreferConstructorsToType | SymbolInfoOptions.ResolveAliases, cancellationToken);


if ((object)containingInvocationInfo.Symbol != null)
{
ParameterSymbol param = FindNamedParameter(((Symbol)containingInvocationInfo.Symbol).GetParameters(), argumentName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1853,6 +1853,7 @@ internal protected virtual CSharpSyntaxNode GetBindableSyntaxNode(CSharpSyntaxNo
case SyntaxKind.OperatorDeclaration:
case SyntaxKind.ConversionOperatorDeclaration:
case SyntaxKind.GlobalStatement:
case SyntaxKind.Subpattern:
return node;
}

Expand Down
8 changes: 8 additions & 0 deletions src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2161,6 +2161,14 @@ internal static CSharpSyntaxNode GetStandaloneNode(CSharpSyntaxNode node)
return parent;
}

break;

case SyntaxKind.NameColon:
if (parent.Parent.IsKind(SyntaxKind.Subpattern))
{
return parent.Parent;
}

break;
}

Expand Down
7 changes: 6 additions & 1 deletion src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ public static bool IsInNamespaceOrTypeContext(ExpressionSyntax node)
public static bool IsNamedArgumentName(SyntaxNode node)
{
// An argument name is an IdentifierName inside a NameColon, inside an Argument, inside an ArgumentList, inside an
// Invocation, ObjectCreation, ObjectInitializer, or ElementAccess.
// Invocation, ObjectCreation, ObjectInitializer, ElementAccess or Subpattern.

if (!node.IsKind(IdentifierName))
{
Expand All @@ -260,6 +260,11 @@ public static bool IsNamedArgumentName(SyntaxNode node)
}

var parent2 = parent1.Parent;
if (parent2.IsKind(SyntaxKind.Subpattern))
{
return true;
}

if (parent2 == null || !(parent2.IsKind(Argument) || parent2.IsKind(AttributeArgument)))
{
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,11 @@ protected static void AssertNoGlobalStatements(SyntaxTree tree)
Assert.Empty(tree.GetRoot().DescendantNodes().OfType<GlobalStatementSyntax>());
}

protected CSharpCompilation CreatePatternCompilation(string source)
{
return CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns);
}

#endregion helpers
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests
[CompilerTrait(CompilerFeature.Patterns)]
public class PatternMatchingTests2 : PatternMatchingTestBase
{
CSharpCompilation CreatePatternCompilation(string source)
{
return CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns);
}

[Fact]
public void Patterns2_00()
{
Expand Down
Loading

0 comments on commit 607639b

Please sign in to comment.