diff --git a/src/EditorFeatures/CSharpTest/AddBraces/AddBracesFixAllTests.cs b/src/EditorFeatures/CSharpTest/AddBraces/AddBracesFixAllTests.cs new file mode 100644 index 0000000000000..bbe422ca5f38c --- /dev/null +++ b/src/EditorFeatures/CSharpTest/AddBraces/AddBracesFixAllTests.cs @@ -0,0 +1,267 @@ +using System.Threading.Tasks; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.AddBraces +{ + public partial class AddBracesTests + { + [Fact] + [Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] + public async Task TestFixAllInDocument() + { + var input = @" + + + +class Program1 +{ + static void Main() + { + {|FixAllInDocument:if|} (true) return; + if (true) return; + } +} + + +class Program2 +{ + static void Main() + { + if (true) return; + } +} + + + + +class Program3 +{ + static void Main() + { + if (true) return; + } +} + + +"; + + var expected = @" + + + +class Program1 +{ + static void Main() + { + if (true) + { + return; + } + + if (true) + { + return; + } + } +} + + +class Program2 +{ + static void Main() + { + if (true) return; + } +} + + + + +class Program3 +{ + static void Main() + { + if (true) return; + } +} + + +"; + + await TestAsync(input, expected, compareTokens: false, fixAllActionEquivalenceKey: null); + } + + [Fact] + [Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] + public async Task TestFixAllInProject() + { + var input = @" + + + +class Program1 +{ + static void Main() + { + {|FixAllInProject:if|} (true) return; + } +} + + +class Program2 +{ + static void Main() + { + if (true) return; + } +} + + + + +class Program3 +{ + static void Main() + { + if (true) return; + } +} + + +"; + + var expected = @" + + + +class Program1 +{ + static void Main() + { + if (true) + { + return; + } + } +} + + +class Program2 +{ + static void Main() + { + if (true) + { + return; + } + } +} + + + + +class Program3 +{ + static void Main() + { + if (true) return; + } +} + + +"; + + await TestAsync(input, expected, compareTokens: false, fixAllActionEquivalenceKey: null); + } + + [Fact] + [Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] + public async Task TestFixAllInSolution() + { + var input = @" + + + +class Program1 +{ + static void Main() + { + {|FixAllInSolution:if|} (true) return; + } +} + + +class Program2 +{ + static void Main() + { + if (true) return; + } +} + + + + +class Program3 +{ + static void Main() + { + if (true) return; + } +} + + +"; + + var expected = @" + + + +class Program1 +{ + static void Main() + { + if (true) + { + return; + } + } +} + + +class Program2 +{ + static void Main() + { + if (true) + { + return; + } + } +} + + + + +class Program3 +{ + static void Main() + { + if (true) + { + return; + } + } +} + + +"; + + await TestAsync(input, expected, compareTokens: false, fixAllActionEquivalenceKey: null); + } + } +} diff --git a/src/EditorFeatures/CSharpTest/AddBraces/AddBracesTests.cs b/src/EditorFeatures/CSharpTest/AddBraces/AddBracesTests.cs new file mode 100644 index 0000000000000..de6cf47d2a672 --- /dev/null +++ b/src/EditorFeatures/CSharpTest/AddBraces/AddBracesTests.cs @@ -0,0 +1,606 @@ +using System; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Diagnostics.AddBraces; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.AddBraces +{ + public partial class AddBracesTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest + { + internal override Tuple CreateDiagnosticProviderAndFixer(Workspace workspace) + { + return new Tuple(new CSharpAddBracesDiagnosticAnalyzer(), + new CSharpAddBracesCodeFixProvider()); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + public async Task DoNotFireForIfWithBraces() + { + await TestMissingAsync( + @" +class Program +{ + static void Main() + { + [|if|] (true) { return; } + } +} +"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + public async Task DoNotFireForElseWithBraces() + { + await TestMissingAsync( + @" +class Program +{ + static void Main() + { + if (true) { return; } + [|else|] { return; } + } +} +"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + public async Task DoNotFireForElseWithChildIf() + { + await TestMissingAsync( + @" +class Program +{ + static void Main() + { + if (true) return; + [|else|] if (false) return; + } +} +"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + public async Task DoNotFireForForWithBraces() + { + await TestMissingAsync( + @" +class Program +{ + static void Main() + { + [|for|] (var i = 0; i < 5; i++) { return; } + } +} +"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + public async Task DoNotFireForForEachWithBraces() + { + await TestMissingAsync( + @" +class Program +{ + static void Main() + { + [|foreach|] (var c in ""test"") { return; } + } +} +"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + public async Task DoNotFireForWhileWithBraces() + { + await TestMissingAsync( + @" +class Program +{ + static void Main() + { + [|while|] (true) { return; } + } +} +"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + public async Task DoNotFireForDoWhileWithBraces() + { + await TestMissingAsync( + @" +class Program +{ + static void Main() + { + [|do|] { return; } while (true); + } +} +"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + public async Task DoNotFireForUsingWithBraces() + { + await TestMissingAsync( + @" +class Program +{ + static void Main() + { + [|using|] (var f = new Fizz()) + { + return; + } + } +} + +class Fizz : IDisposable +{ + public void Dispose() + { + throw new NotImplementedException(); + } +} +"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + public async Task DoNotFireForUsingWithChildUsing() + { + await TestMissingAsync( + @" +class Program +{ + static void Main() + { + [|using|] (var f = new Fizz()) + using (var b = new Buzz()) + return; + } +} + +class Fizz : IDisposable +{ + public void Dispose() + { + throw new NotImplementedException(); + } +} + +class Buzz : IDisposable +{ + public void Dispose() + { + throw new NotImplementedException(); + } +} +"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + public async Task DoNotFireForLockWithBraces() + { + await TestMissingAsync( + @" +class Program +{ + static void Main() + { + var str = ""test""; + [|lock|] (str) + { + return; + } + } +} +"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + public async Task DoNotFireForLockWithChildLock() + { + await TestMissingAsync( + @" +class Program +{ + static void Main() + { + var str1 = ""test""; + var str2 = ""test""; + + [|lock|] (str1) + lock (str2) + return; + } +} +"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + public async Task FireForIfWithoutBraces() + { + await TestAsync( + @" +class Program +{ + static void Main() + { + [|if|] (true) return; + } +}", + + @" +class Program +{ + static void Main() + { + if (true) + { + return; + } + } +}", + index: 0, + compareTokens: false); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + public async Task FireForElseWithoutBraces() + { + await TestAsync( + @" +class Program +{ + static void Main() + { + if (true) { return; } + [|else|] return; + } +}", + + @" +class Program +{ + static void Main() + { + if (true) { return; } + else + { + return; + } + } +}", + index: 0, + compareTokens: false); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + public async Task FireForIfNestedInElseWithoutBraces() + { + await TestAsync( + @" +class Program +{ + static void Main() + { + if (true) return; + else [|if|] (false) return; + } +}", + + @" +class Program +{ + static void Main() + { + if (true) return; + else if (false) + { + return; + } + } +}", + index: 0, + compareTokens: false); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + public async Task FireForForWithoutBraces() + { + await TestAsync( + @" +class Program +{ + static void Main() + { + [|for|] (var i = 0; i < 5; i++) return; + } +}", + + @" +class Program +{ + static void Main() + { + for (var i = 0; i < 5; i++) + { + return; + } + } +}", + index: 0, + compareTokens: false); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + public async Task FireForForEachWithoutBraces() + { + await TestAsync( + @" +class Program +{ + static void Main() + { + [|foreach|] (var c in ""test"") return; + } +}", + + @" +class Program +{ + static void Main() + { + foreach (var c in ""test"") + { + return; + } + } +}", + index: 0, + compareTokens: false); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + public async Task FireForWhileWithoutBraces() + { + await TestAsync( + @" +class Program +{ + static void Main() + { + [|while|] (true) return; + } +}", + + @" +class Program +{ + static void Main() + { + while (true) + { + return; + } + } +}", + index: 0, + compareTokens: false); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + public async Task FireForDoWhileWithoutBraces() + { + await TestAsync( + @" +class Program +{ + static void Main() + { + [|do|] return; while (true); + } +}", + + @" +class Program +{ + static void Main() + { + do + { + return; + } + while (true); + } +}", + index: 0, + compareTokens: false); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + public async Task FireForUsingWithoutBraces() + { + await TestAsync( + @" +class Program +{ + static void Main() + { + [|using|] (var f = new Fizz()) + return; + } +} + +class Fizz : IDisposable +{ + public void Dispose() + { + throw new NotImplementedException(); + } +}", + + @" +class Program +{ + static void Main() + { + using (var f = new Fizz()) + { + return; + } + } +} + +class Fizz : IDisposable +{ + public void Dispose() + { + throw new NotImplementedException(); + } +}", + + index: 0, + compareTokens: false); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + public async Task FireForUsingWithoutBracesNestedInUsing() + { + await TestAsync( + @" +class Program +{ + static void Main() + { + using (var f = new Fizz()) + [|using|] (var b = new Buzz()) + return; + } +} + +class Fizz : IDisposable +{ + public void Dispose() + { + throw new NotImplementedException(); + } +} + +class Buzz : IDisposable +{ + public void Dispose() + { + throw new NotImplementedException(); + } +}", + + @" +class Program +{ + static void Main() + { + using (var f = new Fizz()) + using (var b = new Buzz()) + { + return; + } + } +} + +class Fizz : IDisposable +{ + public void Dispose() + { + throw new NotImplementedException(); + } +} + +class Buzz : IDisposable +{ + public void Dispose() + { + throw new NotImplementedException(); + } +}", + + index: 0, + compareTokens: false); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + public async Task FireForLockWithoutBraces() + { + await TestAsync( + @" +class Program +{ + static void Main() + { + var str = ""test""; + [|lock|] (str) + return; + } +}", + + @" +class Program +{ + static void Main() + { + var str = ""test""; + lock (str) + { + return; + } + } +}", + + index: 0, + compareTokens: false); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddBraces)] + public async Task FireForLockWithoutBracesNestedInLock() + { + await TestAsync( + @" +class Program +{ + static void Main() + { + var str1 = ""test""; + var str2 = ""test""; + + lock (str1) + [|lock|] (str2) // VS thinks this should be indented one more level + return; + } +}", + + @" +class Program +{ + static void Main() + { + var str1 = ""test""; + var str2 = ""test""; + + lock (str1) + lock (str2) // VS thinks this should be indented one more level + { + return; + } + } +}", + + index: 0, + compareTokens: false); + } + } +} \ No newline at end of file diff --git a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj index a76f3747eb359..99ca30785b2d4 100644 --- a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj +++ b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj @@ -121,6 +121,8 @@ + + diff --git a/src/EditorFeatures/TestUtilities/Traits.cs b/src/EditorFeatures/TestUtilities/Traits.cs index 2d7bc0d0fd5f8..05cd4d22e817a 100644 --- a/src/EditorFeatures/TestUtilities/Traits.cs +++ b/src/EditorFeatures/TestUtilities/Traits.cs @@ -30,6 +30,7 @@ public static class Features public const string CodeActionsAddConstructorParameters = "CodeActions.AddConstructorParameters"; public const string CodeActionsAddAsync = "CodeActions.AddAsync"; public const string CodeActionsAddAwait = "CodeActions.AddAwait"; + public const string CodeActionsAddBraces = "CodeActions.AddBraces"; public const string CodeActionsAddImport = "CodeActions.AddImport"; public const string CodeActionsAddMissingReference = "CodeActions.AddMissingReference"; public const string CodeActionsAddUsing = "CodeActions.AddUsing"; diff --git a/src/Features/CSharp/Portable/AddBraces/CSharpAddBracesCodeFixProvider.cs b/src/Features/CSharp/Portable/AddBraces/CSharpAddBracesCodeFixProvider.cs new file mode 100644 index 0000000000000..fe6e40848e26c --- /dev/null +++ b/src/Features/CSharp/Portable/AddBraces/CSharpAddBracesCodeFixProvider.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.Diagnostics.AddBraces +{ + [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AddBraces), Shared] + [ExtensionOrder(After = PredefinedCodeFixProviderNames.AddAwait)] + internal class CSharpAddBracesCodeFixProvider : CodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(IDEDiagnosticIds.AddBracesDiagnosticId); + + public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; + + public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) + { + context.RegisterCodeFix( + new MyCodeAction( + FeaturesResources.AddBraces, + c => AddBracesAsync(context, c)), + context.Diagnostics); + + return SpecializedTasks.EmptyTask; + } + + protected async Task AddBracesAsync(CodeFixContext context, CancellationToken cancellationToken) + { + var root = await context.Document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var diagnostic = context.Diagnostics.First(); + var diagnosticSpan = diagnostic.Location.SourceSpan; + var statement = root.FindNode(diagnosticSpan); + + var newRoot = root.ReplaceNode(statement, GetReplacementNode(statement)); + return context.Document.WithSyntaxRoot(newRoot); + } + + private SyntaxNode GetReplacementNode(SyntaxNode statement) + { + switch (statement.Kind()) + { + case SyntaxKind.IfStatement: + var ifSyntax = (IfStatementSyntax)statement; + return GetNewBlock(statement, ifSyntax.Statement); + + case SyntaxKind.ElseClause: + var elseClause = (ElseClauseSyntax)statement; + return GetNewBlock(statement, elseClause.Statement); + + case SyntaxKind.ForStatement: + var forSyntax = (ForStatementSyntax)statement; + return GetNewBlock(statement, forSyntax.Statement); + + case SyntaxKind.ForEachStatement: + var forEachSyntax = (ForEachStatementSyntax)statement; + return GetNewBlock(statement, forEachSyntax.Statement); + + case SyntaxKind.WhileStatement: + var whileSyntax = (WhileStatementSyntax)statement; + return GetNewBlock(statement, whileSyntax.Statement); + + case SyntaxKind.DoStatement: + var doSyntax = (DoStatementSyntax)statement; + return GetNewBlock(statement, doSyntax.Statement); + + case SyntaxKind.UsingStatement: + var usingSyntax = (UsingStatementSyntax)statement; + return GetNewBlock(statement, usingSyntax.Statement); + + case SyntaxKind.LockStatement: + var lockSyntax = (LockStatementSyntax)statement; + return GetNewBlock(statement, lockSyntax.Statement); + } + + return default(SyntaxNode); + } + + private SyntaxNode GetNewBlock(SyntaxNode statement, StatementSyntax statementBody) => + statement.ReplaceNode(statementBody, SyntaxFactory.Block(statementBody)); + + private class MyCodeAction : CodeAction.DocumentChangeAction + { + public MyCodeAction(string title, Func> createChangedDocument) : + base(title, createChangedDocument) + { + } + } + } +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/AddBraces/CSharpAddBracesDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/AddBraces/CSharpAddBracesDiagnosticAnalyzer.cs new file mode 100644 index 0000000000000..c55c0d791bfe7 --- /dev/null +++ b/src/Features/CSharp/Portable/AddBraces/CSharpAddBracesDiagnosticAnalyzer.cs @@ -0,0 +1,156 @@ +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp.Diagnostics.AddBraces +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal sealed class CSharpAddBracesDiagnosticAnalyzer : DiagnosticAnalyzer, IBuiltInAnalyzer + { + private static readonly LocalizableString s_localizableTitle = + new LocalizableResourceString(nameof(FeaturesResources.AddBraces), FeaturesResources.ResourceManager, + typeof(FeaturesResources)); + + private static readonly LocalizableString s_localizableMessage = + new LocalizableResourceString(nameof(WorkspacesResources.AddBraces), WorkspacesResources.ResourceManager, + typeof(WorkspacesResources)); + + private static readonly DiagnosticDescriptor s_descriptor = new DiagnosticDescriptor(IDEDiagnosticIds.AddBracesDiagnosticId, + s_localizableTitle, + s_localizableMessage, + DiagnosticCategory.Style, + DiagnosticSeverity.Hidden, + isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(s_descriptor); + + public override void Initialize(AnalysisContext context) + { + var syntaxKindsOfInterest = + ImmutableArray.Create(SyntaxKind.IfStatement, + SyntaxKind.ElseClause, + SyntaxKind.ForStatement, + SyntaxKind.ForEachStatement, + SyntaxKind.WhileStatement, + SyntaxKind.DoStatement, + SyntaxKind.UsingStatement, + SyntaxKind.LockStatement); + + context.RegisterSyntaxNodeAction(AnalyzeNode, syntaxKindsOfInterest); + } + + public DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SemanticSpanAnalysis; + + public void AnalyzeNode(SyntaxNodeAnalysisContext context) + { + var node = context.Node; + + if (node.IsKind(SyntaxKind.IfStatement)) + { + var ifStatement = (IfStatementSyntax)node; + if (AnalyzeIfStatement(ifStatement)) + { + context.ReportDiagnostic(Diagnostic.Create(s_descriptor, + ifStatement.IfKeyword.GetLocation(), SyntaxFacts.GetText(SyntaxKind.IfKeyword))); + } + } + + if (node.IsKind(SyntaxKind.ElseClause)) + { + var elseClause = (ElseClauseSyntax)node; + if (AnalyzeElseClause(elseClause)) + { + context.ReportDiagnostic(Diagnostic.Create(s_descriptor, + elseClause.ElseKeyword.GetLocation(), SyntaxFacts.GetText(SyntaxKind.ElseKeyword))); + } + } + + if (node.IsKind(SyntaxKind.ForStatement)) + { + var forStatement = (ForStatementSyntax)node; + if (AnalyzeForStatement(forStatement)) + { + context.ReportDiagnostic(Diagnostic.Create(s_descriptor, + forStatement.ForKeyword.GetLocation(), SyntaxFacts.GetText(SyntaxKind.ForKeyword))); + } + } + + if (node.IsKind(SyntaxKind.ForEachStatement)) + { + var forEachStatement = (ForEachStatementSyntax)node; + if (AnalyzeForEachStatement(forEachStatement)) + { + context.ReportDiagnostic(Diagnostic.Create(s_descriptor, + forEachStatement.ForEachKeyword.GetLocation(), SyntaxFacts.GetText(SyntaxKind.ForEachKeyword))); + } + } + + if (node.IsKind(SyntaxKind.WhileStatement)) + { + var whileStatement = (WhileStatementSyntax)node; + if (AnalyzeWhileStatement(whileStatement)) + { + context.ReportDiagnostic(Diagnostic.Create(s_descriptor, + whileStatement.WhileKeyword.GetLocation(), SyntaxFacts.GetText(SyntaxKind.WhileKeyword))); + } + } + + if (node.IsKind(SyntaxKind.DoStatement)) + { + var doStatement = (DoStatementSyntax)node; + if (AnalyzeDoStatement(doStatement)) + { + context.ReportDiagnostic(Diagnostic.Create(s_descriptor, + doStatement.DoKeyword.GetLocation(), SyntaxFacts.GetText(SyntaxKind.DoKeyword))); + } + } + + if (node.IsKind(SyntaxKind.UsingStatement)) + { + var usingStatement = (UsingStatementSyntax)context.Node; + if (AnalyzeUsingStatement(usingStatement)) + { + context.ReportDiagnostic(Diagnostic.Create(s_descriptor, + usingStatement.UsingKeyword.GetLocation(), SyntaxFacts.GetText(SyntaxKind.UsingKeyword))); + } + } + + if (node.IsKind(SyntaxKind.LockStatement)) + { + var lockStatement = (LockStatementSyntax)context.Node; + if (AnalyzeLockStatement(lockStatement)) + { + context.ReportDiagnostic(Diagnostic.Create(s_descriptor, + lockStatement.LockKeyword.GetLocation(), SyntaxFacts.GetText(SyntaxKind.LockKeyword))); + } + } + } + + private bool AnalyzeIfStatement(IfStatementSyntax ifStatement) => + !ifStatement.Statement.IsKind(SyntaxKind.Block); + + private bool AnalyzeElseClause(ElseClauseSyntax elseClause) => + !elseClause.Statement.IsKind(SyntaxKind.Block) && + !elseClause.Statement.IsKind(SyntaxKind.IfStatement); + + private bool AnalyzeForStatement(ForStatementSyntax forStatement) => + !forStatement.Statement.IsKind(SyntaxKind.Block); + + private bool AnalyzeForEachStatement(ForEachStatementSyntax forEachStatement) => + !forEachStatement.Statement.IsKind(SyntaxKind.Block); + + private bool AnalyzeWhileStatement(WhileStatementSyntax whileStatement) => + !whileStatement.Statement.IsKind(SyntaxKind.Block); + + private bool AnalyzeDoStatement(DoStatementSyntax doStatement) => + !doStatement.Statement.IsKind(SyntaxKind.Block); + + private bool AnalyzeUsingStatement(UsingStatementSyntax usingStatement) => + !usingStatement.Statement.IsKind(SyntaxKind.Block) && + !usingStatement.Statement.IsKind(SyntaxKind.UsingStatement); + + private bool AnalyzeLockStatement(LockStatementSyntax lockStatement) => + !lockStatement.Statement.IsKind(SyntaxKind.Block) && + !lockStatement.Statement.IsKind(SyntaxKind.LockStatement); + } +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/CSharpFeatures.csproj b/src/Features/CSharp/Portable/CSharpFeatures.csproj index 4754af6c0c27f..3afce75098375 100644 --- a/src/Features/CSharp/Portable/CSharpFeatures.csproj +++ b/src/Features/CSharp/Portable/CSharpFeatures.csproj @@ -58,6 +58,8 @@ InternalUtilities\LambdaUtilities.cs + + diff --git a/src/Features/Core/Portable/CodeFixes/PredefinedCodeFixProviderNames.cs b/src/Features/Core/Portable/CodeFixes/PredefinedCodeFixProviderNames.cs index 04b55ede8ae42..05b705dcd77c5 100644 --- a/src/Features/Core/Portable/CodeFixes/PredefinedCodeFixProviderNames.cs +++ b/src/Features/Core/Portable/CodeFixes/PredefinedCodeFixProviderNames.cs @@ -7,6 +7,7 @@ internal static class PredefinedCodeFixProviderNames public const string AddAwait = "Add Await For Expression"; public const string AddAsync = "Add Async To Member"; public const string ApplyNamingStyle = "Apply Naming Style"; + public const string AddBraces = "Add Braces"; public const string ChangeReturnType = "Change Return Type"; public const string ChangeToYield = "Change To Yield"; public const string ConvertToAsync = "Convert To Async"; diff --git a/src/Features/Core/Portable/Diagnostics/Analyzers/IDEDiagnosticIds.cs b/src/Features/Core/Portable/Diagnostics/Analyzers/IDEDiagnosticIds.cs index 5cc2ac8ed844d..1a7baf126ed01 100644 --- a/src/Features/Core/Portable/Diagnostics/Analyzers/IDEDiagnosticIds.cs +++ b/src/Features/Core/Portable/Diagnostics/Analyzers/IDEDiagnosticIds.cs @@ -10,10 +10,11 @@ internal static class IDEDiagnosticIds public const string RemoveUnnecessaryCastDiagnosticId = "IDE0004"; public const string RemoveUnnecessaryImportsDiagnosticId = "IDE0005"; public const string IntellisenseBuildFailedDiagnosticId = "IDE0006"; - public const string PopulateSwitchDiagnosticId = "IDE0010"; public const string UseImplicitTypeDiagnosticId = "IDE0007"; public const string UseExplicitTypeDiagnosticId = "IDE0008"; public const string AddQualificationDiagnosticId = "IDE0009"; + public const string PopulateSwitchDiagnosticId = "IDE0010"; + public const string AddBracesDiagnosticId = "IDE0011"; // Analyzer error Ids public const string AnalyzerChangedId = "IDE1001"; diff --git a/src/Features/Core/Portable/FeaturesResources.Designer.cs b/src/Features/Core/Portable/FeaturesResources.Designer.cs index 0ddee1ef1b547..4f7580c97822b 100644 --- a/src/Features/Core/Portable/FeaturesResources.Designer.cs +++ b/src/Features/Core/Portable/FeaturesResources.Designer.cs @@ -105,6 +105,15 @@ internal static string Add_missing_switch_cases { return ResourceManager.GetString("Add_missing_switch_cases", resourceCulture); } } + + /// + /// Looks up a localized string similar to Add braces. + /// + internal static string AddBraces { + get { + return ResourceManager.GetString("AddBraces", resourceCulture); + } + } /// /// Looks up a localized string similar to Adding '{0}' around an active statement will prevent the debug session from continuing.. diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index 9dd02027a2b98..5c9e26a6f11b5 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -1013,4 +1013,7 @@ This version used in: {2} Variadic SignatureHelpItem must have at least one parameter. + + Add braces + \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/WorkspacesResources.Designer.cs b/src/Workspaces/Core/Portable/WorkspacesResources.Designer.cs index fc44ee628f48f..d751975ac3af5 100644 --- a/src/Workspaces/Core/Portable/WorkspacesResources.Designer.cs +++ b/src/Workspaces/Core/Portable/WorkspacesResources.Designer.cs @@ -70,6 +70,15 @@ internal static string AbsolutePathExpected { } } + /// + /// Looks up a localized string similar to Add braces to '{0}' statement.. + /// + internal static string AddBraces { + get { + return ResourceManager.GetString("AddBraces", resourceCulture); + } + } + /// /// Looks up a localized string similar to Added:. /// diff --git a/src/Workspaces/Core/Portable/WorkspacesResources.resx b/src/Workspaces/Core/Portable/WorkspacesResources.resx index 980ebbcbec55d..b060e8d1201b1 100644 --- a/src/Workspaces/Core/Portable/WorkspacesResources.resx +++ b/src/Workspaces/Core/Portable/WorkspacesResources.resx @@ -471,4 +471,7 @@ Member access should be qualified. + + Add braces to '{0}' statement. + \ No newline at end of file