Skip to content

Commit 2efacec

Browse files
committed
Implemented SA1414
1 parent 320b386 commit 2efacec

8 files changed

Lines changed: 406 additions & 1 deletion

File tree

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/ReadabilityRules/SA1141CodeFixProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
3434
CodeAction.Create(
3535
ReadabilityResources.SA1141CodeFix,
3636
cancellationToken => GetTransformedDocumentAsync(context.Document, diagnostic, cancellationToken),
37-
nameof(SA1139CodeFixProvider)),
37+
nameof(SA1141CodeFixProvider)),
3838
diagnostic);
3939
}
4040

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3+
4+
namespace StyleCop.Analyzers.Test.CSharp7.MaintainabilityRules
5+
{
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Linq;
9+
using System.Text;
10+
using System.Threading;
11+
using System.Threading.Tasks;
12+
using Microsoft.CodeAnalysis.Testing;
13+
using StyleCop.Analyzers.MaintainabilityRules;
14+
using Xunit;
15+
using static StyleCop.Analyzers.Test.Verifiers.StyleCopDiagnosticVerifier<
16+
StyleCop.Analyzers.MaintainabilityRules.SA1414TupleTypesInSignaturesShouldHaveElementNames>;
17+
18+
/// <summary>
19+
/// This class contains the CSharp 7.x unit tests for SA1414.
20+
/// </summary>
21+
/// <seealso cref="SA1414TupleTypesInSignaturesShouldHaveElementNames"/>
22+
public class SA1414CSharp7UnitTests
23+
{
24+
[Theory]
25+
[InlineData("(int valueA, int valueB)")]
26+
[InlineData("(int valueA, (long valueB, float valueC, string valueD) valueE)")]
27+
[InlineData("System.Collections.Generic.List<(int valueA, long valueB)>")]
28+
public async Task ValidateTuplesWithElementNamesAsync(string typeExpression)
29+
{
30+
var testCode = $@"using System;
31+
32+
public delegate {typeExpression} TestDelegate({typeExpression} p1);
33+
34+
public class TestClass
35+
{{
36+
public TestClass({typeExpression} p1)
37+
{{
38+
}}
39+
40+
public {typeExpression} TestMethod({typeExpression} p1)
41+
{{
42+
throw new NotImplementedException();
43+
}}
44+
45+
public {typeExpression} TestProperty {{ get; set; }}
46+
public {typeExpression} this[int index]
47+
{{
48+
get
49+
{{
50+
throw new NotImplementedException();
51+
}}
52+
}}
53+
54+
public static explicit operator TestClass({typeExpression} p1)
55+
{{
56+
throw new NotImplementedException();
57+
}}
58+
59+
public static implicit operator {typeExpression}(TestClass p1)
60+
{{
61+
throw new NotImplementedException();
62+
}}
63+
}}
64+
";
65+
66+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
67+
}
68+
69+
[Theory]
70+
[InlineData("([|int|], [|int|])")]
71+
[InlineData("(int valueA, [|int|])")]
72+
[InlineData("([|int|], int valueB)")]
73+
[InlineData("([|int|], [|([|long|], [|float|], [|string|])|])")]
74+
[InlineData("([|int|], (long valueB, [|float|], string valueD) valueE)")]
75+
[InlineData("(int valueA, ([|long|], float valueC, [|string|]) valueE)")]
76+
[InlineData("System.Collections.Generic.List<([|int|], [|long|])>")]
77+
public async Task ValidateTuplesWithoutElementNamesAsync(string typeExpression)
78+
{
79+
var testCode = $@"using System;
80+
81+
public delegate {typeExpression} TestDelegate({typeExpression} p1);
82+
83+
public class TestClass
84+
{{
85+
public TestClass({typeExpression} p1)
86+
{{
87+
}}
88+
89+
public {typeExpression} TestMethod({typeExpression} p1)
90+
{{
91+
throw new NotImplementedException();
92+
}}
93+
94+
public {typeExpression} TestProperty {{ get; set; }}
95+
public {typeExpression} this[int index]
96+
{{
97+
get
98+
{{
99+
throw new NotImplementedException();
100+
}}
101+
}}
102+
103+
public static explicit operator TestClass({typeExpression} p1)
104+
{{
105+
throw new NotImplementedException();
106+
}}
107+
108+
public static implicit operator {typeExpression}(TestClass p1)
109+
{{
110+
throw new NotImplementedException();
111+
}}
112+
}}
113+
";
114+
115+
DiagnosticResult[] expected =
116+
{
117+
// The actual locations have been specified inline.
118+
};
119+
120+
await VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
121+
}
122+
}
123+
}

StyleCop.Analyzers/StyleCop.Analyzers/MaintainabilityRules/MaintainabilityResources.Designer.cs

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

StyleCop.Analyzers/StyleCop.Analyzers/MaintainabilityRules/MaintainabilityResources.resx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,4 +150,13 @@
150150
<data name="SA1413Title" xml:space="preserve">
151151
<value>Use trailing comma in multi-line initializers</value>
152152
</data>
153+
<data name="SA1414Description" xml:space="preserve">
154+
<value>Tuple types appearing in member declarations should have explicitly named tuple elements.</value>
155+
</data>
156+
<data name="SA1414MessageFormat" xml:space="preserve">
157+
<value>Tuple types in signatures should have element names</value>
158+
</data>
159+
<data name="SA1414Title" xml:space="preserve">
160+
<value>Tuple types in signatures should have element names</value>
161+
</data>
153162
</root>
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3+
4+
namespace StyleCop.Analyzers.MaintainabilityRules
5+
{
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Collections.Immutable;
9+
using System.Text;
10+
using Microsoft.CodeAnalysis;
11+
using Microsoft.CodeAnalysis.CSharp;
12+
using Microsoft.CodeAnalysis.CSharp.Syntax;
13+
using Microsoft.CodeAnalysis.Diagnostics;
14+
using StyleCop.Analyzers.Helpers;
15+
using StyleCop.Analyzers.Lightup;
16+
17+
[DiagnosticAnalyzer(LanguageNames.CSharp)]
18+
[NoCodeFix("Cannot generate appropriate names.")]
19+
public class SA1414TupleTypesInSignaturesShouldHaveElementNames : DiagnosticAnalyzer
20+
{
21+
/// <summary>
22+
/// The ID for diagnostics produced by the <see cref="SA1414TupleTypesInSignaturesShouldHaveElementNames"/> analyzer.
23+
/// </summary>
24+
public const string DiagnosticId = "SA1414";
25+
26+
private static readonly LocalizableString Title = new LocalizableResourceString(nameof(MaintainabilityResources.SA1414Title), MaintainabilityResources.ResourceManager, typeof(MaintainabilityResources));
27+
private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(MaintainabilityResources.SA1414MessageFormat), MaintainabilityResources.ResourceManager, typeof(MaintainabilityResources));
28+
private static readonly LocalizableString Description = new LocalizableResourceString(nameof(MaintainabilityResources.SA1414Description), MaintainabilityResources.ResourceManager, typeof(MaintainabilityResources));
29+
private static readonly string HelpLink = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1141.md";
30+
31+
private static readonly Action<SyntaxNodeAnalysisContext> MethodDeclarationAction = HandleMethodDeclaration;
32+
private static readonly Action<SyntaxNodeAnalysisContext> ConstructorDeclarationAction = HandleConstructorDeclaration;
33+
private static readonly Action<SyntaxNodeAnalysisContext> PropertyDeclarationAction = HandlePropertyDeclaration;
34+
private static readonly Action<SyntaxNodeAnalysisContext> IndexerDeclarationAction = HandleIndexerDeclaration;
35+
private static readonly Action<SyntaxNodeAnalysisContext> ConversionOperatorDeclarationAction = HandleConversionOperatorDeclaration;
36+
private static readonly Action<SyntaxNodeAnalysisContext> DelegateDeclarationAction = HandleDelegateDeclaration;
37+
38+
private static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, AnalyzerCategory.ReadabilityRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description, HelpLink);
39+
40+
/// <inheritdoc/>
41+
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Descriptor);
42+
43+
/// <inheritdoc/>
44+
public override void Initialize(AnalysisContext context)
45+
{
46+
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
47+
context.EnableConcurrentExecution();
48+
49+
context.RegisterSyntaxNodeAction(MethodDeclarationAction, SyntaxKind.MethodDeclaration);
50+
context.RegisterSyntaxNodeAction(ConstructorDeclarationAction, SyntaxKind.ConstructorDeclaration);
51+
context.RegisterSyntaxNodeAction(PropertyDeclarationAction, SyntaxKind.PropertyDeclaration);
52+
context.RegisterSyntaxNodeAction(IndexerDeclarationAction, SyntaxKind.IndexerDeclaration);
53+
context.RegisterSyntaxNodeAction(ConversionOperatorDeclarationAction, SyntaxKind.ConversionOperatorDeclaration);
54+
context.RegisterSyntaxNodeAction(DelegateDeclarationAction, SyntaxKind.DelegateDeclaration);
55+
}
56+
57+
private static void HandleMethodDeclaration(SyntaxNodeAnalysisContext context)
58+
{
59+
if (!context.SupportsTuples())
60+
{
61+
return;
62+
}
63+
64+
var methodDeclaration = (MethodDeclarationSyntax)context.Node;
65+
66+
CheckType(context, methodDeclaration.ReturnType);
67+
CheckParameterList(context, methodDeclaration.ParameterList);
68+
}
69+
70+
private static void HandleConstructorDeclaration(SyntaxNodeAnalysisContext context)
71+
{
72+
if (!context.SupportsTuples())
73+
{
74+
return;
75+
}
76+
77+
var constructorDeclaration = (ConstructorDeclarationSyntax)context.Node;
78+
79+
CheckParameterList(context, constructorDeclaration.ParameterList);
80+
}
81+
82+
private static void HandlePropertyDeclaration(SyntaxNodeAnalysisContext context)
83+
{
84+
if (!context.SupportsTuples())
85+
{
86+
return;
87+
}
88+
89+
var propertyDeclaration = (PropertyDeclarationSyntax)context.Node;
90+
91+
CheckType(context, propertyDeclaration.Type);
92+
}
93+
94+
private static void HandleIndexerDeclaration(SyntaxNodeAnalysisContext context)
95+
{
96+
if (!context.SupportsTuples())
97+
{
98+
return;
99+
}
100+
101+
var indexerDeclaration = (IndexerDeclarationSyntax)context.Node;
102+
103+
CheckType(context, indexerDeclaration.Type);
104+
}
105+
106+
private static void HandleConversionOperatorDeclaration(SyntaxNodeAnalysisContext context)
107+
{
108+
if (!context.SupportsTuples())
109+
{
110+
return;
111+
}
112+
113+
var conversionOperatorDeclarion = (ConversionOperatorDeclarationSyntax)context.Node;
114+
115+
CheckType(context, conversionOperatorDeclarion.Type);
116+
CheckParameterList(context, conversionOperatorDeclarion.ParameterList);
117+
}
118+
119+
private static void HandleDelegateDeclaration(SyntaxNodeAnalysisContext context)
120+
{
121+
if (!context.SupportsTuples())
122+
{
123+
return;
124+
}
125+
126+
var delegateDeclarion = (DelegateDeclarationSyntax)context.Node;
127+
128+
CheckType(context, delegateDeclarion.ReturnType);
129+
CheckParameterList(context, delegateDeclarion.ParameterList);
130+
}
131+
132+
private static void CheckParameterList(SyntaxNodeAnalysisContext context, ParameterListSyntax parameterList)
133+
{
134+
foreach (var parameter in parameterList.Parameters)
135+
{
136+
CheckType(context, parameter.Type);
137+
}
138+
}
139+
140+
private static void CheckType(SyntaxNodeAnalysisContext context, TypeSyntax typeSyntax)
141+
{
142+
switch (typeSyntax.Kind())
143+
{
144+
case SyntaxKindEx.TupleType:
145+
CheckTupleType(context, (TupleTypeSyntaxWrapper)typeSyntax);
146+
break;
147+
148+
case SyntaxKind.QualifiedName:
149+
CheckType(context, ((QualifiedNameSyntax)typeSyntax).Right);
150+
break;
151+
152+
case SyntaxKind.GenericName:
153+
CheckGenericName(context, (GenericNameSyntax)typeSyntax);
154+
break;
155+
}
156+
}
157+
158+
private static void CheckTupleType(SyntaxNodeAnalysisContext context, TupleTypeSyntaxWrapper tupleTypeSyntax)
159+
{
160+
foreach (var tupleElementSyntax in tupleTypeSyntax.Elements)
161+
{
162+
CheckType(context, tupleElementSyntax.Type);
163+
164+
if (tupleElementSyntax.Identifier.IsKind(SyntaxKind.None))
165+
{
166+
var location = tupleElementSyntax.SyntaxNode.GetLocation();
167+
context.ReportDiagnostic(Diagnostic.Create(Descriptor, location));
168+
}
169+
}
170+
}
171+
172+
private static void CheckGenericName(SyntaxNodeAnalysisContext context, GenericNameSyntax genericNameSyntax)
173+
{
174+
foreach (var typeArgument in genericNameSyntax.TypeArgumentList.Arguments)
175+
{
176+
CheckType(context, typeArgument);
177+
}
178+
}
179+
}
180+
}

StyleCopAnalyzers.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "documentation", "documentat
163163
documentation\SA1411.md = documentation\SA1411.md
164164
documentation\SA1412.md = documentation\SA1412.md
165165
documentation\SA1413.md = documentation\SA1413.md
166+
documentation\SA1414.md = documentation\SA1414.md
166167
documentation\SA1500.md = documentation\SA1500.md
167168
documentation\SA1501.md = documentation\SA1501.md
168169
documentation\SA1502.md = documentation\SA1502.md

documentation/MaintainabilityRules.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ Identifier | Name | Description
1818
[SA1411](SA1411.md) | AttributeConstructorMustNotUseUnnecessaryParenthesis | An attribute declaration does not contain any parameters, yet it still includes parenthesis.
1919
[SA1412](SA1412.md) | StoreFilesAsUtf8 | The encoding of the file is not UTF-8 with byte order mark.
2020
[SA1413](SA1413.md) | UseTrailingCommasInMultiLineInitializers | A multi-line initializer should use a comma on the last item.
21+
[SA1414](SA1414.md) | TupleTypesInSignaturesShouldHaveElementNames | Tuple types appearing in member declarations should have explicitly named tuple elements.

0 commit comments

Comments
 (0)