Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue offering to remove an necessary cast to dynamic #76098

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.RemoveUnnecessaryCast;
CSharpRemoveUnnecessaryCastCodeFixProvider>;

[Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryCast)]
public class RemoveUnnecessaryCastTests
public sealed class RemoveUnnecessaryCastTests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public sealed class

ever thought about just changing all the tests in one shot?

{
[Theory, CombinatorialData]
public void TestStandardProperty(AnalyzerProperty property)
Expand Down Expand Up @@ -13934,4 +13934,145 @@ public void AppendFormatted<T>(T value)
ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
}.RunAsync();
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71695")]
public async Task TestObjectToDynamic1()
{
await new VerifyCS.Test
{
TestCode = """
#nullable enable

class C
{
public T? M<T>(object? o)
{
return (dynamic?)o;
}
}
""",
LanguageVersion = LanguageVersion.CSharp12,
ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
}.RunAsync();
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71695")]
public async Task TestObjectToDynamic2()
{
await new VerifyCS.Test
{
TestCode = """
class C
{
public T M<T>(object o)
{
return (dynamic)o;
}
}
""",
LanguageVersion = LanguageVersion.CSharp12,
ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
}.RunAsync();
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71695")]
public async Task TestNumericThroughDynamic()
{
await new VerifyCS.Test
{
TestCode = """
class C
{
public int M(long o)
{
return (dynamic)o;
}
}
""",
LanguageVersion = LanguageVersion.CSharp12,
ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
}.RunAsync();
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71695")]
public async Task TestNullableThroughDynamic()
{
await new VerifyCS.Test
{
TestCode = """
class C
{
public int M(int? o)
{
return (dynamic)o;
}
}
""",
LanguageVersion = LanguageVersion.CSharp12,
ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
}.RunAsync();
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71695")]
public async Task TestEnumThroughDynamic1()
{
await new VerifyCS.Test
{
TestCode = """
using System;

class C
{
public int M(ConsoleColor o)
{
return (dynamic)o;
}
}
""",
LanguageVersion = LanguageVersion.CSharp12,
ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
}.RunAsync();
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71695")]
public async Task TestEnumThroughDynamic2()
{
await new VerifyCS.Test
{
TestCode = """
using System;

class C
{
public ConsoleColor M(int o)
{
return (dynamic)o;
}
}
""",
LanguageVersion = LanguageVersion.CSharp12,
ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
}.RunAsync();
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71695")]
public async Task TestConstantThroughDynamic()
{
await new VerifyCS.Test
{
TestCode = """
using System;

class C
{
public int M()
{
return (dynamic)0L;
}
}
""",
LanguageVersion = LanguageVersion.CSharp12,
ReferenceAssemblies = ReferenceAssemblies.Net.Net80,
}.RunAsync();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,17 @@ public static async Task VerifyCodeFixAsync(
=> await VerifyCodeFixAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource);

/// <inheritdoc cref="CodeFixVerifier{TAnalyzer, TCodeFix, TTest, TVerifier}.VerifyCodeFixAsync(string, DiagnosticResult, string)"/>
public static async Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource)
public static async Task VerifyCodeFixAsync(
[StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string source,
DiagnosticResult expected,
[StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string fixedSource)
=> await VerifyCodeFixAsync(source, [expected], fixedSource);

/// <inheritdoc cref="CodeFixVerifier{TAnalyzer, TCodeFix, TTest, TVerifier}.VerifyCodeFixAsync(string, DiagnosticResult[], string)"/>
public static async Task VerifyCodeFixAsync(string source, DiagnosticResult[] expected, string fixedSource)
public static async Task VerifyCodeFixAsync(
[StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string source,
DiagnosticResult[] expected,
[StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string fixedSource)
{
var test = new Test
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,14 @@ private static bool IsConversionCastSafeToRemove(
if (rewrittenConvertedType is null || rewrittenConvertedType.TypeKind == TypeKind.Error || !rewrittenConversion.Exists)
return false;

// If removing the conversion caused us to now become an explicit conversion (a conversion that can cause
// lossyness), then we must block as that's disallowed by the language.
//
// Note: compiler API is slightly odd here as they return such an 'IsExplicit+Exists' conversion when casting
// the expression inside a string interpolation. So we ignore that case here
if (rewrittenConversion.IsExplicit && castNode.WalkUpParentheses().Parent is not InterpolationSyntax)
return false;

if (CastRemovalWouldCauseUnintendedReferenceComparisonWarning(rewrittenExpression, rewrittenSemanticModel, cancellationToken))
return false;

Expand Down
Loading