Skip to content

Commit

Permalink
Add explicit cast to UseCoalesceExpressionForIfNullStatementCheckCode…
Browse files Browse the repository at this point in the history
…FixProvider
  • Loading branch information
Orachor committed Jul 20, 2024
1 parent 4996324 commit 2bcf6b4
Show file tree
Hide file tree
Showing 8 changed files with 427 additions and 16 deletions.
1 change: 1 addition & 0 deletions src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
<Compile Include="$(MSBuildThisFileDirectory)UnsealClass\CSharpUnsealClassCodeFixProvider.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UpdateProjectToAllowUnsafe\CSharpUpdateProjectToAllowUnsafeCodeFixProvider.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UpgradeProject\CSharpUpgradeProjectCodeFixProvider.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UseCoalesceExpression\CSharpUseCoalesceExpressionForIfNullStatementCheckCodeFixProvider.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UseCollectionExpression\CSharpCollectionExpressionRewriter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UseCollectionExpression\CSharpUseCollectionExpressionForBuilderCodeFixProvider.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UseCollectionExpression\CSharpUseCollectionExpressionForFluentCodeFixProvider.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Composition;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.UseCoalesceExpression;

namespace Microsoft.CodeAnalysis.CSharp.UseCoalesceExpression;

[ExtensionOrder(Before = PredefinedCodeFixProviderNames.AddBraces)]
[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.UseCoalesceExpressionForIfNullStatementCheck), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class CSharpUseCoalesceExpressionForIfNullStatementCheckCodeFixProvider()
: AbstractUseCoalesceExpressionForIfNullStatementCheckCodeFixProvider
{
protected override bool ShouldAddExplicitCast(
ISyntaxFactsService syntaxFacts, SemanticModel semanticModel,
SyntaxNode expressionToCoalesce, SyntaxNode whenTrueStatement,
[NotNullWhen(true)] out ITypeSymbol? castTo,
CancellationToken cancellationToken)
{
castTo = null;

if (!syntaxFacts.IsSimpleAssignmentStatement(whenTrueStatement))
return false;

syntaxFacts.GetPartsOfAssignmentStatement(whenTrueStatement, out var left, out var right);

var leftPartTypeSymbol = semanticModel.GetTypeInfo(expressionToCoalesce, cancellationToken).Type;
var rightPartTypeSymbol = semanticModel.GetTypeInfo(right, cancellationToken).Type;
var finalDestinationTypeSymbol = semanticModel.GetTypeInfo(left, cancellationToken).Type;

if (leftPartTypeSymbol == null || rightPartTypeSymbol == null || finalDestinationTypeSymbol == null)
return false;

if (leftPartTypeSymbol.Equals(rightPartTypeSymbol))
return false;

if (semanticModel.Compilation.HasImplicitConversion(leftPartTypeSymbol, rightPartTypeSymbol) ||
semanticModel.Compilation.HasImplicitConversion(rightPartTypeSymbol, leftPartTypeSymbol))
{
return false;
}

castTo = finalDestinationTypeSymbol;
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@

using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Analyzers.UseCoalesceExpression;
using Microsoft.CodeAnalysis.CSharp.UseCoalesceExpression;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.UseCoalesceExpression;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.CSharp.Analyzers.UnitTests.UseCoalesceExpression;

using VerifyCS = CSharpCodeFixVerifier<
CSharpUseCoalesceExpressionForIfNullStatementCheckDiagnosticAnalyzer,
UseCoalesceExpressionForIfNullStatementCheckCodeFixProvider>;
CSharpUseCoalesceExpressionForIfNullStatementCheckCodeFixProvider>;

[Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)]
public class UseCoalesceExpressionForIfNullStatementCheckTests
Expand Down Expand Up @@ -458,4 +459,236 @@ void M()
LanguageVersion = LanguageVersion.CSharp9,
}.RunAsync();
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74460")]
public async Task TestLocalDeclaration_CastWithParenthesizedExpression()
{
await new VerifyCS.Test
{
TestCode =
"""
interface I
{
}

class C : I
{
void M(object o)
{
I item = o as C;
[|if|] (item == null)
{
item = o as D;
}
}
}

class D : I
{
}
""",
FixedCode =
"""
interface I
{
}

class C : I
{
void M(object o)
{
I item = (I)(o as C) ?? o as D;
}
}

class D : I
{
}
"""
}.RunAsync();
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74460")]
public async Task TestLocalDeclaration_CastWithoutParenthesizedExpression()
{
await new VerifyCS.Test
{
TestCode =
"""
interface I
{
}

class C : I
{
void M(C c, D d)
{
I item = c;
[|if|] (item == null)
{
item = d;
}
}
}

class D : I
{
}
""",
FixedCode =
"""
interface I
{
}

class C : I
{
void M(C c, D d)
{
I item = (I)c ?? d;
}
}

class D : I
{
}
"""
}.RunAsync();
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74460")]
public async Task TestLocalDeclaration_NoCastWhenEqualSymbol()
{
await new VerifyCS.Test
{
TestCode =
"""
interface I
{
}

class C : I
{
void M(C c1, C c2)
{
I item = c1;
[|if|] (item == null)
{
item = c2;
}
}
}
""",
FixedCode =
"""
interface I
{
}

class C : I
{
void M(C c1, C c2)
{
I item = c1 ?? c2;
}
}
"""
}.RunAsync();
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74460")]
public async Task TestLocalDeclaration_NoCastWhenDerivedClass()
{
await new VerifyCS.Test
{
TestCode =
"""
interface I
{
}

class C : I
{
void M(C c, D d)
{
I item = c;
[|if|] (item == null)
{
item = d;
}
}
}

class D : C
{
}
""",
FixedCode =
"""
interface I
{
}

class C : I
{
void M(C c, D d)
{
I item = c ?? d;
}
}

class D : C
{
}
"""
}.RunAsync();
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74460")]
public async Task TestLocalDeclaration_NoCastWhenDerivedClassReversed()
{
await new VerifyCS.Test
{
TestCode =
"""
interface I
{
}

class C : D
{
void M(C c, D d)
{
I item = c;
[|if|] (item == null)
{
item = d;
}
}
}

class D : I
{
}
""",
FixedCode =
"""
interface I
{
}

class C : D
{
void M(C c, D d)
{
I item = c ?? d;
}
}

class D : I
{
}
"""
}.RunAsync();
}
}
2 changes: 1 addition & 1 deletion src/Analyzers/Core/CodeFixes/CodeFixes.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
<Compile Include="$(MSBuildThisFileDirectory)SimplifyInterpolation\AbstractSimplifyInterpolationCodeFixProvider.cs" />
<Compile Include="$(MSBuildThisFileDirectory)SimplifyLinqExpression\AbstractSimplifyLinqExpressionCodeFixProvider`3.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UpgradeProject\AbstractUpgradeProjectCodeFixProvider.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UseCoalesceExpression\UseCoalesceExpressionForIfNullStatementCheckCodeFixProvider.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UseCoalesceExpression\AbstractUseCoalesceExpressionForIfNullStatementCheckCodeFixProvider.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UseCoalesceExpression\UseCoalesceExpressionForTernaryConditionalCheckCodeFixProvider.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UseCoalesceExpression\UseCoalesceExpressionForNullableTernaryConditionalCheckCodeFixProvider.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UseCollectionExpression\AbstractUseCollectionExpressionCodeFixProvider.cs" />
Expand Down
Loading

0 comments on commit 2bcf6b4

Please sign in to comment.