Skip to content

Commit 55804f5

Browse files
committed
Add readability configuration setting 'allowBuiltInTypeAliases'
1 parent 355e690 commit 55804f5

5 files changed

Lines changed: 118 additions & 3 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1121UnitTests.cs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@ namespace StyleCop.Analyzers.Test.ReadabilityRules
2323
/// </summary>
2424
public class SA1121UnitTests : CodeFixVerifier
2525
{
26+
private const string AllowBuiltInTypeAliasesSettings = @"
27+
{
28+
""settings"": {
29+
""readabilityRules"": {
30+
""allowBuiltInTypeAliases"": true
31+
}
32+
}
33+
}
34+
";
35+
2636
private static readonly Tuple<string, string>[] ReferenceTypesData = new Tuple<string, string>[]
2737
{
2838
new Tuple<string, string>("object", nameof(Object)),
@@ -60,6 +70,8 @@ public class SA1121UnitTests : CodeFixVerifier
6070

6171
private static readonly Tuple<string, string>[] AllTypesData = ReferenceTypesData.Concat(ValueTypesData).ToArray();
6272

73+
private string currentTestSettings;
74+
6375
public static IEnumerable<object[]> ReferenceTypes
6476
{
6577
get
@@ -831,6 +843,53 @@ class Bar
831843
await this.VerifyCSharpFixAsync(oldSource, newSource, allowNewCompilerDiagnostics: true).ConfigureAwait(false);
832844
}
833845

846+
[Fact]
847+
public async Task TestMissleadingUsingAllowAliasesAsync()
848+
{
849+
string oldSource = @"namespace Foo
850+
{
851+
using Int32 = System.UInt32;
852+
class Bar
853+
{
854+
Int32 value = 3;
855+
}
856+
}
857+
";
858+
string newSource = @"namespace Foo
859+
{
860+
using Int32 = System.UInt32;
861+
class Bar
862+
{
863+
uint value = 3;
864+
}
865+
}
866+
";
867+
868+
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(6, 5);
869+
870+
this.currentTestSettings = AllowBuiltInTypeAliasesSettings;
871+
await this.VerifyCSharpDiagnosticAsync(oldSource, expected, CancellationToken.None).ConfigureAwait(false);
872+
await this.VerifyCSharpDiagnosticAsync(newSource, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
873+
await this.VerifyCSharpFixAsync(oldSource, newSource, allowNewCompilerDiagnostics: true).ConfigureAwait(false);
874+
}
875+
876+
[Fact]
877+
public async Task TestUsingNameChangeAllowAliasesAsync()
878+
{
879+
string testSource = @"namespace Foo
880+
{
881+
using MyInt = System.UInt32;
882+
class Bar
883+
{
884+
MyInt value = 3;
885+
}
886+
}
887+
";
888+
889+
this.currentTestSettings = AllowBuiltInTypeAliasesSettings;
890+
await this.VerifyCSharpDiagnosticAsync(testSource, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
891+
}
892+
834893
[Fact]
835894
public async Task TestWrongTypeAsync()
836895
{
@@ -935,6 +994,11 @@ public void Bar()
935994
await this.VerifyCSharpFixAsync(string.Format(testCode, fullName), string.Format(testCode, predefined), cancellationToken: CancellationToken.None).ConfigureAwait(false);
936995
}
937996

997+
protected override string GetSettings()
998+
{
999+
return this.currentTestSettings ?? base.GetSettings();
1000+
}
1001+
9381002
protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
9391003
{
9401004
yield return new SA1121UseBuiltInTypeAlias();

StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/SA1121UseBuiltInTypeAlias.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace StyleCop.Analyzers.ReadabilityRules
1111
using Microsoft.CodeAnalysis.CSharp.Syntax;
1212
using Microsoft.CodeAnalysis.Diagnostics;
1313
using StyleCop.Analyzers.Helpers;
14+
using StyleCop.Analyzers.Settings.ObjectModel;
1415

1516
/// <summary>
1617
/// The code uses one of the basic C# types, but does not use the built-in alias for the type.
@@ -163,7 +164,7 @@ public Analyzer(ConcurrentDictionary<SyntaxTree, bool> usingAliasCache)
163164
this.usingAliasCache = usingAliasCache;
164165
}
165166

166-
public void HandleIdentifierNameSyntax(SyntaxNodeAnalysisContext context)
167+
public void HandleIdentifierNameSyntax(SyntaxNodeAnalysisContext context, StyleCopSettings settings)
167168
{
168169
IdentifierNameSyntax identifierNameSyntax = (IdentifierNameSyntax)context.Node;
169170
if (identifierNameSyntax.IsVar)
@@ -207,7 +208,8 @@ public void HandleIdentifierNameSyntax(SyntaxNodeAnalysisContext context)
207208

208209
// Most source files will not have any using alias directives. Then we don't have to use semantics
209210
// if the identifier name doesn't match the name of a special type
210-
if (!identifierNameSyntax.SyntaxTree.ContainsUsingAlias(this.usingAliasCache))
211+
if (settings.ReadabilityRules.AllowBuiltInTypeAliases
212+
|| !identifierNameSyntax.SyntaxTree.ContainsUsingAlias(this.usingAliasCache))
211213
{
212214
switch (identifierNameSyntax.Identifier.ValueText)
213215
{
@@ -234,6 +236,15 @@ public void HandleIdentifierNameSyntax(SyntaxNodeAnalysisContext context)
234236
}
235237

236238
SemanticModel semanticModel = context.SemanticModel;
239+
240+
// We go straight to the symbol here. We don't need to check alias information because aliases will fall
241+
// into one of two categories:
242+
//
243+
// 1. The alias to a built-in type matches the name of a built-in type (e.g. using Int32 = Int32;). In
244+
// this case, a diagnostic is reported even if allowBuiltInTypeAliases=true.
245+
// 2. The alias to a built-in type is a different name. In this case, if allowBuiltInTypeAliases is true
246+
// then the above code would have already returned due to the renamed symbol not being in the set of
247+
// strings checked by the analyzer above.
237248
INamedTypeSymbol symbol = semanticModel.GetSymbolInfo(identifierNameSyntax, context.CancellationToken).Symbol as INamedTypeSymbol;
238249

239250
switch (symbol?.SpecialType)

StyleCop.Analyzers/StyleCop.Analyzers/Settings/ObjectModel/ReadabilitySettings.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,17 @@ namespace StyleCop.Analyzers.Settings.ObjectModel
77

88
internal class ReadabilitySettings
99
{
10+
/// <summary>
11+
/// This is the backing field for the <see cref="AllowBuiltInTypeAliases"/> property.
12+
/// </summary>
13+
private bool allowBuiltInTypeAliases;
14+
1015
/// <summary>
1116
/// Initializes a new instance of the <see cref="ReadabilitySettings"/> class.
1217
/// </summary>
1318
protected internal ReadabilitySettings()
1419
{
20+
this.allowBuiltInTypeAliases = false;
1521
}
1622

1723
/// <summary>
@@ -21,6 +27,21 @@ protected internal ReadabilitySettings()
2127
protected internal ReadabilitySettings(JsonObject readabilitySettingsObject)
2228
: this()
2329
{
30+
foreach (var kvp in readabilitySettingsObject)
31+
{
32+
switch (kvp.Key)
33+
{
34+
case "allowBuiltInTypeAliases":
35+
this.allowBuiltInTypeAliases = kvp.ToBooleanValue();
36+
break;
37+
38+
default:
39+
break;
40+
}
41+
}
2442
}
43+
44+
public bool AllowBuiltInTypeAliases =>
45+
this.allowBuiltInTypeAliases;
2546
}
2647
}

StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@
4646
"description": "Configuration for readability rules (SA1100-)",
4747
"additionalProperties": false,
4848
"properties": {
49+
"allowBuiltInTypeAliases": {
50+
"type": "boolean",
51+
"description": "When true, diagnostics will not be reported for using aliases of built-in types.",
52+
"default": false
53+
}
4954
}
5055
},
5156
"orderingRules": {

documentation/Configuration.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,21 @@ This section describes the features of readability rules which can be configured
100100
}
101101
```
102102

103-
> Currently there are no configurable settings for readability rules.
103+
### Aliases for Built-In Types
104+
105+
| Property | Default Value | Minimum Version | Summary |
106+
| --- | --- | --- | --- |
107+
| `allowBuiltInTypeAliases` | **false** | 1.1.0-beta007 | Specifies whether aliases are allowed for built-in types. |
108+
109+
By default, SA1121 reports a diagnostic for the use of named aliases for built-in types:
110+
111+
```csharp
112+
using HRESULT = System.Int32;
113+
114+
HRESULT hr = SomeNativeOperation(); // SA1121
115+
```
116+
117+
The `allowBuiltInTypeAliases` configuration property can be set to `true` to allow cases like this while continuing to report diagnostics for direct references to the metadata type name, `Int32`.
104118

105119
## Ordering Rules
106120

0 commit comments

Comments
 (0)