Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pattern Using #27729

Merged
merged 24 commits into from
Jun 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

result.DeclaredAccessibility != Accessibility.Public [](start = 39, length = 52)

Does it really have to be public? Or only accessible here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the foreach pattern requires public. Let's stick with that for now and I'll add it as a question for LDM in an email.

{
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);
}
}
}
}