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

Offer to add parameter to constructor with no existing parameters #33624

Merged
merged 3 commits into from
Feb 28, 2019
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 @@ -642,5 +642,46 @@ public C(int i, int hello)
}"
);
}

[WorkItem(33602, "https://github.com/dotnet/roslyn/issues/33602")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddConstructorParametersFromMembers)]
public async Task TestConstructorWithNoParameters()
Copy link
Member

Choose a reason for hiding this comment

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

Can we also have a test with only implicit default ctor?

Copy link
Contributor Author

@chborl chborl Feb 27, 2019

Choose a reason for hiding this comment

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

Yes, we have this test:

class C
{
    [|int i;|]
    int Hello { get; set; }
}

{
await TestInRegularAndScriptAsync(
@"
class C
{
[|int i;
int Hello { get; set; }|]
public C()
{
}
}",
@"
class C
{
int i;
int Hello { get; set; }
public C(int i, int hello)
{
this.i = i;
Hello = hello;
}
}"
);
}

[WorkItem(33602, "https://github.com/dotnet/roslyn/issues/33602")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddConstructorParametersFromMembers)]
public async Task TestDefaultConstructor()
{
await TestMissingAsync(
@"
class C
{
[|int i;|]
int Hello { get; set; }
}");
Copy link
Member

Choose a reason for hiding this comment

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

probably shoudl offer to create an explicit constructor here since hte implicit one def exists.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The "generate constructor" refactoring is offered here, and it will generate the constructor with the selected field as a parameter.

The "add parameters to constructor" is not offered, but I think that is expected.

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -202,5 +202,40 @@ Class Program
End Sub
End Class")
End Function

<WorkItem(33602, "https://github.com/dotnet/roslyn/issues/33602")>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddConstructorParametersFromMembers)>
Public Async Function TestConstructorWithNoParameters() As Task
Await TestInRegularAndScriptAsync(
"
Class Program
[|Private i As Integer
Property Hello As Integer = 1|]
Public Sub New()
End Sub
End Class",
"
Class Program
[|Private i As Integer
Property Hello As Integer = 1|]
Public Sub New(i As Integer, hello As Integer)
Me.i = i
Me.Hello = hello
End Sub
End Class"
)
End Function

<WorkItem(33602, "https://github.com/dotnet/roslyn/issues/33602")>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddConstructorParametersFromMembers)>
Public Async Function TestDefaultConstructor() As Task
Await TestMissingAsync(
"
Class Program
[|Private i As Integer|]
End Class"
)

End Function
End Class
End Namespace
Original file line number Diff line number Diff line change
Expand Up @@ -77,28 +77,31 @@ private bool TryInitialize(
/// <summary>
/// Try to find a constructor in <paramref name="containingType"/> whose parameters is the subset of <paramref name="parameters"/> by comparing name.
/// If multiple constructors meet the condition, the one with more parameters will be returned.
/// It will not consider those constructors as potential candidates if:
/// 1. Constructor with empty parameter list.
/// 2. Constructor's parameter list contains 'ref' or 'params'
/// It will not consider those constructors as potential candidates if the constructor's parameter list
/// contains 'ref' or 'params'
/// </summary>
private IMethodSymbol GetDelegatedConstructorBasedOnParameterNames(
INamedTypeSymbol containingType,
ImmutableArray<IParameterSymbol> parameters)
{
var parameterNames = parameters.SelectAsArray(p => p.Name);
return containingType.InstanceConstructors
.Where(constructor => AreParametersContainedInConstructor(constructor, parameterNames))
.Where(constructor => IsApplicableConstructor(constructor, parameterNames))
.OrderByDescending(constructor => constructor.Parameters.Length)
.FirstOrDefault();
}

private bool AreParametersContainedInConstructor(
private bool IsApplicableConstructor(
IMethodSymbol constructor,
ImmutableArray<string> parametersName)
{
var constructorParams = constructor.Parameters;
return constructorParams.Length > 0
&& constructorParams.All(parameter => parameter.RefKind == RefKind.None)
if (constructorParams.Length == 0)
{
return !constructor.IsImplicitlyDeclared;
Copy link
Member

Choose a reason for hiding this comment

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

What's the behavior if we only have an implicit default ctor? Refactoring will not be available?

Copy link
Member

Choose a reason for hiding this comment

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

yeah, i would prefer it's avialable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See this: #33624 (comment)

}

return constructorParams.All(parameter => parameter.RefKind == RefKind.None)
&& !constructorParams.Any(p => p.IsParams)
&& parametersName.Except(constructorParams.Select(p => p.Name)).Any();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,11 @@ public async Task<ImmutableArray<CodeAction>> AddConstructorParametersFromMember

private IEnumerable<CodeAction> CreateCodeActions(Document document, State state)
{
var lastParameter = state.ConstructorToAddTo.Parameters.Last();
if (!lastParameter.IsOptional)
var parameters = state.ConstructorToAddTo.Parameters;
if (parameters.Length == 0 ||
(parameters.Length > 0 && !parameters.Last().IsOptional))
{
// return a code action to add required parameters
yield return new AddConstructorParametersCodeAction(this, document, state, state.MissingParameters);
}

Expand All @@ -71,6 +73,7 @@ private IEnumerable<CodeAction> CreateCodeActions(Document document, State state
isOptional: true,
hasDefaultValue: true));

// return a code action to add optional parameters
yield return new AddConstructorParametersCodeAction(this, document, state, missingParameters);
}
}
Expand Down