diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/NamingRules/SA1313UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/NamingRules/SA1313UnitTests.cs
index 7787662b3..b5b667d31 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/NamingRules/SA1313UnitTests.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/NamingRules/SA1313UnitTests.cs
@@ -362,6 +362,118 @@ public override void Method(int Param1, int param2, int Other)
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
}
+ ///
+ /// This is a regression test for DotNetAnalyzers/StyleCopAnalyzers#1442:
+ /// https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/1442
+ ///
+ /// A representing the asynchronous operation.
+ [Fact]
+ public async Task TestSimpleLambaExpressionAsync()
+ {
+ var testCode = @"public class TypeName
+{
+ public void MethodName()
+ {
+ System.Action action = Ignored => { };
+ }
+}";
+
+ DiagnosticResult expected = this.CSharpDiagnostic().WithArguments("Ignored").WithLocation(5, 37);
+
+ await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
+ }
+
+ ///
+ /// This is a regression test for DotNetAnalyzers/StyleCopAnalyzers#1343:
+ /// https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/1343
+ ///
+ /// A representing the asynchronous operation.
+ [Fact]
+ public async Task TestLambdaParameterNamedUnderscoreAsync()
+ {
+ var testCode = @"public class TypeName
+{
+ public void MethodName()
+ {
+ System.Action action1 = _ => { };
+ System.Action action2 = (_) => { };
+ System.Action action3 = delegate(int _) { };
+ }
+}";
+
+ await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ }
+
+ ///
+ /// This is a regression test for DotNetAnalyzers/StyleCopAnalyzers#1343:
+ /// https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/1343
+ ///
+ ///
+ /// This diagnostic does not check whether or not a parameter named _ is being used.
+ ///
+ /// A representing the asynchronous operation.
+ [Fact]
+ public async Task TestLambdaParameterNamedUnderscoreUsageAsync()
+ {
+ var testCode = @"public class TypeName
+{
+ public void MethodName()
+ {
+ System.Func function1 = _ => _;
+ System.Func function2 = (_) => _;
+ System.Func function3 = delegate(int _) { return _; };
+ }
+}";
+
+ await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ }
+
+ ///
+ /// This is a regression test for DotNetAnalyzers/StyleCopAnalyzers#1343:
+ /// https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/1343
+ ///
+ /// A representing the asynchronous operation.
+ [Fact]
+ public async Task TestLambdaParameterMultipleUnderscoresAsync()
+ {
+ var testCode = @"public class TypeName
+{
+ public void MethodName()
+ {
+ System.Action action1 = __ => { };
+ System.Action action2 = (__) => { };
+ System.Action action3 = delegate(int __) { };
+ }
+}";
+
+ DiagnosticResult[] expected =
+ {
+ this.CSharpDiagnostic().WithArguments("__").WithLocation(5, 38),
+ this.CSharpDiagnostic().WithArguments("__").WithLocation(6, 39),
+ this.CSharpDiagnostic().WithArguments("__").WithLocation(7, 51)
+ };
+ await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
+ }
+
+ ///
+ /// This is a regression test for DotNetAnalyzers/StyleCopAnalyzers#1343:
+ /// https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/1343
+ ///
+ /// A representing the asynchronous operation.
+ [Fact]
+ public async Task TestMethodParameterNamedUnderscoreAsync()
+ {
+ var testCode = @"public class TypeName
+{
+ public void MethodName(int _)
+ {
+ }
+}";
+
+ DiagnosticResult expected = this.CSharpDiagnostic().WithArguments("_").WithLocation(3, 32);
+ await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
+ }
+
protected override IEnumerable GetCSharpDiagnosticAnalyzers()
{
yield return new SA1313ParameterNamesMustBeginWithLowerCaseLetter();
diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/NamingRules/SA1313ParameterNamesMustBeginWithLowerCaseLetter.cs b/StyleCop.Analyzers/StyleCop.Analyzers/NamingRules/SA1313ParameterNamesMustBeginWithLowerCaseLetter.cs
index e80324fc0..09017ef63 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers/NamingRules/SA1313ParameterNamesMustBeginWithLowerCaseLetter.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers/NamingRules/SA1313ParameterNamesMustBeginWithLowerCaseLetter.cs
@@ -16,6 +16,9 @@ namespace StyleCop.Analyzers.NamingRules
///
/// A violation of this rule occurs when the name of a parameter does not begin with a lower-case letter.
///
+ /// An exception to this rule is made for lambda parameters named _. These parameters are often used to
+ /// designate a placeholder parameter which is not actually used in the body of the lambda expression.
+ ///
/// If the parameter name is intended to match the name of an item associated with Win32 or COM, and thus
/// needs to begin with an upper-case letter, place the parameter within a special NativeMethods class. A
/// NativeMethods class is any class which contains a name ending in NativeMethods, and is intended as
@@ -80,6 +83,11 @@ private static void HandleParameterSyntax(SyntaxNodeAnalysisContext context)
return;
}
+ if (name == "_" && IsInLambda(syntax))
+ {
+ return;
+ }
+
if (NameMatchesAbstraction(syntax, context.SemanticModel))
{
return;
@@ -89,9 +97,31 @@ private static void HandleParameterSyntax(SyntaxNodeAnalysisContext context)
context.ReportDiagnostic(Diagnostic.Create(Descriptor, identifier.GetLocation(), name));
}
+ private static bool IsInLambda(ParameterSyntax syntax)
+ {
+ if (syntax.Parent.IsKind(SyntaxKind.SimpleLambdaExpression))
+ {
+ return true;
+ }
+
+ if (syntax.Parent.Parent.IsKind(SyntaxKind.ParenthesizedLambdaExpression)
+ || syntax.Parent.Parent.IsKind(SyntaxKind.AnonymousMethodExpression))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
private static bool NameMatchesAbstraction(ParameterSyntax syntax, SemanticModel semanticModel)
{
- var parameterList = (ParameterListSyntax)syntax.Parent;
+ var parameterList = syntax.Parent as ParameterListSyntax;
+ if (parameterList == null)
+ {
+ // This occurs for simple lambda expressions (without parentheses)
+ return false;
+ }
+
var index = parameterList.Parameters.IndexOf(syntax);
var declaringMember = syntax.Parent.Parent;
diff --git a/documentation/SA1313.md b/documentation/SA1313.md
index 11d0a5171..e6970f912 100644
--- a/documentation/SA1313.md
+++ b/documentation/SA1313.md
@@ -23,6 +23,9 @@ The name of a parameter in C# does not begin with a lower-case letter.
A violation of this rule occurs when the name of a parameter does not begin with a lower-case letter.
+An exception to this rule is made for lambda parameters named `_`. These parameters are often used to designate a
+placeholder parameter which is not actually used in the body of the lambda expression.
+
If the parameter name is intended to match the name of an item associated with Win32 or COM, and thus needs to begin
with an upper-case letter, place the parameter within a special `NativeMethods` class. A `NativeMethods` class is any
class which contains a name ending in `NativeMethods`, and is intended as a placeholder for Win32 or COM wrappers.