Skip to content

Commit da11cbc

Browse files
committed
Update SA1129 for native-sized integers
1 parent ff14d11 commit da11cbc

File tree

5 files changed

+178
-2
lines changed

5 files changed

+178
-2
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/ReadabilityRules/SA1129CodeFixProvider.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ private static SyntaxNode GetReplacementNode(Project project, SyntaxNode node, S
7171
var namedTypeSymbol = (symbolInfo.Symbol as IMethodSymbol)?.ContainingType;
7272

7373
var type = GetOrCreateTypeSyntax(project, newExpression, namedTypeSymbol);
74+
bool supportsNativeSizedIntegers = semanticModel.Compilation.SupportsNativeSizedIntegers();
75+
bool usesNativeSizedIntegerKeyword = IsNativeSizedIntegerKeyword(type);
7476

7577
SyntaxNode replacement;
7678

@@ -83,6 +85,12 @@ private static SyntaxNode GetReplacementNode(Project project, SyntaxNode node, S
8385
{
8486
replacement = SyntaxFactory.DefaultExpression(type);
8587
}
88+
else if (!supportsNativeSizedIntegers && usesNativeSizedIntegerKeyword)
89+
{
90+
// nint.Zero and nuint.Zero are only available in C# 11 with .NET 7+. For older versions, we need to
91+
// keep using 'default(nint)'.
92+
replacement = SyntaxFactory.DefaultExpression(type);
93+
}
8694
else
8795
{
8896
string fieldName;
@@ -121,6 +129,11 @@ private static SyntaxNode GetReplacementNode(Project project, SyntaxNode node, S
121129
.WithTrailingTrivia(newExpression.SyntaxNode.GetTrailingTrivia());
122130
}
123131

132+
private static bool IsNativeSizedIntegerKeyword(TypeSyntax type)
133+
{
134+
return type is IdentifierNameSyntax { Identifier.ValueText: "nint" or "nuint" };
135+
}
136+
124137
private static TypeSyntax GetOrCreateTypeSyntax(Project project, BaseObjectCreationExpressionSyntaxWrapper baseObjectCreationExpression, INamedTypeSymbol constructedType)
125138
{
126139
if (baseObjectCreationExpression.SyntaxNode is ObjectCreationExpressionSyntax objectCreationExpressionSyntax)

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/ReadabilityRules/SA1129CSharp11UnitTests.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,82 @@
33

44
namespace StyleCop.Analyzers.Test.CSharp11.ReadabilityRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.CodeAnalysis.Testing;
69
using StyleCop.Analyzers.Test.CSharp10.ReadabilityRules;
10+
using Xunit;
11+
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
12+
StyleCop.Analyzers.ReadabilityRules.SA1129DoNotUseDefaultValueTypeConstructor,
13+
StyleCop.Analyzers.ReadabilityRules.SA1129CodeFixProvider>;
714

815
public partial class SA1129CSharp11UnitTests : SA1129CSharp10UnitTests
916
{
17+
[Fact]
18+
[WorkItem(3969, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3969")]
19+
public async Task VerifyNativeSizedIntegerCreationWithNet70Async()
20+
{
21+
var testCode = @"
22+
using System;
23+
24+
class TestClass
25+
{
26+
void TestMethod()
27+
{
28+
nint nativeInt = [|new nint()|];
29+
nuint nativeUInt = [|new nuint()|];
30+
}
31+
}";
32+
33+
var fixedCode = @"
34+
using System;
35+
36+
class TestClass
37+
{
38+
void TestMethod()
39+
{
40+
nint nativeInt = nint.Zero;
41+
nuint nativeUInt = nuint.Zero;
42+
}
43+
}";
44+
45+
await VerifyCSharpFixAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, fixedCode, CancellationToken.None).ConfigureAwait(false);
46+
}
47+
48+
[Fact]
49+
[WorkItem(3969, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3969")]
50+
public async Task VerifyNativeSizedIntegerCreationWithNet60Async()
51+
{
52+
var testCode = @"
53+
using System;
54+
55+
class TestClass
56+
{
57+
void TestMethod()
58+
{
59+
nint nativeInt = [|new nint()|];
60+
nuint nativeUInt = [|new nuint()|];
61+
}
62+
}";
63+
64+
var fixedCode = @"
65+
using System;
66+
67+
class TestClass
68+
{
69+
void TestMethod()
70+
{
71+
nint nativeInt = default(nint);
72+
nuint nativeUInt = default(nuint);
73+
}
74+
}";
75+
76+
await new CSharpTest
77+
{
78+
ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
79+
TestCode = testCode,
80+
FixedCode = fixedCode,
81+
}.RunAsync(CancellationToken.None).ConfigureAwait(false);
82+
}
1083
}
1184
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/ReadabilityRules/SA1129CSharp9UnitTests.cs

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
22
// Licensed under the MIT License. See LICENSE in the project root for license information.
33

4-
#nullable disable
5-
64
namespace StyleCop.Analyzers.Test.CSharp9.ReadabilityRules
75
{
86
using System.Threading;
97
using System.Threading.Tasks;
108
using Microsoft.CodeAnalysis.Testing;
9+
using StyleCop.Analyzers.Lightup;
1110
using StyleCop.Analyzers.Test.CSharp8.ReadabilityRules;
1211
using Xunit;
1312
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
@@ -46,5 +45,68 @@ internal static S F()
4645

4746
await VerifyCSharpFixAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, fixedTestCode, CancellationToken.None).ConfigureAwait(false);
4847
}
48+
49+
[Fact]
50+
[WorkItem(3969, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3969")]
51+
public async Task VerifyNativeSizedIntegerCreationAsync()
52+
{
53+
var testCode = @"
54+
using System;
55+
56+
class TestClass
57+
{
58+
void TestMethod()
59+
{
60+
nint nativeInt = [|new nint()|];
61+
nuint nativeUInt = [|new nuint()|];
62+
}
63+
}";
64+
65+
var fixedTestCode = @"
66+
using System;
67+
68+
class TestClass
69+
{
70+
void TestMethod()
71+
{
72+
nint nativeInt = default(nint);
73+
nuint nativeUInt = default(nuint);
74+
}
75+
}";
76+
77+
// Force C# 9 language version even in later test scenarios
78+
await new CSharpTest(LanguageVersionEx.CSharp9)
79+
{
80+
TestCode = testCode,
81+
FixedCode = fixedTestCode,
82+
}.RunAsync(CancellationToken.None).ConfigureAwait(false);
83+
}
84+
85+
[Fact]
86+
[WorkItem(3969, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3969")]
87+
public async Task VerifyNativeSizedIntegerDefaultParameterValuesAsync()
88+
{
89+
var testCode = @"
90+
using System;
91+
92+
class TestClass
93+
{
94+
void TestMethod(nint nativeInt = [|new nint()|], nuint nativeUInt = [|new nuint()|])
95+
{
96+
}
97+
}";
98+
99+
var fixedTestCode = @"
100+
using System;
101+
102+
class TestClass
103+
{
104+
void TestMethod(nint nativeInt = default(nint), nuint nativeUInt = default(nuint))
105+
{
106+
}
107+
}";
108+
109+
await VerifyCSharpFixAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, fixedTestCode, CancellationToken.None).ConfigureAwait(false);
110+
}
49111
}
50112
}

StyleCop.Analyzers/StyleCop.Analyzers/Helpers/LanguageFeatureHelpers.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
namespace StyleCop.Analyzers.Helpers
77
{
88
using System.Diagnostics.CodeAnalysis;
9+
using Microsoft.CodeAnalysis;
910
using Microsoft.CodeAnalysis.CSharp;
1011
using Microsoft.CodeAnalysis.Diagnostics;
1112
using StyleCop.Analyzers.Lightup;
@@ -58,5 +59,29 @@ internal static bool SupportsInferredTupleElementNames(this SyntaxNodeAnalysisCo
5859
var csharpParseOptions = context.Node.SyntaxTree.Options as CSharpParseOptions;
5960
return (csharpParseOptions != null) && (csharpParseOptions.LanguageVersion >= LanguageVersionEx.CSharp7_1);
6061
}
62+
63+
internal static bool SupportsNativeSizedIntegers(this Compilation compilation)
64+
{
65+
if (compilation is not CSharpCompilation { LanguageVersion: >= LanguageVersionEx.CSharp11 } csharpCompilation)
66+
{
67+
return false;
68+
}
69+
70+
var runtimeFeatureType = csharpCompilation.GetTypeByMetadataName("System.Runtime.CompilerServices.RuntimeFeature");
71+
if (runtimeFeatureType is null)
72+
{
73+
return false;
74+
}
75+
76+
foreach (var member in runtimeFeatureType.GetMembers("NumericIntPtr"))
77+
{
78+
if (member is IFieldSymbol { IsConst: true, Type.SpecialType: SpecialType.System_String })
79+
{
80+
return true;
81+
}
82+
}
83+
84+
return false;
85+
}
6186
}
6287
}

documentation/SA1129.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ should always be created using the syntax `default(T)` instead.
4545

4646
To fix a violation of this rule, replace the syntax `new T()` with the equivalent syntax `default(T)`.
4747

48+
For native-sized integers, the code fix will use `IntPtr.Zero`/`UIntPtr.Zero` when the project targets C# 11 with
49+
runtime support for native-sized integers (e.g. .NET 7 or later), and `default(nint)`/`default(nuint)` otherwise.
50+
4851
## How to suppress violations
4952

5053
```csharp

0 commit comments

Comments
 (0)