From b1c8eacaf57bb43e3880f26c3b2180a98bf45ba2 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 9 Sep 2015 21:46:37 -0500 Subject: [PATCH 1/2] Fix invalid cast in SA1313 Fixes #1442 --- .../NamingRules/SA1313UnitTests.cs | 21 +++++++++++++++++++ ...ameterNamesMustBeginWithLowerCaseLetter.cs | 8 ++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/NamingRules/SA1313UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/NamingRules/SA1313UnitTests.cs index 7787662b3..78064bb40 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/NamingRules/SA1313UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/NamingRules/SA1313UnitTests.cs @@ -362,6 +362,27 @@ 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); + } + 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..9f4b52661 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers/NamingRules/SA1313ParameterNamesMustBeginWithLowerCaseLetter.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers/NamingRules/SA1313ParameterNamesMustBeginWithLowerCaseLetter.cs @@ -91,7 +91,13 @@ private static void HandleParameterSyntax(SyntaxNodeAnalysisContext context) 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; From 58e7be66a6765d77b48ad69f360e2986ae946db1 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 9 Sep 2015 21:59:52 -0500 Subject: [PATCH 2/2] Allow a lambda parameter to have the name '_' Fixes #1343 --- .../NamingRules/SA1313UnitTests.cs | 91 +++++++++++++++++++ ...ameterNamesMustBeginWithLowerCaseLetter.cs | 24 +++++ documentation/SA1313.md | 3 + 3 files changed, 118 insertions(+) diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/NamingRules/SA1313UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/NamingRules/SA1313UnitTests.cs index 78064bb40..b5b667d31 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/NamingRules/SA1313UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/NamingRules/SA1313UnitTests.cs @@ -383,6 +383,97 @@ public void MethodName() 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 9f4b52661..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,6 +97,22 @@ 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 = syntax.Parent as ParameterListSyntax; 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.