Skip to content

Commit

Permalink
Exempt file-local types from SA1402 [resolves #3803]
Browse files Browse the repository at this point in the history
  • Loading branch information
6bee committed Aug 30, 2024
1 parent ff5c432 commit c4b4ac9
Show file tree
Hide file tree
Showing 10 changed files with 265 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
/// <summary>
/// Verifies that SA1402 is not reported for file-local class types.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
[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<DiagnosticResult>(),
CancellationToken.None).ConfigureAwait(false);
}

/// <summary>
/// Verifies that SA1402 is not reported for file-local static class types.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
[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<DiagnosticResult>(),
CancellationToken.None).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
/// <summary>
/// Verifies that SA1402 is not reported for file-local delegate types.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
[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<DiagnosticResult>(),
CancellationToken.None).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
/// <summary>
/// Verifies that SA1402 is not reported for file-local enum types.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
[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<DiagnosticResult>(),
CancellationToken.None).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
/// <summary>
/// Verifies that SA1402 is not reported for file-local interface types.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
[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<DiagnosticResult>(),
CancellationToken.None).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
/// <summary>
/// Verifies that SA1402 is not reported for file-local record types.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
[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<DiagnosticResult>(),
CancellationToken.None).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
/// <summary>
/// Verifies that SA1402 is not reported for file-local record class types.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
[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<DiagnosticResult>(),
CancellationToken.None).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
/// <summary>
/// Verifies that SA1402 is not reported for file-local record struct types.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
[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<DiagnosticResult>(),
CancellationToken.None).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
/// <summary>
/// Verifies that SA1402 is not reported for file-local struct types.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
[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<DiagnosticResult>(),
CancellationToken.None).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context, StyleCop
private static IEnumerable<MemberDeclarationSyntax> GetTopLevelTypeDeclarations(SyntaxNode root, StyleCopSettings settings)
{
var allTypeDeclarations = root.DescendantNodes(descendIntoChildren: node => ContainsTopLevelTypeDeclarations(node)).OfType<MemberDeclarationSyntax>().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;
}

Expand Down Expand Up @@ -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);
}
}
}
2 changes: 2 additions & 0 deletions documentation/SA1402.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit c4b4ac9

Please sign in to comment.