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