diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractMethodTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractMethodTests.cs index 04246690c783b..802113ccfba6f 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractMethodTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractMethodTests.cs @@ -2815,5 +2815,313 @@ private void NewMethod() } }"); } + + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsExtractMethod)] + public Task TestExtractNullableObjectWithExplicitCast() + => TestInRegularAndScriptAsync( +@"#nullable enable + +using System; + +class C +{ + void M() + { + object? o = null; + var s = (string?)[|o|]; + Console.WriteLine(s); + } +}", +@"#nullable enable + +using System; + +class C +{ + void M() + { + object? o = null; + var s = (string?){|Rename:GetO|}(o); + Console.WriteLine(s); + } + + private static object? GetO(object? o) + { + return o; + } +}"); + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsExtractMethod)] + public Task TestExtractNotNullableObjectWithExplicitCast() + => TestInRegularAndScriptAsync( +@"#nullable enable + +using System; + +class C +{ + void M() + { + object? o = new object(); + var s = (string)[|o|]; + Console.WriteLine(s); + } +}", +@"#nullable enable + +using System; + +class C +{ + void M() + { + object? o = new object(); + var s = (string){|Rename:GetO|}(o); + Console.WriteLine(s); + } + + private static object GetO(object o) + { + return o; + } +}"); + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsExtractMethod)] + public Task TestExtractNotNullableWithExplicitCast() + => TestInRegularAndScriptAsync( +@"#nullable enable + +using System; + +class A +{ +} + +class B : A +{ +} + +class C +{ + void M() + { + B? b = new B(); + var s = (A)[|b|]; + } +}", +@"#nullable enable + +using System; + +class A +{ +} + +class B : A +{ +} + +class C +{ + void M() + { + B? b = new B(); + var s = (A){|Rename:GetB|}(b); + } + + private static B GetB(B b) + { + return b; + } +}"); + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsExtractMethod)] + public Task TestExtractNullableWithExplicitCast() + => TestInRegularAndScriptAsync( +@"#nullable enable + +using System; + +class A +{ +} + +class B : A +{ +} + +class C +{ + void M() + { + B? b = null; + var s = (A)[|b|]; + } +}", +@"#nullable enable + +using System; + +class A +{ +} + +class B : A +{ +} + +class C +{ + void M() + { + B? b = null; + var s = (A){|Rename:GetB|}(b); + } + + private static B? GetB(B? b) + { + return b; + } +}"); + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsExtractMethod)] + public Task TestExtractNotNullableWithExplicitCastSelected() + => TestInRegularAndScriptAsync( +@"#nullable enable + +using System; + +class C +{ + void M() + { + object? o = new object(); + var s = [|(string)o|]; + Console.WriteLine(s); + } +}", +@"#nullable enable + +using System; + +class C +{ + void M() + { + object? o = new object(); + var s = {|Rename:GetS|}(o); + Console.WriteLine(s); + } + + private static string GetS(object o) + { + return (string)o; + } +}"); + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsExtractMethod)] + public Task TestExtractNullableWithExplicitCastSelected() + => TestInRegularAndScriptAsync( +@"#nullable enable + +using System; + +class C +{ + void M() + { + object? o = null; + var s = [|(string?)o|]; + Console.WriteLine(s); + } +}", +@"#nullable enable + +using System; + +class C +{ + void M() + { + object? o = null; + var s = {|Rename:GetS|}(o); + Console.WriteLine(s); + } + + private static string? GetS(object? o) + { + return (string?)o; + } +}"); + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsExtractMethod)] + public Task TestExtractNullableNonNullFlowWithExplicitCastSelected() + => TestInRegularAndScriptAsync( +@"#nullable enable + +using System; + +class C +{ + void M() + { + object? o = new object(); + var s = [|(string?)o|]; + Console.WriteLine(s); + } +}", +@"#nullable enable + +using System; + +class C +{ + void M() + { + object? o = new object(); + var s = {|Rename:GetS|}(o); + Console.WriteLine(s); + } + + private static string? GetS(object o) + { + return (string?)o; + } +}"); + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsExtractMethod)] + public Task TestExtractNullableToNonNullableWithExplicitCastSelected() + => TestInRegularAndScriptAsync( +@"#nullable enable + +using System; + +class C +{ + void M() + { + object? o = null; + var s = [|(string)o|]; + Console.WriteLine(s); + } +}", +@"#nullable enable + +using System; + +class C +{ + void M() + { + object? o = null; + var s = {|Rename:GetS|}(o); + Console.WriteLine(s); + } + + private static string? GetS(object? o) + { + return (string)o; + } +}"); } } diff --git a/src/Features/CSharp/Portable/ExtractMethod/CSharpSelectionResult.ExpressionResult.cs b/src/Features/CSharp/Portable/ExtractMethod/CSharpSelectionResult.ExpressionResult.cs index fc1ce6446aa9d..fbd6bc200df4a 100644 --- a/src/Features/CSharp/Portable/ExtractMethod/CSharpSelectionResult.ExpressionResult.cs +++ b/src/Features/CSharp/Portable/ExtractMethod/CSharpSelectionResult.ExpressionResult.cs @@ -74,14 +74,14 @@ public override ITypeSymbol GetContainingScopeType() // 2. if it doesn't, even if the cast itself wasn't included in the selection, we will treat it // as it was in the selection var regularType = GetRegularExpressionType(model, node); - if (regularType != null && !regularType.IsObjectType()) + if (regularType != null) { return regularType; } if (node.Parent is CastExpressionSyntax castExpression) { - return model.GetTypeInfo(castExpression.Type).GetTypeWithAnnotatedNullability(); + return model.GetTypeInfo(castExpression).GetTypeWithFlowNullability(); } } diff --git a/src/Workspaces/Core/Portable/Utilities/NullableHelpers/NullableExtensions.cs b/src/Workspaces/Core/Portable/Utilities/NullableHelpers/NullableExtensions.cs index 85794ed5deeaf..a22ee319cbb47 100644 --- a/src/Workspaces/Core/Portable/Utilities/NullableHelpers/NullableExtensions.cs +++ b/src/Workspaces/Core/Portable/Utilities/NullableHelpers/NullableExtensions.cs @@ -79,10 +79,10 @@ public static T WithoutNullability(this T typeSymbol) where T : INamespaceOrT } public static ITypeSymbol GetConvertedTypeWithFlowNullability(this TypeInfo typeInfo) - => typeInfo.ConvertedType?.WithNullability(typeInfo.Nullability.FlowState); + => typeInfo.ConvertedType?.WithNullability(typeInfo.ConvertedNullability.FlowState); public static ITypeSymbol GetConvertedTypeWithAnnotatedNullability(this TypeInfo typeInfo) - => typeInfo.ConvertedType?.WithNullability(typeInfo.Nullability.Annotation); + => typeInfo.ConvertedType?.WithNullability(typeInfo.ConvertedNullability.Annotation); public static ITypeSymbol GetTypeWithFlowNullability(this TypeInfo typeInfo) => typeInfo.Type?.WithNullability(typeInfo.Nullability.FlowState);