Skip to content

Commit

Permalink
Merge pull request #4368 from sharwell/foreach-copy
Browse files Browse the repository at this point in the history
Initial support for IForEachLoopOperation in DoNotCopyValue
  • Loading branch information
sharwell authored Oct 26, 2020
2 parents 01ebd22 + 9f68083 commit 8982993
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 2 deletions.
24 changes: 22 additions & 2 deletions src/Roslyn.Diagnostics.Analyzers/Core/DoNotCopyValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -364,9 +364,14 @@ bool IsSupportedConversion()
return false;
}

if (Acquire(operation.Operand) == RefKind.None)
switch (Acquire(operation.Operand))
{
return true;
case RefKind.None:
case RefKind.Ref when operation.Conversion.IsIdentity:
return true;

default:
break;
}

return false;
Expand Down Expand Up @@ -540,6 +545,21 @@ public override void VisitForEachLoop(IForEachLoopOperation operation)
CheckLocalSymbolInUnsupportedContext(operation, local);
}

var instance = operation.Collection;
var instance2 = (operation.Collection as IConversionOperation)?.Operand;
if (Acquire(operation.Collection) != RefKind.Ref)
{
instance = null;
instance2 = null;
}
else if (Acquire(instance2) != RefKind.Ref)
{
instance2 = null;
}

using var releaser = TryAddForVisit(_handledOperations, instance, out _);
using var releaser2 = TryAddForVisit(_handledOperations, instance2, out _);

CheckTypeInUnsupportedContext(operation);
base.VisitForEachLoop(operation);
}
Expand Down
119 changes: 119 additions & 0 deletions src/Roslyn.Diagnostics.Analyzers/UnitTests/DoNotCopyValueTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,125 @@ struct CannotCopy
public int Second { get; set; }
}
internal sealed class NonCopyableAttribute : System.Attribute { }
";

await new VerifyCS.Test
{
TestCode = source,
LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8,
}.RunAsync();
}

[Fact]
public async Task AllowCustomForeachEnumerator()
{
var source = @"
using System.Runtime.InteropServices;
class C
{
void Method()
{
var cannotCopy = new CannotCopy();
foreach (var obj in cannotCopy)
{
}
}
}
[NonCopyable]
struct CannotCopy
{
public Enumerator GetEnumerator() => throw null;
public struct Enumerator
{
public object Current => throw null;
public bool MoveNext() => throw null;
}
}
internal sealed class NonCopyableAttribute : System.Attribute { }
";

await new VerifyCS.Test
{
TestCode = source,
LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8,
}.RunAsync();
}

[Fact]
public async Task AllowCustomForeachEnumeratorDisposableObject()
{
var source = @"
using System;
using System.Runtime.InteropServices;
class C
{
void Method()
{
using var cannotCopy = new CannotCopy();
foreach (var obj in cannotCopy)
{
}
}
}
[NonCopyable]
struct CannotCopy : IDisposable
{
public void Dispose() => throw null;
public Enumerator GetEnumerator() => throw null;
public struct Enumerator
{
public object Current => throw null;
public bool MoveNext() => throw null;
}
}
internal sealed class NonCopyableAttribute : System.Attribute { }
";

await new VerifyCS.Test
{
TestCode = source,
LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8,
}.RunAsync();
}

[Fact]
public async Task AllowCustomForeachReadonlyEnumerator()
{
var source = @"
using System.Runtime.InteropServices;
class C
{
void Method()
{
var cannotCopy = new CannotCopy();
foreach (var obj in cannotCopy)
{
}
}
}
[NonCopyable]
struct CannotCopy
{
public readonly Enumerator GetEnumerator() => throw null;
public struct Enumerator
{
public object Current => throw null;
public bool MoveNext() => throw null;
}
}
internal sealed class NonCopyableAttribute : System.Attribute { }
";

Expand Down

0 comments on commit 8982993

Please sign in to comment.