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

Fixed incorrect offer to interpolate string if used string.Format with named arguments. #20832

Merged
merged 4 commits into from
Jul 25, 2017

Conversation

zaytsev-victor
Copy link
Contributor

@zaytsev-victor zaytsev-victor commented Jul 12, 2017

Customer scenario

Incorrect offer to interpolate string if used string.Format with named arguments.

Bugs this fixes:
Fixes #19162

Workarounds, if any
Don't use this refactoring for string.Format with named arguments.

Risk
Low

Performance impact

Is this a regression from a previous update?
No
Root cause analysis:
Doesn't implemented support for string.Format with named arguments

How was the bug found?

customer reported

@Pilchie Pilchie added Area-IDE Community The pull request was submitted by a contributor who is not a Microsoft employee. labels Jul 12, 2017
@Pilchie Pilchie requested review from jmarolf and a team July 12, 2017 18:10
return null;
}

return argument.NameColon.Name.Identifier.Text;
Copy link
Member

Choose a reason for hiding this comment

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

nit: consider : => argument?.NameColon?.Name.Identifier.Text

Copy link
Member

Choose a reason for hiding this comment

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

Also, use ISyntaxFactsService. It already has GetNameForArgument

Copy link
Member

Choose a reason for hiding this comment

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

(can be used directly from base class).


private TArgumentSyntax GetArgument(SeparatedSyntaxList<TArgumentSyntax> arguments, int index)
{
if (arguments.Count > 4)
Copy link
Member

Choose a reason for hiding this comment

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

needs explanation.

Copy link
Contributor Author

@zaytsev-victor zaytsev-victor Jul 12, 2017

Choose a reason for hiding this comment

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

if argument more than 4 it means that it is used the following overload String.Format(String format, params Object[] args) without using named arguments. So we 100% sure in that case that we have no named arguments.

Copy link
Member

Choose a reason for hiding this comment

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

i meant: needs explanation in the code (i.e. add a comment) :)

@sharwell sharwell self-assigned this Jul 13, 2017
@zaytsev-victor
Copy link
Contributor Author

@CyrusNajmabadi, should we suggest refactoring in this case:

void Foo()
{
    string.Format("{0} string", "test");
}

?
It will produce a error CS0201

@CyrusNajmabadi
Copy link
Member

should we suggest refactoring in this case:

I would be ok with that. I would also be ok with it not being shown :)

@sharwell
Copy link
Member

@zaytsev-victor

should we suggest refactoring in this case:

We could always emit this as _ = $"..." (discard). Another option would be allowing this code fix to result in the error, providing a separate general code fix for CS0201 which assigns the value either to a discard or a new variable.

@sharwell sharwell added this to the 15.5 milestone Jul 18, 2017

public const string ArgsArgumentName = "args";

public static readonly Dictionary<int, string> ParamsArgumentNames = new Dictionary<int, string>
Copy link
Member

Choose a reason for hiding this comment

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

💡 If you are just going to index into it (which the code currently is doing), we can benefit from both immutability and performance by changing this to ImmutableArray<string>:

public static readonly ImmutableArray<string> ParamsArgumentNames =
  ImmutableArray.Create("", "arg0", "arg1", "arg2");

Copy link
Contributor

@jmarolf jmarolf left a comment

Choose a reason for hiding this comment

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


private SyntaxNode GetParamsArgument(SeparatedSyntaxList<TArgumentSyntax> arguments, ISyntaxFactsService syntaxFactsService)
{
var i = arguments.IndexOf(argument => GetArgumentName(argument, syntaxFactsService) == StringFormatArguments.ArgsArgumentName);
Copy link
Contributor

Choose a reason for hiding this comment

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

How will this work in a case-insensitive language like Visual Basic?


private TArgumentSyntax GetFormatArgument(SeparatedSyntaxList<TArgumentSyntax> arguments, ISyntaxFactsService syntaxFactsService)
{
var index = arguments.IndexOf(argument => GetArgumentName(argument, syntaxFactsService) == StringFormatArguments.FormatArgumentName);
Copy link
Contributor

Choose a reason for hiding this comment

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

How will this work in a case-insensitive language like Visual Basic?

return arguments[index];
}

return arguments.FirstOrDefault(argument => GetArgumentName(argument, syntaxFactsService) == StringFormatArguments.ParamsArgumentNames[index]) ?? arguments[index];
Copy link
Contributor

Choose a reason for hiding this comment

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

How will this work in a case-insensitive language like Visual Basic?

=> arguments.FirstOrDefault(argument => string.Equals(GetArgumentName(argument, syntaxFactsService), StringFormatArguments.FormatArgumentName, StringComparison.OrdinalIgnoreCase)) ?? arguments[1];

private TArgumentSyntax GetFormatArgument(SeparatedSyntaxList<TArgumentSyntax> arguments, ISyntaxFactsService syntaxFactsService)
=> arguments.FirstOrDefault(argument => string.Equals(GetArgumentName(argument, syntaxFactsService), StringFormatArguments.FormatArgumentName, StringComparison.OrdinalIgnoreCase)) ?? arguments[0];
Copy link
Contributor

Choose a reason for hiding this comment

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

In VB we want the comparison to be case-insensitive, but I believe we want a case sensitive comparison for C# correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jmarolf Since String.Format("{0}", ARg0: 1) is incorrect syntax 'convert to interpolate string' refactoring wouldn't be suggested

Copy link
Contributor

Choose a reason for hiding this comment

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

Just tried it with your changes. You are correct.

@Pilchie Pilchie merged commit 117082e into dotnet:master Jul 25, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-IDE cla-already-signed Community The pull request was submitted by a contributor who is not a Microsoft employee.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Incorrect offer to interpolate string
6 participants