diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForClassCSharp11UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForClassCSharp11UnitTests.cs
index e57806802..6ee267eb7 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForClassCSharp11UnitTests.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForClassCSharp11UnitTests.cs
@@ -3,9 +3,61 @@
namespace StyleCop.Analyzers.Test.CSharp11.MaintainabilityRules
{
+ using System;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Microsoft.CodeAnalysis.Testing;
using StyleCop.Analyzers.Test.CSharp10.MaintainabilityRules;
+ using Xunit;
public partial class SA1402ForClassCSharp11UnitTests : SA1402ForClassCSharp10UnitTests
{
+ ///
+ /// Verifies that SA1402 is not reported for file-scoped class types.
+ ///
+ /// A representing the asynchronous unit test.
+ [Fact]
+ [WorkItem(3803, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3803")]
+ public async Task VerifyFileLocalClassExemptionAsync()
+ {
+ var testCode = $@"namespace TestNamespace;
+
+public class TestClass1 {{ }}
+
+file class TestClass2 {{ }}
+";
+
+ var expectedDiagnostic = this.Diagnostic().WithLocation(0);
+
+ await this.VerifyCSharpDiagnosticAsync(
+ testCode,
+ this.GetSettings(),
+ Array.Empty(),
+ CancellationToken.None).ConfigureAwait(false);
+ }
+
+ ///
+ /// Verifies that SA1402 is not reported for file-local static class types.
+ ///
+ /// A representing the asynchronous unit test.
+ [Fact]
+ [WorkItem(3803, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3803")]
+ public async Task VerifyFileLocalStaticClassExemptionAsync()
+ {
+ var testCode = $@"namespace TestNamespace;
+
+public class TestClass1 {{ }}
+
+file static class TestClass2 {{ }}
+";
+
+ var expectedDiagnostic = this.Diagnostic().WithLocation(0);
+
+ await this.VerifyCSharpDiagnosticAsync(
+ testCode,
+ this.GetSettings(),
+ Array.Empty(),
+ CancellationToken.None).ConfigureAwait(false);
+ }
}
}
diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForDelegateCSharp11UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForDelegateCSharp11UnitTests.cs
index d832ca32a..90e07c01d 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForDelegateCSharp11UnitTests.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForDelegateCSharp11UnitTests.cs
@@ -3,9 +3,37 @@
namespace StyleCop.Analyzers.Test.CSharp11.MaintainabilityRules
{
+ using System;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Microsoft.CodeAnalysis.Testing;
using StyleCop.Analyzers.Test.CSharp10.MaintainabilityRules;
+ using Xunit;
public partial class SA1402ForDelegateCSharp11UnitTests : SA1402ForDelegateCSharp10UnitTests
{
+ ///
+ /// Verifies that SA1402 is not reported for file-scoped delegate types.
+ ///
+ /// A representing the asynchronous unit test.
+ [Fact]
+ [WorkItem(3803, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3803")]
+ public async Task VerifyFileLocalDelegateExemptionAsync()
+ {
+ var testCode = $@"namespace TestNamespace;
+
+public class TestClass {{ }}
+
+file delegate void TestDelegate();
+";
+
+ var expectedDiagnostic = Diagnostic().WithLocation(0);
+
+ await VerifyCSharpDiagnosticAsync(
+ testCode,
+ this.GetSettings(),
+ Array.Empty(),
+ CancellationToken.None).ConfigureAwait(false);
+ }
}
}
diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForEnumCSharp11UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForEnumCSharp11UnitTests.cs
index c8704cd43..132274ec3 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForEnumCSharp11UnitTests.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForEnumCSharp11UnitTests.cs
@@ -3,9 +3,37 @@
namespace StyleCop.Analyzers.Test.CSharp11.MaintainabilityRules
{
+ using System;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Microsoft.CodeAnalysis.Testing;
using StyleCop.Analyzers.Test.CSharp10.MaintainabilityRules;
+ using Xunit;
public partial class SA1402ForEnumCSharp11UnitTests : SA1402ForEnumCSharp10UnitTests
{
+ ///
+ /// Verifies that SA1402 is not reported for file-local enum types.
+ ///
+ /// A representing the asynchronous unit test.
+ [Fact]
+ [WorkItem(3803, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3803")]
+ public async Task VerifyFileLocalEnumExemptionAsync()
+ {
+ var testCode = $@"namespace TestNamespace;
+
+public class TestClass1 {{ }}
+
+file enum TestEnum {{ }}
+";
+
+ var expectedDiagnostic = Diagnostic().WithLocation(0);
+
+ await VerifyCSharpDiagnosticAsync(
+ testCode,
+ this.GetSettings(),
+ Array.Empty(),
+ CancellationToken.None).ConfigureAwait(false);
+ }
}
}
diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForInterfaceCSharp11UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForInterfaceCSharp11UnitTests.cs
index 231b4a135..0a4949a53 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForInterfaceCSharp11UnitTests.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForInterfaceCSharp11UnitTests.cs
@@ -3,9 +3,37 @@
namespace StyleCop.Analyzers.Test.CSharp11.MaintainabilityRules
{
+ using System;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Microsoft.CodeAnalysis.Testing;
using StyleCop.Analyzers.Test.CSharp10.MaintainabilityRules;
+ using Xunit;
public partial class SA1402ForInterfaceCSharp11UnitTests : SA1402ForInterfaceCSharp10UnitTests
{
+ ///
+ /// Verifies that SA1402 is not reported for file-local interface types.
+ ///
+ /// A representing the asynchronous unit test.
+ [Fact]
+ [WorkItem(3803, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3803")]
+ public async Task VerifyFileLocalInterfaceExemptionAsync()
+ {
+ var testCode = $@"namespace TestNamespace;
+
+public interface TestInterface1 {{ }}
+
+file interface TestInterface2 {{ }}
+";
+
+ var expectedDiagnostic = this.Diagnostic().WithLocation(0);
+
+ await this.VerifyCSharpDiagnosticAsync(
+ testCode,
+ this.GetSettings(),
+ Array.Empty(),
+ CancellationToken.None).ConfigureAwait(false);
+ }
}
}
diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForRecordCSharp11UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForRecordCSharp11UnitTests.cs
index dde412102..d267c9ffe 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForRecordCSharp11UnitTests.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForRecordCSharp11UnitTests.cs
@@ -3,9 +3,37 @@
namespace StyleCop.Analyzers.Test.CSharp11.MaintainabilityRules
{
+ using System;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Microsoft.CodeAnalysis.Testing;
using StyleCop.Analyzers.Test.CSharp10.MaintainabilityRules;
+ using Xunit;
public partial class SA1402ForRecordCSharp11UnitTests : SA1402ForRecordCSharp10UnitTests
{
+ ///
+ /// Verifies that SA1402 is not reported for file-local record types.
+ ///
+ /// A representing the asynchronous unit test.
+ [Fact]
+ [WorkItem(3803, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3803")]
+ public async Task VerifyFileLocalRecordExemptionAsync()
+ {
+ var testCode = $@"namespace TestNamespace;
+
+public record TestRecord1 {{ }}
+
+file record TestRecord2 {{ }}
+";
+
+ var expectedDiagnostic = this.Diagnostic().WithLocation(0);
+
+ await this.VerifyCSharpDiagnosticAsync(
+ testCode,
+ this.GetSettings(),
+ Array.Empty(),
+ CancellationToken.None).ConfigureAwait(false);
+ }
}
}
diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForRecordClassCSharp11UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForRecordClassCSharp11UnitTests.cs
index 33b95102b..72684190e 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForRecordClassCSharp11UnitTests.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForRecordClassCSharp11UnitTests.cs
@@ -3,9 +3,37 @@
namespace StyleCop.Analyzers.Test.CSharp11.MaintainabilityRules
{
+ using System;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Microsoft.CodeAnalysis.Testing;
using StyleCop.Analyzers.Test.CSharp10.MaintainabilityRules;
+ using Xunit;
public partial class SA1402ForRecordClassCSharp11UnitTests : SA1402ForRecordClassCSharp10UnitTests
{
+ ///
+ /// Verifies that SA1402 is not reported for file-local record class types.
+ ///
+ /// A representing the asynchronous unit test.
+ [Fact]
+ [WorkItem(3803, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3803")]
+ public async Task VerifyFileLocalRecordClassExemptionAsync()
+ {
+ var testCode = $@"namespace TestNamespace;
+
+public class TestClass {{ }}
+
+file record class TestRecordClass {{ }}
+";
+
+ var expectedDiagnostic = this.Diagnostic().WithLocation(0);
+
+ await this.VerifyCSharpDiagnosticAsync(
+ testCode,
+ this.GetSettings(),
+ Array.Empty(),
+ CancellationToken.None).ConfigureAwait(false);
+ }
}
}
diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForRecordStructCSharp11UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForRecordStructCSharp11UnitTests.cs
index 687501446..149eec957 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForRecordStructCSharp11UnitTests.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForRecordStructCSharp11UnitTests.cs
@@ -3,9 +3,37 @@
namespace StyleCop.Analyzers.Test.CSharp11.MaintainabilityRules
{
+ using System;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Microsoft.CodeAnalysis.Testing;
using StyleCop.Analyzers.Test.CSharp10.MaintainabilityRules;
+ using Xunit;
public partial class SA1402ForRecordStructCSharp11UnitTests : SA1402ForRecordStructCSharp10UnitTests
{
+ ///
+ /// Verifies that SA1402 is not reported for file-local record struct types.
+ ///
+ /// A representing the asynchronous unit test.
+ [Fact]
+ [WorkItem(3803, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3803")]
+ public async Task VerifyFileLocalRecordStructExemptionAsync()
+ {
+ var testCode = $@"namespace TestNamespace;
+
+public class TestClass {{ }}
+
+file record struct TestRecordStruct {{ }}
+";
+
+ var expectedDiagnostic = this.Diagnostic().WithLocation(0);
+
+ await this.VerifyCSharpDiagnosticAsync(
+ testCode,
+ this.GetSettings(),
+ Array.Empty(),
+ CancellationToken.None).ConfigureAwait(false);
+ }
}
}
diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForStructCSharp11UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForStructCSharp11UnitTests.cs
index 44d5b33c3..92a06210d 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForStructCSharp11UnitTests.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/MaintainabilityRules/SA1402ForStructCSharp11UnitTests.cs
@@ -3,9 +3,37 @@
namespace StyleCop.Analyzers.Test.CSharp11.MaintainabilityRules
{
+ using System;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Microsoft.CodeAnalysis.Testing;
using StyleCop.Analyzers.Test.CSharp10.MaintainabilityRules;
+ using Xunit;
public partial class SA1402ForStructCSharp11UnitTests : SA1402ForStructCSharp10UnitTests
{
+ ///
+ /// Verifies that SA1402 is not reported for file-local struct types.
+ ///
+ /// A representing the asynchronous unit test.
+ [Fact]
+ [WorkItem(3803, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3803")]
+ public async Task VerifyFileLocalStructExemptionAsync()
+ {
+ var testCode = $@"namespace TestNamespace;
+
+public struct TestStruct1 {{ }}
+
+file struct TestStruct2 {{ }}
+";
+
+ var expectedDiagnostic = this.Diagnostic().WithLocation(0);
+
+ await this.VerifyCSharpDiagnosticAsync(
+ testCode,
+ this.GetSettings(),
+ Array.Empty(),
+ CancellationToken.None).ConfigureAwait(false);
+ }
}
}
diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/MaintainabilityRules/SA1402FileMayOnlyContainASingleType.cs b/StyleCop.Analyzers/StyleCop.Analyzers/MaintainabilityRules/SA1402FileMayOnlyContainASingleType.cs
index b9ca8c9e2..a76c56c93 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers/MaintainabilityRules/SA1402FileMayOnlyContainASingleType.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers/MaintainabilityRules/SA1402FileMayOnlyContainASingleType.cs
@@ -99,7 +99,7 @@ private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context, StyleCop
private static IEnumerable GetTopLevelTypeDeclarations(SyntaxNode root, StyleCopSettings settings)
{
var allTypeDeclarations = root.DescendantNodes(descendIntoChildren: node => ContainsTopLevelTypeDeclarations(node)).OfType().ToList();
- var relevantTypeDeclarations = allTypeDeclarations.Where(x => IsRelevantType(x, settings)).ToList();
+ var relevantTypeDeclarations = allTypeDeclarations.Where(x => IsRelevantType(x, settings)).Where(x => !IsFileLocalType(x)).ToList();
return relevantTypeDeclarations;
}
@@ -136,5 +136,19 @@ private static bool IsRelevantType(SyntaxNode node, StyleCopSettings settings)
return isRelevant;
}
+
+ private static bool IsFileLocalType(SyntaxNode node)
+ {
+ const SyntaxKind FileKeyword = (SyntaxKind)8449;
+
+ var modifiers = node switch
+ {
+ BaseTypeDeclarationSyntax x => x.Modifiers,
+ DelegateDeclarationSyntax x => x.Modifiers,
+ _ => default,
+ };
+
+ return modifiers.Any(FileKeyword);
+ }
}
}
diff --git a/documentation/SA1402.md b/documentation/SA1402.md
index 2a09c0ee5..a313b3c99 100644
--- a/documentation/SA1402.md
+++ b/documentation/SA1402.md
@@ -27,6 +27,8 @@ It is possible to configure which kind of types this rule should affect. By defa
It is also possible to place multiple parts of the same partial type within the same file.
+File-local types declared using `file` modifier are exempt from this rule.
+
## How to fix violations
To fix an instance of this violation, move each type into its own file.