Skip to content

Commit 85d5f04

Browse files
authored
Merge pull request #2577 from vweijsters/fix-2572
Fixed false positive for ambiguous method overloads in SA1410
2 parents d6beabc + 30751a3 commit 85d5f04

2 files changed

Lines changed: 154 additions & 0 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.Test/MaintainabilityRules/SA1410UnitTests.cs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,134 @@ public void Bar()
9999
await this.VerifyCSharpFixAsync(oldSource, newSource, cancellationToken: CancellationToken.None).ConfigureAwait(false);
100100
}
101101

102+
/// <summary>
103+
/// Verify that no diagnostic is produced when removing the parentheses from the delegate statement
104+
/// would result in ambiguity on which method overload to call.
105+
/// </summary>
106+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
107+
[Fact]
108+
[WorkItem(2572, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2572")]
109+
public async Task TestMethodOverloadAmbiguityAsync()
110+
{
111+
var testCode = @"public class TestClass
112+
{
113+
public delegate bool Delegate1();
114+
public delegate bool Delegate2(int x);
115+
116+
public void TestMethod()
117+
{
118+
var thread = new System.Threading.Thread(delegate()
119+
{
120+
// ...
121+
});
122+
123+
this.TestMethod2(delegate() { return true; });
124+
this.TestMethod3(delegate() { return true; });
125+
}
126+
127+
public void TestMethod2(Delegate1 d)
128+
{
129+
}
130+
131+
public void TestMethod2(Delegate2 d)
132+
{
133+
}
134+
}
135+
136+
public static class TestClassExtensions
137+
{
138+
public static void TestMethod3(this TestClass obj, TestClass.Delegate1 d)
139+
{
140+
}
141+
142+
public static void TestMethod3(this TestClass obj, TestClass.Delegate2 d)
143+
{
144+
}
145+
}
146+
";
147+
148+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
149+
}
150+
151+
/// <summary>
152+
/// Verify that the ambiguity detection is specific enough
153+
/// </summary>
154+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
155+
[Fact]
156+
[WorkItem(2572, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2572")]
157+
public async Task TestMethodOverloadNonAmbiguityAsync()
158+
{
159+
var testCode = @"public class TestClass
160+
{
161+
public delegate bool Delegate1();
162+
public delegate bool Delegate2(int x);
163+
164+
public void TestMethod()
165+
{
166+
this.TestMethod2(delegate() { return true; });
167+
this.TestMethod3(0, delegate() { return true; });
168+
this.TestMethod4(delegate() { return true; }, 1);
169+
this.TestMethod5(delegate() { return true; });
170+
this.TestMethod6(delegate() { return true; });
171+
}
172+
173+
public void TestMethod2(Delegate1 d)
174+
{
175+
}
176+
177+
public void TestMethod2(int d)
178+
{
179+
}
180+
181+
public void TestMethod3(int x, Delegate1 d)
182+
{
183+
}
184+
185+
public void TestMethod3(double x, Delegate2 d)
186+
{
187+
}
188+
189+
public void TestMethod4(Delegate1 d, int x)
190+
{
191+
}
192+
193+
public void TestMethod4(Delegate2 d, double x)
194+
{
195+
}
196+
197+
public void TestMethod5(Delegate1 d)
198+
{
199+
}
200+
201+
public void TestMethod5(Delegate2 d, double x)
202+
{
203+
}
204+
205+
public void TestMethod6(Delegate1 d)
206+
{
207+
}
208+
}
209+
210+
public static class TestClassExtensions
211+
{
212+
public static void TestMethod6(this TestClass obj, TestClass.Delegate2 d)
213+
{
214+
}
215+
}
216+
";
217+
218+
DiagnosticResult[] expectedResults =
219+
{
220+
this.CSharpDiagnostic().WithLocation(8, 34),
221+
this.CSharpDiagnostic().WithLocation(9, 37),
222+
this.CSharpDiagnostic().WithLocation(10, 34),
223+
this.CSharpDiagnostic().WithLocation(11, 34),
224+
this.CSharpDiagnostic().WithLocation(12, 34),
225+
};
226+
227+
await this.VerifyCSharpDiagnosticAsync(testCode, expectedResults, CancellationToken.None).ConfigureAwait(false);
228+
}
229+
102230
protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
103231
{
104232
yield return new SA1410RemoveDelegateParenthesisWhenPossible();

StyleCop.Analyzers/StyleCop.Analyzers/MaintainabilityRules/SA1410RemoveDelegateParenthesisWhenPossible.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,34 @@ private static void HandleAnonymousMethodExpression(SyntaxNodeAnalysisContext co
8181
return;
8282
}
8383

84+
// if the delegate is passed as a parameter, verify that there is no ambiguity.
85+
if (syntax.Parent.IsKind(SyntaxKind.Argument))
86+
{
87+
var argumentSyntax = (ArgumentSyntax)syntax.Parent;
88+
var argumentListSyntax = (ArgumentListSyntax)argumentSyntax.Parent;
89+
90+
switch (argumentListSyntax.Parent.Kind())
91+
{
92+
case SyntaxKind.ObjectCreationExpression:
93+
case SyntaxKind.InvocationExpression:
94+
if (HasAmbiguousOverload(context, syntax, argumentListSyntax.Parent))
95+
{
96+
return;
97+
}
98+
99+
break;
100+
}
101+
}
102+
84103
// Remove delegate parenthesis when possible
85104
context.ReportDiagnostic(Diagnostic.Create(Descriptor, syntax.ParameterList.GetLocation()));
86105
}
106+
107+
private static bool HasAmbiguousOverload(SyntaxNodeAnalysisContext context, AnonymousMethodExpressionSyntax anonymousMethodExpression, SyntaxNode methodCallSyntax)
108+
{
109+
var nodeForSpeculation = methodCallSyntax.ReplaceNode(anonymousMethodExpression, anonymousMethodExpression.WithParameterList(null));
110+
var speculativeSymbolInfo = context.SemanticModel.GetSpeculativeSymbolInfo(methodCallSyntax.SpanStart, nodeForSpeculation, SpeculativeBindingOption.BindAsExpression);
111+
return speculativeSymbolInfo.Symbol == null;
112+
}
87113
}
88114
}

0 commit comments

Comments
 (0)