Skip to content

Commit

Permalink
Merge pull request #64662 from mavasani/Issue64291
Browse files Browse the repository at this point in the history
Handle implicit object creation in remove unused values code fixer
  • Loading branch information
mavasani authored Oct 12, 2022
2 parents 40782d7 + bc5f3c3 commit 36c6032
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using System.Composition;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Formatting;
Expand All @@ -17,7 +16,6 @@
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.RemoveUnusedParametersAndValues;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp.RemoveUnusedParametersAndValues
Expand Down Expand Up @@ -57,6 +55,21 @@ protected override SyntaxNode TryUpdateNameForFlaggedNode(SyntaxNode node, Synta

case SyntaxKind.VariableDeclarator:
var variableDeclarator = (VariableDeclaratorSyntax)node;
if (newName.ValueText == AbstractRemoveUnusedParametersAndValuesDiagnosticAnalyzer.DiscardVariableName &&
variableDeclarator.Initializer?.Value is ImplicitObjectCreationExpressionSyntax implicitObjectCreation &&
variableDeclarator.Parent is VariableDeclarationSyntax parent)
{
// If we are generating a discard on the left of an initialization with an implicit object creation on the right,
// then we need to replace the implicit object creation with an explicit one.
// For example: 'TypeName v = new();' must be changed to '_ = new TypeName();'
var objectCreationNode = SyntaxFactory.ObjectCreationExpression(
newKeyword: implicitObjectCreation.NewKeyword,
type: parent.Type,
argumentList: implicitObjectCreation.ArgumentList,
initializer: implicitObjectCreation.Initializer);
variableDeclarator = variableDeclarator.WithInitializer(variableDeclarator.Initializer.WithValue(objectCreationNode));
}

return variableDeclarator.WithIdentifier(newName.WithTriviaFrom(variableDeclarator.Identifier));

case SyntaxKind.SingleVariableDesignation:
Expand All @@ -79,7 +92,7 @@ protected override SyntaxNode TryUpdateNameForFlaggedNode(SyntaxNode node, Synta
}
}

protected override SyntaxNode TryUpdateParentOfUpdatedNode(SyntaxNode parent, SyntaxNode newNameNode, SyntaxEditor editor, ISyntaxFacts syntaxFacts)
protected override SyntaxNode TryUpdateParentOfUpdatedNode(SyntaxNode parent, SyntaxNode newNameNode, SyntaxEditor editor, ISyntaxFacts syntaxFacts, SemanticModel semanticModel)
{
if (newNameNode.IsKind(SyntaxKind.DiscardDesignation)
&& parent is DeclarationPatternSyntax declarationPattern
Expand All @@ -91,6 +104,21 @@ protected override SyntaxNode TryUpdateParentOfUpdatedNode(SyntaxNode parent, Sy

return SyntaxFactory.TypePattern(declarationPattern.Type).WithTrailingTrivia(trailingTrivia);
}
else if (parent is AssignmentExpressionSyntax assignment &&
assignment.Right is ImplicitObjectCreationExpressionSyntax implicitObjectCreation &&
newNameNode is IdentifierNameSyntax { Identifier.ValueText: AbstractRemoveUnusedParametersAndValuesDiagnosticAnalyzer.DiscardVariableName } &&
semanticModel.GetTypeInfo(implicitObjectCreation).Type is { } type)
{
// If we are generating a discard on the left of an assignment with an implicit object creation on the right,
// then we need to replace the implicit object creation with an explicit one.
// For example: 'v = new();' must be changed to '_ = new TypeOfV();'
var objectCreationNode = SyntaxFactory.ObjectCreationExpression(
newKeyword: implicitObjectCreation.NewKeyword,
type: type.GenerateTypeSyntax(allowVar: false),
argumentList: implicitObjectCreation.ArgumentList,
initializer: implicitObjectCreation.Initializer);
return assignment.Update((ExpressionSyntax)newNameNode, assignment.OperatorToken, objectCreationNode);
}

return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8846,5 +8846,73 @@ void M()
}
}", optionName);
}

[Fact]
[WorkItem(64291, "https://github.com/dotnet/roslyn/issues/64291")]
public async Task TestImplicitObjectCreationInInitialization()
{
var source =
@"class C
{
void M()
{
C {|IDE0059:c|} = new();
}
}";
var fixedSource =
@"class C
{
void M()
{
_ = new C();
}
}";

await new VerifyCS.Test
{
TestCode = source,
FixedCode = fixedSource,
Options =
{
{ CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.DiscardVariable },
},
LanguageVersion = LanguageVersion.CSharp9,
}.RunAsync();
}

[Fact]
[WorkItem(64291, "https://github.com/dotnet/roslyn/issues/64291")]
public async Task TestImplicitObjectCreationInAssignement()
{
var source =
@"class C
{
void M(C c)
{
System.Console.WriteLine(c);
{|IDE0059:c|} = new();
}
}";
var fixedSource =
@"class C
{
void M(C c)
{
System.Console.WriteLine(c);
_ = new C();
}
}";

await new VerifyCS.Test
{
TestCode = source,
FixedCode = fixedSource,
Options =
{
{ CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.DiscardVariable },
},
LanguageVersion = LanguageVersion.CSharp9,
}.RunAsync();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,10 @@ protected abstract SyntaxNode GetReplacementNodeForCompoundAssignment(
/// <param name="newNameNode">The rewritten node produced by <see cref="TryUpdateNameForFlaggedNode"/>.</param>
/// <param name="editor">The syntax editor for the code fix.</param>
/// <param name="syntaxFacts">The syntax facts for the current language.</param>
/// <param name="semanticModel">Semantic model for the tree.</param>
/// <returns>The replacement node to use in the rewritten syntax tree; otherwise, <see langword="null"/> to only
/// rewrite the node originally rewritten by <see cref="TryUpdateNameForFlaggedNode"/>.</returns>
protected virtual SyntaxNode? TryUpdateParentOfUpdatedNode(SyntaxNode parent, SyntaxNode newNameNode, SyntaxEditor editor, ISyntaxFacts syntaxFacts) => null;
protected virtual SyntaxNode? TryUpdateParentOfUpdatedNode(SyntaxNode parent, SyntaxNode newNameNode, SyntaxEditor editor, ISyntaxFacts syntaxFacts, SemanticModel semanticModel) => null;

public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
Expand Down Expand Up @@ -554,7 +555,7 @@ private async Task FixAllValueAssignedIsUnusedDiagnosticsAsync(
}
else
{
var newParentNode = TryUpdateParentOfUpdatedNode(node.GetRequiredParent(), newNameNode, editor, syntaxFacts);
var newParentNode = TryUpdateParentOfUpdatedNode(node.GetRequiredParent(), newNameNode, editor, syntaxFacts, semanticModel);
if (newParentNode is not null)
{
nodeReplacementMap.Add(node.GetRequiredParent(), newParentNode);
Expand Down

0 comments on commit 36c6032

Please sign in to comment.