From a71a31a1e01c8675967e1006fa3249d75e7c25d4 Mon Sep 17 00:00:00 2001 From: Josef Pihrt Date: Thu, 21 Sep 2023 23:05:36 +0200 Subject: [PATCH] Fix property/field lazy init (RCS1250) (#1205) --- ChangeLog.md | 1 + ...mplicitOrExplicitObjectCreationAnalyzer.cs | 52 ++++++++++++++++- ...seImplicitOrExplicitObjectCreationTests.cs | 56 +++++++++++++++++++ 3 files changed, 108 insertions(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index a9168f2f3c..31f78ea76f 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix [RCS1164](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1164) ([#1196](https://github.com/JosefPihrt/Roslynator/pull/1196)). - Fix [RCS1241](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1241) ([#1197](https://github.com/JosefPihrt/Roslynator/pull/1197)). +- Fix [RCS1250](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1250) ([#1205](https://github.com/JosefPihrt/Roslynator/pull/1205)). ## [4.5.0] - 2023-08-27 diff --git a/src/Analyzers/CSharp/Analysis/UseImplicitOrExplicitObjectCreationAnalyzer.cs b/src/Analyzers/CSharp/Analysis/UseImplicitOrExplicitObjectCreationAnalyzer.cs index d069e38ff0..3fde3e9177 100644 --- a/src/Analyzers/CSharp/Analysis/UseImplicitOrExplicitObjectCreationAnalyzer.cs +++ b/src/Analyzers/CSharp/Analysis/UseImplicitOrExplicitObjectCreationAnalyzer.cs @@ -215,11 +215,29 @@ private static void AnalyzeObjectCreationExpression(SyntaxNodeAnalysisContext co } case SyntaxKind.CoalesceExpression: { - if (UseImplicitObjectCreationWhenTypeIsNotObvious(context)) + ObjectCreationTypeStyle style = context.GetObjectCreationTypeStyle(); + + if (style == ObjectCreationTypeStyle.Implicit) { var coalesceExpression = (BinaryExpressionSyntax)parent; AnalyzeExpression(context, objectCreation, coalesceExpression.Left); } + else if (style == ObjectCreationTypeStyle.ImplicitWhenTypeIsObvious + && parent.IsParentKind(SyntaxKind.EqualsValueClause)) + { + if (parent.Parent.Parent is VariableDeclaratorSyntax variableDeclarator) + { + if (variableDeclarator.Parent is VariableDeclarationSyntax variableDeclaration + && variableDeclaration.IsParentKind(SyntaxKind.FieldDeclaration)) + { + AnalyzeType(context, objectCreation, variableDeclaration.Type); + } + } + else if (parent.Parent.Parent is PropertyDeclarationSyntax propertyDeclaration) + { + AnalyzeType(context, objectCreation, propertyDeclaration.Type); + } + } break; } @@ -391,8 +409,40 @@ private static void AnalyzeImplicitObjectCreationExpression(SyntaxNodeAnalysisCo case SyntaxKind.CoalesceAssignmentExpression: case SyntaxKind.AddAssignmentExpression: case SyntaxKind.SubtractAssignmentExpression: + { + if (UseExplicitObjectCreationWhenTypeIsNotObvious(context)) + ReportDiagnostic(context, implicitObjectCreation); + + break; + } case SyntaxKind.CoalesceExpression: { + if (parent.IsParentKind(SyntaxKind.EqualsValueClause)) + { + switch (parent.Parent.Parent) + { + case VariableDeclaratorSyntax variableDeclarator: + { + if (variableDeclarator.Parent is VariableDeclarationSyntax) + { + if (UseExplicitObjectCreation(context)) + ReportDiagnostic(context, implicitObjectCreation); + + return; + } + + break; + } + case PropertyDeclarationSyntax: + { + if (UseExplicitObjectCreation(context)) + ReportDiagnostic(context, implicitObjectCreation); + + return; + } + } + } + if (UseExplicitObjectCreationWhenTypeIsNotObvious(context)) ReportDiagnostic(context, implicitObjectCreation); diff --git a/src/Tests/Analyzers.Tests/RCS1250UseImplicitOrExplicitObjectCreationTests.cs b/src/Tests/Analyzers.Tests/RCS1250UseImplicitOrExplicitObjectCreationTests.cs index 7f5652c43e..9e24b1c267 100644 --- a/src/Tests/Analyzers.Tests/RCS1250UseImplicitOrExplicitObjectCreationTests.cs +++ b/src/Tests/Analyzers.Tests/RCS1250UseImplicitOrExplicitObjectCreationTests.cs @@ -853,6 +853,62 @@ void M() ", options: Options.AddConfigOption(ConfigOptionKeys.ObjectCreationTypeStyle, ConfigOptionValues.ObjectCreationTypeStyle_ImplicitWhenTypeIsObvious)); } + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseImplicitOrExplicitObjectCreation)] + public async Task Test_PreferImplicitWhenTypeIsObvious_PropertyLazyInitialization() + { + await VerifyDiagnosticAndFixAsync(@" +#nullable enable + +public record R(C? P = null) +{ + public C P { get; init; } = P ?? new [|C|](); +} + +public class C +{ +} +", @" +#nullable enable + +public record R(C? P = null) +{ + public C P { get; init; } = P ?? new(); +} + +public class C +{ +} +", options: Options.AddConfigOption(ConfigOptionKeys.ObjectCreationTypeStyle, ConfigOptionValues.ObjectCreationTypeStyle_ImplicitWhenTypeIsObvious)); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseImplicitOrExplicitObjectCreation)] + public async Task Test_PreferImplicitWhenTypeIsObvious_FieldLazyInitialization() + { + await VerifyDiagnosticAndFixAsync(@" +#nullable enable + +public record R(C? P = null) +{ + public C F = P ?? new [|C|](); +} + +public class C +{ +} +", @" +#nullable enable + +public record R(C? P = null) +{ + public C F = P ?? new(); +} + +public class C +{ +} +", options: Options.AddConfigOption(ConfigOptionKeys.ObjectCreationTypeStyle, ConfigOptionValues.ObjectCreationTypeStyle_ImplicitWhenTypeIsObvious)); + } + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseImplicitOrExplicitObjectCreation)] public async Task Test_ConvertImplicitToExplicit_ThrowStatement() {