Skip to content

Commit

Permalink
Clean up 'foreach' handling to match invocation handling
Browse files Browse the repository at this point in the history
  • Loading branch information
sharwell committed Oct 28, 2020
1 parent 653580d commit ff4091f
Showing 1 changed file with 32 additions and 25 deletions.
57 changes: 32 additions & 25 deletions src/Roslyn.Diagnostics.Analyzers/Core/DoNotCopyValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -547,38 +547,45 @@ public override void VisitForEachLoop(IForEachLoopOperation operation)
CheckLocalSymbolInUnsupportedContext(operation, local);
}

var instance = operation.Collection;
// 'foreach' operations have an identity conversion for the collection property, and then invoke the
// GetEnumerator method.
var instance = operation.Collection as IConversionOperation;
var instance2 = (operation.Collection as IConversionOperation)?.Operand;

switch (Acquire(operation.Collection))
if (instance2 is null)
{
case RefKind.Ref:
// No special requirements
break;

case RefKind.RefReadOnly when operation.Syntax is CommonForEachStatementSyntax syntax && operation.SemanticModel.GetForEachStatementInfo(syntax).GetEnumeratorMethod is { IsReadOnly: true }:
// Requirement of readonly GetEnumerator is met
break;

default:
instance = null;
instance2 = null;
break;
// Didn't match the known pattern
instance = null;
}

switch (Acquire(instance2))
else if (instance?.Conversion is not { IsIdentity: true, MethodSymbol: null })
{
case RefKind.Ref:
// No special requirements
break;

case RefKind.RefReadOnly when operation.Syntax is CommonForEachStatementSyntax syntax && operation.SemanticModel.GetForEachStatementInfo(syntax).GetEnumeratorMethod is { IsReadOnly: true }:
// Requirement of readonly GetEnumerator is met
break;
// Not a supported conversion
instance = null;
instance2 = null;
}
else
{
// Treat this as an invocation of the GetEnumerator method.
if (operation.Syntax is CommonForEachStatementSyntax syntax
&& operation.SemanticModel.GetForEachStatementInfo(syntax).GetEnumeratorMethod is { } getEnumeratorMethod)
{
CheckMethodSymbolInUnsupportedContext(operation, getEnumeratorMethod);

default:
if (instance2 is not null
&& _cache.IsNonCopyableType(getEnumeratorMethod.ReceiverType)
&& !getEnumeratorMethod.IsReadOnly
&& Acquire(instance) == RefKind.In)
{
// mark the instance as not checked by this method
instance2 = null;
}
}
else
{
// Not supported
instance = null;
instance2 = null;
break;
}
}

using var releaser = TryAddForVisit(_handledOperations, instance, out _);
Expand Down

0 comments on commit ff4091f

Please sign in to comment.