Skip to content

Commit ec05be8

Browse files
committed
Update SA1100 for covariant return types
1 parent ff14d11 commit ec05be8

File tree

2 files changed

+106
-1
lines changed

2 files changed

+106
-1
lines changed

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

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

44
namespace StyleCop.Analyzers.Test.CSharp9.ReadabilityRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.CodeAnalysis.Testing;
69
using StyleCop.Analyzers.Test.CSharp8.ReadabilityRules;
10+
using StyleCop.Analyzers.Test.Helpers;
11+
using Xunit;
12+
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
13+
StyleCop.Analyzers.ReadabilityRules.SA1100DoNotPrefixCallsWithBaseUnlessLocalImplementationExists,
14+
StyleCop.Analyzers.ReadabilityRules.SA1100CodeFixProvider>;
715

816
public partial class SA1100CSharp9UnitTests : SA1100CSharp8UnitTests
917
{
18+
[Fact]
19+
[WorkItem(3975, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3975")]
20+
public async Task TestCovariantOverrideAllowsBaseCallWhenOverrideExistsAsync()
21+
{
22+
var testCode = @"
23+
public class BaseType
24+
{
25+
}
26+
27+
public class DerivedType : BaseType
28+
{
29+
}
30+
31+
public class BaseClass
32+
{
33+
protected virtual BaseType Create() => new BaseType();
34+
}
35+
36+
public class DerivedClass : BaseClass
37+
{
38+
protected override DerivedType Create()
39+
{
40+
var value = base.Create();
41+
return new DerivedType();
42+
}
43+
}
44+
";
45+
46+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
47+
}
48+
49+
[Fact]
50+
[WorkItem(3975, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3975")]
51+
public async Task TestBaseCallFlaggedWhenOverrideOnlyInIntermediateTypeAsync()
52+
{
53+
var testCode = @"
54+
public class BaseType
55+
{
56+
}
57+
58+
public class DerivedType : BaseType
59+
{
60+
}
61+
62+
public class BaseClass
63+
{
64+
protected virtual BaseType Create() => new BaseType();
65+
}
66+
67+
public class IntermediateClass : BaseClass
68+
{
69+
protected override DerivedType Create() => new DerivedType();
70+
}
71+
72+
public class DerivedClass : IntermediateClass
73+
{
74+
public BaseType CreateThroughBase()
75+
{
76+
return [|base|].Create();
77+
}
78+
}
79+
";
80+
81+
var fixedCode = @"
82+
public class BaseType
83+
{
84+
}
85+
86+
public class DerivedType : BaseType
87+
{
88+
}
89+
90+
public class BaseClass
91+
{
92+
protected virtual BaseType Create() => new BaseType();
93+
}
94+
95+
public class IntermediateClass : BaseClass
96+
{
97+
protected override DerivedType Create() => new DerivedType();
98+
}
99+
100+
public class DerivedClass : IntermediateClass
101+
{
102+
public BaseType CreateThroughBase()
103+
{
104+
return this.Create();
105+
}
106+
}
107+
";
108+
109+
await VerifyCSharpFixAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, fixedCode, CancellationToken.None).ConfigureAwait(false);
110+
}
10111
}
11112
}

documentation/SA1100.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,14 @@ public override string JoinName(string first, string last)
4444
}
4545
```
4646

47-
At this point, the local call to `base.JoinName(...)` most likely introduces a bug into the code. This call will always call the base class method and will cause the local override to be ignored.
47+
At this point, the local call to `base.JoinName(...)` most likely introduces a bug into the code. This call will always call the base class method and will cause the local override to be ignored.
4848

4949
For this reason, calls to members from a base class should not begin with `base.`, unless a local override is implemented, and the developer wants to specifically call the base class member. When there is no local override of the base class member, the call should be prefixed with `this.` rather than `base.`.
5050

51+
### Covariant return types
52+
53+
Starting in C# 9, overrides can return a more derived type than the base member. A `base.` call still bypasses the most-derived override, even when an intermediate class provides a covariant implementation. SA1100 continues to report `base.` calls from a class that does not declare its own override, and the suggested fix remains to call the member through `this.` instead.
54+
5155
## How to fix violations
5256

5357
To fix a violation of this rule, change the `base.` prefix to `this.`.

0 commit comments

Comments
 (0)