Skip to content

Commit 0da1e63

Browse files
Do not offer to to convert new X(e) to [.. e] when the type does not fullfill the collection expr pattern. (#76684)
2 parents 951bb50 + 7e63a88 commit 0da1e63

File tree

3 files changed

+66
-3
lines changed

3 files changed

+66
-3
lines changed

src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForNewDiagnosticAnalyzer.cs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System.Collections;
56
using System.Collections.Generic;
67
using System.Collections.Immutable;
8+
using System.Linq;
79
using Microsoft.CodeAnalysis.CodeStyle;
810
using Microsoft.CodeAnalysis.CSharp.Syntax;
911
using Microsoft.CodeAnalysis.Diagnostics;
@@ -51,13 +53,13 @@ private void AnalyzeBaseObjectCreationExpression(
5153
return;
5254

5355
var symbol = semanticModel.GetSymbolInfo(objectCreationExpression, cancellationToken).Symbol;
54-
if (symbol is not IMethodSymbol { MethodKind: MethodKind.Constructor, Parameters: [var parameter] } ||
55-
parameter.Type.Name != nameof(IEnumerable<int>))
56+
if (symbol is not IMethodSymbol { MethodKind: MethodKind.Constructor, Parameters: [var constructorParameter] } ||
57+
constructorParameter.Type.Name != nameof(IEnumerable<int>))
5658
{
5759
return;
5860
}
5961

60-
if (!Equals(compilation.IEnumerableOfTType(), parameter.Type.OriginalDefinition))
62+
if (!Equals(compilation.IEnumerableOfTType(), constructorParameter.Type.OriginalDefinition))
6163
return;
6264

6365
if (!IsArgumentCompatibleWithIEnumerableOfT(semanticModel, argument, out var unwrapArgument, out var useSpread, cancellationToken))
@@ -71,6 +73,21 @@ private void AnalyzeBaseObjectCreationExpression(
7173
return;
7274
}
7375

76+
// Because we want to replace `new X(enumerable)` with `[.. enumerable]` we need to make sure that the final
77+
// type supports the collection initialization pattern (specifically that it exposes an Add method that takes
78+
// the element type). This prevents us from working on certain types that do allow the former form but not the
79+
// latter.
80+
// If the constructor took an IEnumerable<T>, ensure we find a `public Add(T)` method on the type.
81+
var constructorParameterTypeArg = constructorParameter.Type.GetTypeArguments().Single();
82+
if (!symbol.ContainingType
83+
.GetMembers(nameof(IList.Add))
84+
.OfType<IMethodSymbol>()
85+
.Any(m => m is { DeclaredAccessibility: Accessibility.Public, IsStatic: false, Parameters: [var addParameter] } &&
86+
addParameter.Type.Equals(constructorParameterTypeArg)))
87+
{
88+
return;
89+
}
90+
7491
var locations = ImmutableArray.Create(objectCreationExpression.GetLocation());
7592
var properties = GetDiagnosticProperties(unwrapArgument, useSpread, changesSemantics);
7693

src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForNewTests.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,27 @@ List<int> GetNumbers()
9494
ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
9595
}.RunAsync();
9696
}
97+
98+
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76683")]
99+
public async Task TestNotOnStack()
100+
{
101+
await new VerifyCS.Test
102+
{
103+
TestCode = """
104+
using System.Linq;
105+
using System.Collections.Generic;
106+
using System.Collections.Immutable;
107+
108+
class C
109+
{
110+
Stack<T> GetNumbers<T>(T[] values)
111+
{
112+
return new Stack<T>(values);
113+
}
114+
}
115+
""",
116+
LanguageVersion = LanguageVersion.CSharp12,
117+
ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
118+
}.RunAsync();
119+
}
97120
}

src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests_CollectionExpression.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5941,4 +5941,27 @@ void Main()
59415941
ReferenceAssemblies = ReferenceAssemblies.Net.Net90,
59425942
}.RunAsync();
59435943
}
5944+
5945+
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76683")]
5946+
public async Task TestNotOnStack()
5947+
{
5948+
await new VerifyCS.Test
5949+
{
5950+
TestCode = """
5951+
using System.Linq;
5952+
using System.Collections.Generic;
5953+
using System.Collections.Immutable;
5954+
5955+
class C
5956+
{
5957+
Stack<T> GetNumbers<T>(T[] values)
5958+
{
5959+
return new Stack<T>(values);
5960+
}
5961+
}
5962+
""",
5963+
LanguageVersion = LanguageVersion.CSharp12,
5964+
ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
5965+
}.RunAsync();
5966+
}
59445967
}

0 commit comments

Comments
 (0)