Skip to content

Commit 83549ef

Browse files
authored
Merge pull request #4039 from sharwell/nullable-types
Update for nullable types in C# 8
2 parents f58ccbb + 8fad37d commit 83549ef

File tree

17 files changed

+543
-14
lines changed

17 files changed

+543
-14
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp8/DocumentationRules/SA1633CSharp8UnitTests.cs

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

44
namespace StyleCop.Analyzers.Test.CSharp8.DocumentationRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using StyleCop.Analyzers.DocumentationRules;
69
using StyleCop.Analyzers.Test.CSharp7.DocumentationRules;
10+
using Xunit;
711

812
public partial class SA1633CSharp8UnitTests : SA1633CSharp7UnitTests
913
{
14+
[Fact]
15+
[WorkItem(3006, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3006")]
16+
public async Task TestNullableDirectiveBeforeFileHeaderAsync()
17+
{
18+
var testCode = @"#nullable enable
19+
// <copyright file=""Test0.cs"" company=""FooCorp"">
20+
// Copyright (c) FooCorp. All rights reserved.
21+
// </copyright>
22+
23+
namespace TestNamespace
24+
{
25+
}
26+
";
27+
28+
var expected = Diagnostic(FileHeaderAnalyzers.SA1633DescriptorMissing).WithLocation(1, 1);
29+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
30+
}
1031
}
1132
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp8/ReadabilityRules/SA1125CSharp8UnitTests.cs

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

44
namespace StyleCop.Analyzers.Test.CSharp8.ReadabilityRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.CodeAnalysis.Testing;
69
using StyleCop.Analyzers.Test.CSharp7.ReadabilityRules;
10+
using Xunit;
11+
using static StyleCop.Analyzers.Test.Verifiers.StyleCopDiagnosticVerifier<StyleCop.Analyzers.ReadabilityRules.SA1125UseShorthandForNullableTypes>;
712

813
public partial class SA1125CSharp8UnitTests : SA1125CSharp7UnitTests
914
{
15+
/// <summary>
16+
/// Verifies that the rule continues to report diagnostics for value type nullable long forms when nullable annotations are enabled.
17+
/// </summary>
18+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
19+
[Fact]
20+
[WorkItem(3006, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3006")]
21+
public async Task TestNullableEnableWithLongFormValueTypesAsync()
22+
{
23+
const string testCode = @"#nullable enable
24+
25+
using System;
26+
27+
class TestClass<T>
28+
where T : struct
29+
{
30+
private Nullable<int> field1;
31+
private System.Nullable<int> field2;
32+
private global::System.Nullable<T> field3;
33+
34+
private string? referenceField;
35+
}";
36+
37+
var expectedDiagnostics = new[]
38+
{
39+
Diagnostic().WithLocation(8, 13),
40+
Diagnostic().WithLocation(9, 13),
41+
Diagnostic().WithLocation(10, 13),
42+
};
43+
44+
await VerifyCSharpDiagnosticAsync(testCode, expectedDiagnostics, CancellationToken.None).ConfigureAwait(false);
45+
}
46+
47+
/// <summary>
48+
/// Verifies that nullable reference type annotations are not reported as violations of SA1125.
49+
/// </summary>
50+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
51+
[Fact]
52+
[WorkItem(3006, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3006")]
53+
public async Task TestNullableReferenceAnnotationsNoDiagnosticsAsync()
54+
{
55+
const string testCode = @"#nullable enable
56+
57+
using System.Collections.Generic;
58+
59+
class TestClass<T>
60+
where T : class
61+
{
62+
private string? field;
63+
private List<string?>? list;
64+
65+
public T? Property { get; set; }
66+
67+
public T? Method(T? parameter)
68+
{
69+
return parameter;
70+
}
71+
}";
72+
73+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
74+
}
1075
}
1176
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp8/ReadabilityRules/SA1130CSharp8UnitTests.cs

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

44
namespace StyleCop.Analyzers.Test.CSharp8.ReadabilityRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
68
using StyleCop.Analyzers.Test.CSharp7.ReadabilityRules;
9+
using Xunit;
10+
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
11+
StyleCop.Analyzers.ReadabilityRules.SA1130UseLambdaSyntax,
12+
StyleCop.Analyzers.ReadabilityRules.SA1130CodeFixProvider>;
713

814
public partial class SA1130CSharp8UnitTests : SA1130CSharp7UnitTests
915
{
16+
[Fact]
17+
[WorkItem(3006, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3006")]
18+
public async Task TestDelegateInNullableEnabledGenericAsync()
19+
{
20+
var testCode = @"#nullable enable
21+
using System;
22+
23+
public class TestClass
24+
{
25+
public void TestMethod<T>() where T : class
26+
{
27+
Func<T?> resolve = {|#0:delegate|} { return default; };
28+
}
29+
}
30+
";
31+
32+
var fixedCode = @"#nullable enable
33+
using System;
34+
35+
public class TestClass
36+
{
37+
public void TestMethod<T>() where T : class
38+
{
39+
Func<T?> resolve = () => { return default; };
40+
}
41+
}
42+
";
43+
44+
await VerifyCSharpFixAsync(testCode, Diagnostic().WithLocation(0), fixedCode, CancellationToken.None).ConfigureAwait(false);
45+
}
46+
47+
[Fact]
48+
[WorkItem(3006, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3006")]
49+
public async Task TestDelegateUsedAsNamedArgumentWithNullableTypesAsync()
50+
{
51+
var testCode = @"#nullable enable
52+
using System;
53+
using System.Linq;
54+
55+
public class TestClass
56+
{
57+
public void Test()
58+
{
59+
Invoke(resolve: {|#0:delegate|}
60+
{
61+
return """";
62+
});
63+
}
64+
65+
private void Invoke(string? description = null, Func<object?, string?> resolve = null!)
66+
{
67+
_ = resolve(0);
68+
}
69+
}
70+
";
71+
72+
var fixedCode = @"#nullable enable
73+
using System;
74+
using System.Linq;
75+
76+
public class TestClass
77+
{
78+
public void Test()
79+
{
80+
Invoke(resolve: arg =>
81+
{
82+
return """";
83+
});
84+
}
85+
86+
private void Invoke(string? description = null, Func<object?, string?> resolve = null!)
87+
{
88+
_ = resolve(0);
89+
}
90+
}
91+
";
92+
93+
await VerifyCSharpFixAsync(testCode, Diagnostic().WithLocation(0), fixedCode, CancellationToken.None).ConfigureAwait(false);
94+
}
1095
}
1196
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp8/ReadabilityRules/SA1131CSharp8UnitTests.cs

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

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

815
public partial class SA1131CSharp8UnitTests : SA1131CSharp7UnitTests
916
{
17+
[Fact]
18+
[WorkItem(3006, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3006")]
19+
public async Task TestPatternMatchingNullChecksAsync()
20+
{
21+
var testCode = @"#nullable enable
22+
public class TestClass
23+
{
24+
public bool IsNull(string? value)
25+
{
26+
return value is null;
27+
}
28+
29+
public bool IsNotNull(string? value)
30+
{
31+
return value is { };
32+
}
33+
}
34+
";
35+
36+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
37+
}
1038
}
1139
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp8/SpacingRules/SA1011CSharp8UnitTests.cs

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
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.CSharp8.SpacingRules
75
{
86
using System.Threading;
@@ -16,6 +14,65 @@ namespace StyleCop.Analyzers.Test.CSharp8.SpacingRules
1614

1715
public partial class SA1011CSharp8UnitTests : SA1011CSharp7UnitTests
1816
{
17+
[Fact]
18+
[WorkItem(3006, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3006")]
19+
public async Task VerifyNullableArrayAnnotationsAsync()
20+
{
21+
var testCode = @"#nullable enable
22+
namespace TestNamespace
23+
{
24+
public class TestClass
25+
{
26+
private string[]? field1;
27+
private string[]?[]? field2;
28+
private string?[][]? field3;
29+
30+
public string[]? Property1 => field1;
31+
32+
public string[]?[]? Property2
33+
{
34+
get
35+
{
36+
return new string[]?[] { field1, field2?[0] };
37+
}
38+
}
39+
40+
public string?[] Method(string?[]? values, string[]?[]? other)
41+
{
42+
return values ?? new string?[] { null, other?[0]?[0] };
43+
}
44+
}
45+
}
46+
";
47+
48+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
49+
}
50+
51+
[Fact]
52+
[WorkItem(3006, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3006")]
53+
public async Task VerifyNullForgivingAfterElementAccessAndArrayCreationAsync()
54+
{
55+
var testCode = @"#nullable enable
56+
namespace TestNamespace
57+
{
58+
public class TestClass
59+
{
60+
public string GetValue(string[]? values)
61+
{
62+
return values![0]!;
63+
}
64+
65+
public int GetLength(string?[] items)
66+
{
67+
return new string?[0]!.Length + items[0]! .Length;
68+
}
69+
}
70+
}
71+
";
72+
73+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
74+
}
75+
1976
/// <summary>
2077
/// Verify that declaring a null reference type works for arrays.
2178
/// </summary>

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp8/SpacingRules/SA1013CSharp8UnitTests.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,54 @@ public void TestMethod()
8888
};
8989
}
9090
}
91+
";
92+
93+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
94+
}
95+
96+
/// <summary>
97+
/// Validates that a closing brace followed by a null-forgiving operator is allowed after an object initializer.
98+
/// </summary>
99+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
100+
[Fact]
101+
[WorkItem(3006, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3006")]
102+
public async Task TestObjectInitializerWithNullForgivingOperatorAsync()
103+
{
104+
const string testCode = @"#nullable enable
105+
public class Foo
106+
{
107+
public Foo? Value { get; set; }
108+
109+
public void TestMethod()
110+
{
111+
var item = new Foo { Value = null }!;
112+
var other = new Foo { }!;
113+
}
114+
}
115+
";
116+
117+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
118+
}
119+
120+
/// <summary>
121+
/// Validates that a closing brace followed by a null-forgiving operator is allowed after a collection initializer.
122+
/// </summary>
123+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
124+
[Fact]
125+
[WorkItem(3006, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3006")]
126+
public async Task TestCollectionInitializerWithNullForgivingOperatorAsync()
127+
{
128+
const string testCode = @"#nullable enable
129+
using System.Collections.Generic;
130+
131+
public class Foo
132+
{
133+
public void TestMethod()
134+
{
135+
var numbers = new List<int> { 1, 2, 3 }!;
136+
var nested = new List<List<int>> { new List<int> { 1, 2 }!, new List<int> { 3 }! };
137+
}
138+
}
91139
";
92140

93141
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);

0 commit comments

Comments
 (0)