Skip to content

Commit

Permalink
CA1854: Use unused variable name for out parameter (#7261)
Browse files Browse the repository at this point in the history
* CA1854: Use unused variable name for out parameter

This commit also adds a new convenience extension method for other
fixers to use.

* Run msbuild pack
  • Loading branch information
mpidash authored Aug 26, 2024
1 parent 48136f7 commit e362b92
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,13 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
typeSyntax = IdentifierName(Var);
}
var identifierName = (IdentifierNameSyntax)(variableName is not null
? generator.IdentifierName(variableName)
: generator.FirstUnusedIdentifierName(model, containsKeyInvocation.SpanStart, Value));
var outArgument = (ArgumentSyntax)generator.Argument(RefKind.Out,
DeclarationExpression(
typeSyntax,
SingleVariableDesignation(Identifier(variableName ?? Value))
SingleVariableDesignation(identifierName.Identifier)
)
);
Expand All @@ -154,7 +157,6 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
.AddArgumentListArguments(outArgument);
editor.ReplaceNode(containsKeyInvocation, tryGetValueInvocation);
var identifierName = (IdentifierNameSyntax)generator.IdentifierName(variableName ?? Value);
if (addStatementNode != null)
{
editor.InsertBefore(addStatementNode,
Expand Down
1 change: 0 additions & 1 deletion src/NetAnalyzers/RulesMissingDocumentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,3 @@ Rule ID | Missing Help Link | Title |
CA2022 | <https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2022> | Avoid inexact read with 'Stream.Read' |
CA2023 | <https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2023> | Invalid braces in message template |
CA2265 | <https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2265> | Do not compare Span\<T> to 'null' or 'default' |
CA5360 | <https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5360> | Do Not Call Dangerous Methods In Deserialization |
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,32 @@ bool GetDict(out IDictionary<string, int> dict)
}
return 0;";

private const string GuardedReturnIdentifierUsed = @"
int value = 0;
int value1 = 1;
int value2 = 2;
string key = ""key"";
ConcurrentDictionary<string, int> data = new ConcurrentDictionary<string, int>();
if ({|#0:data.ContainsKey(key)|})
{
return {|#1:data[key]|};
}
return 0;";

private const string GuardedReturnIdentifierUsedFixed = @"
int value = 0;
int value1 = 1;
int value2 = 2;
string key = ""key"";
ConcurrentDictionary<string, int> data = new ConcurrentDictionary<string, int>();
if (data.TryGetValue(key, out int value3))
{
return value3;
}
return 0;";

#region NoDiagnostic

private const string InvalidModifiedBeforeUse = @"
Expand Down Expand Up @@ -1136,6 +1162,33 @@ Dim y
End If
Return 0";

private const string VbGuardedReturnIdentifierUsed = @"
Dim value As Integer = 0
Dim value1 As Integer = 1
Dim value2 As Integer = 2
Dim key As String = ""key""
Dim data As ConcurrentDictionary(Of String, Integer) = New ConcurrentDictionary(Of String, Integer)()
If {|#0:data.ContainsKey(key)|} Then
Return {|#1:data(key)|}
End If
Return 0";

private const string VbGuardedReturnIdentifierUsedFixed = @"
Dim value As Integer = 0
Dim value1 As Integer = 1
Dim value2 As Integer = 2
Dim key As String = ""key""
Dim data As ConcurrentDictionary(Of String, Integer) = New ConcurrentDictionary(Of String, Integer)()
Dim value3 As Integer = Nothing
If data.TryGetValue(key, value3) Then
Return value3
End If
Return 0";

#region NoDiagnostic

private const string VbInvalidModifiedBeforeUse = @"
Expand Down Expand Up @@ -1299,6 +1352,7 @@ End If
[InlineData(GuardedKeyInSimpleAssignment, GuardedKeyInSimpleAssignmentFixed)]
[InlineData(GuardedInlineVariable, GuardedInlineVariableFixed)]
[InlineData(GuardedInlineVariable2, GuardedInlineVariable2Fixed)]
[InlineData(GuardedReturnIdentifierUsed, GuardedReturnIdentifierUsedFixed)]
public Task ShouldReportDiagnostic(string codeSnippet, string fixedCodeSnippet, int additionalLocations = 1)
{
string testCode = CreateCSharpCode(codeSnippet);
Expand Down Expand Up @@ -1372,6 +1426,7 @@ public Task ShouldNotReportDiagnostic(string codeSnippet, LanguageVersion versio
[InlineData(VbGuardedKeyInSimpleAssignment, VbGuardedKeyInSimpleAssignmentFixed)]
[InlineData(VbGuardedInlineVariable, VbGuardedInlineVariableFixed)]
[InlineData(VbGuardedInlineVariable2, VbGuardedInlineVariable2Fixed)]
[InlineData(VbGuardedReturnIdentifierUsed, VbGuardedReturnIdentifierUsedFixed)]
public Task VbShouldReportDiagnostic(string codeSnippet, string fixedCodeSnippet, int additionalLocations = 1)
{
string testCode = CreateVbCode(codeSnippet);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

Imports System.Threading
Imports Analyzer.Utilities
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.CodeActions
Imports Microsoft.CodeAnalysis.CodeFixes
Expand Down Expand Up @@ -112,20 +113,22 @@ Namespace Microsoft.NetCore.VisualBasic.Analyzers.Performance
Async Function(ct As CancellationToken) As Task(Of Document)
Dim editor = Await DocumentEditor.CreateAsync(document, ct).ConfigureAwait(False)
Dim generator = editor.Generator
If variableName Is Nothing Then
variableName = Value
End If

Dim identifierName = DirectCast(If(variableName Is Nothing,
generator.FirstUnusedIdentifierName(semanticModel,
containsKeyAccess.SpanStart,
Value),
generator.IdentifierName(variableName)),
IdentifierNameSyntax)
Dim tryGetValueAccess = generator.MemberAccessExpression(containsKeyAccess.Expression,
TryGetValue)
Dim keyArgument = containsKeyInvocation.ArgumentList.Arguments.FirstOrDefault()
Dim valueAssignment =
generator.LocalDeclarationStatement(dictionaryValueType,
variableName,
identifierName.Identifier.ValueText,
generator.DefaultExpression(dictionaryValueType)).
WithLeadingTrivia(SyntaxFactory.ElasticCarriageReturnLineFeed).
WithoutTrailingTrivia()
Dim identifierName As SyntaxNode = generator.IdentifierName(variableName)
Dim tryGetValueInvocation = generator.InvocationExpression(tryGetValueAccess,
keyArgument,
generator.Argument(identifierName))
Expand Down
34 changes: 34 additions & 0 deletions src/Utilities/Workspaces/SyntaxGeneratorExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -455,5 +455,39 @@ public static SyntaxNode DefaultMethodStatement(this SyntaxGenerator generator,

return node;
}

/// <summary>
/// Creates the first unused identifier name based on the provided base name.
/// </summary>
/// <param name="generator">The <see cref="SyntaxGenerator"/> used to create the identifier name.</param>
/// <param name="semanticModel">The semantic model.</param>
/// <param name="position">The position in the code.</param>
/// <param name="baseName">The base name to use.</param>
/// <param name="maxTries">Maximum number of tries.</param>
/// <returns>
/// A <see cref="SyntaxNode"/> representing an unused identifier name.
/// This can be either the base name itself or a variation of it with a number appended to make it unique.
/// </returns>
public static SyntaxNode FirstUnusedIdentifierName(this SyntaxGenerator generator, SemanticModel semanticModel, int position, string baseName, int maxTries = int.MaxValue)
{
var identifierName = generator.IdentifierName(baseName);

if (semanticModel.GetSpeculativeSymbolInfo(position, identifierName, SpeculativeBindingOption.BindAsExpression).Symbol is null)
{
return identifierName;
}

for (int i = 1; i < maxTries; i++)
{
identifierName = generator.IdentifierName($"{baseName}{i}");

if (semanticModel.GetSpeculativeSymbolInfo(position, identifierName, SpeculativeBindingOption.BindAsExpression).Symbol is null)
{
break;
}
}

return identifierName;
}
}
}

0 comments on commit e362b92

Please sign in to comment.