diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/Helpers/IndentationHelper.cs b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/Helpers/IndentationHelper.cs index 5df50b460..a9717335b 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/Helpers/IndentationHelper.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/Helpers/IndentationHelper.cs @@ -3,6 +3,7 @@ namespace StyleCop.Analyzers.Helpers { + using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Settings.ObjectModel; @@ -65,7 +66,7 @@ public static int GetIndentationSteps(IndentationSettings indentationSettings, S /// The indentation settings to use. /// The number of indentation steps. /// A string containing the amount of whitespace needed for the given indentation steps. - public static string GenerateIndentationString(IndentationSettings indentationSettings, int indentationSteps) + public static string GenerateIndentationStringForSteps(IndentationSettings indentationSettings, int indentationSteps) { string result; var indentationCount = indentationSteps * indentationSettings.IndentationSize; @@ -83,6 +84,42 @@ public static string GenerateIndentationString(IndentationSettings indentationSe return result; } + /// + /// Generate a new indentation string for the given indentation width. + /// + /// The indentation settings to use. + /// The width of the indentation string. + /// A string containing the whitespace needed to indent code to the specified width. + public static string GenerateIndentationString(IndentationSettings indentationSettings, int indentationWidth) => + GenerateIndentationString(indentationSettings, indentationWidth, 0); + + /// + /// Generate a new indentation string for the given indentation width. + /// + /// The indentation settings to use. + /// The width of the indentation string. + /// The starting column for the indentation. + /// A string containing the whitespace needed to indent code to the specified width. + public static string GenerateIndentationString(IndentationSettings indentationSettings, int indentationWidth, int startColumn) + { + if (!indentationSettings.UseTabs) + { + return new string(' ', indentationWidth); + } + + // Adjust the indentation width so a narrower first tab doesn't affect the outcome + indentationWidth += startColumn % indentationSettings.TabSize; + + int tabCount = indentationWidth / indentationSettings.TabSize; + int spaceCount = indentationWidth - (tabCount * indentationSettings.TabSize); + + StringBuilder builder = StringBuilderPool.Allocate(); + builder.EnsureCapacity(tabCount + spaceCount); + builder.Append('\t', tabCount); + builder.Append(' ', spaceCount); + return StringBuilderPool.ReturnAndFree(builder); + } + /// /// Generates a whitespace trivia with the requested indentation. /// @@ -91,7 +128,7 @@ public static string GenerateIndentationString(IndentationSettings indentationSe /// A containing the indentation whitespace. public static SyntaxTrivia GenerateWhitespaceTrivia(IndentationSettings indentationSettings, int indentationSteps) { - return SyntaxFactory.Whitespace(GenerateIndentationString(indentationSettings, indentationSteps)); + return SyntaxFactory.Whitespace(GenerateIndentationStringForSteps(indentationSettings, indentationSteps)); } private static int GetIndentationSteps(IndentationSettings indentationSettings, SyntaxTree syntaxTree, SyntaxTriviaList leadingTrivia) diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/LayoutRules/SA1501CodeFixProvider.cs b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/LayoutRules/SA1501CodeFixProvider.cs index 3fc40f955..df5b1df0a 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/LayoutRules/SA1501CodeFixProvider.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/LayoutRules/SA1501CodeFixProvider.cs @@ -135,7 +135,7 @@ private static SyntaxNode ReformatStatementAndParent(Document document, Indentat if (nextTokenLine == statementCloseLine) { var parentIndentationLevel = IndentationHelper.GetIndentationSteps(indentationSettings, GetStatementParent(statement.Parent)); - var indentationString = IndentationHelper.GenerateIndentationString(indentationSettings, parentIndentationLevel); + var indentationString = IndentationHelper.GenerateIndentationStringForSteps(indentationSettings, parentIndentationLevel); newParentNextToken = newParentNextToken.WithLeadingTrivia(SyntaxFactory.Whitespace(indentationString)); } @@ -179,8 +179,8 @@ private static BlockSyntax ReformatBlock(Document document, IndentationSettings break; } - var indentationString = IndentationHelper.GenerateIndentationString(indentationSettings, parentIndentationLevel); - var statementIndentationString = IndentationHelper.GenerateIndentationString(indentationSettings, parentIndentationLevel + 1); + var indentationString = IndentationHelper.GenerateIndentationStringForSteps(indentationSettings, parentIndentationLevel); + var statementIndentationString = IndentationHelper.GenerateIndentationStringForSteps(indentationSettings, parentIndentationLevel + 1); var newOpenBraceLeadingTrivia = block.OpenBraceToken.LeadingTrivia .WithoutTrailingWhitespace() @@ -258,7 +258,7 @@ private static StatementSyntax ReformatStatement(Document document, IndentationS break; } - var statementIndentationString = IndentationHelper.GenerateIndentationString(indentationSettings, parentIndentationLevel + 1); + var statementIndentationString = IndentationHelper.GenerateIndentationStringForSteps(indentationSettings, parentIndentationLevel + 1); var newFirstTokenLeadingTrivia = statement.GetFirstToken().LeadingTrivia .WithoutTrailingWhitespace() diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/LayoutRules/SA1502CodeFixProvider.cs b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/LayoutRules/SA1502CodeFixProvider.cs index 699963de4..ef1f6e1f6 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/LayoutRules/SA1502CodeFixProvider.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/LayoutRules/SA1502CodeFixProvider.cs @@ -132,8 +132,8 @@ private SyntaxNode ReformatElement(SyntaxNode syntaxRoot, SyntaxNode element, Sy } var parentIndentationLevel = IndentationHelper.GetIndentationSteps(indentationSettings, element); - var indentationString = IndentationHelper.GenerateIndentationString(indentationSettings, parentIndentationLevel); - var contentIndentationString = IndentationHelper.GenerateIndentationString(indentationSettings, parentIndentationLevel + 1); + var indentationString = IndentationHelper.GenerateIndentationStringForSteps(indentationSettings, parentIndentationLevel); + var contentIndentationString = IndentationHelper.GenerateIndentationStringForSteps(indentationSettings, parentIndentationLevel + 1); // reformat opening brace tokenSubstitutions.Add(openBraceToken, this.FormatBraceToken(openBraceToken, indentationString)); diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/ElementOrderCodeFixProvider.cs b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/ElementOrderCodeFixProvider.cs index 8edffa7ab..688a589e2 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/ElementOrderCodeFixProvider.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/ElementOrderCodeFixProvider.cs @@ -141,7 +141,7 @@ private static SyntaxNode MoveMember(SyntaxNode syntaxRoot, MemberDeclarationSyn if (!memberToMove.HasLeadingTrivia) { var targetIndentationLevel = IndentationHelper.GetIndentationSteps(indentationSettings, targetMember); - var indentationString = IndentationHelper.GenerateIndentationString(indentationSettings, targetIndentationLevel); + var indentationString = IndentationHelper.GenerateIndentationStringForSteps(indentationSettings, targetIndentationLevel); memberToMove = memberToMove.WithLeadingTrivia(SyntaxFactory.Whitespace(indentationString)); } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/UsingCodeFixProvider.cs b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/UsingCodeFixProvider.cs index 7f2457ed9..7817bae10 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/UsingCodeFixProvider.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/UsingCodeFixProvider.cs @@ -123,7 +123,7 @@ private static async Task GetTransformedDocumentAsync(Document documen { var rootNamespace = compilationUnit.Members.OfType().First(); var indentationLevel = IndentationHelper.GetIndentationSteps(settings.Indentation, rootNamespace); - usingsIndentation = IndentationHelper.GenerateIndentationString(settings.Indentation, indentationLevel + 1); + usingsIndentation = IndentationHelper.GenerateIndentationStringForSteps(settings.Indentation, indentationLevel + 1); } else { @@ -203,7 +203,7 @@ private static void BuildReplaceMapForNamespaces(UsingsHelper usingsHelper, Dict indentationSteps++; } - var indentation = IndentationHelper.GenerateIndentationString(indentationSettings, indentationSteps); + var indentation = IndentationHelper.GenerateIndentationStringForSteps(indentationSettings, indentationSteps); var modifiedUsings = usingsHelper.GenerateGroupedUsings(usingList, indentation, false, qualifyNames); @@ -231,7 +231,7 @@ private static void BuildReplaceMapForConditionalDirectives(UsingsHelper usingsH indentationSteps++; } - var indentation = IndentationHelper.GenerateIndentationString(indentationSettings, indentationSteps); + var indentation = IndentationHelper.GenerateIndentationStringForSteps(indentationSettings, indentationSteps); var modifiedUsings = usingsHelper.GenerateGroupedUsings(childSpan, indentation, false, qualifyNames: false); diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/ReadabilityRules/IndentationCodeFixProvider.cs b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/ReadabilityRules/IndentationCodeFixProvider.cs new file mode 100644 index 000000000..4456da340 --- /dev/null +++ b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/ReadabilityRules/IndentationCodeFixProvider.cs @@ -0,0 +1,254 @@ +// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + +namespace StyleCop.Analyzers.ReadabilityRules +{ + using System.Collections.Generic; + using System.Collections.Immutable; + using System.Composition; + using System.Threading; + using System.Threading.Tasks; + using Helpers; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CodeActions; + using Microsoft.CodeAnalysis.CodeFixes; + using Microsoft.CodeAnalysis.CSharp; + using Microsoft.CodeAnalysis.Text; + using Settings.ObjectModel; + + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(IndentationCodeFixProvider))] + [Shared] + internal class IndentationCodeFixProvider : CodeFixProvider + { + /// + public override ImmutableArray FixableDiagnosticIds { get; } = + ImmutableArray.Create( + SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId, + SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId); + + /// + public sealed override FixAllProvider GetFixAllProvider() => + null; + + /// + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + foreach (var diagnostic in context.Diagnostics) + { + context.RegisterCodeFix( + CodeAction.Create( + ReadabilityResources.IndentationCodeFix, + cancellationToken => GetTransformedDocumentAsync(context.Document, diagnostic, cancellationToken), + nameof(IndentationCodeFixProvider)), + diagnostic); + } + + return SpecializedTasks.CompletedTask; + } + + private static async Task GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken) + { + var syntaxRoot = await document.GetSyntaxRootAsync().ConfigureAwait(false); + + StyleCopSettings settings = SettingsHelper.GetStyleCopSettings(document.Project.AnalyzerOptions, cancellationToken); + ImmutableArray textChanges = await GetTextChangesAsync(diagnostic, syntaxRoot, settings.Indentation, cancellationToken).ConfigureAwait(false); + if (textChanges.IsEmpty) + { + return document; + } + + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + return document.WithText(text.WithChanges(textChanges)); + } + + private static async Task> GetTextChangesAsync(Diagnostic diagnostic, SyntaxNode syntaxRoot, IndentationSettings indentationSettings, CancellationToken cancellationToken) + { + string replacement; + if (!diagnostic.Properties.TryGetValue(SA1137ElementsShouldHaveTheSameIndentation.ExpectedIndentationKey, out replacement)) + { + return ImmutableArray.Empty; + } + + SyntaxTrivia trivia = syntaxRoot.FindTrivia(diagnostic.Location.SourceSpan.Start); + SyntaxToken token = trivia != default(SyntaxTrivia) ? trivia.Token : syntaxRoot.FindToken(diagnostic.Location.SourceSpan.Start, findInsideTrivia: true); + SyntaxNode node = GetNodeForAdjustment(token); + + TextSpan originalSpan; + if (trivia == default(SyntaxTrivia)) + { + // The warning was reported on a token because the line is not indented + originalSpan = new TextSpan(diagnostic.Location.SourceSpan.Start, 0); + } + else + { + originalSpan = trivia.Span; + } + + FileLinePositionSpan fullSpan = syntaxRoot.SyntaxTree.GetLineSpan(node.FullSpan, cancellationToken); + if (fullSpan.StartLinePosition.Line == fullSpan.EndLinePosition.Line) + { + return ImmutableArray.Create(new TextChange(originalSpan, replacement)); + } + + SyntaxTree tree = node.SyntaxTree; + SourceText sourceText = await tree.GetTextAsync(cancellationToken).ConfigureAwait(false); + + int originalIndentation = GetIndentationWidth(indentationSettings, sourceText.ToString(originalSpan)); + int newIndentation = GetIndentationWidth(indentationSettings, replacement); + + ImmutableArray excludedSpans = SyntaxTreeHelpers.GetExcludedSpans(node); + TextLineCollection lines = sourceText.Lines; + + // For each line in the full span of the syntax node: + // 1. If the line is indented less than originalIndentation, ignore the line + // 2. If the indentation characters are not located within the full span, ignore the line + // 2. If the indentation characters of the line overlap with an excluded span, ignore the line + // 3. Replace the first original.Length characters on the line with replacement + ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(); + for (int i = fullSpan.StartLinePosition.Line; i <= fullSpan.EndLinePosition.Line; i++) + { + TextLine line = lines[i]; + string lineText = sourceText.ToString(line.Span); + + int indentationCount; + int indentationWidth = GetIndentationWidth(indentationSettings, lineText, out indentationCount); + if (indentationWidth < originalIndentation) + { + continue; + } + + if (indentationCount == line.Span.Length) + { + // The line is just whitespace + continue; + } + + TextSpan indentationSpan = new TextSpan(line.Start, indentationCount); + if (indentationSpan.Start >= node.FullSpan.End) + { + // The line does not contain any non-whitespace content which is part of the full span of the node + continue; + } + + if (!node.FullSpan.Contains(indentationSpan)) + { + // The indentation of the line is not part of the full span of the node + continue; + } + + if (IsExcluded(excludedSpans, indentationSpan)) + { + // The line indentation is partially- or fully-excluded from adjustments + continue; + } + + if (originalIndentation == indentationWidth) + { + builder.Add(new TextChange(indentationSpan, replacement)); + } + else if (newIndentation > originalIndentation) + { + // TODO: This needs to handle UseTabs setting + builder.Add(new TextChange(new TextSpan(indentationSpan.End, 0), new string(' ', newIndentation - originalIndentation))); + } + else if (newIndentation < originalIndentation) + { + builder.Add(new TextChange(indentationSpan, IndentationHelper.GenerateIndentationString(indentationSettings, indentationWidth + (newIndentation - originalIndentation)))); + } + } + + return builder.ToImmutable(); + } + + private static SyntaxNode GetNodeForAdjustment(SyntaxToken token) + { + return token.Parent; + } + + private static int GetIndentationWidth(IndentationSettings indentationSettings, string text) + { + int ignored; + return GetIndentationWidth(indentationSettings, text, out ignored); + } + + private static int GetIndentationWidth(IndentationSettings indentationSettings, string text, out int count) + { + int tabSize = indentationSettings.TabSize; + int indentationWidth = 0; + for (int i = 0; i < text.Length; i++) + { + switch (text[i]) + { + case ' ': + indentationWidth++; + break; + + case '\t': + indentationWidth = tabSize * ((indentationWidth / tabSize) + 1); + break; + + default: + count = i; + return indentationWidth; + } + } + + count = text.Length; + return indentationWidth; + } + + private static bool IsExcluded(ImmutableArray excludedSpans, TextSpan textSpan) + { + int index = excludedSpans.BinarySearch(textSpan); + if (index > 0) + { + return true; + } + + int nextLarger = ~index; + if (nextLarger > 0 && excludedSpans[nextLarger - 1].OverlapsWith(textSpan)) + { + return true; + } + + if (nextLarger < excludedSpans.Length - 1 && excludedSpans[nextLarger].OverlapsWith(textSpan)) + { + return true; + } + + return false; + } + + private class FixAll : DocumentBasedFixAllProvider + { + public static FixAllProvider Instance { get; } = + new FixAll(); + + protected override string CodeActionTitle => + ReadabilityResources.IndentationCodeFix; + + protected override async Task FixAllInDocumentAsync(FixAllContext fixAllContext, Document document, ImmutableArray diagnostics) + { + if (diagnostics.IsEmpty) + { + return null; + } + + var syntaxRoot = await document.GetSyntaxRootAsync().ConfigureAwait(false); + StyleCopSettings settings = SettingsHelper.GetStyleCopSettings(document.Project.AnalyzerOptions, fixAllContext.CancellationToken); + + List changes = new List(); + + foreach (var diagnostic in diagnostics) + { + changes.AddRange(await GetTextChangesAsync(diagnostic, syntaxRoot, settings.Indentation, fixAllContext.CancellationToken).ConfigureAwait(false)); + } + + changes.Sort((left, right) => left.Span.Start.CompareTo(right.Span.Start)); + + var text = await document.GetTextAsync().ConfigureAwait(false); + return await document.WithText(text.WithChanges(changes)).GetSyntaxRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false); + } + } + } +} diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/ReadabilityRules/SA1116CodeFixProvider.cs b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/ReadabilityRules/SA1116CodeFixProvider.cs index 2c4b8fb9b..3e941790b 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/ReadabilityRules/SA1116CodeFixProvider.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/ReadabilityRules/SA1116CodeFixProvider.cs @@ -74,7 +74,7 @@ private static async Task GetTransformedDocumentAsync(Document documen SyntaxTriviaList newTrivia = SyntaxFactory.TriviaList( SyntaxFactory.CarriageReturnLineFeed, - SyntaxFactory.Whitespace(lineText.Substring(0, indentLength) + IndentationHelper.GenerateIndentationString(settings.Indentation, 1))); + SyntaxFactory.Whitespace(lineText.Substring(0, indentLength) + IndentationHelper.GenerateIndentationStringForSteps(settings.Indentation, 1))); SyntaxToken updatedToken = originalToken.WithLeadingTrivia(originalToken.LeadingTrivia.AddRange(newTrivia)); SyntaxNode updatedRoot = root.ReplaceToken(originalToken, updatedToken); diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/ReadabilityRules/SA1127CodeFixProvider.cs b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/ReadabilityRules/SA1127CodeFixProvider.cs index 1eebf0589..f4dbc03dc 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/ReadabilityRules/SA1127CodeFixProvider.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/ReadabilityRules/SA1127CodeFixProvider.cs @@ -61,7 +61,7 @@ private static async Task GetTransformedDocumentAsync(Document documen var parentIndentation = GetParentIndentation(whereToken); var settings = SettingsHelper.GetStyleCopSettings(document.Project.AnalyzerOptions, cancellationToken); - var indentationTrivia = SyntaxFactory.Whitespace(parentIndentation + IndentationHelper.GenerateIndentationString(settings.Indentation, 1)); + var indentationTrivia = SyntaxFactory.Whitespace(parentIndentation + IndentationHelper.GenerateIndentationStringForSteps(settings.Indentation, 1)); var replaceMap = new Dictionary() { diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/StyleCop.Analyzers.CodeFixes.csproj b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/StyleCop.Analyzers.CodeFixes.csproj index 7f2277e47..27997424a 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/StyleCop.Analyzers.CodeFixes.csproj +++ b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/StyleCop.Analyzers.CodeFixes.csproj @@ -108,6 +108,7 @@ + diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1137UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1137UnitTests.cs new file mode 100644 index 000000000..6dfe7c278 --- /dev/null +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1137UnitTests.cs @@ -0,0 +1,1967 @@ +// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + +namespace StyleCop.Analyzers.Test.ReadabilityRules +{ + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.CodeAnalysis.CodeFixes; + using Microsoft.CodeAnalysis.Diagnostics; + using StyleCop.Analyzers.ReadabilityRules; + using TestHelper; + using Xunit; + + /// + /// This class contains unit tests for . + /// + public class SA1137UnitTests : CodeFixVerifier + { + [Theory] + [InlineData("class")] + [InlineData("struct")] + [InlineData("interface")] + [InlineData("enum")] + public async Task TestNamespaceDeclarationAsync(string baseTypeKind) + { + string testCode = $@" +using System; + +namespace Namespace0 +{{ + [My] [My] {baseTypeKind} TypeName {{ }} +}} + +namespace Namespace1 +{{ + [My] + [My] {baseTypeKind} TypeName {{ }} +}} + +namespace Namespace2 +{{ + [My] + [My] + {baseTypeKind} TypeName {{ }} +}} + +namespace Namespace3 +{{ + [My] + [My] + {baseTypeKind} TypeName {{ }} +}} + +namespace Namespace4 +{{ + {baseTypeKind} TypeName1 {{ }} + + [My] {baseTypeKind} TypeName2 {{ }} +}} + +namespace Namespace5 +{{ + {baseTypeKind} TypeName1 {{ }} + + [My] + [My] {baseTypeKind} TypeName2 {{ }} +}} + +namespace Namespace6 +{{ + {baseTypeKind} TypeName1 {{ }} + + [My] + [My] {baseTypeKind} TypeName2 {{ }} +}} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute {{ }} +"; + string fixedCode = $@" +using System; + +namespace Namespace0 +{{ + [My] [My] {baseTypeKind} TypeName {{ }} +}} + +namespace Namespace1 +{{ + [My] + [My] {baseTypeKind} TypeName {{ }} +}} + +namespace Namespace2 +{{ + [My] + [My] + {baseTypeKind} TypeName {{ }} +}} + +namespace Namespace3 +{{ + [My] + [My] + {baseTypeKind} TypeName {{ }} +}} + +namespace Namespace4 +{{ + {baseTypeKind} TypeName1 {{ }} + + [My] {baseTypeKind} TypeName2 {{ }} +}} + +namespace Namespace5 +{{ + {baseTypeKind} TypeName1 {{ }} + + [My] + [My] {baseTypeKind} TypeName2 {{ }} +}} + +namespace Namespace6 +{{ + {baseTypeKind} TypeName1 {{ }} + + [My] + [My] {baseTypeKind} TypeName2 {{ }} +}} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute {{ }} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(12, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(18, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(24, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(25, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(33, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(41, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(48, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Theory] + [InlineData("class")] + [InlineData("struct")] + [InlineData("interface")] + public async Task TestTypeDeclarationConstraintClausesAsync(string typeKind) + { + string testCode = $@" +{typeKind} NonGenericType +{{ +}} + +{typeKind} TypeWithoutConstraints +{{ +}} + +{typeKind} TypeWithOneConstraint + where T : new() +{{ +}} + +{typeKind} TypeWithMultipleConstraints1 where T1 : new() + where T2 : new() + where T3 : new() +{{ +}} + +{typeKind} TypeWithMultipleConstraints2 +where T1 : new() + where T2 : new() + where T3 : new() +{{ +}} +"; + string fixedCode = $@" +{typeKind} NonGenericType +{{ +}} + +{typeKind} TypeWithoutConstraints +{{ +}} + +{typeKind} TypeWithOneConstraint + where T : new() +{{ +}} + +{typeKind} TypeWithMultipleConstraints1 where T1 : new() + where T2 : new() + where T3 : new() +{{ +}} + +{typeKind} TypeWithMultipleConstraints2 +where T1 : new() +where T2 : new() +where T3 : new() +{{ +}} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(17, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(23, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(24, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Theory] + [InlineData("class", "int", " { }")] + [InlineData("struct", "int", " { }")] + [InlineData("interface", "event System.EventHandler", ";")] + public async Task TestTypeDeclarationMembersAsync(string typeKind, string fieldType, string methodBody) + { + string testCode = $@" +{typeKind} Container1 +{{ + {fieldType} X1; + int Y1 {{ get; }} +void Z1(){methodBody} +}} + +{typeKind} Container2 +{{ +{fieldType} X2; + int Y2 {{ get; }} + void Z2(){methodBody} +}} +"; + string fixedCode = $@" +{typeKind} Container1 +{{ + {fieldType} X1; + int Y1 {{ get; }} + void Z1(){methodBody} +}} + +{typeKind} Container2 +{{ +{fieldType} X2; +int Y2 {{ get; }} +void Z2(){methodBody} +}} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(5, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(12, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(13, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + /// + /// This test demonstrates the behavior of SA1137 and its code fix with respect to documentation comments. + /// Currently both operations ignore documentation comments, but in the future the implementation may be updated + /// to examine and correct them similarly to attribute lists. + /// + /// A representing the asynchronous unit test. + [Fact] + public async Task TestDocumentationCommentBehaviorAsync() + { + string testCode = @" +using System; +enum Enum1 +{ + /// + /// Summary. + /// + [My] + Element1, + + /// + /// Summary. + /// + Element2, +} + +enum Enum2 +{ + /// + /// Summary. + /// + [My] +Element1, + + /// + /// Summary. + /// + Element2, +} + +enum Enum3 +{ + /// + /// Summary. + /// + [My] Element1, + + /// + /// Summary. + /// + Element2, +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + string fixedCode = @" +using System; +enum Enum1 +{ + /// + /// Summary. + /// + [My] + Element1, + + /// + /// Summary. + /// + Element2, +} + +enum Enum2 +{ +/// +/// Summary. +/// +[My] +Element1, + +/// +/// Summary. +/// +Element2, +} + +enum Enum3 +{ + /// + /// Summary. + /// + [My] Element1, + + /// + /// Summary. + /// + Element2, +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(14, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(22, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(28, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(36, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestEnumDeclarationAsync() + { + string testCode = @" +using System; +enum Enum1 +{ + [My] + Element1, + + Element2, +} + +enum Enum2 +{ + [My] +Element1, + + Element2, +} + +enum Enum3 +{ + [My] Element1, + + Element2, +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + string fixedCode = @" +using System; +enum Enum1 +{ + [My] + Element1, + + Element2, +} + +enum Enum2 +{ +[My] +Element1, + +Element2, +} + +enum Enum3 +{ + [My] Element1, + + Element2, +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(5, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(13, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(16, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(21, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestMethodDeclarationAsync() + { + string testCode = @" +class Container +{ + void NonGenericType() + { + } + + void TypeWithoutConstraints() + { + } + + void TypeWithOneConstraint() + where T : new() + { + } + + void TypeWithMultipleConstraints1() where T1 : new() + where T2 : new() + where T3 : new() + { + } + + void TypeWithMultipleConstraints2() + where T1 : new() + where T2 : new() + where T3 : new() + { + } +} +"; + string fixedCode = @" +class Container +{ + void NonGenericType() + { + } + + void TypeWithoutConstraints() + { + } + + void TypeWithOneConstraint() + where T : new() + { + } + + void TypeWithMultipleConstraints1() where T1 : new() + where T2 : new() + where T3 : new() + { + } + + void TypeWithMultipleConstraints2() + where T1 : new() + where T2 : new() + where T3 : new() + { + } +} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(19, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(25, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(26, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestPropertyAccessorListAsync() + { + string testCode = @" +using System; + +class Container +{ + int Property1 + { + [My] + get; + + set; + } + + int Property2 + { + [My] +get; + + set; + } + + int Property3 + { + [My] get; + + set; + } +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + string fixedCode = @" +using System; + +class Container +{ + int Property1 + { + [My] + get; + + set; + } + + int Property2 + { +[My] +get; + +set; + } + + int Property3 + { + [My] get; + + set; + } +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(16, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(19, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(24, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestIndexerAccessorListAsync() + { + string testCode = @" +using System; + +interface IContainer1 +{ + int this[int arg] + { + [My] + get; + + set; + } +} + +interface IContainer2 +{ + int this[int arg] + { + [My] +get; + + set; + } +} + +interface IContainer3 +{ + int this[int arg] + { + [My] get; + + set; + } +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + string fixedCode = @" +using System; + +interface IContainer1 +{ + int this[int arg] + { + [My] + get; + + set; + } +} + +interface IContainer2 +{ + int this[int arg] + { +[My] +get; + +set; + } +} + +interface IContainer3 +{ + int this[int arg] + { + [My] get; + + set; + } +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(19, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(22, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(30, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestEventAccessorListAsync() + { + string testCode = @" +using System; + +class Container +{ + event EventHandler Event1 + { + [My] + add { } + + remove { } + } + + event EventHandler Event2 + { + [My] +add { } + + remove { } + } + + event EventHandler Event3 + { + [My] add { } + + remove { } + } +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + string fixedCode = @" +using System; + +class Container +{ + event EventHandler Event1 + { + [My] + add { } + + remove { } + } + + event EventHandler Event2 + { +[My] +add { } + +remove { } + } + + event EventHandler Event3 + { + [My] add { } + + remove { } + } +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(16, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(19, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(24, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestValidVariableDeclarationAsync() + { + string testCode = @" +using System; +class Container +{ + private int T1; + private int + T2; + private int + T3, T4; + + private event EventHandler T5; + private event EventHandler + T6; + private event EventHandler + T7, T8; + + void MethodName() + { + int t1; + int + t2; + int + t3, t4; + } +} +"; + + await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestVariableDeclarationAsync() + { + string testCode = @" +using System; +class Container +{ + private int + X1, + Y1, +Z1; + + private int +X2, + Y2, + Z2; + + private event EventHandler + X3, + Y3, +Z3; + + private event EventHandler +X4, + Y4, + Z4; + + void MethodName() + { + int + X1, + Y1, +Z1; + + int +X2, + Y2, + Z2; + } +} +"; + string fixedCode = @" +using System; +class Container +{ + private int + X1, + Y1, + Z1; + + private int +X2, +Y2, +Z2; + + private event EventHandler + X3, + Y3, + Z3; + + private event EventHandler +X4, +Y4, +Z4; + + void MethodName() + { + int + X1, + Y1, + Z1; + + int +X2, +Y2, +Z2; + } +} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(7, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(12, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(13, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(17, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(18, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(22, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(23, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(29, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(30, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(34, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(35, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Theory] + [InlineData("class", " { }")] + [InlineData("void", "() { }")] + public async Task TestValidTypeParameterListAsync(string prefix, string suffix) + { + string testCode = $@" +class Container +{{ + {prefix} ClassName1{suffix} + + {prefix} ClassName2< + T>{suffix} + + {prefix} ClassName3< + T1, T2>{suffix} +}} +"; + + await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + } + + [Theory] + [InlineData("class", " { }")] + [InlineData("void", "() { }")] + public async Task TestTypeParameterListAsync(string prefix, string suffix) + { + string testCode = $@" +class Container +{{ + {prefix} NonZeroAlignment< + X, + Y, +Z>{suffix} + + {prefix} ZeroAlignment< +X, + Y, + Z>{suffix} +}} +"; + string fixedCode = $@" +class Container +{{ + {prefix} NonZeroAlignment< + X, + Y, + Z>{suffix} + + {prefix} ZeroAlignment< +X, +Y, +Z>{suffix} +}} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(7, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(12, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestParameterListAsync() + { + string testCode = @" +class Container +{ + void NonZeroAlignment( + int X, + int Y, +int Z) { } + + void ZeroAlignment( +int X, + int Y, + int Z) { } +} +"; + string fixedCode = @" +class Container +{ + void NonZeroAlignment( + int X, + int Y, + int Z) { } + + void ZeroAlignment( +int X, +int Y, +int Z) { } +} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(7, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(12, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestBracketedParameterListAsync() + { + string testCode = @" +class Container1 +{ + int this[ + int X, + int Y, +int Z] => 0; +} + +class Container2 +{ + int this[ +int X, + int Y, + int Z] => 0; +} +"; + string fixedCode = @" +class Container1 +{ + int this[ + int X, + int Y, + int Z] => 0; +} + +class Container2 +{ + int this[ +int X, +int Y, +int Z] => 0; +} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(7, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(14, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(15, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestArgumentListAsync() + { + string testCode = @" +class Container +{ + int NonZeroAlignment(int x, int y, int z) => NonZeroAlignment( + 0, + 0, +0); + + int ZeroAlignment(int x, int y, int z) => ZeroAlignment( +0, + 0, + 0); +} +"; + string fixedCode = @" +class Container +{ + int NonZeroAlignment(int x, int y, int z) => NonZeroAlignment( + 0, + 0, + 0); + + int ZeroAlignment(int x, int y, int z) => ZeroAlignment( +0, +0, +0); +} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(7, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(12, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestBracketedArgumentListAsync() + { + string testCode = @" +class Container1 +{ + int this[int x, int y, int z] => this[ + 0, + 0, +0]; +} + +class Container2 +{ + int this[int x, int y, int z] => this[ +0, + 0, + 0]; +} +"; + string fixedCode = @" +class Container1 +{ + int this[int x, int y, int z] => this[ + 0, + 0, + 0]; +} + +class Container2 +{ + int this[int x, int y, int z] => this[ +0, +0, +0]; +} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(7, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(14, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(15, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestAttributeListAsync() + { + string testCode = @" +using System; + +[My] +[ + My] +[ + My, My] +class TypeName1 +{ +} + +[ + My, + My, +My] +[ +My, + My, + My] +class TypeName2 +{ +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + string fixedCode = @" +using System; + +[My] +[ + My] +[ + My, My] +class TypeName1 +{ +} + +[ + My, + My, + My] +[ +My, +My, +My] +class TypeName2 +{ +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(15, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(16, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(19, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(20, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestAttributeArgumentListAsync() + { + string testCode = @" +using System; + +[My(0)] +[My( + 0)] +[My( + 0, Y = 2)] +class TypeName1 +{ +} + +[My( + 0, + Y = 2, +Z = 3)] +[My( +0, + Y = 2, + Z = 3)] +class TypeName2 +{ +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute +{ + public MyAttribute() { } + public MyAttribute(int value) { } + + public int Y { get; set; } + public int Z { get; set; } +} +"; + string fixedCode = @" +using System; + +[My(0)] +[My( + 0)] +[My( + 0, Y = 2)] +class TypeName1 +{ +} + +[My( + 0, + Y = 2, + Z = 3)] +[My( +0, +Y = 2, +Z = 3)] +class TypeName2 +{ +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute +{ + public MyAttribute() { } + public MyAttribute(int value) { } + + public int Y { get; set; } + public int Z { get; set; } +} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(15, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(16, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(19, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(20, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestBlockAsync() + { + string testCode = @" +class ClassName +{ + void MethodName() + { + label1: + if (true) + { + } + + label2: + while (true) + { + } + +label3: +while (true) + { +label4a: + label4b: +int x; + + label5a: +label5b: + int y; + } + } +} +"; + string fixedCode = @" +class ClassName +{ + void MethodName() + { + label1: + if (true) + { + } + + label2: + while (true) + { + } + + label3: + while (true) + { + label4a: + label4b: + int x; + + label5a: + label5b: + int y; + } + } +} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(12, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(13, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(14, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(16, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(17, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(18, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(20, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(23, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(25, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(26, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, numberOfFixAllIterations: 2, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestLeadingCommentAsync() + { + string testCode = @" +using System.Collections.Generic; +class ClassName +{ + void MethodName() + { + /* var x = */ new List(); + var y = new List(); + } +} +"; + + await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestSwitchStatementAsync() + { + string testCode = @" +class ClassName +{ + void MethodName() + { + switch (0) + { + case 0: + label1: + if (true) + { + } + + break; + + case 1: +case 2: + label2: + while (true) + { + } + + break; + +default: +label3a: + label3b: +break; + } + } +} +"; + string fixedCode = @" +class ClassName +{ + void MethodName() + { + switch (0) + { + case 0: + label1: + if (true) + { + } + + break; + + case 1: + case 2: + label2: + while (true) + { + } + + break; + + default: + label3a: + label3b: + break; + } + } +} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(14, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(16, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(17, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(18, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(19, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(20, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(21, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(23, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(25, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(26, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(27, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(28, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, numberOfFixAllIterations: 2, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestValidInitializerExpressionAsync() + { + string testCode = @" +using System.Collections.Generic; +class ClassName +{ + void EmptyInitializersMethod() + { + // array initializer + int[] array = { }; + + // collection initializer + List list = new List { }; + + // complex element initializer + Dictionary dictionary = new Dictionary { }; + + // object initializer + var obj = new StructName { }; + } + + void SingleLineInitializersMethod() + { + // array initializer + int[] array = { 0 }; + + // collection initializer + List list = new List { 0 }; + + // complex element initializer + Dictionary dictionary = new Dictionary { { 0, 0 } }; + + // object initializer + var obj = new StructName { X = 0 }; + } + + void SingleElementInitializersMethod() + { + // array initializer + int[] array = + { + 0, + }; + + // collection initializer + List list = + new List + { + 0, + }; + + // complex element initializer + Dictionary dictionary = + new Dictionary + { + { 0, 0 }, + }; + + // object initializer + var obj = + new StructName + { + X = 0, + }; + } + + void SharedLineInitializersMethod() + { + // array initializer + int[] array = + { + 0, 0, + }; + + // collection initializer + List list = + new List + { + 0, 0, + }; + + // complex element initializer + Dictionary dictionary = + new Dictionary + { + { 0, 0 }, { 0, 0 }, + }; + + // object initializer + var obj = + new StructName + { + X = 0, Y = 0, + }; + } +} + +struct StructName +{ + public int X, Y, Z; +} +"; + + await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestInitializerExpressionAsync() + { + string testCode = @" +using System.Collections.Generic; +class ClassName +{ + void NonZeroAlignmentMethod() + { + // array initializer + int[] array = + { + 0, + 0, +0, + }; + + // collection initializer + List list = + new List + { + 0, + 0, +0, + }; + + // complex element initializer + Dictionary dictionary = + new Dictionary + { + { 0, 0 }, + { 0, 0 }, +{ 0, 0 }, + }; + + // object initializer + var obj = + new StructName + { + X = 0, + Y = 0, +Z = 0, + }; + } + + void ZeroAlignmentMethod() + { + // array initializer + int[] array = + { +0, + 0, + 0, + }; + + // collection initializer + List list = + new List + { +0, + 0, + 0, + }; + + // complex element initializer + Dictionary dictionary = + new Dictionary + { +{ 0, 0 }, + { 0, 0 }, + { 0, 0 }, + }; + + // object initializer + var obj = + new StructName + { +X = 0, + Y = 0, + Z = 0, + }; + } +} + +struct StructName +{ + public int X, Y, Z; +} +"; + string fixedCode = @" +using System.Collections.Generic; +class ClassName +{ + void NonZeroAlignmentMethod() + { + // array initializer + int[] array = + { + 0, + 0, + 0, + }; + + // collection initializer + List list = + new List + { + 0, + 0, + 0, + }; + + // complex element initializer + Dictionary dictionary = + new Dictionary + { + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + }; + + // object initializer + var obj = + new StructName + { + X = 0, + Y = 0, + Z = 0, + }; + } + + void ZeroAlignmentMethod() + { + // array initializer + int[] array = + { +0, +0, +0, + }; + + // collection initializer + List list = + new List + { +0, +0, +0, + }; + + // complex element initializer + Dictionary dictionary = + new Dictionary + { +{ 0, 0 }, +{ 0, 0 }, +{ 0, 0 }, + }; + + // object initializer + var obj = + new StructName + { +X = 0, +Y = 0, +Z = 0, + }; + } +} + +struct StructName +{ + public int X, Y, Z; +} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(12, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(20, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(21, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(29, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(30, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(38, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(39, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(49, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(50, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(58, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(59, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(67, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(68, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(76, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(77, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestValidAnonymousObjectCreationExpressionAsync() + { + string testCode = @" +class ClassName +{ + void SingleLineInitializersMethod() + { + var obj = new { X = 0 }; + } + + void SingleElementInitializersMethod() + { + var obj = + new + { + X = 0, + }; + } + + void SharedLineInitializersMethod() + { + var obj = + new + { + X = 0, Y = 0, + }; + } +} +"; + + await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestAnonymousObjectCreationExpressionAsync() + { + string testCode = @" +class ClassName +{ + void NonZeroAlignmentMethod() + { + var obj = + new + { + X = 0, + Y = 0, +Z = 0, + }; + } + + void ZeroAlignmentMethod() + { + var obj = + new + { +X = 0, + Y = 0, + Z = 0, + }; + } +} +"; + string fixedCode = @" +class ClassName +{ + void NonZeroAlignmentMethod() + { + var obj = + new + { + X = 0, + Y = 0, + Z = 0, + }; + } + + void ZeroAlignmentMethod() + { + var obj = + new + { +X = 0, +Y = 0, +Z = 0, + }; + } +} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(10, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(21, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(22, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + /// + protected override IEnumerable GetDisabledDiagnostics() + { + yield return SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId; + } + + /// + protected override IEnumerable GetCSharpDiagnosticAnalyzers() + { + yield return new SA1137ElementsShouldHaveTheSameIndentation(); + } + + /// + protected override CodeFixProvider GetCSharpCodeFixProvider() + { + return new IndentationCodeFixProvider(); + } + } +} diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1138UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1138UnitTests.cs new file mode 100644 index 000000000..e87d2c2c4 --- /dev/null +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1138UnitTests.cs @@ -0,0 +1,2967 @@ +// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + +namespace StyleCop.Analyzers.Test.ReadabilityRules +{ + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.CodeAnalysis.CodeFixes; + using Microsoft.CodeAnalysis.Diagnostics; + using StyleCop.Analyzers.ReadabilityRules; + using TestHelper; + using Xunit; + + /// + /// This class contains unit tests for . + /// + /// + /// This set of tests is run with both SA1137 and SA1138 enabled, since they are intended to be used + /// together. + /// + public class SA1138UnitTests : CodeFixVerifier + { + protected string BlockStatementTestCode => @" +class ClassName +{ + void MethodName() + { + label1: + if (true) + { + } + + label2: + while (true) + { + } + +label3: +while (true) + { +label4a: + label4b: +int x; + + label5a: +label5b: + int y; + } + } +} +"; + + protected virtual string BlockStatementFixedCode => @" +class ClassName +{ + void MethodName() + { + label1: + if (true) + { + } + + label2: + while (true) + { + } + + label3: + while (true) + { + label4a: + label4b: + int x; + + label5a: + label5b: + int y; + } + } +} +"; + + protected virtual DiagnosticResult[] BlockStatementExpectedDiagnostics => + new[] + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(12, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(16, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(17, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(18, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(19, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(20, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(21, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(24, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(25, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(26, 1), + }; + + protected string SwitchStatementTestCode => @" +class ClassName +{ + void MethodName() + { + switch (0) + { + case 0: + label1: + if (true) + { + } + + break; + + case 1: +case 2: + label2: + while (true) + { + } + + break; + +default: +label3a: + label3b: +break; + } + } +} +"; + + protected virtual string SwitchStatementFixedCode => @" +class ClassName +{ + void MethodName() + { + switch (0) + { + case 0: + label1: + if (true) + { + } + + break; + + case 1: + case 2: + label2: + while (true) + { + } + + break; + + default: + label3a: + label3b: + break; + } + } +} +"; + + protected virtual DiagnosticResult[] SwitchStatementExpectedDiagnostics => + new[] + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(9, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(10, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(16, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(17, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(18, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(19, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(20, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(21, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(23, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(25, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(26, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(27, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(28, 1), + }; + + protected string InitializerExpressionTestCode => @" +using System.Collections.Generic; +class ClassName +{ + void NonZeroAlignmentMethod() + { + // array initializer + int[] array = + { + 0, + 0, +0, + }; + + // collection initializer + List list = + new List + { + 0, + 0, +0, + }; + + // complex element initializer + Dictionary dictionary = + new Dictionary + { + { 0, 0 }, + { 0, 0 }, +{ 0, 0 }, + }; + + // object initializer + var obj = + new StructName + { + X = 0, + Y = 0, +Z = 0, + }; + } + + void ZeroAlignmentMethod() + { + // array initializer + int[] array = + { +0, + 0, + 0, + }; + + // collection initializer + List list = + new List + { +0, + 0, + 0, + }; + + // complex element initializer + Dictionary dictionary = + new Dictionary + { +{ 0, 0 }, + { 0, 0 }, + { 0, 0 }, + }; + + // object initializer + var obj = + new StructName + { +X = 0, + Y = 0, + Z = 0, + }; + } +} + +struct StructName +{ + public int X, Y, Z; +} +"; + + protected virtual string InitializerExpressionFixedCode => @" +using System.Collections.Generic; +class ClassName +{ + void NonZeroAlignmentMethod() + { + // array initializer + int[] array = + { + 0, + 0, + 0, + }; + + // collection initializer + List list = + new List + { + 0, + 0, + 0, + }; + + // complex element initializer + Dictionary dictionary = + new Dictionary + { + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + }; + + // object initializer + var obj = + new StructName + { + X = 0, + Y = 0, + Z = 0, + }; + } + + void ZeroAlignmentMethod() + { + // array initializer + int[] array = + { +0, +0, +0, + }; + + // collection initializer + List list = + new List + { +0, +0, +0, + }; + + // complex element initializer + Dictionary dictionary = + new Dictionary + { +{ 0, 0 }, +{ 0, 0 }, +{ 0, 0 }, + }; + + // object initializer + var obj = + new StructName + { +X = 0, +Y = 0, +Z = 0, + }; + } +} + +struct StructName +{ + public int X, Y, Z; +} +"; + + protected virtual DiagnosticResult[] InitializerExpressionExpectedDiagnostics => + new[] + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(12, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(20, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(21, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(29, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(30, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(38, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(39, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(49, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(50, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(58, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(59, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(67, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(68, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(76, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(77, 1), + }; + + protected string AnonymousObjectCreationExpressionTestCode => @" +class ClassName +{ + void NonZeroAlignmentMethod() + { + var obj = + new + { + X = 0, + Y = 0, +Z = 0, + }; + } + + void ZeroAlignmentMethod() + { + var obj = + new + { +X = 0, + Y = 0, + Z = 0, + }; + } +} +"; + + protected virtual string AnonymousObjectCreationExpressionFixedCode => @" +class ClassName +{ + void NonZeroAlignmentMethod() + { + var obj = + new + { + X = 0, + Y = 0, + Z = 0, + }; + } + + void ZeroAlignmentMethod() + { + var obj = + new + { +X = 0, +Y = 0, +Z = 0, + }; + } +} +"; + + protected virtual DiagnosticResult[] AnonymousObjectCreationExpressionExpectedDiagnostics => + new[] + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(10, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(21, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(22, 1), + }; + + protected string DestructorDeclarationTestCode => @" +class ClassName +{ +~ClassName() +{ +return; +} +} +"; + + protected virtual string DestructorDeclarationFixedCode => @" +class ClassName +{ + ~ClassName() + { + return; + } +} +"; + + protected virtual DiagnosticResult[] DestructorDeclarationExpectedDiagnostics => + new[] + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(4, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(5, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(7, 1), + }; + + protected string OperatorDeclarationTestCode => @" +class ClassName +{ +public static bool operator !(ClassName obj) +{ +return false; +} +} +"; + + protected virtual string OperatorDeclarationFixedCode => @" +class ClassName +{ + public static bool operator !(ClassName obj) + { + return false; + } +} +"; + + protected virtual DiagnosticResult[] OperatorDeclarationExpectedDiagnostics => + new[] + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(4, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(5, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(7, 1), + }; + + protected string ConversionOperatorDeclarationTestCode => @" +class ClassName +{ +public static explicit operator bool(ClassName obj) +{ +return false; +} +} +"; + + protected virtual string ConversionOperatorDeclarationFixedCode => @" +class ClassName +{ + public static explicit operator bool(ClassName obj) + { + return false; + } +} +"; + + protected virtual DiagnosticResult[] ConversionOperatorExpectedDiagnostics => + new[] + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(4, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(5, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(7, 1), + }; + + protected string CheckedStatementTestCode => @" +class ClassName +{ + void MethodName() + { +checked +{ +int y = 3 + 2; +} + +unchecked +{ +int y = 3 + 2; +} + } +} +"; + + protected virtual string CheckedStatementFixedCode => @" +class ClassName +{ + void MethodName() + { + checked + { + int y = 3 + 2; + } + + unchecked + { + int y = 3 + 2; + } + } +} +"; + + protected virtual DiagnosticResult[] CheckedStatementExpectedDiagnostics => + new[] + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(7, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(9, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(12, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(13, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(14, 1), + }; + + protected string DoStatementTestCode => @" +class ClassName +{ + void MethodName() + { +do +{ +int y = 3 + 2; +} +while (true); + +do +return; +while (true); + +do +do +return; +while (true); +while (true); + } +} +"; + + protected virtual string DoStatementFixedCode => @" +class ClassName +{ + void MethodName() + { + do + { + int y = 3 + 2; + } + while (true); + + do + return; + while (true); + + do + do + return; + while (true); + while (true); + } +} +"; + + protected virtual DiagnosticResult[] DoStatementExpectedDiagnostics => + new[] + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(7, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(9, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(10, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(12, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(13, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(14, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(16, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(17, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(18, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(19, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(20, 1), + }; + + protected string FixedStatementTestCode => @" +class ClassName +{ + unsafe void MethodName() + { + int[] x = new int[1]; +fixed (int* p = &x[0]) +{ +int y = 3 + 2; +} + +fixed (int* p = &x[0]) +return; + +fixed (int* p = &x[0]) +fixed (int* q = &x[0]) +return; + } +} +"; + + protected virtual string FixedStatementFixedCode => @" +class ClassName +{ + unsafe void MethodName() + { + int[] x = new int[1]; + fixed (int* p = &x[0]) + { + int y = 3 + 2; + } + + fixed (int* p = &x[0]) + return; + + fixed (int* p = &x[0]) + fixed (int* q = &x[0]) + return; + } +} +"; + + protected virtual DiagnosticResult[] FixedStatementExpectedDiagnostics => + new[] + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(7, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(9, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(10, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(12, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(13, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(15, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(16, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(17, 1), + }; + + protected string ForEachStatementTestCode => @" +class ClassName +{ + void MethodName() + { +foreach (int x in new int[3]) +{ +int y = 3 + 2; +} + +foreach (int x in new int[3]) +return; + +foreach (int x in new int[3]) +foreach (int y in new int[3]) +return; + } +} +"; + + protected virtual string ForEachStatementFixedCode => @" +class ClassName +{ + void MethodName() + { + foreach (int x in new int[3]) + { + int y = 3 + 2; + } + + foreach (int x in new int[3]) + return; + + foreach (int x in new int[3]) + foreach (int y in new int[3]) + return; + } +} +"; + + protected virtual DiagnosticResult[] ForEachStatementExpectedDiagnostics => + new[] + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(7, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(9, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(12, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(14, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(15, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(16, 1), + }; + + protected string ForStatementTestCode => @" +class ClassName +{ + void MethodName() + { +for (int i = 0; i < 3; i++) +{ +int y = 3 + 2; +} + +for (int i = 0; i < 3; i++) +return; + +for (int i = 0; i < 3; i++) +for (int j = 0; j < 3; j++) +return; + } +} +"; + + protected virtual string ForStatementFixedCode => @" +class ClassName +{ + void MethodName() + { + for (int i = 0; i < 3; i++) + { + int y = 3 + 2; + } + + for (int i = 0; i < 3; i++) + return; + + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + return; + } +} +"; + + protected virtual DiagnosticResult[] ForStatementExpectedDiagnostics => + new[] + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(7, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(9, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(12, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(14, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(15, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(16, 1), + }; + + protected string IfStatementTestCode => @" +class ClassName +{ + void MethodName() + { +if (1 == 2) +{ +int y = 3 + 2; +} +else if (2 == 3) +{ +int y = 3 + 2; +} +else +{ +int y = 3 + 2; +} + +if (1 == 2) +return; +else if (2 == 3) +{ +int y = 3 + 2; +} +else +{ +int y = 3 + 2; +} + +if (1 == 2) +{ +int y = 3 + 2; +} +else if (2 == 3) +return; +else +{ +int y = 3 + 2; +} + +if (1 == 2) +{ +int y = 3 + 2; +} +else if (2 == 3) +{ +int y = 3 + 2; +} +else +return; + +if (1 == 2) +{ +int y = 3 + 2; +} +else +{ +if (2 == 3) +{ +int y = 3 + 2; +} +else +{ +return; +} +} + } +} +"; + + protected virtual string IfStatementFixedCode => @" +class ClassName +{ + void MethodName() + { + if (1 == 2) + { + int y = 3 + 2; + } + else if (2 == 3) + { + int y = 3 + 2; + } + else + { + int y = 3 + 2; + } + + if (1 == 2) + return; + else if (2 == 3) + { + int y = 3 + 2; + } + else + { + int y = 3 + 2; + } + + if (1 == 2) + { + int y = 3 + 2; + } + else if (2 == 3) + return; + else + { + int y = 3 + 2; + } + + if (1 == 2) + { + int y = 3 + 2; + } + else if (2 == 3) + { + int y = 3 + 2; + } + else + return; + + if (1 == 2) + { + int y = 3 + 2; + } + else + { + if (2 == 3) + { + int y = 3 + 2; + } + else + { + return; + } + } + } +} +"; + + protected virtual DiagnosticResult[] IfStatementExpectedDiagnostics => + new[] + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(7, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(9, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(10, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(12, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(13, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(14, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(15, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(16, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(17, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(19, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(20, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(21, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(22, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(23, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(24, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(25, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(26, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(27, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(28, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(30, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(31, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(32, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(33, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(34, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(35, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(36, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(37, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(38, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(39, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(41, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(42, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(43, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(44, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(45, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(46, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(47, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(48, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(49, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(50, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(52, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(53, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(54, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(55, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(56, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(57, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(58, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(59, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(60, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(61, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(62, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(63, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(64, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(65, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(66, 1), + }; + + protected string LockStatementTestCode => @" +class ClassName +{ + void MethodName() + { +lock (new object()) +{ +int y = 3 + 2; +} + +lock (new object()) +return; + +lock (new object()) +lock (new object()) +return; + } +} +"; + + protected virtual string LockStatementFixedCode => @" +class ClassName +{ + void MethodName() + { + lock (new object()) + { + int y = 3 + 2; + } + + lock (new object()) + return; + + lock (new object()) + lock (new object()) + return; + } +} +"; + + protected virtual DiagnosticResult[] LockStatementExpectedDiagnostics => + new[] + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(7, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(9, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(12, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(14, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(15, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(16, 1), + }; + + protected string UsingStatementTestCode => @" +using System; +class ClassName +{ + void MethodName() + { +using (default(IDisposable)) +{ +int y = 3 + 2; +} + +using (default(IDisposable)) +using (default(IDisposable)) +{ +int y = 3 + 2; +} + +using (default(IDisposable)) +{ +using (default(IDisposable)) +{ +int y = 3 + 2; +} +} + +using (default(IDisposable)) +return; + +using (default(IDisposable)) +using (default(IDisposable)) +return; + } +} +"; + + protected virtual string UsingStatementFixedCode => @" +using System; +class ClassName +{ + void MethodName() + { + using (default(IDisposable)) + { + int y = 3 + 2; + } + + using (default(IDisposable)) + using (default(IDisposable)) + { + int y = 3 + 2; + } + + using (default(IDisposable)) + { + using (default(IDisposable)) + { + int y = 3 + 2; + } + } + + using (default(IDisposable)) + return; + + using (default(IDisposable)) + using (default(IDisposable)) + return; + } +} +"; + + protected virtual DiagnosticResult[] UsingStatementExpectedDiagnostics => + new[] + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(7, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(9, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(10, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(12, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(13, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(14, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(15, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(16, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(18, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(19, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(20, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(21, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(22, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(23, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(24, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(26, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(27, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(29, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(30, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(31, 1), + }; + + protected string WhileStatementTestCode => @" +class ClassName +{ + void MethodName() + { +while (true) +{ +int y = 3 + 2; +} + +while (true) +return; + +while (true) +while (true) +return; + } +} +"; + + protected virtual string WhileStatementFixedCode => @" +class ClassName +{ + void MethodName() + { + while (true) + { + int y = 3 + 2; + } + + while (true) + return; + + while (true) + while (true) + return; + } +} +"; + + protected virtual DiagnosticResult[] WhileStatementExpectedDiagnostics => + new[] + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(7, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(9, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(12, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(14, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(15, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(16, 1), + }; + + [Theory] + [InlineData("class")] + [InlineData("struct")] + [InlineData("interface")] + [InlineData("enum")] + public async Task TestNamespaceDeclarationAsync(string baseTypeKind) + { + string testCode = $@" +using System; + +namespace Namespace0 +{{ + [My] [My] {baseTypeKind} TypeName {{ }} +}} + +namespace Namespace1 +{{ + [My] + [My] {baseTypeKind} TypeName {{ }} +}} + +namespace Namespace2 +{{ + [My] + [My] + {baseTypeKind} TypeName {{ }} +}} + +namespace Namespace3 +{{ + [My] + [My] + {baseTypeKind} TypeName {{ }} +}} + +namespace Namespace4 +{{ + {baseTypeKind} TypeName1 {{ }} + + [My] {baseTypeKind} TypeName2 {{ }} +}} + +namespace Namespace5 +{{ + {baseTypeKind} TypeName1 {{ }} + + [My] + [My] {baseTypeKind} TypeName2 {{ }} +}} + +namespace Namespace6 +{{ + {baseTypeKind} TypeName1 {{ }} + + [My] + [My] {baseTypeKind} TypeName2 {{ }} +}} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute {{ }} +"; + string fixedCode = $@" +using System; + +namespace Namespace0 +{{ + [My] [My] {baseTypeKind} TypeName {{ }} +}} + +namespace Namespace1 +{{ + [My] + [My] {baseTypeKind} TypeName {{ }} +}} + +namespace Namespace2 +{{ + [My] + [My] + {baseTypeKind} TypeName {{ }} +}} + +namespace Namespace3 +{{ + [My] + [My] + {baseTypeKind} TypeName {{ }} +}} + +namespace Namespace4 +{{ + {baseTypeKind} TypeName1 {{ }} + + [My] {baseTypeKind} TypeName2 {{ }} +}} + +namespace Namespace5 +{{ + {baseTypeKind} TypeName1 {{ }} + + [My] + [My] {baseTypeKind} TypeName2 {{ }} +}} + +namespace Namespace6 +{{ + {baseTypeKind} TypeName1 {{ }} + + [My] + [My] {baseTypeKind} TypeName2 {{ }} +}} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute {{ }} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(12, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(17, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(19, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(26, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(33, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(41, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(48, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Theory] + [InlineData("class")] + [InlineData("struct")] + [InlineData("interface")] + public async Task TestTypeDeclarationConstraintClausesAsync(string typeKind) + { + string testCode = $@" +{typeKind} NonGenericType +{{ +}} + +{typeKind} TypeWithoutConstraints +{{ +}} + +{typeKind} TypeWithOneConstraint + where T : new() +{{ +}} + +{typeKind} TypeWithMultipleConstraints1 where T1 : new() + where T2 : new() + where T3 : new() +{{ +}} + +{typeKind} TypeWithMultipleConstraints2 +where T1 : new() + where T2 : new() + where T3 : new() +{{ +}} +"; + string fixedCode = $@" +{typeKind} NonGenericType +{{ +}} + +{typeKind} TypeWithoutConstraints +{{ +}} + +{typeKind} TypeWithOneConstraint + where T : new() +{{ +}} + +{typeKind} TypeWithMultipleConstraints1 where T1 : new() + where T2 : new() + where T3 : new() +{{ +}} + +{typeKind} TypeWithMultipleConstraints2 + where T1 : new() + where T2 : new() + where T3 : new() +{{ +}} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(17, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(22, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(24, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Theory] + [InlineData("class", "int", " { }")] + [InlineData("struct", "int", " { }")] + [InlineData("interface", "event System.EventHandler", ";")] + public async Task TestTypeDeclarationMembersAsync(string typeKind, string fieldType, string methodBody) + { + string testCode = $@" +{typeKind} Container1 +{{ + {fieldType} X1; + int Y1 {{ get; }} +void Z1(){methodBody} +}} + +{typeKind} Container2 +{{ +{fieldType} X2; + int Y2 {{ get; }} + void Z2(){methodBody} +}} +"; + string fixedCode = $@" +{typeKind} Container1 +{{ + {fieldType} X1; + int Y1 {{ get; }} + void Z1(){methodBody} +}} + +{typeKind} Container2 +{{ + {fieldType} X2; + int Y2 {{ get; }} + void Z2(){methodBody} +}} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(4, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(5, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(12, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(13, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + /// + /// This test demonstrates the behavior of SA1137 and its code fix with respect to documentation comments. + /// Currently both operations ignore documentation comments, but in the future the implementation may be updated + /// to examine and correct them similarly to attribute lists. + /// + /// A representing the asynchronous unit test. + [Fact] + public async Task TestDocumentationCommentBehaviorAsync() + { + string testCode = @" +using System; +enum Enum1 +{ + /// + /// Summary. + /// + [My] + Element1, + + /// + /// Summary. + /// + Element2, +} + +enum Enum2 +{ + /// + /// Summary. + /// + [My] +Element1, + + /// + /// Summary. + /// + Element2, +} + +enum Enum3 +{ + /// + /// Summary. + /// + [My] Element1, + + /// + /// Summary. + /// + Element2, +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + string fixedCode = @" +using System; +enum Enum1 +{ + /// + /// Summary. + /// + [My] + Element1, + + /// + /// Summary. + /// + Element2, +} + +enum Enum2 +{ + /// + /// Summary. + /// + [My] + Element1, + + /// + /// Summary. + /// + Element2, +} + +enum Enum3 +{ + /// + /// Summary. + /// + [My] Element1, + + /// + /// Summary. + /// + Element2, +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(14, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(22, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(23, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(28, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(36, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(41, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestEnumDeclarationAsync() + { + string testCode = @" +using System; +enum Enum1 +{ + [My] + Element1, + + Element2, +} + +enum Enum2 +{ + [My] +Element1, + + Element2, +} + +enum Enum3 +{ + [My] Element1, + + Element2, +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + string fixedCode = @" +using System; +enum Enum1 +{ + [My] + Element1, + + Element2, +} + +enum Enum2 +{ + [My] + Element1, + + Element2, +} + +enum Enum3 +{ + [My] Element1, + + Element2, +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(5, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(13, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(14, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(16, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(21, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(23, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestMethodDeclarationAsync() + { + string testCode = @" +class Container +{ + void NonGenericType() + { + } + + void TypeWithoutConstraints() + { + } + + void TypeWithOneConstraint() + where T : new() + { + } + + void TypeWithMultipleConstraints1() where T1 : new() + where T2 : new() + where T3 : new() + { + } + + void TypeWithMultipleConstraints2() + where T1 : new() + where T2 : new() + where T3 : new() + { + } +} +"; + string fixedCode = @" +class Container +{ + void NonGenericType() + { + } + + void TypeWithoutConstraints() + { + } + + void TypeWithOneConstraint() + where T : new() + { + } + + void TypeWithMultipleConstraints1() where T1 : new() + where T2 : new() + where T3 : new() + { + } + + void TypeWithMultipleConstraints2() + where T1 : new() + where T2 : new() + where T3 : new() + { + } +} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(19, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(24, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(26, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestPropertyAccessorListAsync() + { + string testCode = @" +using System; + +class Container +{ + int Property1 + { + [My] + get; + + set; + } + + int Property2 + { + [My] +get; + + set; + } + + int Property3 + { + [My] get; + + set; + } +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + string fixedCode = @" +using System; + +class Container +{ + int Property1 + { + [My] + get; + + set; + } + + int Property2 + { + [My] + get; + + set; + } + + int Property3 + { + [My] get; + + set; + } +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(16, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(17, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(19, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(24, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(26, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestIndexerAccessorListAsync() + { + string testCode = @" +using System; + +interface IContainer1 +{ + int this[int arg] + { + [My] + get; + + set; + } +} + +interface IContainer2 +{ + int this[int arg] + { + [My] +get; + + set; + } +} + +interface IContainer3 +{ + int this[int arg] + { + [My] get; + + set; + } +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + string fixedCode = @" +using System; + +interface IContainer1 +{ + int this[int arg] + { + [My] + get; + + set; + } +} + +interface IContainer2 +{ + int this[int arg] + { + [My] + get; + + set; + } +} + +interface IContainer3 +{ + int this[int arg] + { + [My] get; + + set; + } +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(19, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(20, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(22, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(30, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(32, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestEventAccessorListAsync() + { + string testCode = @" +using System; + +class Container +{ + event EventHandler Event1 + { + [My] + add { } + + remove { } + } + + event EventHandler Event2 + { + [My] +add { } + + remove { } + } + + event EventHandler Event3 + { + [My] add { } + + remove { } + } +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + string fixedCode = @" +using System; + +class Container +{ + event EventHandler Event1 + { + [My] + add { } + + remove { } + } + + event EventHandler Event2 + { + [My] + add { } + + remove { } + } + + event EventHandler Event3 + { + [My] add { } + + remove { } + } +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(16, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(17, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(19, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(24, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(26, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestValidVariableDeclarationAsync() + { + string testCode = @" +using System; +class Container +{ + private int T1; + private int + T2; + private int + T3, T4; + + private event EventHandler T5; + private event EventHandler + T6; + private event EventHandler + T7, T8; + + void MethodName() + { + int t1; + int + t2; + int + t3, t4; + } +} +"; + + await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestVariableDeclarationAsync() + { + string testCode = @" +using System; +class Container +{ + private int + X1, + Y1, +Z1; + + private int +X2, + Y2, + Z2; + + private event EventHandler + X3, + Y3, +Z3; + + private event EventHandler +X4, + Y4, + Z4; + + void MethodName() + { + int + X1, + Y1, +Z1; + + int +X2, + Y2, + Z2; + } +} +"; + string fixedCode = @" +using System; +class Container +{ + private int + X1, + Y1, + Z1; + + private int +X2, +Y2, +Z2; + + private event EventHandler + X3, + Y3, + Z3; + + private event EventHandler +X4, +Y4, +Z4; + + void MethodName() + { + int + X1, + Y1, + Z1; + + int +X2, +Y2, +Z2; + } +} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(7, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(12, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(13, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(17, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(18, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(22, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(23, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(29, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(30, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(34, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(35, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Theory] + [InlineData("class", " { }")] + [InlineData("void", "() { }")] + public async Task TestValidTypeParameterListAsync(string prefix, string suffix) + { + string testCode = $@" +class Container +{{ + {prefix} ClassName1{suffix} + + {prefix} ClassName2< + T>{suffix} + + {prefix} ClassName3< + T1, T2>{suffix} +}} +"; + + await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + } + + [Theory] + [InlineData("class", " { }")] + [InlineData("void", "() { }")] + public async Task TestTypeParameterListAsync(string prefix, string suffix) + { + string testCode = $@" +class Container +{{ + {prefix} NonZeroAlignment< + X, + Y, +Z>{suffix} + + {prefix} ZeroAlignment< +X, + Y, + Z>{suffix} +}} +"; + string fixedCode = $@" +class Container +{{ + {prefix} NonZeroAlignment< + X, + Y, + Z>{suffix} + + {prefix} ZeroAlignment< + X, + Y, + Z>{suffix} +}} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(7, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(10, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(11, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestParameterListAsync() + { + string testCode = @" +class Container +{ + void NonZeroAlignment( + int X, + int Y, +int Z) { } + + void ZeroAlignment( +int X, + int Y, + int Z) { } +} +"; + string fixedCode = @" +class Container +{ + void NonZeroAlignment( + int X, + int Y, + int Z) { } + + void ZeroAlignment( + int X, + int Y, + int Z) { } +} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(7, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(10, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(11, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestBracketedParameterListAsync() + { + string testCode = @" +class Container1 +{ + int this[ + int X, + int Y, +int Z] => 0; +} + +class Container2 +{ + int this[ +int X, + int Y, + int Z] => 0; +} +"; + string fixedCode = @" +class Container1 +{ + int this[ + int X, + int Y, + int Z] => 0; +} + +class Container2 +{ + int this[ + int X, + int Y, + int Z] => 0; +} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(7, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(13, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(14, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestArgumentListAsync() + { + string testCode = @" +class Container +{ + int NonZeroAlignment(int x, int y, int z) => NonZeroAlignment( + 0, + 0, +0); + + int ZeroAlignment(int x, int y, int z) => ZeroAlignment( +0, + 0, + 0); +} +"; + string fixedCode = @" +class Container +{ + int NonZeroAlignment(int x, int y, int z) => NonZeroAlignment( + 0, + 0, + 0); + + int ZeroAlignment(int x, int y, int z) => ZeroAlignment( +0, +0, +0); +} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(7, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(12, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestBracketedArgumentListAsync() + { + string testCode = @" +class Container1 +{ + int this[int x, int y, int z] => this[ + 0, + 0, +0]; +} + +class Container2 +{ + int this[int x, int y, int z] => this[ +0, + 0, + 0]; +} +"; + string fixedCode = @" +class Container1 +{ + int this[int x, int y, int z] => this[ + 0, + 0, + 0]; +} + +class Container2 +{ + int this[int x, int y, int z] => this[ +0, +0, +0]; +} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(7, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(14, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(15, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestAssemblyAttributeListAsync() + { + string testCode = @" +using System; + +[assembly: My] +[assembly: + My] +[assembly: + My, My] + +[assembly: + My, + My, +My] +[assembly: +My, + My, + My] + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + string fixedCode = @" +using System; + +[assembly: My] +[assembly: + My] +[assembly: + My, My] + +[assembly: + My, + My, + My] +[assembly: + My, + My, + My] + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(11, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(12, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(13, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(15, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(16, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(17, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestAttributeListAsync() + { + string testCode = @" +using System; + +[My] +[ + My] +[ + My, My] +class TypeName1 +{ +} + +[ + My, + My, +My] +[ +My, + My, + My] +class TypeName2 +{ +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + string fixedCode = @" +using System; + +[My] +[ + My] +[ + My, My] +class TypeName1 +{ +} + +[ + My, + My, + My] +[ + My, + My, + My] +class TypeName2 +{ +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute { } +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(14, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(15, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(16, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(18, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(19, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(20, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestAttributeArgumentListAsync() + { + string testCode = @" +using System; + +[My(0)] +[My( + 0)] +[My( + 0, Y = 2)] +class TypeName1 +{ +} + +[My( + 0, + Y = 2, +Z = 3)] +[My( +0, + Y = 2, + Z = 3)] +class TypeName2 +{ +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute +{ + public MyAttribute() { } + public MyAttribute(int value) { } + + public int Y { get; set; } + public int Z { get; set; } +} +"; + string fixedCode = @" +using System; + +[My(0)] +[My( + 0)] +[My( + 0, Y = 2)] +class TypeName1 +{ +} + +[My( + 0, + Y = 2, + Z = 3)] +[My( +0, +Y = 2, +Z = 3)] +class TypeName2 +{ +} + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : Attribute +{ + public MyAttribute() { } + public MyAttribute(int value) { } + + public int Y { get; set; } + public int Z { get; set; } +} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(15, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(16, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(19, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1137DiagnosticId).WithLocation(20, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestBlockStatementAsync() + { + await this.VerifyCSharpDiagnosticAsync(this.BlockStatementTestCode, this.BlockStatementExpectedDiagnostics, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(this.BlockStatementFixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(this.BlockStatementTestCode, this.BlockStatementFixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestLeadingCommentAsync() + { + string testCode = @" +using System.Collections.Generic; +class ClassName +{ + void MethodName() + { + /* var x = */ new List(); + var y = new List(); + + if (true) + /* things */ { + } + } +} +"; + + await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestSwitchStatementAsync() + { + await this.VerifyCSharpDiagnosticAsync(this.SwitchStatementTestCode, this.SwitchStatementExpectedDiagnostics, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(this.SwitchStatementFixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(this.SwitchStatementTestCode, this.SwitchStatementFixedCode, numberOfFixAllIterations: 2, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestValidInitializerExpressionAsync() + { + string testCode = @" +using System.Collections.Generic; +class ClassName +{ + void EmptyInitializersMethod() + { + // array initializer + int[] array = { }; + + // collection initializer + List list = new List { }; + + // complex element initializer + Dictionary dictionary = new Dictionary { }; + + // object initializer + var obj = new StructName { }; + } + + void SingleLineInitializersMethod() + { + // array initializer + int[] array = { 0 }; + + // collection initializer + List list = new List { 0 }; + + // complex element initializer + Dictionary dictionary = new Dictionary { { 0, 0 } }; + + // object initializer + var obj = new StructName { X = 0 }; + } + + void SingleElementInitializersMethod() + { + // array initializer + int[] array = + { + 0, + }; + + // collection initializer + List list = + new List + { + 0, + }; + + // complex element initializer + Dictionary dictionary = + new Dictionary + { + { 0, 0 }, + }; + + // object initializer + var obj = + new StructName + { + X = 0, + }; + } + + void SharedLineInitializersMethod() + { + // array initializer + int[] array = + { + 0, 0, + }; + + // collection initializer + List list = + new List + { + 0, 0, + }; + + // complex element initializer + Dictionary dictionary = + new Dictionary + { + { 0, 0 }, { 0, 0 }, + }; + + // object initializer + var obj = + new StructName + { + X = 0, Y = 0, + }; + } +} + +struct StructName +{ + public int X, Y, Z; +} +"; + + await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestInitializerExpressionAsync() + { + await this.VerifyCSharpDiagnosticAsync(this.InitializerExpressionTestCode, this.InitializerExpressionExpectedDiagnostics, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(this.InitializerExpressionFixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(this.InitializerExpressionTestCode, this.InitializerExpressionFixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestValidAnonymousObjectCreationExpressionAsync() + { + string testCode = @" +class ClassName +{ + void SingleLineInitializersMethod() + { + var obj = new { X = 0 }; + } + + void SingleElementInitializersMethod() + { + var obj = + new + { + X = 0, + }; + } + + void SharedLineInitializersMethod() + { + var obj = + new + { + X = 0, Y = 0, + }; + } +} +"; + + await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestAnonymousObjectCreationExpressionAsync() + { + await this.VerifyCSharpDiagnosticAsync(this.AnonymousObjectCreationExpressionTestCode, this.AnonymousObjectCreationExpressionExpectedDiagnostics, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(this.AnonymousObjectCreationExpressionFixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(this.AnonymousObjectCreationExpressionTestCode, this.AnonymousObjectCreationExpressionFixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestDestructorDeclarationAsync() + { + await this.VerifyCSharpDiagnosticAsync(this.DestructorDeclarationTestCode, this.DestructorDeclarationExpectedDiagnostics, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(this.DestructorDeclarationFixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(this.DestructorDeclarationTestCode, this.DestructorDeclarationFixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestOperatorDeclarationAsync() + { + await this.VerifyCSharpDiagnosticAsync(this.OperatorDeclarationTestCode, this.OperatorDeclarationExpectedDiagnostics, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(this.OperatorDeclarationFixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(this.OperatorDeclarationTestCode, this.OperatorDeclarationFixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestConversionOperatorDeclarationAsync() + { + await this.VerifyCSharpDiagnosticAsync(this.ConversionOperatorDeclarationTestCode, this.ConversionOperatorExpectedDiagnostics, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(this.ConversionOperatorDeclarationFixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(this.ConversionOperatorDeclarationTestCode, this.ConversionOperatorDeclarationFixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestCheckedStatementAsync() + { + await this.VerifyCSharpDiagnosticAsync(this.CheckedStatementTestCode, this.CheckedStatementExpectedDiagnostics, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(this.CheckedStatementFixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(this.CheckedStatementTestCode, this.CheckedStatementFixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestDoStatementAsync() + { + await this.VerifyCSharpDiagnosticAsync(this.DoStatementTestCode, this.DoStatementExpectedDiagnostics, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(this.DoStatementFixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(this.DoStatementTestCode, this.DoStatementFixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestFixedStatementAsync() + { + await this.VerifyCSharpDiagnosticAsync(this.FixedStatementTestCode, this.FixedStatementExpectedDiagnostics, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(this.FixedStatementFixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(this.FixedStatementTestCode, this.FixedStatementFixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestForEachStatementAsync() + { + await this.VerifyCSharpDiagnosticAsync(this.ForEachStatementTestCode, this.ForEachStatementExpectedDiagnostics, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(this.ForEachStatementFixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(this.ForEachStatementTestCode, this.ForEachStatementFixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestForStatementAsync() + { + await this.VerifyCSharpDiagnosticAsync(this.ForStatementTestCode, this.ForStatementExpectedDiagnostics, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(this.ForStatementFixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(this.ForStatementTestCode, this.ForStatementFixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestIfStatementAsync() + { + await this.VerifyCSharpDiagnosticAsync(this.IfStatementTestCode, this.IfStatementExpectedDiagnostics, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(this.IfStatementFixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(this.IfStatementTestCode, this.IfStatementFixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestLockStatementAsync() + { + await this.VerifyCSharpDiagnosticAsync(this.LockStatementTestCode, this.LockStatementExpectedDiagnostics, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(this.LockStatementFixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(this.LockStatementTestCode, this.LockStatementFixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestUsingStatementAsync() + { + await this.VerifyCSharpDiagnosticAsync(this.UsingStatementTestCode, this.UsingStatementExpectedDiagnostics, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(this.UsingStatementFixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(this.UsingStatementTestCode, this.UsingStatementFixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestWhileStatementAsync() + { + await this.VerifyCSharpDiagnosticAsync(this.WhileStatementTestCode, this.WhileStatementExpectedDiagnostics, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(this.WhileStatementFixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(this.WhileStatementTestCode, this.WhileStatementFixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + public async Task TestNestedBlockStatementAsync() + { + string testCode = @" +class ClassName +{ + void MethodName() + { +{ +{ +int y = 3 + 2; +} +} + } +} +"; + string fixedCode = @" +class ClassName +{ + void MethodName() + { + { + { + int y = 3 + 2; + } + } + } +} +"; + + DiagnosticResult[] expected = + { + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(6, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(7, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(8, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(9, 1), + this.CSharpDiagnostic(SA1137ElementsShouldHaveTheSameIndentation.SA1138DiagnosticId).WithLocation(10, 1), + }; + + await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); + } + + /// + protected override IEnumerable GetCSharpDiagnosticAnalyzers() + { + yield return new SA1137ElementsShouldHaveTheSameIndentation(); + } + + /// + protected override CodeFixProvider GetCSharpCodeFixProvider() + { + return new IndentationCodeFixProvider(); + } + } +} diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/StyleCop.Analyzers.Test.csproj b/StyleCop.Analyzers/StyleCop.Analyzers.Test/StyleCop.Analyzers.Test.csproj index 67918296c..19c033bc4 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/StyleCop.Analyzers.Test.csproj +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/StyleCop.Analyzers.Test.csproj @@ -326,6 +326,8 @@ + + diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/AnalyzerExtensions.cs b/StyleCop.Analyzers/StyleCop.Analyzers/AnalyzerExtensions.cs index 783354a2a..87d17e689 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers/AnalyzerExtensions.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers/AnalyzerExtensions.cs @@ -95,6 +95,43 @@ public static void RegisterSyntaxTreeActionHonorExclusions(this CompilationStart }); } + /// + /// Register an action to be executed at completion of parsing of a code document. A syntax tree action reports + /// diagnostics about the of a document. + /// + /// This method honors exclusions. + /// The analysis context. + /// Action to be executed at completion of parsing of a document. + public static void RegisterSyntaxTreeActionHonorExclusions(this CompilationStartAnalysisContext context, Action action) + { + Compilation compilation = context.Compilation; + ConcurrentDictionary cache = GetOrCreateGeneratedDocumentCache(compilation); + StrongBox settingsCache = GetOrCreateStyleCopSettingsCache(compilation); + + context.RegisterSyntaxTreeAction( + c => + { + if (c.IsGeneratedDocument(cache)) + { + return; + } + + // Honor the containing document item's ExcludeFromStylecop=True + // MSBuild metadata, if analyzers have access to it. + //// TODO: code here + + StyleCopSettings settings = settingsCache.Value; + if (settings == null) + { + StyleCopSettings updatedSettings = SettingsHelper.GetStyleCopSettings(c.Options, c.CancellationToken); + StyleCopSettings previous = Interlocked.CompareExchange(ref settingsCache.Value, updatedSettings, null); + settings = previous ?? updatedSettings; + } + + action(c, compilation, settings); + }); + } + /// /// Gets or creates a cache which can be used with methods to /// efficiently determine whether or not a source file is considered generated. diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/SyntaxTreeHelpers.cs b/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/SyntaxTreeHelpers.cs index c9e509682..ceff1c9d7 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/SyntaxTreeHelpers.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/SyntaxTreeHelpers.cs @@ -5,11 +5,13 @@ namespace StyleCop.Analyzers.Helpers { using System; using System.Collections.Concurrent; + using System.Collections.Immutable; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; + using Microsoft.CodeAnalysis.Text; internal static class SyntaxTreeHelpers { @@ -69,6 +71,118 @@ internal static bool ContainsUsingAlias(this SyntaxTree tree, ConcurrentDictiona return generated; } + /// + /// Locates all spans within the of a syntax node which should be excluded + /// from formatting operations. + /// + /// + /// Spans are excluded from formatting operations any time they have a direct impact on the semantics of + /// the compiled application. Spans are also excluded in cases where the impact is unknown. + /// + /// Spans disabled due to preprocessor directives (). + /// The contents of commented code (comments starting with ////). + /// Content in a CDATA section of an XML comment (). + /// Content in a character literal (). + /// Content in a string literal (). + /// Literal content in an interpolated string literal (). + /// + /// + /// The syntax node to examine. + /// + /// A collection of instances indicating the spans to exclude from formatting operations. + /// The collection is ordered according to the start position of spans, and does not contain any overlapping or + /// adjacent spans. + /// + internal static ImmutableArray GetExcludedSpans(SyntaxNode root) + { + ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(); + + // Locate disabled text + foreach (var trivia in root.DescendantTrivia(descendIntoTrivia: true)) + { + if (trivia.IsKind(SyntaxKind.DisabledTextTrivia)) + { + builder.Add(trivia.Span); + } + else if (trivia.IsKind(SyntaxKind.SingleLineCommentTrivia)) + { + if (trivia.ToString().StartsWith("////")) + { + // Exclude comments starting with //// because they could contain commented code which contains + // string or character literals, and we don't want to change the contents of those strings. + builder.Add(trivia.Span); + } + } + } + + // Locate string literals + foreach (var token in root.DescendantTokens(descendIntoTrivia: true)) + { + switch (token.Kind()) + { + case SyntaxKind.XmlTextLiteralToken: + if (token.Parent.IsKind(SyntaxKind.XmlCDataSection)) + { + builder.Add(token.Span); + } + + break; + + case SyntaxKind.CharacterLiteralToken: + case SyntaxKind.StringLiteralToken: + case SyntaxKind.InterpolatedStringTextToken: + builder.Add(token.Span); + break; + + default: + break; + } + } + + // Sort the results + builder.Sort(); + + // Combine adjacent and overlapping spans + ReduceTextSpans(builder); + + return builder.ToImmutable(); + } + + private static void ReduceTextSpans(ImmutableArray.Builder sortedTextSpans) + { + if (sortedTextSpans.Count == 0) + { + return; + } + + int currentIndex = 0; + for (int nextIndex = 1; nextIndex < sortedTextSpans.Count; nextIndex++) + { + TextSpan current = sortedTextSpans[currentIndex]; + TextSpan next = sortedTextSpans[nextIndex]; + if (current.End < next.Start) + { + // Increment currentIndex this iteration + currentIndex++; + + // Only increment nextIndex this iteration if necessary to ensure nextIndex > currentIndex on the + // next iteration. At this point we already incremented currentIndex, but haven't incremented + // nextIndex. + if (currentIndex > nextIndex) + { + nextIndex--; + } + + continue; + } + + // Since sortedTextSpans is sorted, we already know current and next overlap + sortedTextSpans[currentIndex] = TextSpan.FromBounds(current.Start, next.End); + } + + sortedTextSpans.Count = currentIndex + 1; + } + private static bool ContainsUsingAliasNoCache(SyntaxTree tree) { var nodes = tree.GetRoot().DescendantNodes(node => node.IsKind(SyntaxKind.CompilationUnit) || node.IsKind(SyntaxKind.NamespaceDeclaration)); diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/TokenHelper.cs b/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/TokenHelper.cs index 44ca8547d..8aad5f521 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/TokenHelper.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/TokenHelper.cs @@ -15,18 +15,44 @@ internal static class TokenHelper /// Gets a value indicating whether the is first in line. /// /// The token to process. - /// true if token is first in line, otherwise false. - internal static bool IsFirstInLine(this SyntaxToken token) + /// to consider the token first-in-line even when + /// non-whitespace trivia precedes the token; otherwise, to only consider tokens first + /// in line if they are preceded solely by whitespace. + /// + /// if is first in line; otherwise, . + /// + internal static bool IsFirstInLine(this SyntaxToken token, bool allowNonWhitespaceTrivia = true) { var fullLineSpan = token.SyntaxTree.GetLineSpan(token.FullSpan); - if (token.SyntaxTree == null || fullLineSpan.StartLinePosition.Character == 0) + bool firstInLine; + if (fullLineSpan.StartLinePosition.Character == 0) { - return true; + firstInLine = true; + } + else + { + var tokenLineSpan = token.SyntaxTree.GetLineSpan(token.Span); + firstInLine = tokenLineSpan.StartLinePosition.Line != fullLineSpan.StartLinePosition.Line; } - var tokenLineSpan = token.SyntaxTree.GetLineSpan(token.Span); - return tokenLineSpan.StartLinePosition.Line != fullLineSpan.StartLinePosition.Line; + if (firstInLine && !allowNonWhitespaceTrivia) + { + foreach (var trivia in token.LeadingTrivia.Reverse()) + { + if (!trivia.IsKind(SyntaxKind.WhitespaceTrivia)) + { + if (!trivia.HasBuiltinEndLine()) + { + firstInLine = false; + } + + break; + } + } + } + + return firstInLine; } /// diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/ReadabilityResources.Designer.cs b/StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/ReadabilityResources.Designer.cs index b999bfc69..7da74e923 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/ReadabilityResources.Designer.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/ReadabilityResources.Designer.cs @@ -61,6 +61,15 @@ internal ReadabilityResources() { } } + /// + /// Looks up a localized string similar to Fix indentation. + /// + internal static string IndentationCodeFix { + get { + return ResourceManager.GetString("IndentationCodeFix", resourceCulture); + } + } + /// /// Looks up a localized string similar to Remove region. /// @@ -1159,6 +1168,60 @@ internal static string SA1134Title { } } + /// + /// Looks up a localized string similar to Elements at the same level in the syntax tree should have the same indentation.. + /// + internal static string SA1137Description { + get { + return ResourceManager.GetString("SA1137Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Elements should have the same indentation. + /// + internal static string SA1137MessageFormat { + get { + return ResourceManager.GetString("SA1137MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Elements should have the same indentation. + /// + internal static string SA1137Title { + get { + return ResourceManager.GetString("SA1137Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Code elements should be properly indented to avoid misinterpretation by readers.. + /// + internal static string SA1138Description { + get { + return ResourceManager.GetString("SA1138Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Indent elements correctly. + /// + internal static string SA1138MessageFormat { + get { + return ResourceManager.GetString("SA1138MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Indent elements correctly. + /// + internal static string SA1138Title { + get { + return ResourceManager.GetString("SA1138Title", resourceCulture); + } + } + /// /// Looks up a localized string similar to Remove 'this.' prefix. /// diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/ReadabilityResources.resx b/StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/ReadabilityResources.resx index 1f7a2e197..9592bb24f 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/ReadabilityResources.resx +++ b/StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/ReadabilityResources.resx @@ -117,6 +117,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Fix indentation + Remove region @@ -483,6 +486,24 @@ Attributes must not share line + + Elements at the same level in the syntax tree should have the same indentation. + + + Elements should have the same indentation + + + Elements should have the same indentation + + + Code elements should be properly indented to avoid misinterpretation by readers. + + + Indent elements correctly + + + Indent elements correctly + Remove 'this.' prefix diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/SA1137ElementsShouldHaveTheSameIndentation.cs b/StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/SA1137ElementsShouldHaveTheSameIndentation.cs new file mode 100644 index 000000000..b591a93e4 --- /dev/null +++ b/StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/SA1137ElementsShouldHaveTheSameIndentation.cs @@ -0,0 +1,1263 @@ +// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + +namespace StyleCop.Analyzers.ReadabilityRules +{ + using System; + using System.Collections.Immutable; + using System.Diagnostics; + using System.Linq; + using Helpers; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp; + using Microsoft.CodeAnalysis.CSharp.Syntax; + using Microsoft.CodeAnalysis.Diagnostics; + using Settings.ObjectModel; + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal class SA1137ElementsShouldHaveTheSameIndentation : DiagnosticAnalyzer + { + public const string ExpectedIndentationKey = "ExpectedIndentation"; + + /// + /// The ID for diagnostics produced by the analyzer. + /// + public const string SA1137DiagnosticId = "SA1137"; + + /// + /// The ID for diagnostics produced by the SA1138 (Indent elements correctly) analyzer. + /// + public const string SA1138DiagnosticId = "SA1138"; + + private static readonly LocalizableString SA1137Title = new LocalizableResourceString(nameof(ReadabilityResources.SA1137Title), ReadabilityResources.ResourceManager, typeof(ReadabilityResources)); + private static readonly LocalizableString SA1137MessageFormat = new LocalizableResourceString(nameof(ReadabilityResources.SA1137MessageFormat), ReadabilityResources.ResourceManager, typeof(ReadabilityResources)); + private static readonly LocalizableString SA1137Description = new LocalizableResourceString(nameof(ReadabilityResources.SA1137Description), ReadabilityResources.ResourceManager, typeof(ReadabilityResources)); + private static readonly string SA1137HelpLink = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1137.md"; + + private static readonly DiagnosticDescriptor SA1137Descriptor = + new DiagnosticDescriptor(SA1137DiagnosticId, SA1137Title, SA1137MessageFormat, AnalyzerCategory.ReadabilityRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, SA1137Description, SA1137HelpLink); + + private static readonly LocalizableString SA1138Title = new LocalizableResourceString(nameof(ReadabilityResources.SA1138Title), ReadabilityResources.ResourceManager, typeof(ReadabilityResources)); + private static readonly LocalizableString SA1138MessageFormat = new LocalizableResourceString(nameof(ReadabilityResources.SA1138MessageFormat), ReadabilityResources.ResourceManager, typeof(ReadabilityResources)); + private static readonly LocalizableString SA1138Description = new LocalizableResourceString(nameof(ReadabilityResources.SA1138Description), ReadabilityResources.ResourceManager, typeof(ReadabilityResources)); + private static readonly string SA1138HelpLink = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1138.md"; + + private static readonly DiagnosticDescriptor SA1138Descriptor = + new DiagnosticDescriptor(SA1138DiagnosticId, SA1138Title, SA1138MessageFormat, AnalyzerCategory.ReadabilityRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, SA1138Description, SA1138HelpLink); + + private static readonly Action CompilationStartAction = HandleCompilationStart; +#pragma warning disable RS1008 // Avoid storing per-compilation data into the fields of a diagnostic analyzer. + private static readonly Action SyntaxTreeAction = HandleSyntaxTree; +#pragma warning restore RS1008 // Avoid storing per-compilation data into the fields of a diagnostic analyzer. + + /// + public override ImmutableArray SupportedDiagnostics { get; } = + ImmutableArray.Create(SA1137Descriptor, SA1138Descriptor); + + /// + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(CompilationStartAction); + } + + private static void HandleCompilationStart(CompilationStartAnalysisContext context) + { + context.RegisterSyntaxTreeActionHonorExclusions(SyntaxTreeAction); + } + + private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context, Compilation compilation, StyleCopSettings settings) + { + var walker = new IndentationWalker(context, compilation, settings); + walker.Visit(context.Tree.GetRoot(context.CancellationToken)); + } + + private static void AddMembersAndAttributes(ImmutableList.Builder elements, SeparatedSyntaxList members) + where T : SyntaxNode + { + foreach (SyntaxNode member in members) + { + AddMemberAndAttributes(elements, member); + } + } + + private static void AddMembersAndAttributes(ImmutableList.Builder elements, SyntaxList members) + where T : SyntaxNode + { + foreach (SyntaxNode member in members) + { + AddMemberAndAttributes(elements, member); + } + } + + private static void AddMemberAndAttributes(ImmutableList.Builder elements, SyntaxNode member) + { + switch (member.Kind()) + { + case SyntaxKind.ClassDeclaration: + case SyntaxKind.StructDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.EnumDeclaration: + elements.AddRange(((BaseTypeDeclarationSyntax)member).AttributeLists); + break; + + case SyntaxKind.FieldDeclaration: + case SyntaxKind.EventFieldDeclaration: + elements.AddRange(((BaseFieldDeclarationSyntax)member).AttributeLists); + break; + + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.EventDeclaration: + case SyntaxKind.IndexerDeclaration: + elements.AddRange(((BasePropertyDeclarationSyntax)member).AttributeLists); + break; + + case SyntaxKind.MethodDeclaration: + case SyntaxKind.ConstructorDeclaration: + case SyntaxKind.DestructorDeclaration: + case SyntaxKind.OperatorDeclaration: + case SyntaxKind.ConversionOperatorDeclaration: + elements.AddRange(((BaseMethodDeclarationSyntax)member).AttributeLists); + break; + + case SyntaxKind.GetAccessorDeclaration: + case SyntaxKind.SetAccessorDeclaration: + case SyntaxKind.AddAccessorDeclaration: + case SyntaxKind.RemoveAccessorDeclaration: + case SyntaxKind.UnknownAccessorDeclaration: + elements.AddRange(((AccessorDeclarationSyntax)member).AttributeLists); + break; + + case SyntaxKind.TypeParameter: + elements.AddRange(((TypeParameterSyntax)member).AttributeLists); + break; + + case SyntaxKind.Parameter: + elements.AddRange(((ParameterSyntax)member).AttributeLists); + break; + + default: + break; + } + + elements.Add(member); + } + + private static void CheckElements(SyntaxTreeAnalysisContext context, Compilation compilation, StyleCopSettings settings, int? indentationLevel, SyntaxList elements) + where T : SyntaxNode + { + if (!elements.Any()) + { + return; + } + + if (elements.Count == 1 && compilation.IsAnalyzerSuppressed(SA1138DiagnosticId)) + { + return; + } + + CheckElements(context, compilation, settings, indentationLevel, elements.ToImmutableList()); + } + + private static void CheckElements(SyntaxTreeAnalysisContext context, Compilation compilation, StyleCopSettings settings, int? indentationLevel, SeparatedSyntaxList elements) + where T : SyntaxNode + { + if (!elements.Any()) + { + return; + } + + if (elements.Count == 1 && compilation.IsAnalyzerSuppressed(SA1138DiagnosticId)) + { + return; + } + + CheckElements(context, compilation, settings, indentationLevel, elements.ToImmutableList()); + } + + private static void CheckElements(SyntaxTreeAnalysisContext context, Compilation compilation, StyleCopSettings settings, int? indentationLevel, ImmutableList elements) + where T : SyntaxNode + { + DiagnosticDescriptor descriptor = SA1137Descriptor; + + bool enableAbsoluteIndentationAnalysis = !compilation.IsAnalyzerSuppressed(SA1138DiagnosticId); + + if (elements.IsEmpty || (elements.Count == 1 && !enableAbsoluteIndentationAnalysis)) + { + return; + } + + elements = elements.RemoveAll( + element => + { + SyntaxToken firstToken = GetFirstTokenForAnalysis(element); + return firstToken.IsMissingOrDefault() || !firstToken.IsFirstInLine(allowNonWhitespaceTrivia: false); + }); + + if (elements.IsEmpty || (elements.Count == 1 && !enableAbsoluteIndentationAnalysis)) + { + return; + } + + // Try to reorder the list so the first item is not an attribute list. This element will establish the + // expected indentation for the entire collection. + int desiredFirst = elements.FindIndex(x => !x.IsKind(SyntaxKind.AttributeList)); + if (desiredFirst > 0) + { + T newFirstElement = elements[desiredFirst]; + elements = elements.RemoveAt(desiredFirst).Insert(0, newFirstElement); + } + + bool first = true; + string expectedIndentation = null; + foreach (T element in elements) + { + SyntaxToken firstToken = GetFirstTokenForAnalysis(element); + SyntaxTrivia indentationTrivia = firstToken.LeadingTrivia.LastOrDefault(); + string indentation = indentationTrivia.IsKind(SyntaxKind.WhitespaceTrivia) ? indentationTrivia.ToString() : string.Empty; + + if (first) + { + if (enableAbsoluteIndentationAnalysis && indentationLevel != null) + { + descriptor = SA1138Descriptor; + if (settings.Indentation.UseTabs) + { + expectedIndentation = new string('\t', indentationLevel.Value); + } + else + { + expectedIndentation = new string(' ', settings.Indentation.IndentationSize * indentationLevel.Value); + } + } + else + { + expectedIndentation = indentation; + } + + first = false; + + if (!enableAbsoluteIndentationAnalysis) + { + continue; + } + } + + if (string.Equals(expectedIndentation, indentation, StringComparison.Ordinal)) + { + // This handles the case where elements are indented properly + continue; + } + + Location location; + if (indentation.Length == 0) + { + location = firstToken.GetLocation(); + } + else + { + location = indentationTrivia.GetLocation(); + } + + ImmutableDictionary properties = ImmutableDictionary.Create().SetItem(ExpectedIndentationKey, expectedIndentation); + context.ReportDiagnostic(Diagnostic.Create(descriptor, location, properties)); + } + } + + private static void CheckTokens(SyntaxTreeAnalysisContext context, Compilation compilation, StyleCopSettings settings, int? indentationLevel, ImmutableArray tokens, bool reportFirstToken = false) + { + DiagnosticDescriptor descriptor = SA1137Descriptor; + SyntaxToken originalFirstToken = tokens.FirstOrDefault(); + + bool enableAbsoluteIndentationAnalysis = !compilation.IsAnalyzerSuppressed(SA1138DiagnosticId); + + if (tokens.IsEmpty || (tokens.Length == 1 && !enableAbsoluteIndentationAnalysis)) + { + return; + } + + tokens = tokens.RemoveAll( + token => + { + return token.IsMissingOrDefault() || !token.IsFirstInLine(allowNonWhitespaceTrivia: false); + }); + + if (tokens.IsEmpty || (tokens.Length == 1 && !enableAbsoluteIndentationAnalysis)) + { + return; + } + + bool first = true; + string expectedIndentation = null; + foreach (SyntaxToken token in tokens) + { + SyntaxTrivia indentationTrivia = token.LeadingTrivia.LastOrDefault(); + string indentation = indentationTrivia.IsKind(SyntaxKind.WhitespaceTrivia) ? indentationTrivia.ToString() : string.Empty; + + if (first) + { + if (enableAbsoluteIndentationAnalysis && indentationLevel != null) + { + descriptor = SA1138Descriptor; + if (settings.Indentation.UseTabs) + { + expectedIndentation = new string('\t', indentationLevel.Value); + } + else + { + expectedIndentation = new string(' ', settings.Indentation.IndentationSize * indentationLevel.Value); + } + } + else + { + expectedIndentation = indentation; + } + + first = false; + + if (!enableAbsoluteIndentationAnalysis) + { + continue; + } + } + + if (string.Equals(expectedIndentation, indentation, StringComparison.Ordinal)) + { + // This handles the case where elements are indented properly + continue; + } + + Location location; + if (indentation.Length == 0) + { + location = token.GetLocation(); + } + else + { + location = indentationTrivia.GetLocation(); + } + + if (!reportFirstToken && token == originalFirstToken) + { + continue; + } + + ImmutableDictionary properties = ImmutableDictionary.Create().SetItem(ExpectedIndentationKey, expectedIndentation); + context.ReportDiagnostic(Diagnostic.Create(descriptor, location, properties)); + } + } + + private static SyntaxToken GetFirstTokenForAnalysis(SyntaxNode node) + { + SyntaxToken firstToken = node.GetFirstToken(); + if (!node.IsKind(SyntaxKind.AttributeList)) + { + while (firstToken.IsKind(SyntaxKind.OpenBracketToken) + && firstToken.Parent.IsKind(SyntaxKind.AttributeList)) + { + // Skip over the attribute list since it's not the focus of this check + firstToken = firstToken.Parent.GetLastToken().GetNextToken(); + } + } + + return firstToken; + } + + private class IndentationWalker : CSharpSyntaxWalker + { + private readonly SyntaxTreeAnalysisContext context; + private readonly Compilation compilation; + private readonly StyleCopSettings settings; + + private int indentationLevel; + private int unknownIndentationLevel; + + public IndentationWalker(SyntaxTreeAnalysisContext context, Compilation compilation, StyleCopSettings settings) + { + this.context = context; + this.compilation = compilation; + this.settings = settings; + } + + private int? IndentationLevel + { + get + { + if (this.unknownIndentationLevel > 0) + { + return null; + } + + return this.indentationLevel; + } + } + + private int? BlockIndentation + { + get + { + switch (this.settings.Indentation.IndentBlock) + { + case true: + return 1; + + case false: + return 0; + + default: + return null; + } + } + } + + private int? SwitchSectionIndentation + { + get + { + switch (this.settings.Indentation.IndentSwitchSection) + { + case true: + return 1; + + case false: + return 0; + + default: + return null; + } + } + } + + private int? LabelAdjustment + { + get + { + switch (this.settings.Indentation.LabelPositioning) + { + case LabelPositioning.LeftMost: + return -this.IndentationLevel; + + case LabelPositioning.OneLess: + return this.IndentationLevel > 0 ? -1 : 0; + + case LabelPositioning.NoIndent: + return 0; + + default: + // Disable indentation check for labels + return null; + } + } + } + + private int? SwitchStatementAdjustment + { + get + { + switch (this.settings.Indentation.IndentSwitchCaseSection) + { + case true: + return 1; + + case false: + return 0; + + default: + return null; + } + } + } + + public override void DefaultVisit(SyntaxNode node) + { + this.unknownIndentationLevel++; + try + { + base.DefaultVisit(node); + } + finally + { + this.unknownIndentationLevel--; + } + } + + public override void VisitCompilationUnit(CompilationUnitSyntax node) + { + using (this.AdjustIndentation(0)) + { + this.AnalyzeCompilationUnit(node); + base.VisitCompilationUnit(node); + } + } + + public override void VisitNamespaceDeclaration(NamespaceDeclarationSyntax node) + { + using (this.AdjustIndentation(1)) + { + this.AnalyzeNamespaceDeclaration(node); + base.VisitNamespaceDeclaration(node); + } + } + + public override void VisitClassDeclaration(ClassDeclarationSyntax node) + { + using (this.AdjustIndentation(1)) + { + this.AnalyzeTypeDeclaration(node); + base.VisitClassDeclaration(node); + } + } + + public override void VisitStructDeclaration(StructDeclarationSyntax node) + { + using (this.AdjustIndentation(1)) + { + this.AnalyzeTypeDeclaration(node); + base.VisitStructDeclaration(node); + } + } + + public override void VisitInterfaceDeclaration(InterfaceDeclarationSyntax node) + { + using (this.AdjustIndentation(1)) + { + this.AnalyzeTypeDeclaration(node); + base.VisitInterfaceDeclaration(node); + } + } + + public override void VisitEnumDeclaration(EnumDeclarationSyntax node) + { + using (this.AdjustIndentation(1)) + { + this.AnalyzeEnumDeclaration(node); + base.VisitEnumDeclaration(node); + } + } + + public override void VisitMethodDeclaration(MethodDeclarationSyntax node) + { + using (this.AdjustIndentation(1)) + { + this.AnalyzeMethodDeclaration(node); + base.VisitMethodDeclaration(node); + } + } + + public override void VisitAccessorList(AccessorListSyntax node) + { + using (this.AdjustIndentation(0)) + { + this.AnalyzeAccessorList(node); + base.VisitAccessorList(node); + } + } + + public override void VisitVariableDeclaration(VariableDeclarationSyntax node) + { + this.AnalyzeVariableDeclaration(node); + base.VisitVariableDeclaration(node); + } + + public override void VisitTypeParameterList(TypeParameterListSyntax node) + { + using (this.AdjustIndentation(0)) + { + this.AnalyzeTypeParameterList(node); + base.VisitTypeParameterList(node); + } + } + + public override void VisitParameterList(ParameterListSyntax node) + { + this.AnalyzeBaseParameterList(node); + base.VisitParameterList(node); + } + + public override void VisitBracketedParameterList(BracketedParameterListSyntax node) + { + this.AnalyzeBaseParameterList(node); + base.VisitBracketedParameterList(node); + } + + public override void VisitArgumentList(ArgumentListSyntax node) + { + this.AnalyzeBaseArgumentList(node); + base.VisitArgumentList(node); + } + + public override void VisitBracketedArgumentList(BracketedArgumentListSyntax node) + { + this.AnalyzeBaseArgumentList(node); + base.VisitBracketedArgumentList(node); + } + + public override void VisitAttributeList(AttributeListSyntax node) + { + // Indentation is provided by the owner of the attribute list unless the owner is the compilation unit + // itself (assembly and module attributes). + int adjustment = node.Parent.IsKind(SyntaxKind.CompilationUnit) ? 1 : 0; + using (this.AdjustIndentation(adjustment)) + { + this.AnalyzeAttributeList(node); + base.VisitAttributeList(node); + } + } + + public override void VisitAttributeArgumentList(AttributeArgumentListSyntax node) + { + this.AnalyzeAttributeArgumentList(node); + base.VisitAttributeArgumentList(node); + } + + public override void VisitBlock(BlockSyntax node) + { + int? adjustment; + if (node.Parent.IsKind(SyntaxKind.Block)) + { + // The current indentation level is the level of the block itself + adjustment = this.BlockIndentation; + } + else + { + // The parent indented by 1; update to match the BlockIndentation value + adjustment = this.BlockIndentation - 1; + } + + using (this.AdjustIndentation(adjustment)) + { + this.AnalyzeBlock(node); + base.VisitBlock(node); + } + } + + public override void VisitSwitchStatement(SwitchStatementSyntax node) + { + using (this.AdjustIndentation(this.SwitchSectionIndentation)) + { + this.AnalyzeSwitchStatement(node); + base.VisitSwitchStatement(node); + } + } + + public override void VisitInitializerExpression(InitializerExpressionSyntax node) + { + this.AnalyzeInitializerExpression(node); + base.VisitInitializerExpression(node); + } + + public override void VisitAnonymousObjectCreationExpression(AnonymousObjectCreationExpressionSyntax node) + { + this.AnalyzeAnonymousObjectCreationExpression(node); + base.VisitAnonymousObjectCreationExpression(node); + } + + public override void VisitConstructorDeclaration(ConstructorDeclarationSyntax node) + { + using (this.AdjustIndentation(1)) + { + this.AnalyzeBaseMethodDeclaration(node); + base.VisitConstructorDeclaration(node); + } + } + + public override void VisitDestructorDeclaration(DestructorDeclarationSyntax node) + { + using (this.AdjustIndentation(1)) + { + this.AnalyzeBaseMethodDeclaration(node); + base.VisitDestructorDeclaration(node); + } + } + + public override void VisitOperatorDeclaration(OperatorDeclarationSyntax node) + { + using (this.AdjustIndentation(1)) + { + this.AnalyzeBaseMethodDeclaration(node); + base.VisitOperatorDeclaration(node); + } + } + + public override void VisitConversionOperatorDeclaration(ConversionOperatorDeclarationSyntax node) + { + using (this.AdjustIndentation(1)) + { + this.AnalyzeBaseMethodDeclaration(node); + base.VisitConversionOperatorDeclaration(node); + } + } + + public override void VisitPropertyDeclaration(PropertyDeclarationSyntax node) + { + using (this.AdjustIndentation(1)) + { + this.AnalyzeBasePropertyDeclaration(node); + base.VisitPropertyDeclaration(node); + } + } + + public override void VisitEventDeclaration(EventDeclarationSyntax node) + { + using (this.AdjustIndentation(1)) + { + this.AnalyzeBasePropertyDeclaration(node); + base.VisitEventDeclaration(node); + } + } + + public override void VisitIndexerDeclaration(IndexerDeclarationSyntax node) + { + using (this.AdjustIndentation(1)) + { + this.AnalyzeBasePropertyDeclaration(node); + base.VisitIndexerDeclaration(node); + } + } + + public override void VisitAccessorDeclaration(AccessorDeclarationSyntax node) + { + using (this.AdjustIndentation(1)) + { + this.AnalyzeAccessorDeclaration(node); + base.VisitAccessorDeclaration(node); + } + } + + public override void VisitLabeledStatement(LabeledStatementSyntax node) + { + using (this.AdjustIndentation(0)) + { + base.VisitLabeledStatement(node); + } + } + + public override void VisitCheckedStatement(CheckedStatementSyntax node) + { + using (this.AdjustIndentation(1)) + { + this.AnalyzeCheckedStatement(node); + base.VisitCheckedStatement(node); + } + } + + public override void VisitDoStatement(DoStatementSyntax node) + { + using (this.AdjustIndentation(1)) + { + this.AnalyzeDoStatement(node); + base.VisitDoStatement(node); + } + } + + public override void VisitFixedStatement(FixedStatementSyntax node) + { + using (this.AdjustIndentation(1)) + { + this.AnalyzeFixedStatement(node); + base.VisitFixedStatement(node); + } + } + + public override void VisitForEachStatement(ForEachStatementSyntax node) + { + using (this.AdjustIndentation(1)) + { + this.AnalyzeForEachStatement(node); + base.VisitForEachStatement(node); + } + } + + public override void VisitForStatement(ForStatementSyntax node) + { + using (this.AdjustIndentation(1)) + { + this.AnalyzeForStatement(node); + base.VisitForStatement(node); + } + } + + public override void VisitIfStatement(IfStatementSyntax node) + { + int adjustment = node.Parent.IsKind(SyntaxKind.ElseClause) ? 0 : 1; + using (this.AdjustIndentation(adjustment)) + { + this.AnalyzeIfStatement(node); + base.VisitIfStatement(node); + } + } + + public override void VisitElseClause(ElseClauseSyntax node) + { + // This clause inherits the indentation from the if statement + using (this.AdjustIndentation(0)) + { + base.VisitElseClause(node); + } + } + + public override void VisitLockStatement(LockStatementSyntax node) + { + using (this.AdjustIndentation(1)) + { + this.AnalyzeLockStatement(node); + base.VisitLockStatement(node); + } + } + + public override void VisitUsingStatement(UsingStatementSyntax node) + { + // Allow consecutive using statements without nesting indentation. + int adjustment = node.Parent.IsKind(SyntaxKind.UsingStatement) ? 0 : 1; + using (this.AdjustIndentation(adjustment)) + { + this.AnalyzeUsingStatement(node); + base.VisitUsingStatement(node); + } + } + + public override void VisitWhileStatement(WhileStatementSyntax node) + { + using (this.AdjustIndentation(1)) + { + this.AnalyzeWhileStatement(node); + base.VisitWhileStatement(node); + } + } + + private void AnalyzeCompilationUnit(CompilationUnitSyntax node) + { + var elements = ImmutableList.CreateBuilder(); + + elements.AddRange(node.Externs); + elements.AddRange(node.Usings); + elements.AddRange(node.AttributeLists); + AddMembersAndAttributes(elements, node.Members); + + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, elements.ToImmutable()); + } + + private void AnalyzeNamespaceDeclaration(NamespaceDeclarationSyntax node) + { + var tokens = ImmutableArray.Create(GetFirstTokenForAnalysis(node), node.OpenBraceToken, node.CloseBraceToken); + CheckTokens(this.context, this.compilation, this.settings, this.IndentationLevel - 1, tokens); + + var elements = ImmutableList.CreateBuilder(); + + elements.AddRange(node.Externs); + elements.AddRange(node.Usings); + AddMembersAndAttributes(elements, node.Members); + + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, elements.ToImmutable()); + } + + private void AnalyzeTypeDeclaration(TypeDeclarationSyntax node) + { + var tokens = ImmutableArray.Create(GetFirstTokenForAnalysis(node), node.OpenBraceToken, node.CloseBraceToken); + CheckTokens(this.context, this.compilation, this.settings, this.IndentationLevel - 1, tokens); + + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, node.ConstraintClauses); + + var elements = ImmutableList.CreateBuilder(); + AddMembersAndAttributes(elements, node.Members); + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, elements.ToImmutable()); + } + + private void AnalyzeEnumDeclaration(EnumDeclarationSyntax node) + { + var tokens = ImmutableArray.Create(GetFirstTokenForAnalysis(node), node.OpenBraceToken, node.CloseBraceToken); + CheckTokens(this.context, this.compilation, this.settings, this.IndentationLevel - 1, tokens); + + var elements = ImmutableList.CreateBuilder(); + foreach (EnumMemberDeclarationSyntax enumMemberDeclaration in node.Members) + { + elements.AddRange(enumMemberDeclaration.AttributeLists); + elements.Add(enumMemberDeclaration); + } + + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, elements.ToImmutable()); + } + + private void AnalyzeBaseMethodDeclaration(BaseMethodDeclarationSyntax node) + { + if (node.Body != null) + { + var tokens = ImmutableArray.Create(GetFirstTokenForAnalysis(node), node.Body.OpenBraceToken, node.Body.CloseBraceToken); + CheckTokens(this.context, this.compilation, this.settings, this.IndentationLevel - 1, tokens); + } + } + + private void AnalyzeMethodDeclaration(MethodDeclarationSyntax node) + { + this.AnalyzeBaseMethodDeclaration(node); + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, node.ConstraintClauses); + } + + private void AnalyzeBasePropertyDeclaration(BasePropertyDeclarationSyntax node) + { + if (node.AccessorList != null) + { + var tokens = ImmutableArray.Create(GetFirstTokenForAnalysis(node), node.AccessorList.OpenBraceToken, node.AccessorList.CloseBraceToken); + CheckTokens(this.context, this.compilation, this.settings, this.IndentationLevel - 1, tokens); + } + } + + private void AnalyzeAccessorList(AccessorListSyntax node) + { + var elements = ImmutableList.CreateBuilder(); + AddMembersAndAttributes(elements, node.Accessors); + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, elements.ToImmutable()); + } + + private void AnalyzeAccessorDeclaration(AccessorDeclarationSyntax node) + { + if (node.Body != null) + { + var tokens = ImmutableArray.Create(GetFirstTokenForAnalysis(node), node.Body.OpenBraceToken, node.Body.CloseBraceToken); + CheckTokens(this.context, this.compilation, this.settings, this.IndentationLevel - 1, tokens); + } + } + + private void AnalyzeVariableDeclaration(VariableDeclarationSyntax node) + { + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, node.Variables); + } + + private void AnalyzeTypeParameterList(TypeParameterListSyntax node) + { + var elements = ImmutableList.CreateBuilder(); + AddMembersAndAttributes(elements, node.Parameters); + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, elements.ToImmutable()); + } + + private void AnalyzeBaseParameterList(BaseParameterListSyntax node) + { + var elements = ImmutableList.CreateBuilder(); + AddMembersAndAttributes(elements, node.Parameters); + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, elements.ToImmutable()); + } + + private void AnalyzeBaseArgumentList(BaseArgumentListSyntax node) + { + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, node.Arguments); + } + + private void AnalyzeAttributeList(AttributeListSyntax node) + { + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, node.Attributes); + } + + private void AnalyzeAttributeArgumentList(AttributeArgumentListSyntax node) + { + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, node.Arguments); + } + + private void AnalyzeBlock(BlockSyntax node) + { + var statements = ImmutableList.CreateBuilder(); + var labeledStatements = ImmutableList.CreateBuilder(); + + foreach (var statement in node.Statements) + { + StatementSyntax statementToAlign = statement; + while (statementToAlign.IsKind(SyntaxKind.LabeledStatement)) + { + labeledStatements.Add(statementToAlign); + statementToAlign = ((LabeledStatementSyntax)statementToAlign).Statement; + } + + statements.Add(statementToAlign); + } + + if (node.Parent.IsKind(SyntaxKind.Block)) + { + CheckTokens(this.context, this.compilation, this.settings, this.IndentationLevel - this.BlockIndentation, ImmutableArray.Create(node.OpenBraceToken, node.CloseBraceToken)); + } + + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, statements.ToImmutable()); + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel + this.LabelAdjustment, labeledStatements.ToImmutable()); + } + + private void AnalyzeSwitchStatement(SwitchStatementSyntax node) + { + var tokens = ImmutableArray.Create(GetFirstTokenForAnalysis(node), node.OpenBraceToken, node.CloseBraceToken); + CheckTokens(this.context, this.compilation, this.settings, this.IndentationLevel - this.SwitchSectionIndentation, tokens); + + var labels = ImmutableList.CreateBuilder(); + var statements = ImmutableList.CreateBuilder(); + var labeledStatements = ImmutableList.CreateBuilder(); + foreach (SwitchSectionSyntax switchSection in node.Sections) + { + labels.AddRange(switchSection.Labels); + foreach (var statement in switchSection.Statements) + { + StatementSyntax statementToAlign = statement; + while (statementToAlign.IsKind(SyntaxKind.LabeledStatement)) + { + labeledStatements.Add(statementToAlign); + statementToAlign = ((LabeledStatementSyntax)statementToAlign).Statement; + } + + statements.Add(statementToAlign); + } + } + + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, labels.ToImmutable()); + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel + this.SwitchStatementAdjustment, statements.ToImmutable()); + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel + this.SwitchStatementAdjustment + this.LabelAdjustment, labeledStatements.ToImmutable()); + } + + private void AnalyzeCheckedStatement(CheckedStatementSyntax node) + { + var tokens = ImmutableArray.Create(GetFirstTokenForAnalysis(node), node.Block.OpenBraceToken, node.Block.CloseBraceToken); + CheckTokens(this.context, this.compilation, this.settings, this.IndentationLevel - 1, tokens); + } + + private void AnalyzeDoStatement(DoStatementSyntax node) + { + ImmutableArray tokens; + + if (node.Statement.IsKind(SyntaxKind.Block)) + { + BlockSyntax block = (BlockSyntax)node.Statement; + tokens = ImmutableArray.Create(GetFirstTokenForAnalysis(node), block.OpenBraceToken, block.CloseBraceToken, node.WhileKeyword); + } + else + { + tokens = ImmutableArray.Create(GetFirstTokenForAnalysis(node), node.WhileKeyword); + + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, ImmutableList.Create(node.Statement)); + } + + CheckTokens(this.context, this.compilation, this.settings, this.IndentationLevel - 1, tokens); + } + + private void AnalyzeFixedStatement(FixedStatementSyntax node) + { + if (node.Statement.IsKind(SyntaxKind.Block)) + { + BlockSyntax block = (BlockSyntax)node.Statement; + var tokens = ImmutableArray.Create(GetFirstTokenForAnalysis(node), block.OpenBraceToken, block.CloseBraceToken); + CheckTokens(this.context, this.compilation, this.settings, this.IndentationLevel - 1, tokens); + } + else + { + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, ImmutableList.Create(node.Statement)); + } + } + + private void AnalyzeForEachStatement(ForEachStatementSyntax node) + { + if (node.Statement.IsKind(SyntaxKind.Block)) + { + BlockSyntax block = (BlockSyntax)node.Statement; + var tokens = ImmutableArray.Create(GetFirstTokenForAnalysis(node), block.OpenBraceToken, block.CloseBraceToken); + CheckTokens(this.context, this.compilation, this.settings, this.IndentationLevel - 1, tokens); + } + else + { + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, ImmutableList.Create(node.Statement)); + } + } + + private void AnalyzeForStatement(ForStatementSyntax node) + { + if (node.Statement.IsKind(SyntaxKind.Block)) + { + BlockSyntax block = (BlockSyntax)node.Statement; + var tokens = ImmutableArray.Create(GetFirstTokenForAnalysis(node), block.OpenBraceToken, block.CloseBraceToken); + CheckTokens(this.context, this.compilation, this.settings, this.IndentationLevel - 1, tokens); + } + else + { + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, ImmutableList.Create(node.Statement)); + } + } + + private void AnalyzeIfStatement(IfStatementSyntax node) + { + if (node.Parent.IsKind(SyntaxKind.ElseClause)) + { + // Already checked. + return; + } + + ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(6); + IfStatementSyntax current = node; + while (current != null) + { + builder.Add(current.IfKeyword); + if (current.Statement.IsKind(SyntaxKind.Block)) + { + BlockSyntax block = (BlockSyntax)current.Statement; + builder.Add(block.OpenBraceToken); + builder.Add(block.CloseBraceToken); + } + else + { + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, ImmutableList.Create(current.Statement)); + } + + if (current.Else != null) + { + builder.Add(current.Else.ElseKeyword); + if (current.Else.Statement.IsKind(SyntaxKind.Block)) + { + BlockSyntax block = (BlockSyntax)current.Else.Statement; + builder.Add(block.OpenBraceToken); + builder.Add(block.CloseBraceToken); + + current = null; + } + else if (current.Else.Statement.IsKind(SyntaxKind.IfStatement)) + { + current = (IfStatementSyntax)current.Else.Statement; + } + else + { + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, ImmutableList.Create(current.Else.Statement)); + + current = null; + } + } + else + { + current = null; + } + } + + CheckTokens(this.context, this.compilation, this.settings, this.IndentationLevel - 1, builder.ToImmutable()); + } + + private void AnalyzeLockStatement(LockStatementSyntax node) + { + if (node.Statement.IsKind(SyntaxKind.Block)) + { + BlockSyntax block = (BlockSyntax)node.Statement; + var tokens = ImmutableArray.Create(GetFirstTokenForAnalysis(node), block.OpenBraceToken, block.CloseBraceToken); + CheckTokens(this.context, this.compilation, this.settings, this.IndentationLevel - 1, tokens); + } + else + { + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, ImmutableList.Create(node.Statement)); + } + } + + private void AnalyzeUsingStatement(UsingStatementSyntax node) + { + if (node.Parent.IsKind(SyntaxKind.UsingStatement)) + { + // Already checked. + return; + } + + ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(3); + + UsingStatementSyntax current = node; + while (current != null) + { + builder.Add(current.UsingKeyword); + if (current.Statement.IsKind(SyntaxKind.UsingStatement)) + { + current = (UsingStatementSyntax)current.Statement; + continue; + } + else if (current.Statement.IsKind(SyntaxKind.Block)) + { + BlockSyntax block = (BlockSyntax)current.Statement; + builder.Add(block.OpenBraceToken); + builder.Add(block.CloseBraceToken); + current = null; + } + else + { + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, ImmutableList.Create(current.Statement)); + + current = null; + } + } + + CheckTokens(this.context, this.compilation, this.settings, this.IndentationLevel - 1, builder.ToImmutable()); + } + + private void AnalyzeWhileStatement(WhileStatementSyntax node) + { + if (node.Statement.IsKind(SyntaxKind.Block)) + { + BlockSyntax block = (BlockSyntax)node.Statement; + var tokens = ImmutableArray.Create(GetFirstTokenForAnalysis(node), block.OpenBraceToken, block.CloseBraceToken); + CheckTokens(this.context, this.compilation, this.settings, this.IndentationLevel - 1, tokens); + } + else + { + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, ImmutableList.Create(node.Statement)); + } + } + + private void AnalyzeInitializerExpression(InitializerExpressionSyntax node) + { + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, node.Expressions); + } + + private void AnalyzeAnonymousObjectCreationExpression(AnonymousObjectCreationExpressionSyntax node) + { + CheckElements(this.context, this.compilation, this.settings, this.IndentationLevel, node.Initializers); + } + + private IndentationAdjuster AdjustIndentation(int? levels) + { + return new IndentationAdjuster(this, levels); + } + + private struct IndentationAdjuster : IDisposable + { + private readonly IndentationWalker walker; + private readonly int? levels; + + public IndentationAdjuster(IndentationWalker walker, int? levels) + { + this.walker = walker; + this.levels = levels; + + if (levels != null) + { + IncreaseIndentationLevel(walker, levels.Value); + } + } + + public void Dispose() + { + if (this.levels != null) + { + DecreaseIndentationLevel(this.walker, this.levels.Value); + } + } + + private static void IncreaseIndentationLevel(IndentationWalker walker, int levels) + { + if (walker.IndentationLevel == null) + { + return; + } + + Debug.Assert(walker.indentationLevel >= 0, "Assertion failed: walker.indentationLevel >= 0"); + Debug.Assert(walker.unknownIndentationLevel == 0, "Assertion failed: walker.unknownIndentationLevel == 0"); + walker.indentationLevel += levels; + walker.unknownIndentationLevel--; + } + + private static void DecreaseIndentationLevel(IndentationWalker walker, int levels) + { + if (walker.IndentationLevel == null) + { + return; + } + + Debug.Assert(walker.indentationLevel >= levels, "Assertion failed: walker.indentationLevel >= levels"); + Debug.Assert(walker.unknownIndentationLevel == -1, "Assertion failed: walker.unknownIndentationLevel == -1"); + walker.indentationLevel -= levels; + walker.unknownIndentationLevel++; + } + } + } + } +} diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/Settings/ObjectModel/IndentationSettings.cs b/StyleCop.Analyzers/StyleCop.Analyzers/Settings/ObjectModel/IndentationSettings.cs index 38e0f1512..5eef0ca7e 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers/Settings/ObjectModel/IndentationSettings.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers/Settings/ObjectModel/IndentationSettings.cs @@ -26,6 +26,30 @@ internal class IndentationSettings [JsonProperty("useTabs", DefaultValueHandling = DefaultValueHandling.Include)] private bool useTabs; + /// + /// This is the backing field for the property. + /// + [JsonProperty("indentBlock", DefaultValueHandling = DefaultValueHandling.Include)] + private bool? indentBlock; + + /// + /// This is the backing field for the property. + /// + [JsonProperty("indentSwitchSection", DefaultValueHandling = DefaultValueHandling.Include)] + private bool? indentSwitchSection; + + /// + /// This is the backing field for the property. + /// + [JsonProperty("indentSwitchCaseSection", DefaultValueHandling = DefaultValueHandling.Include)] + private bool? indentSwitchCaseSection; + + /// + /// This is the backing field for the property. + /// + [JsonProperty("labelPositioning", DefaultValueHandling = DefaultValueHandling.Include)] + private LabelPositioning? labelPositioning; + /// /// Initializes a new instance of the class during JSON deserialization. /// @@ -35,6 +59,11 @@ protected internal IndentationSettings() this.indentationSize = 4; this.tabSize = 4; this.useTabs = false; + + this.indentBlock = true; + this.indentSwitchSection = true; + this.indentSwitchCaseSection = true; + this.labelPositioning = ObjectModel.LabelPositioning.OneLess; } public int IndentationSize => @@ -45,5 +74,17 @@ protected internal IndentationSettings() public bool UseTabs => this.useTabs; + + public bool? IndentBlock => + this.indentBlock; + + public bool? IndentSwitchSection => + this.indentSwitchSection; + + public bool? IndentSwitchCaseSection => + this.indentSwitchCaseSection; + + public LabelPositioning? LabelPositioning => + this.labelPositioning; } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/Settings/ObjectModel/LabelPositioning.cs b/StyleCop.Analyzers/StyleCop.Analyzers/Settings/ObjectModel/LabelPositioning.cs new file mode 100644 index 000000000..94c8655c7 --- /dev/null +++ b/StyleCop.Analyzers/StyleCop.Analyzers/Settings/ObjectModel/LabelPositioning.cs @@ -0,0 +1,23 @@ +// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + +namespace StyleCop.Analyzers.Settings.ObjectModel +{ + internal enum LabelPositioning + { + /// + /// Placed in the Zeroth column of the text editor. + /// + LeftMost, + + /// + /// Placed at one less indent to the current context. + /// + OneLess, + + /// + /// Placed at the same indent as the current context. + /// + NoIndent, + } +} diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json b/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json index d2aa0fb63..7e8c6bb96 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json +++ b/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json @@ -31,6 +31,59 @@ "description": "Specifies whether indentation should use hard tabs instead of spaces.", "type": "boolean", "default": false + }, + "indentBlock": { + "description": "Specifies the indentation behavior for the contents of blocks.\r\ntrue: Indent the contents of blocks.\r\nfalse: Do not indent the contents of blocks.\r\nnull: Do not check the absolute indentation of blocks.", + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": true + }, + "indentSwitchSection": { + "description": "Specifies the indentation behavior for case contents.\r\ntrue: Indent case labels within a switch statement.\r\nfalse: Do not indent case labels within a switch statement.\r\nnull: Do not check the absolute indentation of switch section labels.", + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": true + }, + "indentSwitchCaseSection": { + "description": "Specifies the indentation behavior for case labels.\r\ntrue: Indent the contents of a switch section.\r\nfalse: Do not indent the contents of a switch section.\r\nnull: Do not check the absolute indentation of switch section contents.", + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": true + }, + "labelPositioning": { + "description": "Specifies the indentation behavior for labels.\r\nleftMost: Labels are placed in the left-most column.\r\noneLess: Labels are indented one less than other statements.\r\nnoIndent: Labels are indented in the same manner as other statements.\r\nnull: Do not check the absolute indentation of labels.", + "anyOf": [ + { + "type": "string", + "enum": [ + "leftMost", + "oneLess", + "noIndent" + ] + }, + { + "type": "null" + } + ], + "default": "oneLess" } } }, diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/StyleCop.Analyzers.csproj b/StyleCop.Analyzers/StyleCop.Analyzers/StyleCop.Analyzers.csproj index b5ce2feb9..787cfbcdc 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers/StyleCop.Analyzers.csproj +++ b/StyleCop.Analyzers/StyleCop.Analyzers/StyleCop.Analyzers.csproj @@ -267,12 +267,14 @@ + + diff --git a/StyleCopAnalyzers.sln b/StyleCopAnalyzers.sln index bd31f65d5..2a690e699 100644 --- a/StyleCopAnalyzers.sln +++ b/StyleCopAnalyzers.sln @@ -104,6 +104,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "documentation", "documentat documentation\SA1132.md = documentation\SA1132.md documentation\SA1133.md = documentation\SA1133.md documentation\SA1134.md = documentation\SA1134.md + documentation\SA1137.md = documentation\SA1137.md documentation\SA1200.md = documentation\SA1200.md documentation\SA1201.md = documentation\SA1201.md documentation\SA1202.md = documentation\SA1202.md diff --git a/documentation/SA1137.md b/documentation/SA1137.md new file mode 100644 index 000000000..228edaeec --- /dev/null +++ b/documentation/SA1137.md @@ -0,0 +1,149 @@ +## SA1137 + + + + + + + + + + + + + + +
TypeNameSA1137ElementsShouldHaveTheSameIndentation
CheckIdSA1137
CategoryReadability Rules
+ +:memo: This rule is new for StyleCop Analyzers, and was not present in StyleCop Classic. + +## Cause + +Two sibling elements which each start on their own line have different levels of indentation. + +## Rule description + +A violation of this rule occurs when two or more sibling elements each start on their own line but are not indented the +same amount. + +For example, the following code would produce a violation of this rule: + +```csharp +public void MethodName() +{ + A(); + B(); // SA1137: Expected the indentation to match the previous line +} +``` + +The following code would not produce any violations: + +```csharp +public void MethodName() +{ + A(); + B(); +} +``` + +Note that relative indentation and indentation in independent groups of siblings is not checked by this rule. The +following code shows a more complex example which would not produce any violations: + +```csharp +public void Method1() +{ + A(); + B(); +} + +public void Method2() +{ + A(); + B(); +} + +public void Method3() +{ +A(); +B(); +} + +public void Method4() + { + A(); + B(); + } +``` + +### Attributes + +Attribute lists are evaluated by this rule as individual elements at the same level as the element they are applied to. +However, when determining the expected indentation for sibling elements, attribute lists are given lower priority. In +other words, the first element in the same group which starts on its own line, if one exists, determines the expected +indentation for the group. If no such element exists, the indentation of the first attribute list is used instead. For +example, the following code shows locations where SA1137 is reported relative to attribute lists. + +```csharp + [Obsolete] // SA1137 (expected no indentation) +public void Method() // OK (this line establishes indentation for the group) +{ +} + + public Task MethodAsync() // SA1137 (expected no indentation) + { // OK (not part of the analysis group) + } +``` + +The following example shows a case where the indentation of an attribute list *is* used for the group. + +```csharp +public void Method(int x, // Ignored (parameter does not start on its own line) + [In] int y, // OK (this line establishes indentation for the group) + [In] int z) // SA1137 +{ +} +``` + +### Labels + +Labels which appear within a block statement are evaluated in a special manner. Each label within a block is expected to +have the same indentation, even if that indentation differs from the indentation used by statements within the block. +The specific indentation of labels relative to other statements is not examined for this rule. + +The following example shows a block with statements and labels. + +```csharp +public int MethodName() +{ + int x; + + beginning: // OK (label indentation may differ from other statements) + x = 3; // SA1137 (should be indented four spaces to match 'int x;' above) + +end: // SA1137 (should be indented two spaces to match 'beginning:' above) + return x; +} +``` + +Inside of a `switch` statement, the `case` and `default` labels form an additional category. The specific rules in this +case are: + +1. `case` and `default` labels in a `switch` statement need to use the same indentation. +2. Other labels in a `switch` statement need to use the same indentation (but this may differ from the indentation of + `case` labels) +3. Statements in a `switch` statement need to use the same indentation (but this may differ from both of the above + labels). + +## How to fix violations + +To fix a violation of this rule, adjust the indentation of sibling elements to match. + +## How to suppress violations + +```csharp +class TypeName { } + +#pragma warning disable SA1137 // Elements should have the same indentation + class Indented { } +#pragma warning restore SA1137 // Elements should have the same indentation +```