|
// 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.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.CSharp.UseNamedArguments;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseNamedArguments;
using VerifyCS = CSharpCodeRefactoringVerifier<CSharpUseNamedArgumentsCodeRefactoringProvider>;
[UseExportProvider]
[Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public class UseNamedArgumentsTests
{
private static Task TestMissingInRegularAndScriptAsync(string code)
{
return VerifyCS.VerifyRefactoringAsync(code, code);
}
private static Task TestWithCSharp7(string initialMarkup, string expectedMarkup)
{
return new VerifyCS.Test
{
TestCode = initialMarkup,
FixedCode = expectedMarkup,
LanguageVersion = LanguageVersion.CSharp7,
}.RunAsync();
}
private static Task TestWithCSharp7_2(string initialMarkup, string expectedMarkup, int index = 0)
{
return new VerifyCS.Test
{
TestCode = initialMarkup,
FixedCode = expectedMarkup,
CodeActionIndex = index,
LanguageVersion = LanguageVersion.CSharp7_2,
}.RunAsync();
}
private static Task TestWithCSharp7_3(string initialMarkup, string expectedMarkup)
{
return new VerifyCS.Test
{
TestCode = initialMarkup,
FixedCode = expectedMarkup,
LanguageVersion = LanguageVersion.CSharp7_3,
}.RunAsync();
}
[Fact]
public async Task TestFirstArgument()
{
await TestWithCSharp7(
@"class C { void M(int arg1, int arg2) => M([||]1, 2); }",
@"class C { void M(int arg1, int arg2) => M(arg1: 1, arg2: 2); }");
}
[Fact]
public async Task TestFirstArgument_CSharp7_2_FirstOption()
{
// First option only adds the named argument to the specific parameter you're on.
await TestWithCSharp7_2(
@"class C { void M(int arg1, int arg2) => M([||]1, 2); }",
@"class C { void M(int arg1, int arg2) => M(arg1: 1, 2); }");
}
[Fact]
public async Task TestFirstArgument_CSharp7_2_SecondOption()
{
// Second option only adds the named argument to parameter you're on and all trailing parameters.
await TestWithCSharp7_2(
@"class C { void M(int arg1, int arg2) => M([||]1, 2); }",
@"class C { void M(int arg1, int arg2) => M(arg1: 1, arg2: 2); }",
index: 1);
}
[Fact]
public async Task TestNonFirstArgument()
{
await TestWithCSharp7(
@"class C { void M(int arg1, int arg2) => M(1, [||]2); }",
@"class C { void M(int arg1, int arg2) => M(1, arg2: 2); }");
}
[Fact]
public async Task TestNonFirstArgument_CSharp_7_2()
{
// Because we're on the last argument, we should only offer one refactoring to the user.
var initialMarkup = @"class C { void M(int arg1, int arg2) => M(1, [||]2); }";
await new VerifyCS.Test
{
TestCode = initialMarkup,
FixedCode = @"class C { void M(int arg1, int arg2) => M(1, arg2: 2); }",
LanguageVersion = LanguageVersion.CSharp7_2,
ExactActionSetOffered = [string.Format(FeaturesResources.Add_argument_name_0, "arg2")],
}.RunAsync();
}
[Fact]
public async Task TestDelegate()
{
await TestWithCSharp7(
@"class C { void M(System.Action<int> f) => f([||]1); }",
@"class C { void M(System.Action<int> f) => f(obj: 1); }");
}
[Fact]
public async Task TestConditionalMethod()
{
await TestWithCSharp7(
@"class C { void M(int arg1, int arg2) => this?.M([||]1, 2); }",
@"class C { void M(int arg1, int arg2) => this?.M(arg1: 1, arg2: 2); }");
}
[Fact]
public async Task TestConditionalIndexer()
{
await TestWithCSharp7(
@"class C { int? this[int arg1, int arg2] => this?[[||]1, 2]; }",
@"class C { int? this[int arg1, int arg2] => this?[arg1: 1, arg2: 2]; }");
}
[Fact]
public async Task TestThisConstructorInitializer()
{
await TestWithCSharp7(
@"class C { C(int arg1, int arg2) {} C() : this([||]1, 2) {} }",
@"class C { C(int arg1, int arg2) {} C() : this(arg1: 1, arg2: 2) {} }");
}
[Fact]
public async Task TestBaseConstructorInitializer()
{
await TestWithCSharp7(
@"class C { public C(int arg1, int arg2) {} } class D : C { D() : base([||]1, 2) {} }",
@"class C { public C(int arg1, int arg2) {} } class D : C { D() : base(arg1: 1, arg2: 2) {} }");
}
[Fact]
public async Task TestConstructor()
{
await TestWithCSharp7(
@"class C { C(int arg1, int arg2) { new C([||]1, 2); } }",
@"class C { C(int arg1, int arg2) { new C(arg1: 1, arg2: 2); } }");
}
[Fact]
public async Task TestIndexer()
{
await TestWithCSharp7(
@"class C { char M(string arg1) => arg1[[||]0]; }",
@"class C { char M(string arg1) => arg1[index: 0]; }");
}
[Fact]
public async Task TestMissingOnArrayIndexer()
{
await TestMissingInRegularAndScriptAsync(
@"class C { int M(int[] arg1) => arg1[[||]0]; }");
}
[Fact]
public async Task TestMissingOnConditionalArrayIndexer()
{
await TestMissingInRegularAndScriptAsync(
@"class C { int? M(int[] arg1) => arg1?[[||]0]; }");
}
[Fact]
public async Task TestMissingOnEmptyArgumentList()
{
await TestMissingInRegularAndScriptAsync(
@"class C { void M() => M([||]); }");
}
[Fact]
public async Task TestMissingOnExistingArgumentName()
{
await TestMissingInRegularAndScriptAsync(
@"class C { void M(int arg) => M([||]arg: 1); }");
}
[Fact]
public async Task TestEmptyParams()
{
await TestWithCSharp7(
@"class C { void M(int arg1, params int[] arg2) => M([||]1); }",
@"class C { void M(int arg1, params int[] arg2) => M(arg1: 1); }");
}
[Fact]
public async Task TestSingleParams()
{
await TestWithCSharp7(
@"class C { void M(int arg1, params int[] arg2) => M([||]1, 2); }",
@"class C { void M(int arg1, params int[] arg2) => M(arg1: 1, arg2: 2); }");
}
[Fact]
public async Task TestNamedParams()
{
await TestWithCSharp7(
@"class C { void M(int arg1, params int[] arg2) => M([||]1, arg2: new int[0]); }",
@"class C { void M(int arg1, params int[] arg2) => M(arg1: 1, arg2: new int[0]); }");
}
[Fact]
public async Task TestExistingArgumentNames()
{
await TestWithCSharp7(
@"class C { void M(int arg1, int arg2) => M([||]1, arg2: 2); }",
@"class C { void M(int arg1, int arg2) => M(arg1: 1, arg2: 2); }");
}
[Fact]
public async Task TestExistingUnorderedArgumentNames()
{
await TestWithCSharp7(
@"class C { void M(int arg1, int arg2, int arg3) => M([||]1, arg3: 3, arg2: 2); }",
@"class C { void M(int arg1, int arg2, int arg3) => M(arg1: 1, arg3: 3, arg2: 2); }");
}
[Fact]
public async Task TestPreserveTrivia()
{
await TestWithCSharp7(
"""
class C { void M(int arg1, ref int arg2) => M(
[||]1,
ref arg1
); }
""",
"""
class C { void M(int arg1, ref int arg2) => M(
arg1: 1,
arg2: ref arg1
); }
""");
}
[Fact]
public async Task TestMissingOnNameOf()
{
await TestMissingInRegularAndScriptAsync(
@"class C { string M() => nameof([||]M); }");
}
[Fact]
public async Task TestAttribute()
{
await TestWithCSharp7(
"""
[C([||]1, 2)]
class C : System.Attribute { public C(int arg1, int arg2) {} }
""",
"""
[C(arg1: 1, arg2: 2)]
class C : System.Attribute { public C(int arg1, int arg2) {} }
""");
}
[Fact]
public async Task TestAttributeWithNamedProperties()
{
await TestWithCSharp7(
"""
[C([||]1, P = 2)]
class C : System.Attribute { public C(int arg1) {} public int P { get; set; } }
""",
"""
[C(arg1: 1, P = 2)]
class C : System.Attribute { public C(int arg1) {} public int P { get; set; } }
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35525")]
public async Task TestAvailableOnSelectionOfArgument1()
{
await TestWithCSharp7(
"""
class C
{
void M(int arg1, int arg2)
=> M([|1 + 2|], 2);
}
""",
"""
class C
{
void M(int arg1, int arg2)
=> M(arg1: 1 + 2, arg2: 2);
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/18848")]
public async Task TestAvailableOnFirstTokenOfArgument1()
{
await TestWithCSharp7(
"""
class C
{
void M(int arg1, int arg2)
=> M([||]1 + 2, 2);
}
""",
"""
class C
{
void M(int arg1, int arg2)
=> M(arg1: 1 + 2, arg2: 2);
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/18848")]
public async Task TestAvailableOnFirstTokenOfArgument2()
{
await TestWithCSharp7(
"""
class C
{
void M(int arg1, int arg2)
=> M(1[||] + 2, 2);
}
""",
"""
class C
{
void M(int arg1, int arg2)
=> M(arg1: 1 + 2, arg2: 2);
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/18848")]
public async Task TestNotMissingWhenInsideSingleLineArgument1()
{
await TestWithCSharp7(
"""
using System;
class C
{
void M(Action arg1, int arg2)
=> M([||]() => { }, 2);
}
""",
"""
using System;
class C
{
void M(Action arg1, int arg2)
=> M(arg1: () => { }, arg2: 2);
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/18848")]
public async Task TestNotMissingWhenInsideSingleLineArgument2_CSharp7()
{
await TestWithCSharp7(
"""
class C
{
void M(int arg1, int arg2)
=> M(1 [||]+ 2, 2);
}
""",
"""
class C
{
void M(int arg1, int arg2)
=> M(arg1: 1 + 2, arg2: 2);
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/18848")]
public async Task TestNotMissingWhenInsideSingleLineArgument2()
{
await TestWithCSharp7_3(
"""
class C
{
void M(int arg1, int arg2)
=> M(1 [||]+ 2, 2);
}
""",
"""
class C
{
void M(int arg1, int arg2)
=> M(arg1: 1 + 2, 2);
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/18848")]
public async Task TestNotMissingWhenInsideSingleLineArgument3()
{
await TestWithCSharp7(
"""
using System;
class C
{
void M(Action arg1, int arg2)
=> M(() => { [||] }, 2);
}
""",
"""
using System;
class C
{
void M(Action arg1, int arg2)
=> M(arg1: () => { }, arg2: 2);
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/18848")]
public async Task TestNotMissingWhenInsideSingleLineArgument4()
{
await TestWithCSharp7(
"""
class C
{
void M(int arg1, int arg2)
=> M(1 [||]+ 2, 2);
}
""",
"""
class C
{
void M(int arg1, int arg2)
=> M(arg1: 1 + 2, arg2: 2);
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/18848")]
public async Task TestMissingNotOnStartingLineOfArgument1()
{
await TestMissingInRegularAndScriptAsync(
"""
using System;
class C
{
void M(Action arg1, int arg2)
=> M(() => {
[||]
}, 2);
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/18848")]
public async Task TestMissingWithSelection()
{
await TestMissingInRegularAndScriptAsync(
"""
using System;
class C
{
void M(Action arg1, int arg2)
=> M([|{|CS1503:1 + 2|}|], 3);
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/19175")]
public async Task TestCaretPositionAtTheEnd1()
{
await TestWithCSharp7(
"""
class C
{
void M(int arg1) => M(arg1[||]);
}
""",
"""
class C
{
void M(int arg1) => M(arg1: arg1);
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/19175")]
public async Task TestCaretPositionAtTheEnd2()
{
await TestWithCSharp7(
"""
class C
{
void M(int arg1, int arg2) => M(arg1[||], arg2);
}
""",
"""
class C
{
void M(int arg1, int arg2) => M(arg1: arg1, arg2: arg2);
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/19758")]
public async Task TestOnTuple()
{
await VerifyCS.VerifyRefactoringAsync(
"""
using System.Linq;
class C
{
void M(int[] arr) => arr.Zip(arr, (p1, p2) => ([||]p1, p2));
}
""",
"""
using System.Linq;
class C
{
void M(int[] arr) => arr.Zip(arr, resultSelector: (p1, p2) => (p1, p2));
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23269")]
public async Task TestCharacterEscape1()
{
await TestWithCSharp7(
"""
class C
{
void M(int @default, int @params) => M([||]1, 2);
}
""",
"""
class C
{
void M(int @default, int @params) => M(@default: 1, @params: 2);
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23269")]
public async Task TestCharacterEscape2()
{
await TestWithCSharp7(
"""
[C([||]1, 2)]
class C : System.Attribute
{
public C(int @default, int @params) {}
}
""",
"""
[C(@default: 1, @params: 2)]
class C : System.Attribute
{
public C(int @default, int @params) {}
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/39852")]
public async Task TestMissingForImplicitRangeIndexer()
{
await TestMissingInRegularAndScriptAsync(
@"class C { string M(string arg1) => arg1[[||]1..^1]; }" + TestSources.Range + TestSources.Index);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/39852")]
public async Task TestMissingForImplicitIndexIndexer()
{
await TestMissingInRegularAndScriptAsync(
@"class C { string M(string arg1) => {|CS0029:arg1[[||]^1]|}; }" + TestSources.Index);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/39852")]
public async Task TestForRealRangeIndexer()
{
await VerifyCS.VerifyRefactoringAsync(
"""
using System;
class C {
int this[Range range] => default;
int M(C arg1) => arg1[[||]1..^1];
}
""" + TestSources.Range + TestSources.Index,
"""
using System;
class C {
int this[Range range] => default;
int M(C arg1) => arg1[range: 1..^1];
}
""" + TestSources.Range + TestSources.Index);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/39852")]
public async Task TestForRealIndexIndexer()
{
await VerifyCS.VerifyRefactoringAsync(
"""
using System;
class C {
int this[Index index] => default;
int M(C arg1) => arg1[[||]^1];
}
""" + TestSources.Index,
"""
using System;
class C {
int this[Index index] => default;
int M(C arg1) => arg1[index: ^1];
}
""" + TestSources.Index);
}
[Fact]
public async Task TestNoTrailingArgumentsToName()
{
// Because we're on the last argument that doesn't have a name, we should only offer one refactoring to the user.
var initialMarkup = @"class C { void M(int arg1, int arg2, int arg3) => M(1, [||]2, arg3: 3); }";
await new VerifyCS.Test
{
TestCode = initialMarkup,
FixedCode = @"class C { void M(int arg1, int arg2, int arg3) => M(1, arg2: 2, arg3: 3); }",
ExactActionSetOffered = [string.Format(FeaturesResources.Add_argument_name_0, "arg2")],
}.RunAsync();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63362")]
public async Task TestTrivia()
{
await VerifyCS.VerifyRefactoringAsync("""
class C
{
static void F(string x, string y)
{
F(
// TODO: 1
nu[||]ll
// TODO: 2
, null
);
}
}
""", """
class C
{
static void F(string x, string y)
{
F(
// TODO: 1
x: null
// TODO: 2
, null
);
}
}
""");
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63362")]
public async Task TestTrivia_Attribute()
{
await VerifyCS.VerifyRefactoringAsync("""
[My(
// Comment
[||]null/*comment2*/,
null)]
class MyAttribute : System.Attribute
{
public MyAttribute(string x, string y) { }
}
""", """
[My(
// Comment
x: null/*comment2*/,
null)]
class MyAttribute : System.Attribute
{
public MyAttribute(string x, string y) { }
}
""");
}
}
|