diff --git a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems index f248575e467bb..c4a127094f72d 100644 --- a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems +++ b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems @@ -74,6 +74,7 @@ + diff --git a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx index 622002791820c..6bc5a997a5b67 100644 --- a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx +++ b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx @@ -434,4 +434,10 @@ Implement with Copilot + + Simplify property accessor + + + Property accessor can be simplified + \ No newline at end of file diff --git a/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs b/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs index 0e5b54eb44ac0..15938f5da924f 100644 --- a/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs +++ b/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs @@ -71,6 +71,7 @@ internal CSharpSimplifierOptions GetSimplifierOptions() public CodeStyleOption2 PreferPrimaryConstructors => GetOption(CSharpCodeStyleOptions.PreferPrimaryConstructors); public CodeStyleOption2 PreferSystemThreadingLock => GetOption(CSharpCodeStyleOptions.PreferSystemThreadingLock); public CodeStyleOption2 PreferUnboundGenericTypeInNameOf => GetOption(CSharpCodeStyleOptions.PreferUnboundGenericTypeInNameOf); + public CodeStyleOption2 PreferSimplePropertyAccessors => GetOption(CSharpCodeStyleOptions.PreferSimplePropertyAccessors); // CodeGenerationOptions diff --git a/src/Analyzers/CSharp/Analyzers/SimplifyPropertyAccessor/CSharpSimplifyPropertyAccessorDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/SimplifyPropertyAccessor/CSharpSimplifyPropertyAccessorDiagnosticAnalyzer.cs new file mode 100644 index 0000000000000..f104c65de68c8 --- /dev/null +++ b/src/Analyzers/CSharp/Analyzers/SimplifyPropertyAccessor/CSharpSimplifyPropertyAccessorDiagnosticAnalyzer.cs @@ -0,0 +1,101 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Linq; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Extensions; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp.SimplifyPropertyAccessor; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +internal sealed class CSharpSimplifyPropertyAccessorDiagnosticAnalyzer : AbstractBuiltInCodeStyleDiagnosticAnalyzer +{ + public CSharpSimplifyPropertyAccessorDiagnosticAnalyzer() + : base(IDEDiagnosticIds.SimplifyPropertyAccessorDiagnosticId, + EnforceOnBuildValues.SimplifyPropertyAccessor, + CSharpCodeStyleOptions.PreferSimplePropertyAccessors, + new LocalizableResourceString(nameof(CSharpAnalyzersResources.Simplify_property_accessor), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources)), + new LocalizableResourceString(nameof(CSharpAnalyzersResources.Property_accessor_can_be_simplified), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources))) + { + } + + public override DiagnosticAnalyzerCategory GetAnalyzerCategory() + => DiagnosticAnalyzerCategory.SyntaxTreeWithoutSemanticsAnalysis; + + protected override void InitializeWorker(AnalysisContext context) + => context.RegisterSyntaxNodeAction(AnalyzePropertyDeclaration, SyntaxKind.PropertyDeclaration); + + private void AnalyzePropertyDeclaration(SyntaxNodeAnalysisContext context) + { + var option = context.GetCSharpAnalyzerOptions().PreferSimplePropertyAccessors; + if (!option.Value || ShouldSkipAnalysis(context, option.Notification)) + return; + + var propertyDeclaration = (PropertyDeclarationSyntax)context.Node; + + if (propertyDeclaration.AccessorList is not { } accessorList || + accessorList.GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error)) + { + return; + } + + foreach (var accessor in accessorList.Accessors) + { + // get { return field; } + // get => field; + if (accessor is (SyntaxKind.GetAccessorDeclaration) { Body.Statements: [ReturnStatementSyntax { Expression.RawKind: (int)SyntaxKind.FieldExpression }] } + or (SyntaxKind.GetAccessorDeclaration) { ExpressionBody.Expression.RawKind: (int)SyntaxKind.FieldExpression }) + { + ReportIfValid(accessor); + } + + // set/init { field = value; } + if (accessor is (SyntaxKind.SetAccessorDeclaration or SyntaxKind.InitAccessorDeclaration) { Body.Statements: [ExpressionStatementSyntax { Expression: var innerBlockBodyExpression }] } && + IsFieldValueAssignmentExpression(innerBlockBodyExpression)) + { + ReportIfValid(accessor); + } + + // set/init => field = value; + if (accessor is (SyntaxKind.SetAccessorDeclaration or SyntaxKind.InitAccessorDeclaration) { ExpressionBody.Expression: var innerExpressionBodyExpression } && + IsFieldValueAssignmentExpression(innerExpressionBodyExpression)) + { + ReportIfValid(accessor); + } + } + + static bool IsFieldValueAssignmentExpression(ExpressionSyntax expression) + { + return expression is AssignmentExpressionSyntax(SyntaxKind.SimpleAssignmentExpression) + { + Left.RawKind: (int)SyntaxKind.FieldExpression, + Right: IdentifierNameSyntax { Identifier.ValueText: "value" } + }; + } + + void ReportIfValid(AccessorDeclarationSyntax accessorDeclaration) + { + // If we are analyzing an accessor of a partial property and all other accessors have no bodies + // then if we simplify our current accessor the property will no longer be a valid + // implementation part. Thus we block that case + if (accessorDeclaration is { Parent: AccessorListSyntax { Parent: BasePropertyDeclarationSyntax containingPropertyDeclaration } containingAccessorList } && + containingPropertyDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword) && + containingAccessorList.Accessors.All(a => ReferenceEquals(a, accessorDeclaration) || a is { Body: null, ExpressionBody: null })) + { + return; + } + + context.ReportDiagnostic(DiagnosticHelper.Create( + Descriptor, + accessorDeclaration.GetLocation(), + option.Notification, + context.Options, + additionalLocations: null, + properties: null)); + } + } +} diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf index 576a142a0ecc7..814d529007007 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf @@ -177,6 +177,11 @@ Upřednostňovat kontrolu hodnoty null před kontrolou typu + + Property accessor can be simplified + Property accessor can be simplified + + Property pattern can be simplified Vzor vlastnosti se dá zjednodušit. @@ -222,6 +227,11 @@ Zjednodušit vyvolání delegáta + + Simplify property accessor + Simplify property accessor + + Simplify property pattern Zjednodušit vzor vlastnosti diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf index 96dccc829b54f..2d855adab93e4 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf @@ -177,6 +177,11 @@ „NULL“-Überprüfung vor Typüberprüfung bevorzugen + + Property accessor can be simplified + Property accessor can be simplified + + Property pattern can be simplified Das Eigenschaftsmuster kann vereinfacht werden. @@ -222,6 +227,11 @@ Vereinfachen des Delegatenaufrufs + + Simplify property accessor + Simplify property accessor + + Simplify property pattern Eigenschaftenmuster vereinfachen diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf index 808bededb715e..d55ce9d83f905 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf @@ -177,6 +177,11 @@ Preferir comprobación "null' sobre comprobación de tipo + + Property accessor can be simplified + Property accessor can be simplified + + Property pattern can be simplified El patrón de propiedad puede ser simplificado @@ -222,6 +227,11 @@ Simplificar la invocación del delegado + + Simplify property accessor + Simplify property accessor + + Simplify property pattern Simplificar el patrón de propiedad diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf index 7125923272b2b..9a10359b62b32 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf @@ -177,6 +177,11 @@ Préférer la vérification « null » à la vérification de type + + Property accessor can be simplified + Property accessor can be simplified + + Property pattern can be simplified Le modèle de propriété peut être simplifié @@ -222,6 +227,11 @@ Simplifier l’appel du délégué + + Simplify property accessor + Simplify property accessor + + Simplify property pattern Simplifier le modèle de propriété diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf index 1c47f131846cf..aaab6c822a1ce 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf @@ -177,6 +177,11 @@ Preferisci il controllo 'null' al controllo del tipo + + Property accessor can be simplified + Property accessor can be simplified + + Property pattern can be simplified Il modello di proprietà può essere semplificato @@ -222,6 +227,11 @@ Semplifica la chiamata del delegato + + Simplify property accessor + Simplify property accessor + + Simplify property pattern Semplifica il modello di proprietà diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf index 498434b70baad..82e40a16053c8 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf @@ -177,6 +177,11 @@ 型のチェックよりも 'null 値' チェックを優先する + + Property accessor can be simplified + Property accessor can be simplified + + Property pattern can be simplified プロパティ パターンは簡略化できます @@ -222,6 +227,11 @@ デリゲートの呼び出しを簡略化する + + Simplify property accessor + Simplify property accessor + + Simplify property pattern プロパティ パターンを簡略化する diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf index 38e28ac74b712..cbf7dbabbfb2f 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf @@ -177,6 +177,11 @@ 형식 검사보다 'null' 검사 선호 + + Property accessor can be simplified + Property accessor can be simplified + + Property pattern can be simplified 속성 패턴을 간소화할 수 있습니다. @@ -222,6 +227,11 @@ 대리자 호출 간소화 + + Simplify property accessor + Simplify property accessor + + Simplify property pattern 속성 패턴 간소화 diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf index 6df51c97b6ce0..c7fa53568ddc9 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf @@ -177,6 +177,11 @@ Ustaw preferencje na sprawdzanie wartości „null” zamiast sprawdzania typu + + Property accessor can be simplified + Property accessor can be simplified + + Property pattern can be simplified Wzorzec właściwości można uprościć @@ -222,6 +227,11 @@ Uprość wywołanie delegata + + Simplify property accessor + Simplify property accessor + + Simplify property pattern Uprość wzorzec właściwości diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf index 3d1c3ff54e34e..b5b883dad260c 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf @@ -177,6 +177,11 @@ Preferir a verificação 'nula' à verificação de tipo + + Property accessor can be simplified + Property accessor can be simplified + + Property pattern can be simplified O padrão da propriedade pode ser simplificado @@ -222,6 +227,11 @@ Simplificar invocação de delegado + + Simplify property accessor + Simplify property accessor + + Simplify property pattern Simplificar o padrão de propriedade diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf index f4c2e65836c99..5cca32124a9e3 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf @@ -177,6 +177,11 @@ Предпочитать проверку "null" проверке типа + + Property accessor can be simplified + Property accessor can be simplified + + Property pattern can be simplified Шаблон свойства можно упростить @@ -222,6 +227,11 @@ Упростить вызов делегата + + Simplify property accessor + Simplify property accessor + + Simplify property pattern Упростить шаблон свойства diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf index fd2429f73c4f1..140bb46e3c161 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf @@ -177,6 +177,11 @@ Tür denetimi yerine 'null' denetimini tercih et + + Property accessor can be simplified + Property accessor can be simplified + + Property pattern can be simplified Özellik deseni basitleştirilebilir @@ -222,6 +227,11 @@ Temsilci çağırmayı basitleştir + + Simplify property accessor + Simplify property accessor + + Simplify property pattern Özellik desenini basitleştir diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf index e86aa58e241ca..a600214b8837e 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf @@ -177,6 +177,11 @@ 与类型检查相比,首选 “Null” 检查 + + Property accessor can be simplified + Property accessor can be simplified + + Property pattern can be simplified 可以简化属性模式 @@ -222,6 +227,11 @@ 简化委托调用 + + Simplify property accessor + Simplify property accessor + + Simplify property pattern 简化属性模式 diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf index c2ee335464254..4019df8915df7 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf @@ -177,6 +177,11 @@ 建議使用 'null' 檢查而非鍵入檢查 + + Property accessor can be simplified + Property accessor can be simplified + + Property pattern can be simplified 可簡化屬性模式 @@ -222,6 +227,11 @@ 簡化委派引動 + + Simplify property accessor + Simplify property accessor + + Simplify property pattern 簡化屬性模式 diff --git a/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems b/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems index 63971fc537b83..968dd83fb7ec0 100644 --- a/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems +++ b/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems @@ -93,6 +93,7 @@ + diff --git a/src/Analyzers/CSharp/CodeFixes/SimplifyPropertyAccessor/CSharpSimplifyPropertyAccessorCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/SimplifyPropertyAccessor/CSharpSimplifyPropertyAccessorCodeFixProvider.cs new file mode 100644 index 0000000000000..e52ed94b96f79 --- /dev/null +++ b/src/Analyzers/CSharp/CodeFixes/SimplifyPropertyAccessor/CSharpSimplifyPropertyAccessorCodeFixProvider.cs @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; + +namespace Microsoft.CodeAnalysis.CSharp.SimplifyPropertyAccessor; + +using static CSharpSyntaxTokens; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.SimplifyPropertyAccessor), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpSimplifyPropertyAccessorCodeFixProvider() : SyntaxEditorBasedCodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds { get; } + = [IDEDiagnosticIds.SimplifyPropertyAccessorDiagnosticId]; + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + RegisterCodeFix(context, CSharpAnalyzersResources.Simplify_property_accessor, nameof(CSharpAnalyzersResources.Simplify_property_accessor)); + return Task.CompletedTask; + } + + protected override Task FixAllAsync(Document document, ImmutableArray diagnostics, SyntaxEditor editor, CancellationToken cancellationToken) + { + using var _ = PooledHashSet.GetInstance(out var seenPartialProperties); + + foreach (var diagnostic in diagnostics) + { + var accessor = (AccessorDeclarationSyntax)diagnostic.Location.FindNode(cancellationToken); + + // If this accessor belongs to a partial property remember that property. + // If we later find another accessor of the same partial property, we reject the fix. + // This is a case where both accessors of a partial property implementation part + // are potentially simplifiable and we are performing a fix-all. + // Analyzer reports both accessors since simplifying each individually won't break anything + // but if we "fix" both the property won't be a valid partial implementation part anymore. + // Therefore we fix only the first accessor we encounter + if (accessor.Parent?.Parent is PropertyDeclarationSyntax containingProperty && + containingProperty.Modifiers.Any(SyntaxKind.PartialKeyword) && + !seenPartialProperties.Add(containingProperty)) + { + continue; + } + + var fixedAccessor = accessor + .WithBody(null) + .WithExpressionBody(null); + + if (fixedAccessor.SemicolonToken == default) + { + fixedAccessor = fixedAccessor.WithSemicolonToken( + SemicolonToken.WithTrailingTrivia(accessor.GetTrailingTrivia())); + } + + editor.ReplaceNode(accessor, fixedAccessor.WithAdditionalAnnotations(Formatter.Annotation)); + } + + return Task.CompletedTask; + } +} diff --git a/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems b/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems index 26423cafb96a6..bd9e6cf7e6502 100644 --- a/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems +++ b/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems @@ -99,6 +99,7 @@ + diff --git a/src/Analyzers/CSharp/Tests/SimplifyPropertyAccessor/SimplifyPropertyAccessorTests.cs b/src/Analyzers/CSharp/Tests/SimplifyPropertyAccessor/SimplifyPropertyAccessorTests.cs new file mode 100644 index 0000000000000..be0f1644c9f56 --- /dev/null +++ b/src/Analyzers/CSharp/Tests/SimplifyPropertyAccessor/SimplifyPropertyAccessorTests.cs @@ -0,0 +1,529 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.SimplifyPropertyAccessor; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Testing; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.SimplifyPropertyAccessor; + +using VerifyCS = CSharpCodeFixVerifier< + CSharpSimplifyPropertyAccessorDiagnosticAnalyzer, + CSharpSimplifyPropertyAccessorCodeFixProvider>; + +[Trait(Traits.Feature, Traits.Features.CodeActionsSimplifyPropertyAccessor)] +public sealed class SimplifyPropertyAccessorTests +{ + private static async Task TestAsync( + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, + LanguageVersion languageVersion = LanguageVersion.CSharp14) + { + await new VerifyCS.Test + { + TestCode = markup, + FixedCode = markup, + LanguageVersion = languageVersion, + }.RunAsync(); + } + + private static async Task TestAsync( + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string initialMarkup, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string fixedMarkup) + { + await new VerifyCS.Test + { + TestCode = initialMarkup, + FixedCode = fixedMarkup, + LanguageVersion = LanguageVersion.CSharp14, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, // for 'IsExternalInit' type + }.RunAsync(); + } + + public static IEnumerable SimplifiableGetterBodies => ["{ return field; }", "=> field;"]; + + public static IEnumerable TrailingTrivia => ["", " // useful comment"]; + + public static IEnumerable SimplifiableSetterBodies => ["{ field = value; }", "=> field = value;"]; + + public static IEnumerable SetInitKeywords => ["set", "init"]; + + [Fact] + public async Task NotInCSharpVersionBefore14() + { + // 'field' is not even parsed as a keyword before C# 14, but let's test this anyway + await TestAsync(""" + class C + { + public int Prop + { + get => {|CS0103:field|}; + set => {|CS0103:field|} = value; + } + } + """, LanguageVersion.CSharp13); + } + + [Fact] + public async Task NotWhenOptionIsDisabled() + { + var code = """ + class C + { + public int Prop + { + get => field; + set => field = value; + } + } + """; + + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp14, + Options = + { + { CSharpCodeStyleOptions.PreferSimplePropertyAccessors, false } + } + }.RunAsync(); + } + + [Fact] + public async Task NotWhenAccessorHasSyntaxError() + { + var code = """ + class C + { + public int Prop + { + get => field{|CS1002:|} + set { field = value {|CS1002:}|} + } + } + """; + + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp14 + }.RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task SimpleGetter( + [CombinatorialMemberData(nameof(SimplifiableGetterBodies))] string getterBody, + [CombinatorialMemberData(nameof(TrailingTrivia))] string trailingTrivia) + { + await TestAsync($$""" + class C + { + public int Prop + { + [|get {{getterBody}}|]{{trailingTrivia}} + set; + } + } + """, $$""" + class C + { + public int Prop + { + get;{{trailingTrivia}} + set; + } + } + """); + } + + [Theory] + [CombinatorialData] + public async Task SimpleSetter( + [CombinatorialMemberData(nameof(SetInitKeywords))] string setterKeyword, + [CombinatorialMemberData(nameof(SimplifiableSetterBodies))] string setterBody, + [CombinatorialMemberData(nameof(TrailingTrivia))] string trailingTrivia) + { + await TestAsync($$""" + class C + { + public int Prop + { + get; + [|{{setterKeyword}} {{setterBody}}|]{{trailingTrivia}} + } + } + """, $$""" + class C + { + public int Prop + { + get; + {{setterKeyword}};{{trailingTrivia}} + } + } + """); + } + + [Theory] + [CombinatorialData] + public async Task FixAll( + [CombinatorialMemberData(nameof(SimplifiableGetterBodies))] string getterBody, + [CombinatorialMemberData(nameof(TrailingTrivia))] string getterTrailingTrivia, + [CombinatorialMemberData(nameof(SetInitKeywords))] string setterKeyword, + [CombinatorialMemberData(nameof(SimplifiableSetterBodies))] string setterBody, + [CombinatorialMemberData(nameof(TrailingTrivia))] string setterTrailingTrivia) + { + await TestAsync($$""" + class C + { + public int Prop + { + [|get {{getterBody}}|]{{getterTrailingTrivia}} + [|{{setterKeyword}} {{setterBody}}|]{{setterTrailingTrivia}} + } + } + """, $$""" + class C + { + public int Prop + { + get;{{getterTrailingTrivia}} + {{setterKeyword}};{{setterTrailingTrivia}} + } + } + """); + } + + [Fact] + public async Task NotWhenPropertyHasNoAccessors() + { + // Just to verify we do not crash etc. + await TestAsync(""" + class C + { + public int {|CS0548:Prop|} { } + } + """); + } + + [Fact] + public async Task EvenWhenPropertyHasTooManyAccessors() + { + await new VerifyCS.Test() + { + TestCode = """ + class C + { + public int Prop { [|get { return field; }|] [|set => field = value;|] [|init { field = value; }|] } + } + """, + FixedCode = """ + class C + { + public int Prop { get; set; init; } + } + """, + LanguageVersion = LanguageVersion.CSharp14, + CompilerDiagnostics = CompilerDiagnostics.None, + }.RunAsync(); + } + + [Fact] + public async Task EvenWhenPropertyHasDuplicateAccessors() + { + await new VerifyCS.Test() + { + TestCode = """ + class C + { + public int Prop { [|get { return field; }|] [|get => field;|] } + } + """, + FixedCode = """ + class C + { + public int Prop { get; get; } + } + """, + LanguageVersion = LanguageVersion.CSharp14, + CompilerDiagnostics = CompilerDiagnostics.None, + }.RunAsync(); + } + + [Fact] + public async Task NotOnIndexerAccessor() + { + // Again 'field' is not even parsed as a keyword + await TestAsync(""" + class C + { + public int this[int i] + { + get => {|CS0103:field|}; + set => {|CS0103:field|} = value; + } + } + """); + } + + [Fact] + public async Task NotOnPropertyLikeEventAccessor() + { + // 'field' keyword? Never heard of that thing... + await TestAsync(""" + using System; + + class C + { + public event EventHandler Tested + { + add => {|CS0201:{|CS0103:field|}|}; + remove => {|CS0103:field|} = value; + } + } + """); + } + + [Fact] + public async Task NotOnPartialPropertyImplementationWithAnotherAccessorEmpty_Get() + { + await TestAsync(""" + partial class C + { + public partial int Prop { get; set; } + public partial int Prop { get => field; set; } + } + """); + } + + [Fact] + public async Task NotOnPartialPropertyImplementationWithAnotherAccessorEmpty_Set() + { + await TestAsync(""" + partial class C + { + public partial int Prop { get; set; } + public partial int Prop { get; set { field = value; } } + } + """); + } + + [Fact] + public async Task PartialPropertyImplementation_BothAccessors1() + { + await TestAsync(""" + partial class C + { + public partial int Prop { get; set; } + + public partial int Prop + { + [|get => field;|] + [|set { field = value; }|] + } + } + """, """ + partial class C + { + public partial int Prop { get; set; } + + public partial int Prop + { + get; + set { field = value; } + } + } + """); + } + + [Fact] + public async Task PartialPropertyImplementation_BothAccessors1_DifferentFiles() + { + await new VerifyCS.Test + { + TestState = + { + Sources = + { + """ + partial class C + { + public partial int Prop { get; set; } + } + """, """ + partial class C + { + public partial int Prop + { + [|get => field;|] + [|set { field = value; }|] + } + } + """ + } + }, + FixedState = + { + Sources = + { + """ + partial class C + { + public partial int Prop { get; set; } + } + """, """ + partial class C + { + public partial int Prop + { + get; + set { field = value; } + } + } + """ + } + }, + LanguageVersion = LanguageVersion.CSharp14, + }.RunAsync(); + } + + [Fact] + public async Task PartialPropertyImplementation_BothAccessors2() + { + await new VerifyCS.Test + { + TestCode = """ + partial class C + { + public partial int Prop { get; set; } + + public partial int Prop + { + [|get => field;|] + [|set { field = value; }|] + } + } + """, + FixedCode = """ + partial class C + { + public partial int Prop { get; set; } + + public partial int Prop + { + get => field; + set; + } + } + """, + LanguageVersion = LanguageVersion.CSharp14, + CodeFixTestBehaviors = CodeFixTestBehaviors.SkipFixAllCheck | CodeFixTestBehaviors.FixOne, + DiagnosticSelector = diagnostics => diagnostics[1], + }.RunAsync(); + } + + [Fact] + public async Task PartialPropertyImplementation_BothAccessors2_DifferentFiles() + { + await new VerifyCS.Test + { + TestState = + { + Sources = + { + """ + partial class C + { + public partial int Prop { get; set; } + } + """, """ + partial class C + { + public partial int Prop + { + [|get => field;|] + [|set { field = value; }|] + } + } + """ + } + }, + FixedState = + { + Sources = + { + """ + partial class C + { + public partial int Prop { get; set; } + } + """, """ + partial class C + { + public partial int Prop + { + get => field; + set; + } + } + """ + } + }, + LanguageVersion = LanguageVersion.CSharp14, + CodeFixTestBehaviors = CodeFixTestBehaviors.SkipFixAllCheck | CodeFixTestBehaviors.FixOne, + DiagnosticSelector = diagnostics => diagnostics[1], + }.RunAsync(); + } + + [Fact] + public async Task MultiplePartialPropertyImplementationsWithBothAccessors() + { + await TestAsync(""" + partial class C + { + public partial int Prop1 { get; set; } + public partial string Prop2 { get; set; } + + public partial int Prop1 + { + [|get => field;|] + [|set { field = value; }|] + } + + public partial string Prop2 + { + [|get => field;|] + [|set { field = value; }|] + } + } + """, """ + partial class C + { + public partial int Prop1 { get; set; } + public partial string Prop2 { get; set; } + + public partial int Prop1 + { + get; + set { field = value; } + } + + public partial string Prop2 + { + get; + set { field = value; } + } + } + """); + } +} diff --git a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs index 45b60093d331d..52ee6ead8fbf8 100644 --- a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs +++ b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs @@ -101,6 +101,7 @@ internal static class EnforceOnBuildValues public const EnforceOnBuild UseSystemThreadingLock = /*IDE0330*/ EnforceOnBuild.Recommended; public const EnforceOnBuild UseUnboundGenericTypeInNameOf = /*IDE0340*/ EnforceOnBuild.Recommended; public const EnforceOnBuild UseImplicitlyTypedLambdaExpression = /*IDE0350*/ EnforceOnBuild.Recommended; + public const EnforceOnBuild SimplifyPropertyAccessor = /*IDE0360*/ EnforceOnBuild.Recommended; /* EnforceOnBuild.WhenExplicitlyEnabled */ public const EnforceOnBuild RemoveUnnecessaryCast = /*IDE0004*/ EnforceOnBuild.WhenExplicitlyEnabled; // TODO: Move to 'Recommended' OR 'HighlyRecommended' bucket once performance problems are addressed: https://github.com/dotnet/roslyn/issues/43304 diff --git a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs index 5d2a44f2f4121..e3cac691d325d 100644 --- a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs +++ b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs @@ -209,6 +209,8 @@ internal static class IDEDiagnosticIds public const string UseImplicitlyTypedLambdaExpressionDiagnosticId = "IDE0350"; + public const string SimplifyPropertyAccessorDiagnosticId = "IDE0360"; + // Analyzer error Ids public const string AnalyzerChangedId = "IDE1001"; public const string AnalyzerDependencyConflictId = "IDE1002"; diff --git a/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs b/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs index 13066ab750497..d1af593b1a51c 100644 --- a/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs +++ b/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs @@ -122,6 +122,7 @@ internal static class PredefinedCodeFixProviderNames public const string SimplifyLinqTypeCheckAndCast = nameof(SimplifyLinqTypeCheckAndCast); public const string SimplifyNames = nameof(SimplifyNames); public const string SimplifyObjectCreation = nameof(SimplifyObjectCreation); + public const string SimplifyPropertyAccessor = nameof(SimplifyPropertyAccessor); public const string SimplifyPropertyPattern = nameof(SimplifyPropertyPattern); public const string SimplifyThisOrMe = nameof(SimplifyThisOrMe); public const string SpellCheck = nameof(SpellCheck); diff --git a/src/Compilers/Test/Core/Traits/Traits.cs b/src/Compilers/Test/Core/Traits/Traits.cs index 9be99ab4f2769..b4a2e48926f4b 100644 --- a/src/Compilers/Test/Core/Traits/Traits.cs +++ b/src/Compilers/Test/Core/Traits/Traits.cs @@ -175,6 +175,7 @@ public static class Features public const string CodeActionsSimplifyInterpolation = "CodeActions.SimplifyInterpolation"; public const string CodeActionsSimplifyLinqExpression = "CodeActions.SimplifyLinqExpression"; public const string CodeActionsSimplifyLinqTypeCheckAndCast = "CodeActions.SimplifyLinqTypeCheckAndCast"; + public const string CodeActionsSimplifyPropertyAccessor = "CodeActions.SimplifyPropertyAccessor"; public const string CodeActionsSimplifyPropertyPattern = "CodeActions.SimplifyPropertyPattern"; public const string CodeActionsSimplifyThisOrMe = "CodeActions.SimplifyThisOrMe"; public const string CodeActionsSimplifyTypeNames = "CodeActions.SimplifyTypeNames"; diff --git a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs index ea55e2675d7f0..2207636e0c816 100644 --- a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs @@ -499,6 +499,9 @@ public void CSharp_VerifyIDEDiagnosticSeveritiesAreConfigurable() # IDE0350 dotnet_diagnostic.IDE0350.severity = %value% + # IDE0360 + dotnet_diagnostic.IDE0360.severity = %value% + # IDE1005 dotnet_diagnostic.IDE1005.severity = %value% @@ -912,6 +915,7 @@ public void CSharp_VerifyIDECodeStyleOptionsAreConfigurable() ("IDE0330", "csharp_prefer_system_threading_lock", "true"), ("IDE0340", "csharp_style_prefer_unbound_generic_type_in_nameof", "true"), ("IDE0350", "csharp_style_prefer_implicitly_typed_lambda_expression", "true"), + ("IDE0360", "csharp_style_prefer_simple_property_accessors", "true"), ("IDE1005", "csharp_style_conditional_delegate_call", "true"), ("IDE1006", null, null), ("IDE1007", null, null), diff --git a/src/Features/RulesMissingDocumentation.md b/src/Features/RulesMissingDocumentation.md index 16fad36e94713..ffc172fbb25b4 100644 --- a/src/Features/RulesMissingDocumentation.md +++ b/src/Features/RulesMissingDocumentation.md @@ -17,6 +17,7 @@ IDE0320 | | Use 'System.Threading.Lock' | IDE0340 | | Use unbound generic type | IDE0350 | | Use implicitly typed lambda | +IDE0360 | | Simplify property accessor | IDE1007 | | | IDE2000 | | Avoid multiple blank lines | IDE2001 | | Embedded statements must be on their own line | diff --git a/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs b/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs index 5e539463775bb..61559fa4ec6a4 100644 --- a/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs +++ b/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs @@ -116,6 +116,7 @@ private static IEnumerable GetCodeBlockCodeStyleOptions(Tiered yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferTopLevelStatements, ServicesVSResources.Prefer_top_level_statements, options, updater); yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferPrimaryConstructors, ServicesVSResources.Prefer_primary_constructors, options, updater); yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferSystemThreadingLock, ServicesVSResources.Prefer_System_Threading_Lock, options, updater); + yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferSimplePropertyAccessors, ServicesVSResources.Prefer_simple_property_accessors, options, updater); } private static IEnumerable GetExpressionCodeStyleOptions(TieredAnalyzerConfigOptions options, OptionUpdater updater) diff --git a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs index f3c391219fd97..6c46347550387 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs @@ -218,6 +218,7 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"csharp_style_prefer_range_operator", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferRangeOperator")}, {"csharp_style_prefer_readonly_struct", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferReadOnlyStruct")}, {"csharp_style_prefer_readonly_struct_member", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferReadOnlyStructMember")}, + {"csharp_style_prefer_simple_property_accessors", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferSimplePropertyAccessors")}, {"csharp_style_prefer_switch_expression", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferSwitchExpression")}, {"csharp_style_prefer_top_level_statements", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferTopLevelStatements")}, {"csharp_style_prefer_tuple_swap", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferTupleSwap")}, diff --git a/src/VisualStudio/Core/Def/ServicesVSResources.resx b/src/VisualStudio/Core/Def/ServicesVSResources.resx index e28500734e3a0..600d3c4725dd2 100644 --- a/src/VisualStudio/Core/Def/ServicesVSResources.resx +++ b/src/VisualStudio/Core/Def/ServicesVSResources.resx @@ -1892,4 +1892,7 @@ Additional information: {1} Experimental feature This is used by the settings UI to mark experimental features. + + Prefer simple property accessors + \ No newline at end of file diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf index 0b26b905f7de8..8b3035f1d58a3 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf @@ -1122,6 +1122,11 @@ Preferovat pole s modifikátorem readonly + + Prefer simple property accessors + Prefer simple property accessors + + Prefer simple 'using' statement Preferovat jednoduchý příkaz using diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf index 8035f25b1fc94..5f228fcab89aa 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf @@ -1122,6 +1122,11 @@ readonly-Felder bevorzugen + + Prefer simple property accessors + Prefer simple property accessors + + Prefer simple 'using' statement Einfache using-Anweisung bevorzugen diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf index 4b41753ca5f3f..430a785d0ea7b 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf @@ -1122,6 +1122,11 @@ Preferir campos de solo lectura + + Prefer simple property accessors + Prefer simple property accessors + + Prefer simple 'using' statement Preferir la instrucción "using" sencilla diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf index 5749db3e0e1b1..9d8fa2ebcaef5 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf @@ -1122,6 +1122,11 @@ Préférer les champs readonly + + Prefer simple property accessors + Prefer simple property accessors + + Prefer simple 'using' statement Préférer une instruction 'using' simple diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf index c7721016e89a5..5763c9972764e 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf @@ -1122,6 +1122,11 @@ Preferisci campi readonly + + Prefer simple property accessors + Prefer simple property accessors + + Prefer simple 'using' statement Preferisci l'istruzione 'using' semplice diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf index 59e1d97670c06..e5412d6495713 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf @@ -1122,6 +1122,11 @@ readonly フィールドを優先する + + Prefer simple property accessors + Prefer simple property accessors + + Prefer simple 'using' statement 単純な 'using' ステートメントを優先する diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf index 809d59abf787a..2482cb9cbbb03 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf @@ -1122,6 +1122,11 @@ 읽기 전용 필드 선호 + + Prefer simple property accessors + Prefer simple property accessors + + Prefer simple 'using' statement 간단한 'using' 문 선호 diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf index 41c55d7aa1c10..6f475dfd3cd55 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf @@ -1122,6 +1122,11 @@ Preferuj pola tylko do odczytu + + Prefer simple property accessors + Prefer simple property accessors + + Prefer simple 'using' statement Preferuj prostą instrukcję „using” diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf index 59a6017fa1bd6..0e24721f23560 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf @@ -1122,6 +1122,11 @@ Preferir campos readonly + + Prefer simple property accessors + Prefer simple property accessors + + Prefer simple 'using' statement Preferir a instrução 'using' simples diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf index 22941317b947c..6cec5615575a9 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf @@ -1122,6 +1122,11 @@ Предпочитать поля только для чтения + + Prefer simple property accessors + Prefer simple property accessors + + Prefer simple 'using' statement Предпочитать простой оператор using diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf index 4af2666078f0a..a979a27474001 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf @@ -1122,6 +1122,11 @@ Saltokunur alanları tercih et + + Prefer simple property accessors + Prefer simple property accessors + + Prefer simple 'using' statement Basit 'using' deyimini tercih et diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf index 50bacb6e87956..41ef5c1ab21c7 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf @@ -1122,6 +1122,11 @@ 首选只读字段 + + Prefer simple property accessors + Prefer simple property accessors + + Prefer simple 'using' statement 首选简单的 "using" 语句 diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf index a6c6d25eac1cf..b5e70be274d29 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf @@ -1122,6 +1122,11 @@ 優先使用唯讀欄位 + + Prefer simple property accessors + Prefer simple property accessors + + Prefer simple 'using' statement 優先使用簡單的 'using' 陳述式 diff --git a/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb b/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb index 9fa99df3d2858..2f52f5d421949 100644 --- a/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb +++ b/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb @@ -146,6 +146,7 @@ csharp_prefer_system_threading_lock = true csharp_style_namespace_declarations = block_scoped csharp_style_prefer_method_group_conversion = true csharp_style_prefer_primary_constructors = true +csharp_style_prefer_simple_property_accessors = true csharp_style_prefer_top_level_statements = true # Expression-level preferences @@ -267,7 +268,7 @@ dotnet_naming_style.begins_with_i.capitalization = pascal_case ' Use the default options Dim options = New OptionStore(workspace.GlobalOptions) Dim groupedOptions = workspace.GetService(Of EditorConfigOptionsEnumerator).GetOptions(LanguageNames.CSharp) - Dim actualText = EditorConfigFileGenerator.Generate(groupedOptions, Options, LanguageNames.CSharp) + Dim actualText = EditorConfigFileGenerator.Generate(groupedOptions, options, LanguageNames.CSharp) AssertEx.EqualOrDiff(expectedText, actualText) End Using End Sub @@ -407,6 +408,7 @@ csharp_prefer_system_threading_lock = true csharp_style_namespace_declarations = block_scoped csharp_style_prefer_method_group_conversion = true csharp_style_prefer_primary_constructors = true +csharp_style_prefer_simple_property_accessors = true csharp_style_prefer_top_level_statements = true # Expression-level preferences diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs index 1c633ea244817..ac70c33905937 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs @@ -297,6 +297,11 @@ private static Option2> CreatePreferE "csharp_style_prefer_primary_constructors", defaultValue: CodeStyleOption2.TrueWithSuggestionEnforcement); + public static readonly Option2> PreferSimplePropertyAccessors = CreateOption( + CSharpCodeStyleOptionGroups.CodeBlockPreferences, + "csharp_style_prefer_simple_property_accessors", + defaultValue: CodeStyleOption2.TrueWithSuggestionEnforcement); + /// /// Options that we expect the user to set in editorconfig. ///