Skip to content

Commit 25c039e

Browse files
authored
Merge pull request #2624 from sharwell/consecutive-usings
Update SA1503 to consider the allowConsecutiveUsings option
2 parents ebcbf1e + 2b5eb31 commit 25c039e

2 files changed

Lines changed: 116 additions & 2 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1503UnitTests.cs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ namespace StyleCop.Analyzers.Test.LayoutRules
1717
/// </summary>
1818
public class SA1503UnitTests : CodeFixVerifier
1919
{
20+
private bool suppressSA1519;
21+
private string consecutiveUsingsSettings;
22+
2023
/// <summary>
2124
/// Gets the statements that will be used in the theory test cases.
2225
/// </summary>
@@ -392,6 +395,101 @@ public void Bar(int i)
392395
await this.VerifyCSharpFixAsync(testCode, testCode).ConfigureAwait(false);
393396
}
394397

398+
[Theory]
399+
[InlineData(true)]
400+
[InlineData(false)]
401+
[WorkItem(2623, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2623")]
402+
public async Task TestMultipleUsingStatementsWithDefaultSettingsAsync(bool suppressSA1519)
403+
{
404+
this.suppressSA1519 = suppressSA1519;
405+
var testCode = @"using System;
406+
public class Foo
407+
{
408+
public void Bar(int i)
409+
{
410+
using (default(IDisposable))
411+
using (default(IDisposable))
412+
{
413+
}
414+
}
415+
}";
416+
417+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
418+
}
419+
420+
[Theory]
421+
[InlineData(true, true, false)]
422+
[InlineData(true, false, false)]
423+
[InlineData(false, true, true)]
424+
[InlineData(false, false, false)]
425+
[WorkItem(2623, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2623")]
426+
public async Task TestMultipleUsingStatementsWithExplicitSettingAsync(bool allowConsecutiveUsings, bool suppressSA1519, bool expectDiagnostic)
427+
{
428+
this.consecutiveUsingsSettings = $@"
429+
{{
430+
""settings"": {{
431+
""layoutRules"": {{
432+
""allowConsecutiveUsings"": {(allowConsecutiveUsings ? "true" : "false")}
433+
}}
434+
}}
435+
}}
436+
";
437+
this.suppressSA1519 = suppressSA1519;
438+
439+
var testCode = @"using System;
440+
public class Foo
441+
{
442+
public void Bar(int i)
443+
{
444+
using (default(IDisposable))
445+
using (default(IDisposable))
446+
{
447+
}
448+
}
449+
}";
450+
var fixedCode = @"using System;
451+
public class Foo
452+
{
453+
public void Bar(int i)
454+
{
455+
using (default(IDisposable))
456+
{
457+
using (default(IDisposable))
458+
{
459+
}
460+
}
461+
}
462+
}";
463+
464+
if (expectDiagnostic)
465+
{
466+
var expected = this.CSharpDiagnostic().WithLocation(7, 9);
467+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
468+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
469+
await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
470+
}
471+
else
472+
{
473+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
474+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
475+
}
476+
}
477+
478+
/// <inheritdoc/>
479+
protected override string GetSettings()
480+
{
481+
return this.consecutiveUsingsSettings ?? base.GetSettings();
482+
}
483+
484+
/// <inheritdoc/>
485+
protected override IEnumerable<string> GetDisabledDiagnostics()
486+
{
487+
if (this.suppressSA1519)
488+
{
489+
yield return SA1519BracesMustNotBeOmittedFromMultiLineChildStatement.DiagnosticId;
490+
}
491+
}
492+
395493
/// <inheritdoc/>
396494
protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
397495
{

StyleCop.Analyzers/StyleCop.Analyzers/LayoutRules/SA1503BracesMustNotBeOmitted.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
namespace StyleCop.Analyzers.LayoutRules
55
{
6+
using System;
67
using System.Collections.Generic;
78
using System.Collections.Immutable;
89
using System.Linq;
@@ -11,6 +12,7 @@ namespace StyleCop.Analyzers.LayoutRules
1112
using Microsoft.CodeAnalysis.CSharp.Syntax;
1213
using Microsoft.CodeAnalysis.Diagnostics;
1314
using StyleCop.Analyzers.Helpers;
15+
using StyleCop.Analyzers.Settings.ObjectModel;
1416

1517
/// <summary>
1618
/// The opening and closing braces for a C# statement have been omitted.
@@ -67,6 +69,9 @@ internal class SA1503BracesMustNotBeOmitted : DiagnosticAnalyzer
6769
private static readonly DiagnosticDescriptor Descriptor =
6870
new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, AnalyzerCategory.LayoutRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description, HelpLink);
6971

72+
private static readonly Action<SyntaxNodeAnalysisContext> IfStatementAction = HandleIfStatement;
73+
private static readonly Action<SyntaxNodeAnalysisContext, StyleCopSettings> UsingStatementAction = HandleUsingStatement;
74+
7075
/// <inheritdoc/>
7176
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
7277
ImmutableArray.Create(Descriptor);
@@ -77,13 +82,13 @@ public override void Initialize(AnalysisContext context)
7782
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
7883
context.EnableConcurrentExecution();
7984

80-
context.RegisterSyntaxNodeAction(HandleIfStatement, SyntaxKind.IfStatement);
85+
context.RegisterSyntaxNodeAction(IfStatementAction, SyntaxKind.IfStatement);
8186
context.RegisterSyntaxNodeAction(ctx => CheckChildStatement(ctx, ((DoStatementSyntax)ctx.Node).Statement), SyntaxKind.DoStatement);
8287
context.RegisterSyntaxNodeAction(ctx => CheckChildStatement(ctx, ((WhileStatementSyntax)ctx.Node).Statement), SyntaxKind.WhileStatement);
8388
context.RegisterSyntaxNodeAction(ctx => CheckChildStatement(ctx, ((ForStatementSyntax)ctx.Node).Statement), SyntaxKind.ForStatement);
8489
context.RegisterSyntaxNodeAction(ctx => CheckChildStatement(ctx, ((ForEachStatementSyntax)ctx.Node).Statement), SyntaxKind.ForEachStatement);
8590
context.RegisterSyntaxNodeAction(ctx => CheckChildStatement(ctx, ((FixedStatementSyntax)ctx.Node).Statement), SyntaxKind.FixedStatement);
86-
context.RegisterSyntaxNodeAction(ctx => CheckChildStatement(ctx, ((UsingStatementSyntax)ctx.Node).Statement), SyntaxKind.UsingStatement);
91+
context.RegisterSyntaxNodeAction(UsingStatementAction, SyntaxKind.UsingStatement);
8792
context.RegisterSyntaxNodeAction(ctx => CheckChildStatement(ctx, ((LockStatementSyntax)ctx.Node).Statement), SyntaxKind.LockStatement);
8893
}
8994

@@ -121,6 +126,17 @@ private static void HandleIfStatement(SyntaxNodeAnalysisContext context)
121126
}
122127
}
123128

129+
private static void HandleUsingStatement(SyntaxNodeAnalysisContext context, StyleCopSettings settings)
130+
{
131+
var usingStatement = (UsingStatementSyntax)context.Node;
132+
if (settings.LayoutRules.AllowConsecutiveUsings && usingStatement.Statement.IsKind(SyntaxKind.UsingStatement))
133+
{
134+
return;
135+
}
136+
137+
CheckChildStatement(context, usingStatement.Statement);
138+
}
139+
124140
private static void CheckChildStatement(SyntaxNodeAnalysisContext context, StatementSyntax childStatement)
125141
{
126142
if (childStatement is BlockSyntax)

0 commit comments

Comments
 (0)