Skip to content

Commit

Permalink
Add code fix for CS7036
Browse files Browse the repository at this point in the history
  • Loading branch information
josefpihrt committed Mar 13, 2021
1 parent da88ce6 commit 9eae730
Show file tree
Hide file tree
Showing 9 changed files with 371 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/CSharp/CSharp/CompilerDiagnosticIdentifiers.Generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ internal static class CompilerDiagnosticIdentifiers
public const string PartialDeclarationsHaveConfictingAccessibilityModifiers = "CS0262";
public const string CannotImplicitlyConvertTypeExplicitConversionExists = "CS0266";
public const string PartialModifierCanOnlyAppearImmediatelyBeforeClassStructInterfaceOrVoid = "CS0267";
public const string PropertyOrIndexerCannotBeUsedInThisContextBecauseSetAccessorIsAccessible = "CS0272";
public const string AccessibilityModifiersMayNotBeUsedOnAccessorsInInterface = "CS0275";
public const string UsingGenericTypeRequiresNumberOfTypeArguments = "CS0305";
public const string NewConstraintMustBeLastConstraintSpecified = "CS0401";
Expand Down Expand Up @@ -167,6 +168,7 @@ internal static class CompilerDiagnosticIdentifiers
public const string ArraysAsAttributeArgumentsIsNotCLSCompliant = "CS3016";
public const string ConstraintTypeIsNotCLSCompliant = "CS3024";
public const string TypeIsNotCLSCompliantBecauseBaseInterfaceIsNotCLSCompliant = "CS3027";
public const string ThereIsNoArgumentGivenThatCorrespondsToRequiredFormalParameter = "CS7036";
public const string OnlyAutoImplementedPropertiesCanHaveInitializers = "CS8050";
public const string ControlCannotFallOutOfSwitchFromFinalCaseLabel = "CS8070";
public const string LocalFunctionMustAlwaysHaveBody = "CS8112";
Expand Down
7 changes: 7 additions & 0 deletions src/CodeFixes/CSharp/CodeFixDescriptors.Generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -888,5 +888,12 @@ public static partial class CodeFixDescriptors
isEnabledByDefault: true,
"CS0539");

/// <summary>RCF0117 (fixes CS7036)</summary>
public static readonly CodeFixDescriptor MoveInitializerExpressionsToConstructor = new CodeFixDescriptor(
id: CodeFixIdentifiers.MoveInitializerExpressionsToConstructor,
title: "Move initializer expressions to constructor",
isEnabledByDefault: true,
"CS7036");

}
}
1 change: 1 addition & 0 deletions src/CodeFixes/CSharp/CodeFixIdentifiers.Generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,6 @@ public static partial class CodeFixIdentifiers
public const string MakeFieldWritable = CodeFixIdentifier.CodeFixIdPrefix + "0114";
public const string ReplaceInvocationWithMemberAccessOrViceVersa = CodeFixIdentifier.CodeFixIdPrefix + "0115";
public const string AddParameterToExplicitlyImplementedInterfaceMember = CodeFixIdentifier.CodeFixIdPrefix + "0116";
public const string MoveInitializerExpressionsToConstructor = CodeFixIdentifier.CodeFixIdPrefix + "0117";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright (c) Josef Pihrt. All rights reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Roslynator.CodeFixes;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

namespace Roslynator.CSharp.CodeFixes
{
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(ObjectCreationExpressionCodeFixProvider))]
[Shared]
public class ObjectCreationExpressionCodeFixProvider : BaseCodeFixProvider
{
public sealed override ImmutableArray<string> FixableDiagnosticIds
{
get { return ImmutableArray.Create(CompilerDiagnosticIdentifiers.ThereIsNoArgumentGivenThatCorrespondsToRequiredFormalParameter); }
}

public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
Diagnostic diagnostic = context.Diagnostics[0];

if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.MoveInitializerExpressionsToConstructor))
return;

SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false);

if (!TryFindFirstAncestorOrSelf(root, context.Span, out ObjectCreationExpressionSyntax objectCreationExpression))
return;

switch (diagnostic.Id)
{
case CompilerDiagnosticIdentifiers.ThereIsNoArgumentGivenThatCorrespondsToRequiredFormalParameter:
{
if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.MoveInitializerExpressionsToConstructor))
break;

if (!objectCreationExpression.Type.Span.Contains(diagnostic.Location.SourceSpan))
return;

ArgumentListSyntax argumentList = objectCreationExpression.ArgumentList;

if (argumentList?.Arguments.Any() == true)
return;

InitializerExpressionSyntax initializer = objectCreationExpression.Initializer;

if (initializer == null)
return;

SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

List<ExpressionSyntax> expressions = null;

foreach (ExpressionSyntax expression in initializer.Expressions)
{
if (expression is AssignmentExpressionSyntax assignment
&& semanticModel.GetDiagnostic(
CompilerDiagnosticIdentifiers.PropertyOrIndexerCannotBeUsedInThisContextBecauseSetAccessorIsAccessible,
assignment.Left.Span,
context.CancellationToken) != null)
{
(expressions ??= new List<ExpressionSyntax>()).Add(expression);
}
}

if (expressions == null)
return;

TypeSyntax type = objectCreationExpression.Type;

if (argumentList == null)
{
argumentList = ArgumentList().WithTrailingTrivia(type.GetTrailingTrivia());
type = type.WithoutTrailingTrivia();
}

SeparatedSyntaxList<ArgumentSyntax> arguments = expressions
.Select(f => Argument(((AssignmentExpressionSyntax)f).Right))
.ToSeparatedSyntaxList();

argumentList = argumentList.WithArguments(arguments);

ObjectCreationExpressionSyntax newObjectCreationExpression = objectCreationExpression.Update(
objectCreationExpression.NewKeyword,
type,
argumentList,
initializer);

SymbolInfo symbolInfo = semanticModel.GetSpeculativeSymbolInfo(
objectCreationExpression.SpanStart,
newObjectCreationExpression,
SpeculativeBindingOption.BindAsExpression);

if (symbolInfo.Symbol is IMethodSymbol methodSymbol
&& methodSymbol.MethodKind == MethodKind.Constructor)
{
CodeAction codeAction = CodeAction.Create(
"Move initializer expressions to constructor",
ct =>
{
InitializerExpressionSyntax newInitializer = initializer.RemoveNodes(expressions, SyntaxRefactorings.DefaultRemoveOptions);
if (newInitializer.Expressions.Count == 0
&& newInitializer
.DescendantTrivia(TextSpan.FromBounds(newInitializer.OpenBraceToken.SpanStart, newInitializer.CloseBraceToken.SpanStart))
.All(f => f.IsWhitespaceOrEndOfLineTrivia()))
{
newInitializer = null;
ArgumentListSyntax newArgumentList = newObjectCreationExpression
.ArgumentList
.TrimTrailingTrivia()
.AppendToTrailingTrivia(initializer.GetTrailingTrivia());
newObjectCreationExpression = newObjectCreationExpression
.WithArgumentList(newArgumentList);
}
newObjectCreationExpression = newObjectCreationExpression
.WithInitializer(newInitializer)
.WithFormatterAnnotation();
return context.Document.ReplaceNodeAsync(objectCreationExpression, newObjectCreationExpression, ct);
},
GetEquivalenceKey(diagnostic));

context.RegisterCodeFix(codeAction, diagnostic);
}

break;
}
}
}
}
}
24 changes: 24 additions & 0 deletions src/CodeFixes/CSharp/CompilerDiagnosticDescriptors.Generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,18 @@ public static partial class CompilerDiagnosticDescriptors
helpLinkUri: "http://docs.microsoft.com/en-us/dotnet/csharp/misc/cs0267",
customTags: WellKnownDiagnosticTags.Compiler);

/// <summary>CS0272</summary>
public static readonly DiagnosticDescriptor PropertyOrIndexerCannotBeUsedInThisContextBecauseSetAccessorIsAccessible = new DiagnosticDescriptor(
id: CompilerDiagnosticIdentifiers.PropertyOrIndexerCannotBeUsedInThisContextBecauseSetAccessorIsAccessible,
title: "The property or indexer 'name' cannot be used in this context because the set accessor is inaccessible.",
messageFormat: "The property or indexer '{0}' cannot be used in this context because the set accessor is inaccessible",
category: "Compiler",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: null,
helpLinkUri: "http://docs.microsoft.com/en-us/dotnet/csharp/misc/cs0272",
customTags: WellKnownDiagnosticTags.Compiler);

/// <summary>CS0275</summary>
public static readonly DiagnosticDescriptor AccessibilityModifiersMayNotBeUsedOnAccessorsInInterface = new DiagnosticDescriptor(
id: CompilerDiagnosticIdentifiers.AccessibilityModifiersMayNotBeUsedOnAccessorsInInterface,
Expand Down Expand Up @@ -1940,6 +1952,18 @@ public static partial class CompilerDiagnosticDescriptors
helpLinkUri: "http://docs.microsoft.com/en-us/dotnet/csharp/misc/cs3027",
customTags: WellKnownDiagnosticTags.Compiler);

/// <summary>CS7036</summary>
public static readonly DiagnosticDescriptor ThereIsNoArgumentGivenThatCorrespondsToRequiredFormalParameter = new DiagnosticDescriptor(
id: CompilerDiagnosticIdentifiers.ThereIsNoArgumentGivenThatCorrespondsToRequiredFormalParameter,
title: "There is no argument given that corresponds to the required formal parameter 'parameter' of 'member'.",
messageFormat: "There is no argument given that corresponds to the required formal parameter '{0}' of '{1}'",
category: "Compiler",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: null,
helpLinkUri: "",
customTags: WellKnownDiagnosticTags.Compiler);

/// <summary>CS8050</summary>
public static readonly DiagnosticDescriptor OnlyAutoImplementedPropertiesCanHaveInitializers = new DiagnosticDescriptor(
id: CompilerDiagnosticIdentifiers.OnlyAutoImplementedPropertiesCanHaveInitializers,
Expand Down
5 changes: 5 additions & 0 deletions src/CodeFixes/CodeFixes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -660,4 +660,9 @@
<Id>CS0539</Id>
</FixableDiagnosticIds>
</CodeFix>
<CodeFix Id="RCF0117" Identifier="MoveInitializerExpressionsToConstructor" Title="Move initializer expressions to constructor">
<FixableDiagnosticIds>
<Id>CS7036</Id>
</FixableDiagnosticIds>
</CodeFix>
</CodeFixes>
14 changes: 14 additions & 0 deletions src/CodeFixes/Diagnostics.xml
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,13 @@
Title="The partial modifier can only appear immediately before 'class', 'struct', or 'interface'."
Message="The 'partial' modifier can only appear immediately before 'class', 'struct', 'interface', or 'void'"
HelpUrl="http://docs.microsoft.com/en-us/dotnet/csharp/misc/cs0267" />
<Diagnostic
Id="CS0272"
Identifier="PropertyOrIndexerCannotBeUsedInThisContextBecauseSetAccessorIsAccessible"
Severity="Error"
Title="The property or indexer 'name' cannot be used in this context because the set accessor is inaccessible."
Message="The property or indexer '{0}' cannot be used in this context because the set accessor is inaccessible"
HelpUrl="http://docs.microsoft.com/en-us/dotnet/csharp/misc/cs0272" />
<Diagnostic
Id="CS0275"
Identifier="AccessibilityModifiersMayNotBeUsedOnAccessorsInInterface"
Expand Down Expand Up @@ -1127,6 +1134,13 @@
Title="'type_1' is not CLS-compliant because base interface 'type_2' is not CLS-compliant."
Message="'{0}' is not CLS-compliant because base interface '{1}' is not CLS-compliant"
HelpUrl="http://docs.microsoft.com/en-us/dotnet/csharp/misc/cs3027" />
<Diagnostic
Id="CS7036"
Identifier="ThereIsNoArgumentGivenThatCorrespondsToRequiredFormalParameter"
Severity="Error"
Title="There is no argument given that corresponds to the required formal parameter 'parameter' of 'member'."
Message="There is no argument given that corresponds to the required formal parameter '{0}' of '{1}'"
HelpUrl="" />
<Diagnostic
Id="CS8050"
Identifier="OnlyAutoImplementedPropertiesCanHaveInitializers"
Expand Down
Loading

0 comments on commit 9eae730

Please sign in to comment.