Skip to content

Commit 99fd634

Browse files
committed
Merge pull request #1725 from vweijsters/fix-1688
Added support to SA1111 for operators and conversion operators
2 parents 2f67c9c + a40ac2c commit 99fd634

2 files changed

Lines changed: 142 additions & 19 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1111UnitTests.cs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1517,11 +1517,141 @@ public void TestMethod2()
15171517
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
15181518
}
15191519

1520+
/// <summary>
1521+
/// Verifies that valid operator declarations will not raise diagnostics.
1522+
/// </summary>
1523+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
1524+
[Fact]
1525+
public async Task VerifyValidOperatorDeclarationsAsync()
1526+
{
1527+
var testCode = @"
1528+
public class TestClass
1529+
{
1530+
public static TestClass operator +(TestClass value1, TestClass value2)
1531+
{
1532+
return new TestClass();
1533+
}
1534+
1535+
public static TestClass operator +(TestClass value)
1536+
{
1537+
return new TestClass();
1538+
}
1539+
1540+
public static explicit operator TestClass(int value)
1541+
{
1542+
return new TestClass();
1543+
}
1544+
}";
1545+
1546+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
1547+
}
1548+
1549+
/// <summary>
1550+
/// Verifies that an unary operator declaration with its closing parenthesis on a different line as the last parameter will produce the expected diagnostics.
1551+
/// </summary>
1552+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
1553+
[Fact]
1554+
public async Task VerifyUnaryOperatorDeclarationWithClosingParanthesisOnNextLineAsync()
1555+
{
1556+
var testCode = @"
1557+
public class TestClass
1558+
{
1559+
public static TestClass operator +(TestClass value
1560+
)
1561+
{
1562+
return new TestClass();
1563+
}
1564+
}";
1565+
1566+
var fixedCode = @"
1567+
public class TestClass
1568+
{
1569+
public static TestClass operator +(TestClass value)
1570+
{
1571+
return new TestClass();
1572+
}
1573+
}";
1574+
1575+
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(5, 1);
1576+
1577+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
1578+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
1579+
await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
1580+
}
1581+
1582+
/// <summary>
1583+
/// Verifies that a binary operator declaration with its closing parenthesis on a different line as the last parameter will produce the expected diagnostics.
1584+
/// </summary>
1585+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
1586+
[Fact]
1587+
public async Task VerifyBinaryOperatorDeclarationWithClosingParanthesisOnNextLineAsync()
1588+
{
1589+
var testCode = @"
1590+
public class TestClass
1591+
{
1592+
public static TestClass operator +(TestClass value1, TestClass value2
1593+
)
1594+
{
1595+
return new TestClass();
1596+
}
1597+
}";
1598+
1599+
var fixedCode = @"
1600+
public class TestClass
1601+
{
1602+
public static TestClass operator +(TestClass value1, TestClass value2)
1603+
{
1604+
return new TestClass();
1605+
}
1606+
}";
1607+
1608+
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(5, 1);
1609+
1610+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
1611+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
1612+
await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
1613+
}
1614+
1615+
/// <summary>
1616+
/// Verifies that a conversion operator declaration with its closing parenthesis on a different line as the last parameter will produce the expected diagnostics.
1617+
/// </summary>
1618+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
1619+
[Fact]
1620+
public async Task VerifyConversionOperatorDeclarationWithClosingParanthesisOnNextLineAsync()
1621+
{
1622+
var testCode = @"
1623+
public class TestClass
1624+
{
1625+
public static explicit operator TestClass(int value
1626+
)
1627+
{
1628+
return new TestClass();
1629+
}
1630+
}";
1631+
1632+
var fixedCode = @"
1633+
public class TestClass
1634+
{
1635+
public static explicit operator TestClass(int value)
1636+
{
1637+
return new TestClass();
1638+
}
1639+
}";
1640+
1641+
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(5, 1);
1642+
1643+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
1644+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
1645+
await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
1646+
}
1647+
1648+
/// <inheritdoc/>
15201649
protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
15211650
{
15221651
yield return new SA1111ClosingParenthesisMustBeOnLineOfLastParameter();
15231652
}
15241653

1654+
/// <inheritdoc/>
15251655
protected override CodeFixProvider GetCSharpCodeFixProvider()
15261656
{
15271657
return new TokenSpacingCodeFixProvider();

StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/SA1111ClosingParenthesisMustBeOnLineOfLastParameter.cs

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,15 @@ internal class SA1111ClosingParenthesisMustBeOnLineOfLastParameter : DiagnosticA
5151
private static readonly DiagnosticDescriptor Descriptor =
5252
new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, AnalyzerCategory.ReadabilityRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description, HelpLink);
5353

54+
private static readonly ImmutableArray<SyntaxKind> HandledMethodSyntaxKinds =
55+
ImmutableArray.Create(
56+
SyntaxKind.MethodDeclaration,
57+
SyntaxKind.ConstructorDeclaration,
58+
SyntaxKind.OperatorDeclaration,
59+
SyntaxKind.ConversionOperatorDeclaration);
60+
5461
private static readonly Action<CompilationStartAnalysisContext> CompilationStartAction = HandleCompilationStart;
55-
private static readonly Action<SyntaxNodeAnalysisContext> MethodDeclarationAction = HandleMethodDeclaration;
56-
private static readonly Action<SyntaxNodeAnalysisContext> ConstructorDeclarationAction = HandleConstructorDeclaration;
62+
private static readonly Action<SyntaxNodeAnalysisContext> BaseMethodDeclarationAction = HandleBaseMethodDeclaration;
5763
private static readonly Action<SyntaxNodeAnalysisContext> InvocationExpressionAction = HandleInvocationExpression;
5864
private static readonly Action<SyntaxNodeAnalysisContext> ObjectCreationExpressionAction = HandleObjectCreationExpression;
5965
private static readonly Action<SyntaxNodeAnalysisContext> IndexerDeclarationAction = HandleIndexerDeclaration;
@@ -76,8 +82,7 @@ public override void Initialize(AnalysisContext context)
7682

7783
private static void HandleCompilationStart(CompilationStartAnalysisContext context)
7884
{
79-
context.RegisterSyntaxNodeActionHonorExclusions(MethodDeclarationAction, SyntaxKind.MethodDeclaration);
80-
context.RegisterSyntaxNodeActionHonorExclusions(ConstructorDeclarationAction, SyntaxKind.ConstructorDeclaration);
85+
context.RegisterSyntaxNodeActionHonorExclusions(BaseMethodDeclarationAction, HandledMethodSyntaxKinds);
8186
context.RegisterSyntaxNodeActionHonorExclusions(InvocationExpressionAction, SyntaxKind.InvocationExpression);
8287
context.RegisterSyntaxNodeActionHonorExclusions(ObjectCreationExpressionAction, SyntaxKind.ObjectCreationExpression);
8388
context.RegisterSyntaxNodeActionHonorExclusions(IndexerDeclarationAction, SyntaxKind.IndexerDeclaration);
@@ -238,18 +243,6 @@ private static void HandleElementAccessExpression(SyntaxNodeAnalysisContext cont
238243
}
239244
}
240245

241-
private static void HandleMethodDeclaration(SyntaxNodeAnalysisContext context)
242-
{
243-
var methodDeclaration = (MethodDeclarationSyntax)context.Node;
244-
HandleBaseMethodDeclaration(context, methodDeclaration);
245-
}
246-
247-
private static void HandleConstructorDeclaration(SyntaxNodeAnalysisContext context)
248-
{
249-
var constructotDeclarationSyntax = (ConstructorDeclarationSyntax)context.Node;
250-
HandleBaseMethodDeclaration(context, constructotDeclarationSyntax);
251-
}
252-
253246
private static void HandleInvocationExpression(SyntaxNodeAnalysisContext context)
254247
{
255248
var invocationExpression = (InvocationExpressionSyntax)context.Node;
@@ -321,10 +314,10 @@ private static void HandleIndexerDeclaration(SyntaxNodeAnalysisContext context)
321314
}
322315
}
323316

324-
private static void HandleBaseMethodDeclaration(
325-
SyntaxNodeAnalysisContext context,
326-
BaseMethodDeclarationSyntax baseMethodDeclarationSyntax)
317+
private static void HandleBaseMethodDeclaration(SyntaxNodeAnalysisContext context)
327318
{
319+
var baseMethodDeclarationSyntax = (BaseMethodDeclarationSyntax)context.Node;
320+
328321
if (baseMethodDeclarationSyntax.ParameterList == null ||
329322
baseMethodDeclarationSyntax.ParameterList.IsMissing ||
330323
!baseMethodDeclarationSyntax.ParameterList.Parameters.Any())

0 commit comments

Comments
 (0)