Skip to content

Commit

Permalink
Merge pull request #7013 from CollinAlpert/issue-6981
Browse files Browse the repository at this point in the history
Generate unique name for CA1861
  • Loading branch information
mavasani authored Oct 31, 2023
2 parents cf0cf55 + 17bde3b commit d3e5965
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

using System;
using System.Linq;
using System.Globalization;
using System.Collections.Generic;
Expand Down Expand Up @@ -51,10 +50,22 @@ private static async Task<Document> ExtractConstArrayAsync(Document document, Sy
DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
SyntaxGenerator generator = editor.Generator;
IArrayCreationOperation arrayArgument = GetArrayCreationOperation(node, model, cancellationToken, out bool isInvoked);
INamedTypeSymbol containingType = model.GetEnclosingSymbol(node.SpanStart, cancellationToken)!.ContainingType;
ISymbol enclosingSymbol = model.GetEnclosingSymbol(node.SpanStart, cancellationToken)!;
INamedTypeSymbol containingType = enclosingSymbol.ContainingType;
HashSet<string> identifiers = new(containingType.MemberNames);
if (enclosingSymbol is IMethodSymbol method)
{
identifiers.AddRange(method.Parameters.Select(p => p.Name));
}

IMethodBodyOperation? containingMethod = arrayArgument.GetAncestor<IMethodBodyOperation>(OperationKind.MethodBody);
if (containingMethod?.BlockBody is not null)
{
identifiers.AddRange(containingMethod.BlockBody.Locals.Select(l => l.Name));
}

// Get a valid member name for the extracted constant
string newMemberName = GetExtractedMemberName(containingType.MemberNames, properties["paramName"] ?? GetMemberNameFromType(arrayArgument));
string newMemberName = GetExtractedMemberName(identifiers, properties["paramName"] ?? GetMemberNameFromType(arrayArgument));

// Get method containing the symbol that is being diagnosed
IOperation? methodContext = arrayArgument.GetAncestor<IMethodBodyOperation>(OperationKind.MethodBody);
Expand Down Expand Up @@ -131,20 +142,20 @@ private static IArrayCreationOperation GetArrayCreationOperation(SyntaxNode node
return (IArrayCreationOperation)model.GetOperation(node.ChildNodes().First(), cancellationToken)!;
}

private static string GetExtractedMemberName(IEnumerable<string> memberNames, string parameterName)
private static string GetExtractedMemberName(ISet<string> identifierNames, string parameterName)
{
bool hasCollectionEnding = s_collectionMemberEndings.Any(x => parameterName.EndsWith(x, true, CultureInfo.InvariantCulture));

if (parameterName == "source" // for LINQ, "sourceArray" is clearer than "source"
|| (memberNames.Contains(parameterName) && !hasCollectionEnding))
|| (identifierNames.Contains(parameterName) && !hasCollectionEnding))
{
parameterName += "Array";
}

if (memberNames.Contains(parameterName))
if (identifierNames.Contains(parameterName))
{
int suffix = 0;
while (memberNames.Contains(parameterName + suffix))
while (identifierNames.Contains(parameterName + suffix))
{
suffix++;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -835,5 +835,127 @@ public void M2(int length)
}
}");
}

[Fact, WorkItem(6981, "https://github.com/dotnet/roslyn-analyzers/issues/6981")]
public Task UseUniqueIdentifier_Parameter()
{
const string source = """
class Sample
{
void A(char separator, char separatorArray)
{
"".Split([|new char[] { 'a', 'b' }|]);
}
}
""";
const string fixedSource = """
class Sample
{
internal static readonly char[] separatorArray0 = new char[] { 'a', 'b' };
void A(char separator, char separatorArray)
{
"".Split(separatorArray0);
}
}
""";

return VerifyCS.VerifyCodeFixAsync(source, fixedSource);
}

[Fact, WorkItem(6981, "https://github.com/dotnet/roslyn-analyzers/issues/6981")]
public Task UseUniqueIdentifier_Local()
{
const string source = """
class Sample
{
void A()
{
object separator = null;
object separatorArray = null;
"".Split([|new char[] { 'a', 'b' }|]);
}
}
""";
const string fixedSource = """
class Sample
{
internal static readonly char[] separatorArray0 = new char[] { 'a', 'b' };
void A()
{
object separator = null;
object separatorArray = null;
"".Split(separatorArray0);
}
}
""";

return VerifyCS.VerifyCodeFixAsync(source, fixedSource);
}

[Fact, WorkItem(6981, "https://github.com/dotnet/roslyn-analyzers/issues/6981")]
public Task UseUniqueIdentifier_Field()
{
const string source = """
class Sample
{
private string separator;
private string separatorArray;
void A()
{
"".Split([|new char[] { 'a', 'b' }|]);
}
}
""";
const string fixedSource = """
class Sample
{
private string separator;
private string separatorArray;
internal static readonly char[] separatorArray0 = new char[] { 'a', 'b' };
void A()
{
"".Split(separatorArray0);
}
}
""";

return VerifyCS.VerifyCodeFixAsync(source, fixedSource);
}

[Fact, WorkItem(6981, "https://github.com/dotnet/roslyn-analyzers/issues/6981")]
public Task UseUniqueIdentifier_FieldAndParameter()
{
const string source = """
class Sample
{
private string separator;
private string separatorArray;
void A(char separatorArray0)
{
"".Split([|new char[] { 'a', 'b' }|]);
}
}
""";
const string fixedSource = """
class Sample
{
private string separator;
private string separatorArray;
internal static readonly char[] separatorArray1 = new char[] { 'a', 'b' };
void A(char separatorArray0)
{
"".Split(separatorArray1);
}
}
""";

return VerifyCS.VerifyCodeFixAsync(source, fixedSource);
}
}
}

0 comments on commit d3e5965

Please sign in to comment.