Skip to content

Commit d76aeef

Browse files
committed
Update SA1400 for default interface members
Fixes #3682
1 parent 414e3fd commit d76aeef

File tree

3 files changed

+104
-0
lines changed

3 files changed

+104
-0
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp8/MaintainabilityRules/SA1400CSharp8UnitTests.cs

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

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

816
public partial class SA1400CSharp8UnitTests : SA1400CSharp7UnitTests
917
{
18+
[Fact]
19+
[WorkItem(3002, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3002")]
20+
public async Task TestDefaultInterfaceImplementationWithoutAccessModifierAsync()
21+
{
22+
var testCode = @"
23+
public interface ITest
24+
{
25+
void M()
26+
{
27+
}
28+
29+
int P
30+
{
31+
get
32+
{
33+
return 0;
34+
}
35+
}
36+
}
37+
";
38+
39+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
40+
}
41+
42+
[Fact]
43+
[WorkItem(3002, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3002")]
44+
public async Task TestStaticInterfaceMemberWithoutAccessModifierAsync()
45+
{
46+
var testCode = @"
47+
public interface ITest
48+
{
49+
static void M()
50+
{
51+
}
52+
}
53+
";
54+
55+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
56+
}
57+
58+
[Fact]
59+
[WorkItem(3002, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3002")]
60+
public async Task TestDelegateDeclarationInsideInterfaceAsync()
61+
{
62+
var testCode = @"
63+
public interface ITest
64+
{
65+
delegate void Handler();
66+
}
67+
";
68+
69+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
70+
}
71+
72+
[Fact]
73+
[WorkItem(3002, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3002")]
74+
public async Task TestStaticFieldInsideInterfaceAsync()
75+
{
76+
var testCode = @"
77+
public interface ITest
78+
{
79+
static int Value = 0;
80+
}
81+
";
82+
83+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
84+
}
85+
86+
[Theory]
87+
[MemberData(nameof(CommonMemberData.BaseTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))]
88+
[WorkItem(3002, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3002")]
89+
public async Task TestTypeDeclarationInsideInterfaceAsync(string typeKind)
90+
{
91+
var testCode = $@"
92+
public interface ITest
93+
{{
94+
{typeKind} NestedType
95+
{{
96+
}}
97+
}}
98+
";
99+
100+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
101+
}
10102
}
11103
}

StyleCop.Analyzers/StyleCop.Analyzers/MaintainabilityRules/SA1400AccessModifierMustBeDeclared.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,22 @@ public override void Initialize(AnalysisContext context)
7272
private static void HandleBaseTypeDeclaration(SyntaxNodeAnalysisContext context)
7373
{
7474
var syntax = (BaseTypeDeclarationSyntax)context.Node;
75+
if (syntax.Parent.IsKind(SyntaxKind.InterfaceDeclaration))
76+
{
77+
return;
78+
}
79+
7580
CheckAccessModifiers(context, syntax.Identifier, syntax.Modifiers);
7681
}
7782

7883
private static void HandleDelegateDeclaration(SyntaxNodeAnalysisContext context)
7984
{
8085
var syntax = (DelegateDeclarationSyntax)context.Node;
86+
if (syntax.Parent.IsKind(SyntaxKind.InterfaceDeclaration))
87+
{
88+
return;
89+
}
90+
8191
CheckAccessModifiers(context, syntax.Identifier, syntax.Modifiers);
8292
}
8393

documentation/SA1400.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ C# allows elements to be defined without an access modifier. Depending upon the
2525

2626
This rule requires an access modifier to be explicitly defined for every element. This removes the need for the reader to make assumptions about the code, improving the readability of the code.
2727

28+
Members declared in an `interface` are implicitly `public` and do not need an explicit access modifier. This includes default interface members introduced in C# 8 (instance members with default implementations, static interface members, and nested interface members such as delegates). SA1400 does not report diagnostics for these interface members.
29+
2830
## How to fix violations
2931

3032
To fix a violation of this rule, add an access modifier to the declaration of the element.

0 commit comments

Comments
 (0)