Skip to content

Commit

Permalink
Merge pull request #3289 from wdolek/bugfix/3279-handle-optional-name…
Browse files Browse the repository at this point in the history
…d-args-correctly

SA1130: Handle optional named arguments correctly
  • Loading branch information
sharwell authored Oct 18, 2021
2 parents a559b3e + 64a563c commit 90287c6
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ private static ImmutableArray<string> GetMethodInvocationArgumentList(SemanticMo
var originalInvocableExpression = argumentListSyntax.Parent;

var originalSymbolInfo = semanticModel.GetSymbolInfo(originalInvocableExpression);
var argumentIndex = argumentListSyntax.Arguments.IndexOf(argumentSyntax);
var argumentIndex = SA1130UseLambdaSyntax.FindParameterIndex(originalSymbolInfo, argumentSyntax, argumentListSyntax);
var parameterList = SA1130UseLambdaSyntax.GetDelegateParameterList(originalSymbolInfo.Symbol, argumentIndex);
return parameterList.Parameters.Select(p => p.Identifier.ToString()).ToImmutableArray();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -880,5 +880,86 @@ public class TestClass

await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
}

[Fact]
[WorkItem(3279, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3279")]
public async Task TestDelegateUsedAsSecondNamedArgumentAsync()
{
var testCode = @"
using System;
using System.Linq;
public class TypeName
{
public void Test()
{
Test2(resolve: delegate
{
return """";
});
}
private void Test2(string description = null, Func<object, string> resolve = null)
{
resolve(0);
}
}";

string fixedCode = @"
using System;
using System.Linq;
public class TypeName
{
public void Test()
{
Test2(resolve: arg =>
{
return """";
});
}
private void Test2(string description = null, Func<object, string> resolve = null)
{
resolve(0);
}
}";

var expected = new[]
{
Diagnostic().WithSpan(8, 24, 8, 32),
};

await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
}

[Fact]
[WorkItem(3279, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3279")]
public async Task VerifyThatUnknownNamedParameterWontCauseCrashAsync()
{
var testCode = @"
using System;
using System.Linq;
public class TypeName
{
public void Test()
{
Test2(unknownParam: delegate
{
return """";
});
}
private void Test2(string description = null, Func<object, string> resolve = null)
{
resolve(0);
}
}";

var expected = DiagnosticResult.CompilerError("CS1739")
.WithMessage("The best overload for 'Test2' does not have a parameter named 'unknownParam'")
.WithSpan(8, 15, 8, 27)
.WithArguments("Test2", "unknownParam");

await VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,27 @@ internal static ParameterListSyntax GetDelegateParameterList(ISymbol symbol, int
return SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(syntaxParameters));
}

internal static int FindParameterIndex(SymbolInfo originalSymbolInfo, ArgumentSyntax argumentSyntax, BaseArgumentListSyntax argumentListSyntax)
{
// if delegate is passed as named argument of method try to find its position by argument name
if (argumentSyntax.NameColon != null
&& originalSymbolInfo.Symbol is IMethodSymbol methodSymbol)
{
var calledMethodParameters = methodSymbol.Parameters;
var argumentIdentifier = argumentSyntax.NameColon.Name.Identifier.ValueText;

for (var i = 0; i < calledMethodParameters.Length; ++i)
{
if (string.Equals(calledMethodParameters[i].Name, argumentIdentifier))
{
return i;
}
}
}

return argumentListSyntax.Arguments.IndexOf(argumentSyntax);
}

private static void HandleAnonymousMethodExpression(SyntaxNodeAnalysisContext context)
{
bool reportDiagnostic = true;
Expand Down Expand Up @@ -138,7 +159,7 @@ private static bool HandleMethodInvocation(SemanticModel semanticModel, Anonymou
return false;
}

var argumentIndex = argumentListSyntax.Arguments.IndexOf(argumentSyntax);
var argumentIndex = FindParameterIndex(originalSymbolInfo, argumentSyntax, argumentListSyntax);

// Determine the parameter list from the method that is invoked, as delegates without parameters are allowed, but they cannot be replaced by a lambda without parameters.
var parameterList = GetDelegateParameterList(originalSymbolInfo.Symbol, argumentIndex);
Expand Down

0 comments on commit 90287c6

Please sign in to comment.