Skip to content

Commit

Permalink
Pattern Using (#27729)
Browse files Browse the repository at this point in the history
  • Loading branch information
fayrose committed Jun 28, 2018
1 parent 8e5768e commit 336e138
Show file tree
Hide file tree
Showing 36 changed files with 934 additions and 341 deletions.
196 changes: 196 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3101,5 +3101,201 @@ internal virtual ImmutableArray<LabelSymbol> Labels
return ImmutableArray<LabelSymbol>.Empty;
}
}

/// <summary>
/// Perform a lookup for the specified method on the specified type. Perform overload resolution
/// on the lookup results.
/// </summary>
/// <param name="patternType">Type to search.</param>
/// <param name="methodName">Method to search for.</param>
/// <param name="lookupResult">Passed in for reusability.</param>
/// <param name="warningsOnly">True if failures should result in warnings; false if they should result in errors.</param>
/// <param name="diagnostics">Populated with binding diagnostics.</param>
/// <returns>The desired method or null.</returns>
internal MethodSymbol FindPatternMethod(TypeSymbol patternType, string methodName, LookupResult lookupResult,
SyntaxNode syntaxExpr, bool warningsOnly, DiagnosticBag diagnostics,
SyntaxTree syntaxTree, MessageID messageID)
{
Debug.Assert(lookupResult.IsClear);

// Not using LookupOptions.MustBeInvocableMember because we don't want the corresponding lookup error.
// We filter out non-methods below.
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
this.LookupMembersInType(
lookupResult,
patternType,
methodName,
arity: 0,
basesBeingResolved: null,
options: LookupOptions.Default,
originalBinder: this,
diagnose: false,
useSiteDiagnostics: ref useSiteDiagnostics);

diagnostics.Add(syntaxExpr, useSiteDiagnostics);

if (!lookupResult.IsMultiViable)
{
ReportPatternMemberLookupDiagnostics(lookupResult, patternType, methodName, syntaxExpr, warningsOnly, diagnostics, messageID);
return null;
}

ArrayBuilder<MethodSymbol> candidateMethods = ArrayBuilder<MethodSymbol>.GetInstance();

foreach (Symbol member in lookupResult.Symbols)
{
if (member.Kind != SymbolKind.Method)
{
candidateMethods.Free();

if (warningsOnly)
{
ReportPatternWarning(diagnostics, patternType, member, syntaxExpr, messageID);
}

return null;
}

MethodSymbol method = (MethodSymbol)member;

// SPEC VIOLATION: The spec says we should apply overload resolution, but Dev10 uses
// some custom logic in ExpressionBinder.BindGrpToParams. The biggest difference
// we've found (so far) is that it only considers methods with zero parameters
// (i.e. doesn't work with "params" or optional parameters).
if (!method.Parameters.Any())
{
candidateMethods.Add((MethodSymbol)member);
}
}

MethodSymbol patternMethod = PerformPatternOverloadResolution(patternType, candidateMethods, syntaxExpr, warningsOnly, diagnostics, syntaxTree, messageID);

candidateMethods.Free();

return patternMethod;
}

/// <summary>
/// The overload resolution portion of FindPatternMethod.
/// </summary>
private MethodSymbol PerformPatternOverloadResolution(
TypeSymbol patternType, ArrayBuilder<MethodSymbol> candidateMethods,
SyntaxNode syntaxExpression, bool warningsOnly, DiagnosticBag diagnostics,
SyntaxTree syntaxTree, MessageID messageID)
{
ArrayBuilder<TypeSymbol> typeArguments = ArrayBuilder<TypeSymbol>.GetInstance();
AnalyzedArguments arguments = AnalyzedArguments.GetInstance();
OverloadResolutionResult<MethodSymbol> overloadResolutionResult = OverloadResolutionResult<MethodSymbol>.GetInstance();

HashSet<DiagnosticInfo> useSiteDiagnostics = null;
// We create a dummy receiver of the invocation so MethodInvocationOverloadResolution knows it was invoked from an instance, not a type
var dummyReceiver = new BoundImplicitReceiver(syntaxExpression, patternType);
this.OverloadResolution.MethodInvocationOverloadResolution(
methods: candidateMethods,
typeArguments: typeArguments,
receiver: dummyReceiver,
arguments: arguments,
result: overloadResolutionResult,
useSiteDiagnostics: ref useSiteDiagnostics);
diagnostics.Add(syntaxExpression, useSiteDiagnostics);

MethodSymbol result = null;

if (overloadResolutionResult.Succeeded)
{
result = overloadResolutionResult.ValidResult.Member;

if (result.IsStatic || result.DeclaredAccessibility != Accessibility.Public)
{
if (warningsOnly)
{
diagnostics.Add(ErrorCode.WRN_PatternStaticOrInaccessible, syntaxExpression.Location, patternType, messageID.Localize(), result);
}

result = null;
}
else if (result.CallsAreOmitted(syntaxTree))
{
// Calls to this method are omitted in the current syntax tree, i.e it is either a partial method with no implementation part OR a conditional method whose condition is not true in this source file.
// We don't want to allow this case, see StatementBinder::bindPatternToMethod.
result = null;
}
}
else if (overloadResolutionResult.Results.Length > 1)
{
if (warningsOnly)
{
diagnostics.Add(ErrorCode.WRN_PatternIsAmbiguous, syntaxExpression.Location, patternType, messageID.Localize(),
overloadResolutionResult.Results[0].Member, overloadResolutionResult.Results[1].Member);
}
}

overloadResolutionResult.Free();
arguments.Free();
typeArguments.Free();

return result;
}

private void ReportPatternWarning(DiagnosticBag diagnostics, TypeSymbol patternType, Symbol patternMemberCandidate, SyntaxNode expression, MessageID messageID)
{
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
if (this.IsAccessible(patternMemberCandidate, ref useSiteDiagnostics))
{
diagnostics.Add(ErrorCode.WRN_PatternBadSignature, expression.Location, patternType, messageID.Localize(), patternMemberCandidate);
}

diagnostics.Add(expression, useSiteDiagnostics);
}

/// <summary>
/// Report appropriate diagnostics when lookup of a pattern member (i.e. GetEnumerator, Current, or MoveNext) fails.
/// </summary>
/// <param name="lookupResult">Failed lookup result.</param>
/// <param name="patternType">Type in which member was looked up.</param>
/// <param name="memberName">Name of looked up member.</param>
/// <param name="warningsOnly">True if failures should result in warnings; false if they should result in errors.</param>
/// <param name="diagnostics">Populated appropriately.</param>
internal void ReportPatternMemberLookupDiagnostics(
LookupResult lookupResult, TypeSymbol patternType,
string memberName, SyntaxNode expression,
bool warningsOnly, DiagnosticBag diagnostics,
MessageID messageID)
{
if (lookupResult.Symbols.Any())
{
if (warningsOnly)
{
ReportPatternWarning(diagnostics, patternType, lookupResult.Symbols.First(), expression, messageID);
}
else
{
lookupResult.Clear();

HashSet<DiagnosticInfo> useSiteDiagnostics = null;
this.LookupMembersInType(
lookupResult,
patternType,
memberName,
arity: 0,
basesBeingResolved: null,
options: LookupOptions.Default,
originalBinder: this,
diagnose: true,
useSiteDiagnostics: ref useSiteDiagnostics);

diagnostics.Add(expression, useSiteDiagnostics);

if (lookupResult.Error != null)
{
diagnostics.Add(lookupResult.Error, expression.Location);
}
}
}
else if (!warningsOnly)
{
diagnostics.Add(ErrorCode.ERR_NoSuchMember, expression.Location, patternType, memberName);
}
}
}
}

0 comments on commit 336e138

Please sign in to comment.