Skip to content

Commit

Permalink
Support collection expressions for non-copyable analyzer (#7210)
Browse files Browse the repository at this point in the history
This supports using a collection expression to initialize a non-copyable collection type.
  • Loading branch information
333fred authored Feb 28, 2024
1 parent 6fcb27c commit 0e9cb2a
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 1 deletion.
2 changes: 1 addition & 1 deletion eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
<!-- Versions for tests and general utility execution. -->
<!-- This version is for utility and executable assemblies. The version here should not overlap with any of the surface
area versions. -->
<MicrosoftCodeAnalysisVersionForTests>4.8.0</MicrosoftCodeAnalysisVersionForTests>
<MicrosoftCodeAnalysisVersionForTests>4.9.0-3.final</MicrosoftCodeAnalysisVersionForTests>
<MicrosoftCodeAnalysisVersionForExecution>4.6.0-1.final</MicrosoftCodeAnalysisVersionForExecution>
<MicrosoftCodeAnalysisCSharpCodeStyleVersion>4.6.0</MicrosoftCodeAnalysisCSharpCodeStyleVersion>
<MicrosoftCodeAnalysisVisualBasicCodeStyleVersion>4.6.0</MicrosoftCodeAnalysisVisualBasicCodeStyleVersion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Linq;
using Analyzer.Utilities;
using Analyzer.Utilities.Extensions;
using Analyzer.Utilities.Lightup;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.FlowAnalysis;
Expand Down Expand Up @@ -1437,6 +1438,9 @@ when SymbolEqualityComparer.Default.Equals(taskType, Cache.ValueTaskT) || Symbol
case OperationKind.Throw:
return RefKind.None;

case OperationKindEx.CollectionExpression:
return RefKind.None;

default:
return RefKind.RefReadOnly;
}
Expand Down
118 changes: 118 additions & 0 deletions src/Roslyn.Diagnostics.Analyzers/UnitTests/DoNotCopyValueTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1400,5 +1400,123 @@ End Structure
// /0/Test0.vb(38,22): warning RS0042: Auto-property 'Private Property Property3 As CannotCopy' cannot have non-copyable type 'CannotCopy'
VerifyVB.Diagnostic(AbstractDoNotCopyValue.NoAutoPropertyRule).WithLocation(1).WithArguments("CannotCopy", "Private Property Property3 As CannotCopy"));
}

[Fact]
public async Task AllowCopyFromCollectionExpression()
{
var source = """
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
[NonCopyable]
[CollectionBuilder(typeof(MyCollection), nameof(Create))]
partial struct MyCollection : IEnumerable<int>
{
public IEnumerator<int> GetEnumerator() => null;
IEnumerator IEnumerable.GetEnumerator() => null;
public static MyCollection Create(ReadOnlySpan<int> r) => throw null;
}
class C
{
void M()
{
MyCollection m = [1, 2, 3];
m = [];
}
}
internal sealed class NonCopyableAttribute : System.Attribute { }
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false)]
internal sealed class CollectionBuilderAttribute : Attribute
{
public CollectionBuilderAttribute(Type builderType, string methodName)
{
BuilderType = builderType;
MethodName = methodName;
}
public Type BuilderType { get; }
public string MethodName { get; }
}
}
""";

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

[Fact]
public async Task DoNotAllowCopyInCollectionExpressionElement()
{
var source = """
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
[NonCopyable]
struct S
{
}
[CollectionBuilder(typeof(MyCollection), nameof(Create))]
partial struct MyCollection : IEnumerable<S>
{
public IEnumerator<S> GetEnumerator() => throw null;
IEnumerator IEnumerable.GetEnumerator() => throw null;
public static MyCollection Create(ReadOnlySpan<S> r) => throw null;
}
class C
{
void M()
{
S s = new();
MyCollection m = [{|#0:s|}, new S()];
}
}
internal sealed class NonCopyableAttribute : System.Attribute { }
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false)]
internal sealed class CollectionBuilderAttribute : Attribute
{
public CollectionBuilderAttribute(Type builderType, string methodName)
{
BuilderType = builderType;
MethodName = methodName;
}
public Type BuilderType { get; }
public string MethodName { get; }
}
}
""";

await new VerifyCS.Test
{
TestCode = source,
LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp12,
ExpectedDiagnostics = {
// /0/Test0.cs(25,27): warning RS0042: Unsupported use of non-copyable type 'S' in 'LocalReference' operation
VerifyCS.Diagnostic(AbstractDoNotCopyValue.UnsupportedUseRule).WithLocation(0).WithArguments("S", "LocalReference")
}
}.RunAsync();
}
}
}
1 change: 1 addition & 0 deletions src/Utilities/Compiler/Lightup/OperationKindEx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ internal static class OperationKindEx
public const OperationKind ImplicitIndexerReference = (OperationKind)0x7b;
public const OperationKind Utf8String = (OperationKind)0x7c;
public const OperationKind Attribute = (OperationKind)0x7d;
public const OperationKind CollectionExpression = (OperationKind)0x7f;
}
}

Expand Down

0 comments on commit 0e9cb2a

Please sign in to comment.