Skip to content

Commit 6cc07c4

Browse files
committed
Merge pull request #1785 from sharwell/usings-config
Using directive placement configuration
2 parents 196ea36 + cb774f0 commit 6cc07c4

18 files changed

Lines changed: 900 additions & 132 deletions
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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.Helpers
5+
{
6+
using System.Collections.Generic;
7+
using System.Collections.Immutable;
8+
using Microsoft.CodeAnalysis;
9+
using Microsoft.CodeAnalysis.CSharp;
10+
using Microsoft.CodeAnalysis.CSharp.Syntax;
11+
12+
internal static class SpecialTypeHelper
13+
{
14+
private static ImmutableDictionary<SpecialType, PredefinedTypeSyntax> PredefinedSpecialTypes { get; } =
15+
new Dictionary<SpecialType, PredefinedTypeSyntax>
16+
{
17+
[SpecialType.System_Boolean] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.BoolKeyword)),
18+
[SpecialType.System_Byte] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ByteKeyword)),
19+
[SpecialType.System_Char] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.CharKeyword)),
20+
[SpecialType.System_Decimal] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.DecimalKeyword)),
21+
[SpecialType.System_Double] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.DoubleKeyword)),
22+
[SpecialType.System_Int16] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ShortKeyword)),
23+
[SpecialType.System_Int32] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.IntKeyword)),
24+
[SpecialType.System_Int64] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.LongKeyword)),
25+
[SpecialType.System_Object] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ObjectKeyword)),
26+
[SpecialType.System_SByte] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.SByteKeyword)),
27+
[SpecialType.System_Single] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.FloatKeyword)),
28+
[SpecialType.System_String] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.StringKeyword)),
29+
[SpecialType.System_UInt16] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.UShortKeyword)),
30+
[SpecialType.System_UInt32] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.UIntKeyword)),
31+
[SpecialType.System_UInt64] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ULongKeyword))
32+
}.ToImmutableDictionary();
33+
34+
public static bool IsPredefinedType(SpecialType specialType)
35+
{
36+
return PredefinedSpecialTypes.ContainsKey(specialType);
37+
}
38+
39+
public static bool TryGetPredefinedType(SpecialType specialType, out PredefinedTypeSyntax predefinedTypeSyntax)
40+
{
41+
return PredefinedSpecialTypes.TryGetValue(specialType, out predefinedTypeSyntax);
42+
}
43+
}
44+
}

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/UsingCodeFixProvider.cs

Lines changed: 228 additions & 51 deletions
Large diffs are not rendered by default.

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/ReadabilityRules/SA1121CodeFixProvider.cs

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,6 @@ namespace StyleCop.Analyzers.ReadabilityRules
2626
[Shared]
2727
internal class SA1121CodeFixProvider : CodeFixProvider
2828
{
29-
private static readonly Dictionary<SpecialType, PredefinedTypeSyntax> PredefinedSpecialTypes = new Dictionary<SpecialType, PredefinedTypeSyntax>
30-
{
31-
[SpecialType.System_Boolean] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.BoolKeyword)),
32-
[SpecialType.System_Byte] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ByteKeyword)),
33-
[SpecialType.System_Char] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.CharKeyword)),
34-
[SpecialType.System_Decimal] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.DecimalKeyword)),
35-
[SpecialType.System_Double] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.DoubleKeyword)),
36-
[SpecialType.System_Int16] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ShortKeyword)),
37-
[SpecialType.System_Int32] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.IntKeyword)),
38-
[SpecialType.System_Int64] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.LongKeyword)),
39-
[SpecialType.System_Object] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ObjectKeyword)),
40-
[SpecialType.System_SByte] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.SByteKeyword)),
41-
[SpecialType.System_Single] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.FloatKeyword)),
42-
[SpecialType.System_String] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.StringKeyword)),
43-
[SpecialType.System_UInt16] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.UShortKeyword)),
44-
[SpecialType.System_UInt32] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.UIntKeyword)),
45-
[SpecialType.System_UInt64] = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ULongKeyword))
46-
};
47-
4829
/// <inheritdoc/>
4930
public override ImmutableArray<string> FixableDiagnosticIds { get; } =
5031
ImmutableArray.Create(SA1121UseBuiltInTypeAlias.DiagnosticId);
@@ -85,7 +66,7 @@ private static SyntaxNode ComputeReplacement(SemanticModel semanticModel, Syntax
8566
var type = semanticModel.GetSymbolInfo(node, cancellationToken).Symbol as INamedTypeSymbol;
8667

8768
PredefinedTypeSyntax typeSyntax;
88-
if (!PredefinedSpecialTypes.TryGetValue(type.SpecialType, out typeSyntax))
69+
if (!SpecialTypeHelper.TryGetPredefinedType(type.SpecialType, out typeSyntax))
8970
{
9071
return node;
9172
}

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/StyleCop.Analyzers.CodeFixes.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
<Compile Include="Helpers\IndentationOptions.cs" />
6666
<Compile Include="Helpers\QueryIndentationHelpers.cs" />
6767
<Compile Include="Helpers\RenameHelper.cs" />
68+
<Compile Include="Helpers\SpecialTypeHelper.cs" />
6869
<Compile Include="LayoutRules\SA1500CodeFixProvider.cs" />
6970
<Compile Include="LayoutRules\SA1501CodeFixProvider.cs" />
7071
<Compile Include="LayoutRules\SA1502CodeFixProvider.cs" />
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
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.OrderingRules
5+
{
6+
using System.Collections.Generic;
7+
using System.Threading;
8+
using System.Threading.Tasks;
9+
using Analyzers.Settings.ObjectModel;
10+
using Microsoft.CodeAnalysis.CodeFixes;
11+
using Microsoft.CodeAnalysis.Diagnostics;
12+
using StyleCop.Analyzers.OrderingRules;
13+
using TestHelper;
14+
using Xunit;
15+
16+
/// <summary>
17+
/// Unit tests for the <see cref="SA1200UsingDirectivesMustBePlacedCorrectly"/> when configured to use
18+
/// <see cref="UsingDirectivesPlacement.OutsideNamespace"/>.
19+
/// </summary>
20+
public class SA1200OutsideNamespaceUnitTests : CodeFixVerifier
21+
{
22+
private const string TestSettings = @"
23+
{
24+
""settings"": {
25+
""orderingRules"": {
26+
""usingDirectivesPlacement"": ""outsideNamespace""
27+
}
28+
}
29+
}
30+
";
31+
32+
private const string ClassDefinition = @"public class TestClass
33+
{
34+
}";
35+
36+
private const string StructDefinition = @"public struct TestStruct
37+
{
38+
}";
39+
40+
private const string InterfaceDefinition = @"public interface TestInterface
41+
{
42+
}";
43+
44+
private const string EnumDefinition = @"public enum TestEnum
45+
{
46+
TestValue
47+
}";
48+
49+
private const string DelegateDefinition = @"public delegate void TestDelegate();";
50+
51+
/// <summary>
52+
/// Verifies that using statements in a namespace produces the expected diagnostics.
53+
/// </summary>
54+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
55+
[Fact]
56+
public async Task TestInvalidUsingStatementsInNamespaceAsync()
57+
{
58+
var testCode = @"namespace TestNamespace
59+
{
60+
using System;
61+
using System.Threading;
62+
}
63+
";
64+
var fixedTestCode = @"using System;
65+
using System.Threading;
66+
67+
namespace TestNamespace
68+
{
69+
}
70+
";
71+
72+
DiagnosticResult[] expectedResults =
73+
{
74+
this.CSharpDiagnostic(SA1200UsingDirectivesMustBePlacedCorrectly.DescriptorOutside).WithLocation(3, 5),
75+
this.CSharpDiagnostic(SA1200UsingDirectivesMustBePlacedCorrectly.DescriptorOutside).WithLocation(4, 5)
76+
};
77+
78+
await this.VerifyCSharpDiagnosticAsync(testCode, expectedResults, CancellationToken.None).ConfigureAwait(false);
79+
await this.VerifyCSharpDiagnosticAsync(fixedTestCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
80+
await this.VerifyCSharpFixAsync(testCode, fixedTestCode).ConfigureAwait(false);
81+
}
82+
83+
/// <summary>
84+
/// Verifies that simplified using statements in a namespace are expanded during the code fix operation.
85+
/// </summary>
86+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
87+
[Fact]
88+
public async Task TestInvalidSimplifiedUsingStatementsInNamespaceAsync()
89+
{
90+
var testCode = @"namespace System
91+
{
92+
using System;
93+
using System.Threading;
94+
using Reflection;
95+
}
96+
";
97+
var fixedTestCode = @"using System;
98+
using System.Reflection;
99+
using System.Threading;
100+
101+
namespace System
102+
{
103+
}
104+
";
105+
106+
DiagnosticResult[] expectedResults =
107+
{
108+
this.CSharpDiagnostic(SA1200UsingDirectivesMustBePlacedCorrectly.DescriptorOutside).WithLocation(3, 5),
109+
this.CSharpDiagnostic(SA1200UsingDirectivesMustBePlacedCorrectly.DescriptorOutside).WithLocation(4, 5),
110+
this.CSharpDiagnostic(SA1200UsingDirectivesMustBePlacedCorrectly.DescriptorOutside).WithLocation(5, 5),
111+
};
112+
113+
await this.VerifyCSharpDiagnosticAsync(testCode, expectedResults, CancellationToken.None).ConfigureAwait(false);
114+
await this.VerifyCSharpDiagnosticAsync(fixedTestCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
115+
await this.VerifyCSharpFixAsync(testCode, fixedTestCode).ConfigureAwait(false);
116+
}
117+
118+
/// <summary>
119+
/// Verifies that simplified using statements in a namespace are expanded during the code fix operation.
120+
/// </summary>
121+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
122+
[Fact]
123+
public async Task TestInvalidSimplifiedUsingStatementsInExtensionNamespaceAsync()
124+
{
125+
var testCode = @"namespace System.MyExtension
126+
{
127+
using System.Threading;
128+
using Reflection;
129+
}
130+
";
131+
var fixedTestCode = @"using System.Reflection;
132+
using System.Threading;
133+
134+
namespace System.MyExtension
135+
{
136+
}
137+
";
138+
139+
DiagnosticResult[] expectedResults =
140+
{
141+
this.CSharpDiagnostic(SA1200UsingDirectivesMustBePlacedCorrectly.DescriptorOutside).WithLocation(3, 5),
142+
this.CSharpDiagnostic(SA1200UsingDirectivesMustBePlacedCorrectly.DescriptorOutside).WithLocation(3, 5),
143+
this.CSharpDiagnostic(SA1200UsingDirectivesMustBePlacedCorrectly.DescriptorOutside).WithLocation(4, 5),
144+
this.CSharpDiagnostic(SA1200UsingDirectivesMustBePlacedCorrectly.DescriptorOutside).WithLocation(4, 5),
145+
};
146+
147+
await this.VerifyCSharpDiagnosticAsync(testCode, expectedResults, CancellationToken.None).ConfigureAwait(false);
148+
await this.VerifyCSharpDiagnosticAsync(fixedTestCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
149+
await this.VerifyCSharpFixAsync(testCode, fixedTestCode).ConfigureAwait(false);
150+
}
151+
152+
/// <summary>
153+
/// Verifies that having using statements in the compilation unit will not produce any diagnostics when there are type definition present.
154+
/// </summary>
155+
/// <param name="typeDefinition">The type definition to test.</param>
156+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
157+
[Theory]
158+
[InlineData(ClassDefinition)]
159+
[InlineData(StructDefinition)]
160+
[InlineData(InterfaceDefinition)]
161+
[InlineData(EnumDefinition)]
162+
[InlineData(DelegateDefinition)]
163+
public async Task TestValidUsingStatementsInCompilationUnitWithTypeDefinitionAsync(string typeDefinition)
164+
{
165+
var testCode = $@"using System;
166+
167+
{typeDefinition}
168+
";
169+
170+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
171+
}
172+
173+
/// <summary>
174+
/// Verifies that having using statements in the compilation unit will not produce any diagnostics when there are attributes present.
175+
/// </summary>
176+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
177+
[Fact]
178+
public async Task TestValidUsingStatementsInCompilationUnitWithAttributesAsync()
179+
{
180+
var testCode = @"using System.Reflection;
181+
182+
[assembly: AssemblyVersion(""1.0.0.0"")]
183+
184+
namespace TestNamespace
185+
{
186+
using System;
187+
using System.Threading;
188+
}
189+
";
190+
var fixedCode = @"using System;
191+
using System.Reflection;
192+
using System.Threading;
193+
194+
[assembly: AssemblyVersion(""1.0.0.0"")]
195+
196+
namespace TestNamespace
197+
{
198+
}
199+
";
200+
201+
DiagnosticResult[] expectedResults =
202+
{
203+
this.CSharpDiagnostic(SA1200UsingDirectivesMustBePlacedCorrectly.DescriptorOutside).WithLocation(7, 5),
204+
this.CSharpDiagnostic(SA1200UsingDirectivesMustBePlacedCorrectly.DescriptorOutside).WithLocation(8, 5)
205+
};
206+
207+
await this.VerifyCSharpDiagnosticAsync(testCode, expectedResults, CancellationToken.None).ConfigureAwait(false);
208+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
209+
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
210+
}
211+
212+
/// <summary>
213+
/// Verifies that having using statements in the compilation unit will not produce diagnostics.
214+
/// </summary>
215+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
216+
[Fact]
217+
public async Task TestValidUsingStatementsInCompilationUnitAsync()
218+
{
219+
var testCode = @"using System;
220+
using System.Threading;
221+
222+
namespace TestNamespace
223+
{
224+
}
225+
";
226+
227+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
228+
}
229+
230+
/// <inheritdoc/>
231+
protected override string GetSettings()
232+
{
233+
return TestSettings;
234+
}
235+
236+
/// <inheritdoc/>
237+
protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
238+
{
239+
yield return new SA1200UsingDirectivesMustBePlacedCorrectly();
240+
}
241+
242+
/// <inheritdoc/>
243+
protected override CodeFixProvider GetCSharpCodeFixProvider()
244+
{
245+
return new UsingCodeFixProvider();
246+
}
247+
}
248+
}

0 commit comments

Comments
 (0)