From 2b9d5352abcc77b869dcfdc7047ad4080cff6375 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 30 Jan 2019 15:11:12 -0800 Subject: [PATCH] Add feature to suggest moving to simplified using statements --- .../UseSimpleUsingStatementCodeFixProvider.cs | 203 ++++++++++++++++++ ...eSimpleUsingStatementDiagnosticAnalyzer.cs | 136 ++++++++++++ .../Diagnostics/Analyzers/IDEDiagnosticIds.cs | 1 + .../Portable/FeaturesResources.Designer.cs | 18 ++ .../Core/Portable/FeaturesResources.resx | 6 + .../Portable/xlf/FeaturesResources.cs.xlf | 10 + .../Portable/xlf/FeaturesResources.de.xlf | 10 + .../Portable/xlf/FeaturesResources.es.xlf | 10 + .../Portable/xlf/FeaturesResources.fr.xlf | 10 + .../Portable/xlf/FeaturesResources.it.xlf | 10 + .../Portable/xlf/FeaturesResources.ja.xlf | 10 + .../Portable/xlf/FeaturesResources.ko.xlf | 10 + .../Portable/xlf/FeaturesResources.pl.xlf | 10 + .../Portable/xlf/FeaturesResources.pt-BR.xlf | 10 + .../Portable/xlf/FeaturesResources.ru.xlf | 10 + .../Portable/xlf/FeaturesResources.tr.xlf | 10 + .../xlf/FeaturesResources.zh-Hans.xlf | 10 + .../xlf/FeaturesResources.zh-Hant.xlf | 10 + .../CodeStyle/CSharpCodeStyleOptions.cs | 7 + ...yntaxNodeExtensions_SharedWithCodeStyle.cs | 3 +- 20 files changed, 502 insertions(+), 2 deletions(-) create mode 100644 src/Features/CSharp/Portable/UseSimpleUsingStatement/UseSimpleUsingStatementCodeFixProvider.cs create mode 100644 src/Features/CSharp/Portable/UseSimpleUsingStatement/UseSimpleUsingStatementDiagnosticAnalyzer.cs diff --git a/src/Features/CSharp/Portable/UseSimpleUsingStatement/UseSimpleUsingStatementCodeFixProvider.cs b/src/Features/CSharp/Portable/UseSimpleUsingStatement/UseSimpleUsingStatementCodeFixProvider.cs new file mode 100644 index 00000000000..e1c9d856e7b --- /dev/null +++ b/src/Features/CSharp/Portable/UseSimpleUsingStatement/UseSimpleUsingStatementCodeFixProvider.cs @@ -0,0 +1,203 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +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 Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.UseSimpleUsingDeclaration +{ + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(UseSimpleUsingStatementCodeFixProvider)), Shared] + internal class UseSimpleUsingStatementCodeFixProvider : SyntaxEditorBasedCodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds { get; } = + ImmutableArray.Create(IDEDiagnosticIds.UseSimpleUsingStatementDiagnosticId); + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + context.RegisterCodeFix(new MyCodeAction( + c => FixAsync(context.Document, context.Diagnostics[0], c)), + context.Diagnostics); + + return Task.CompletedTask; + } + + protected override Task FixAllAsync( + Document document, ImmutableArray diagnostics, + SyntaxEditor editor, CancellationToken cancellationToken) + { + var usingStatements = diagnostics.Select(d => (UsingStatementSyntax)d.AdditionalLocations[0].FindNode(cancellationToken)).ToSet(); + + var rewriter = new Rewriter(usingStatements); + var root = editor.OriginalRoot; + var updatedRoot = rewriter.Visit(root); + + editor.ReplaceNode(root, updatedRoot); + + return Task.CompletedTask; + } + + private class Rewriter : CSharpSyntaxRewriter + { + private readonly ISet _usingStatements; + + public Rewriter(ISet usingStatements) + { + _usingStatements = usingStatements; + } + + private static SyntaxList Expand(UsingStatementSyntax usingStatement) + { + var builder = ArrayBuilder.GetInstance(); + builder.Add(Convert(usingStatement)); + if (usingStatement.Statement is BlockSyntax block) + { + builder.AddRange(block.Statements); + } + else + { + builder.Add(usingStatement.Statement); + } + + var statements = new SyntaxList(builder); + builder.Free(); + return statements; + } + + private bool ShouldExpand(StatementSyntax beforeStatement, StatementSyntax afterStatement) + => _usingStatements.Contains(beforeStatement) && + afterStatement is UsingStatementSyntax usingStatement && + usingStatement.Declaration != null; + + private static LocalDeclarationStatementSyntax Convert(UsingStatementSyntax usingStatement) + { + return SyntaxFactory.LocalDeclarationStatement( + usingStatement.AwaitKeyword, + usingStatement.UsingKeyword, + modifiers: default, + usingStatement.Declaration, + SyntaxFactory.Token(SyntaxKind.SemicolonToken)); + } + + public override SyntaxNode VisitElseClause(ElseClauseSyntax node) + { + // First, descend into the else-clause so we fixup any using statements contained + // inside of it. + var rewrittenElse = (ElseClauseSyntax)base.VisitElseClause(node); + + // Now, if the else-clause previous pointed at a using statement we wanted to + // rewrite, and it still points at a using statement, then expand that using + // statemnet into the else-clause. + var rewrittenStatement = rewrittenElse.Statement; + if (ShouldExpand(node.Statement, rewrittenStatement)) + { + // Can't expand a using-statement directly inside an else-clause. + // have to add a block around it to make sure scoping is preserved + // properly. + var expanded = Expand((UsingStatementSyntax)rewrittenStatement); + return rewrittenElse.WithStatement(SyntaxFactory.Block(expanded)) + .WithAdditionalAnnotations(Formatter.Annotation); + } + + return rewrittenElse; + } + + private bool ExpandAppropriateStatements( + SyntaxList originalStatements, + SyntaxList rewrittenStatements, + ArrayBuilder finalStatements) + { + var changed = false; + if (originalStatements.Count == rewrittenStatements.Count) + { + for (int i = 0, n = rewrittenStatements.Count; i < n; i++) + { + var rewrittenStatement = rewrittenStatements[i]; + if (ShouldExpand(originalStatements[i], rewrittenStatement)) + { + var expanded = Expand((UsingStatementSyntax)rewrittenStatement); + finalStatements.AddRange(expanded); + changed = true; + } + else + { + finalStatements.Add(rewrittenStatement); + } + } + } + + return changed; + } + + public override SyntaxNode VisitBlock(BlockSyntax node) + { + var finalStatements = ArrayBuilder.GetInstance(); + var result = VisitBlockWorker(node, finalStatements); + finalStatements.Free(); + + return result; + } + + private SyntaxNode VisitBlockWorker(BlockSyntax node, ArrayBuilder finalStatements) + { + var rewrittenBlock = (BlockSyntax)base.VisitBlock(node); + + var changed = ExpandAppropriateStatements(node.Statements, rewrittenBlock.Statements, finalStatements); + + if (!changed) + { + // Didn't update any children. Just return as is. + return rewrittenBlock; + } + + return rewrittenBlock.WithStatements(new SyntaxList(finalStatements)) + .WithAdditionalAnnotations(Formatter.Annotation); + } + + public override SyntaxNode VisitSwitchSection(SwitchSectionSyntax node) + { + var finalStatements = ArrayBuilder.GetInstance(); + var result = VisitSwitchSectionWorker(node, finalStatements); + finalStatements.Free(); + + return result; + } + + private SyntaxNode VisitSwitchSectionWorker(SwitchSectionSyntax node, ArrayBuilder finalStatements) + { + var rewrittenSwitchSection = (SwitchSectionSyntax)base.VisitSwitchSection(node); + + var changed = ExpandAppropriateStatements(node.Statements, rewrittenSwitchSection.Statements, finalStatements); + + if (!changed) + { + // Didn't update any children. Just return as is. + return rewrittenSwitchSection; + } + + return rewrittenSwitchSection.WithStatements(new SyntaxList(finalStatements)) + .WithAdditionalAnnotations(Formatter.Annotation); + } + } + + private class MyCodeAction : CodeAction.DocumentChangeAction + { + public MyCodeAction(Func> createChangedDocument) + : base(FeaturesResources.Use_simple_using_statement, createChangedDocument, FeaturesResources.Use_simple_using_statement) + { + } + } + } +} diff --git a/src/Features/CSharp/Portable/UseSimpleUsingStatement/UseSimpleUsingStatementDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseSimpleUsingStatement/UseSimpleUsingStatementDiagnosticAnalyzer.cs new file mode 100644 index 00000000000..7b148173642 --- /dev/null +++ b/src/Features/CSharp/Portable/UseSimpleUsingStatement/UseSimpleUsingStatementDiagnosticAnalyzer.cs @@ -0,0 +1,136 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Extensions; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.UseSimpleUsingStatement +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal class UseSimpleUsingStatementDiagnosticAnalyzer : AbstractBuiltInCodeStyleDiagnosticAnalyzer + { + public UseSimpleUsingStatementDiagnosticAnalyzer() + : base(IDEDiagnosticIds.UseSimpleUsingStatementDiagnosticId, + new LocalizableResourceString(nameof(FeaturesResources.Use_simple_using_statement), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + new LocalizableResourceString(nameof(FeaturesResources.using_statment_can_be_simplified), FeaturesResources.ResourceManager, typeof(FeaturesResources))) + { + } + + public override bool OpenFileOnly(Workspace workspace) => false; + public override DiagnosticAnalyzerCategory GetAnalyzerCategory() + => DiagnosticAnalyzerCategory.SemanticSpanAnalysis; + + protected override void InitializeWorker(AnalysisContext context) + => context.RegisterSyntaxNodeAction(AnalyzeSyntax, SyntaxKind.UsingStatement); + + private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) + { + var usingStatement = (UsingStatementSyntax)context.Node; + if (usingStatement.Declaration == null) + { + return; + } + + var syntaxTree = context.Node.SyntaxTree; + //var options = (CSharpParseOptions)syntaxTree.Options; + //if (options.LanguageVersion < LanguageVersion.CSharp8) + //{ + // return; + //} + + var cancellationToken = context.CancellationToken; + var optionSet = context.Options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult(); + if (optionSet == null) + { + return; + } + + var option = optionSet.GetOption(CSharpCodeStyleOptions.PreferStaticLocalFunction); + if (!option.Value) + { + return; + } + + var parent = usingStatement.Parent; + var okParent = parent is BlockSyntax || + parent is SwitchSectionSyntax; + if (!okParent) + { + return; + } + + // Has to be one of the following forms: + // 1. Using statement is the last statement in the parent. + // 2. Using statement is not the last statment in parent, but is followed by + // something that is unaffected by simplifying the using statement. i.e. + // `return`/`break`/`continue`. *Note*. `return expr` would *not* be ok. + // In that case, `expr` would now be evaluated *before* the using disposed + // the resource, instead of afterwards. Effectly, the statement following + // cannot actually execute any code that might depend on the .Dispose method + // being called or not. + var statements = GetStatements(parent, usingStatement); + if (CanConvertUsingStatement(statements, usingStatement)) + { + context.ReportDiagnostic(DiagnosticHelper.Create( + Descriptor, + usingStatement.UsingKeyword.GetLocation(), + option.Notification.Severity, + additionalLocations: ImmutableArray.Create(usingStatement.GetLocation()), + properties: null)); + } + } + + private bool CanConvertUsingStatement( + SyntaxList statements, + UsingStatementSyntax usingStatement) + { + var index = statements.IndexOf(usingStatement); + if (index == statements.Count - 1) + { + // very last statement in the block. Can be converted. + return true; + } + + // Not the last statement, get the next statement and examine that. + var nextStatement = statements[index + 1]; + if (nextStatement is BreakStatementSyntax || + nextStatement is ContinueStatementSyntax) + { + // using statemnet followed by break/continue. Can conver this as executing + // the break/continue will cause the code to exit the using scope, causing + // Dispose to be called at the same place as before. + return true; + } + + if (nextStatement is ReturnStatementSyntax returnStatement && + returnStatement.Expression == null) + { + // using statemnet followed by `return`. Can conver this as executing + // the `return` will cause the code to exit the using scope, causing + // Dispose to be called at the same place as before. + // + // Note: the expr has to be null. If it was non-null, then the expr would + // now execute before hte using called 'Dispose' instead of after, potentially + // changing semantics. + return true; + } + + // Add any additional cases here in the future. + return false; + } + + private SyntaxList GetStatements(SyntaxNode parent, UsingStatementSyntax usingStatement) + { + switch (parent) + { + case BlockSyntax block: return block.Statements; + case SwitchSectionSyntax switchSection: return switchSection.Statements; + default: throw ExceptionUtilities.UnexpectedValue(parent); + } + } + } +} diff --git a/src/Features/Core/Portable/Diagnostics/Analyzers/IDEDiagnosticIds.cs b/src/Features/Core/Portable/Diagnostics/Analyzers/IDEDiagnosticIds.cs index 0665d15166f..536e1708582 100644 --- a/src/Features/Core/Portable/Diagnostics/Analyzers/IDEDiagnosticIds.cs +++ b/src/Features/Core/Portable/Diagnostics/Analyzers/IDEDiagnosticIds.cs @@ -103,6 +103,7 @@ internal static class IDEDiagnosticIds public const string UseExpressionBodyForLocalFunctionsDiagnosticId = "IDE0061"; public const string MakeLocalFunctionStaticDiagnosticId = "IDE0062"; + public const string UseSimpleUsingStatementDiagnosticId = "IDE0063"; // 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 8fe37590fbe..b2ee52299d2 100644 --- a/src/Features/Core/Portable/FeaturesResources.Designer.cs +++ b/src/Features/Core/Portable/FeaturesResources.Designer.cs @@ -4402,6 +4402,15 @@ internal class FeaturesResources { } } + /// + /// Looks up a localized string similar to Use simple 'using' statement. + /// + internal static string Use_simple_using_statement { + get { + return ResourceManager.GetString("Use_simple_using_statement", resourceCulture); + } + } + /// /// Looks up a localized string similar to Use 'throw' expression. /// @@ -4447,6 +4456,15 @@ internal class FeaturesResources { } } + /// + /// Looks up a localized string similar to 'using' statement can be simplified. + /// + internal static string using_statment_can_be_simplified { + get { + return ResourceManager.GetString("using_statment_can_be_simplified", resourceCulture); + } + } + /// /// Looks up a localized string similar to Value assigned to '{0}' is never used. /// diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index fd8110ed5d3..5e57516b2d4 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -1631,4 +1631,10 @@ This version used in: {2} Make local function 'static' + + Use simple 'using' statement + + + 'using' statement can be simplified + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf index a27fb9bff9a..375e804f49b 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -377,6 +377,11 @@ Use interpolated verbatim string + + Use simple 'using' statement + Use simple 'using' statement + + Warning: Changing namespace may produce invalid code and change code meaning. Warning: Changing namespace may produce invalid code and change code meaning. @@ -2470,6 +2475,11 @@ Tato verze se používá zde: {2}. updating usages in dependent projects + + 'using' statement can be simplified + 'using' statement can be simplified + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index 88f3ba7fcf4..fab08a51346 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -377,6 +377,11 @@ Use interpolated verbatim string + + Use simple 'using' statement + Use simple 'using' statement + + Warning: Changing namespace may produce invalid code and change code meaning. Warning: Changing namespace may produce invalid code and change code meaning. @@ -2470,6 +2475,11 @@ Diese Version wird verwendet in: {2} updating usages in dependent projects + + 'using' statement can be simplified + 'using' statement can be simplified + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index 9b6ec04e243..cbda18961de 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -377,6 +377,11 @@ Use interpolated verbatim string + + Use simple 'using' statement + Use simple 'using' statement + + Warning: Changing namespace may produce invalid code and change code meaning. Warning: Changing namespace may produce invalid code and change code meaning. @@ -2470,6 +2475,11 @@ Esta versión se utiliza en: {2} updating usages in dependent projects + + 'using' statement can be simplified + 'using' statement can be simplified + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index 7b66925d025..280794b3f23 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -377,6 +377,11 @@ Use interpolated verbatim string + + Use simple 'using' statement + Use simple 'using' statement + + Warning: Changing namespace may produce invalid code and change code meaning. Warning: Changing namespace may produce invalid code and change code meaning. @@ -2470,6 +2475,11 @@ Version utilisée dans : {2} updating usages in dependent projects + + 'using' statement can be simplified + 'using' statement can be simplified + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index 40778a5eb72..248537f702a 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -377,6 +377,11 @@ Use interpolated verbatim string + + Use simple 'using' statement + Use simple 'using' statement + + Warning: Changing namespace may produce invalid code and change code meaning. Warning: Changing namespace may produce invalid code and change code meaning. @@ -2470,6 +2475,11 @@ Questa versione è usata {2} updating usages in dependent projects + + 'using' statement can be simplified + 'using' statement can be simplified + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index 3b75c024121..b0273d858cb 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -377,6 +377,11 @@ Use interpolated verbatim string + + Use simple 'using' statement + Use simple 'using' statement + + Warning: Changing namespace may produce invalid code and change code meaning. Warning: Changing namespace may produce invalid code and change code meaning. @@ -2470,6 +2475,11 @@ This version used in: {2} updating usages in dependent projects + + 'using' statement can be simplified + 'using' statement can be simplified + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index 63f1f81673b..891ea170154 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -377,6 +377,11 @@ Use interpolated verbatim string + + Use simple 'using' statement + Use simple 'using' statement + + Warning: Changing namespace may produce invalid code and change code meaning. Warning: Changing namespace may produce invalid code and change code meaning. @@ -2470,6 +2475,11 @@ This version used in: {2} updating usages in dependent projects + + 'using' statement can be simplified + 'using' statement can be simplified + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index 9b14d55597f..514c2461240 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -377,6 +377,11 @@ Use interpolated verbatim string + + Use simple 'using' statement + Use simple 'using' statement + + Warning: Changing namespace may produce invalid code and change code meaning. Warning: Changing namespace may produce invalid code and change code meaning. @@ -2470,6 +2475,11 @@ Ta wersja jest używana wersja: {2} updating usages in dependent projects + + 'using' statement can be simplified + 'using' statement can be simplified + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index 3bd52be8830..5d75aaf5e96 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -377,6 +377,11 @@ Use interpolated verbatim string + + Use simple 'using' statement + Use simple 'using' statement + + Warning: Changing namespace may produce invalid code and change code meaning. Warning: Changing namespace may produce invalid code and change code meaning. @@ -2470,6 +2475,11 @@ Essa versão é usada no: {2} updating usages in dependent projects + + 'using' statement can be simplified + 'using' statement can be simplified + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index 1ad760d5197..b2b41fcae98 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -377,6 +377,11 @@ Use interpolated verbatim string + + Use simple 'using' statement + Use simple 'using' statement + + Warning: Changing namespace may produce invalid code and change code meaning. Warning: Changing namespace may produce invalid code and change code meaning. @@ -2470,6 +2475,11 @@ This version used in: {2} updating usages in dependent projects + + 'using' statement can be simplified + 'using' statement can be simplified + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index 7dfc60479e1..94fbb33931f 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -377,6 +377,11 @@ Use interpolated verbatim string + + Use simple 'using' statement + Use simple 'using' statement + + Warning: Changing namespace may produce invalid code and change code meaning. Warning: Changing namespace may produce invalid code and change code meaning. @@ -2470,6 +2475,11 @@ Bu sürüm şurada kullanılır: {2} updating usages in dependent projects + + 'using' statement can be simplified + 'using' statement can be simplified + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index 793a5acecd4..63bdf5a0f9b 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -377,6 +377,11 @@ Use interpolated verbatim string + + Use simple 'using' statement + Use simple 'using' statement + + Warning: Changing namespace may produce invalid code and change code meaning. Warning: Changing namespace may produce invalid code and change code meaning. @@ -2470,6 +2475,11 @@ This version used in: {2} updating usages in dependent projects + + 'using' statement can be simplified + 'using' statement can be simplified + + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index 7736706ee92..f1635cf9274 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -377,6 +377,11 @@ Use interpolated verbatim string + + Use simple 'using' statement + Use simple 'using' statement + + Warning: Changing namespace may produce invalid code and change code meaning. Warning: Changing namespace may produce invalid code and change code meaning. @@ -2470,6 +2475,11 @@ This version used in: {2} updating usages in dependent projects + + 'using' statement can be simplified + 'using' statement can be simplified + + \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/CodeStyle/CSharpCodeStyleOptions.cs b/src/Workspaces/CSharp/Portable/CodeStyle/CSharpCodeStyleOptions.cs index 030ee1ba46c..f77606ce588 100644 --- a/src/Workspaces/CSharp/Portable/CodeStyle/CSharpCodeStyleOptions.cs +++ b/src/Workspaces/CSharp/Portable/CodeStyle/CSharpCodeStyleOptions.cs @@ -213,6 +213,13 @@ private static Option CreateOption(OptionGroup group, string name, T defau EditorConfigStorageLocation.ForBoolCodeStyleOption("csharp_prefer_static_local_function"), new RoamingProfileStorageLocation($"TextEditor.CSharp.Specific.{nameof(PreferStaticLocalFunction)}")}); + public static readonly Option> PreferSimpleUsingStatement = CreateOption( + CSharpCodeStyleOptionGroups.CodeBlockPreferences, nameof(PreferSimpleUsingStatement), + defaultValue: CodeStyleOptions.TrueWithSuggestionEnforcement, + storageLocations: new OptionStorageLocation[] { + EditorConfigStorageLocation.ForBoolCodeStyleOption("csharp_prefer_simple_using_statement"), + new RoamingProfileStorageLocation($"TextEditor.CSharp.Specific.{nameof(PreferSimpleUsingStatement)}")}); + public static readonly Option> PreferLocalOverAnonymousFunction = CreateOption( CSharpCodeStyleOptionGroups.ExpressionLevelPreferences, nameof(PreferLocalOverAnonymousFunction), defaultValue: CodeStyleOptions.TrueWithSuggestionEnforcement, diff --git a/src/Workspaces/CSharp/Portable/Extensions/SyntaxNodeExtensions_SharedWithCodeStyle.cs b/src/Workspaces/CSharp/Portable/Extensions/SyntaxNodeExtensions_SharedWithCodeStyle.cs index 82b0f5a301b..1333fa29e72 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/SyntaxNodeExtensions_SharedWithCodeStyle.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/SyntaxNodeExtensions_SharedWithCodeStyle.cs @@ -201,8 +201,7 @@ public static (SyntaxToken openBrace, SyntaxToken closeBrace) GetBraces(this Syn public static bool IsEmbeddedStatementOwner(this SyntaxNode node) { - return - node is DoStatementSyntax || + return node is DoStatementSyntax || node is ElseClauseSyntax || node is FixedStatementSyntax || node is CommonForEachStatementSyntax || -- GitLab