This project is read-only.
1
Vote

Better Code Fix support for bad constructor assignments

description

Frequently in constructors we do something like this

public foo(string bar)
{
_bar = bar;
this.bar = bar' // which one depends on our habits/guidelines
}

If the user inadvertently says

bar = bar

the current code fix just offers to disable the warning - not what the user wants.

Please add a code fix that says "yo dude, you meant to assign to a field" (alternate wording anticipated).

These two patterns are so common, that explicitly fixing for them seems desirable.

And a refactor to just make all those assignments would be pretty nifty too.

Kathleen

file attachments

comments

AdamSpeight2008 wrote Nov 1, 2014 at 9:41 PM

Katheleen,

Here is an example for vb.net that just that.
Imports System.Collections.Immutable
Imports Microsoft.CodeAnalysis.Diagnostics

<DiagnosticAnalyzer>
<ExportDiagnosticAnalyzer(DiagnosticAnalyzer.DiagnosticId, LanguageNames.VisualBasic)>
Public Class DiagnosticAnalyzer
  Implements ISyntaxNodeAnalyzer(Of SyntaxKind)

  Friend Const DiagnosticId = "BadConstructorAssignment"
  Friend Const Description = "Did you mean to assign to a parameter arguement?"
  Friend Const MessageFormat = "Did you mean to assign to a parameter arguement? (Parameter name '{0}') "
  Friend Const Category = "Naming"

  Friend Shared Rule As New DiagnosticDescriptor(DiagnosticId, Description, MessageFormat, Category, DiagnosticSeverity.Warning)

  Private ReadOnly Property SyntaxKindsOfInterest As ImmutableArray(Of SyntaxKind) Implements ISyntaxNodeAnalyzer(Of SyntaxKind).SyntaxKindsOfInterest
    Get
      Return ImmutableArray.Create(SyntaxKind.SubNewStatement)
    End Get
  End Property

  Public ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) Implements IDiagnosticAnalyzer.SupportedDiagnostics
    Get
      Return ImmutableArray.Create(Rule)
    End Get
  End Property

  Public Sub AnalyzeNode(node As SyntaxNode, semanticModel As SemanticModel, addDiagnostic As Action(Of Diagnostic), cancellationToken As CancellationToken) Implements ISyntaxNodeAnalyzer(Of SyntaxKind).AnalyzeNode
    Dim constructor = TryCast(node, Syntax.SubNewStatementSyntax)
    If constructor Is Nothing Then Exit Sub
    Dim st = constructor.SyntaxTree
    Dim names = constructor.ParameterList.Parameters.Select(Function(p) p.Identifier.Identifier.GetIdentifierText)
    For Each statement In DirectCast(constructor.Parent, ConstructorBlockSyntax).Statements.OfType(Of AssignmentStatementSyntax)
      Dim assign = DirectCast(statement, AssignmentStatementSyntax)
      If TypeOf assign.Left IsNot SimpleNameSyntax Then Continue For
      Dim lhs_Id = DirectCast(assign.Left, SimpleNameSyntax).Identifier
      If names.Contains(lhs_Id.GetIdentifierText()) Then addDiagnostic(Diagnostic.Create(Rule, Location.Create(st, assign.Left.Span), lhs_Id))
    Next
  End Sub
End Class
I have done the code-fix part yet.

-Adam

AdamSpeight2008 wrote Nov 2, 2014 at 12:15 AM

Kathleen,

Here is the code fix to do the replacement assignment.
<ExportCodeFixProvider(DiagnosticAnalyzer.DiagnosticId, LanguageNames.VisualBasic)>
Friend Class CodeFixProvider
  Implements ICodeFixProvider

  Public Function GetFixableDiagnosticIds() As IEnumerable(Of String) Implements ICodeFixProvider.GetFixableDiagnosticIds
    Return {DiagnosticAnalyzer.DiagnosticId}
  End Function

  Public Async Function GetFixesAsync(document As Document, span As TextSpan, diagnostics As IEnumerable(Of Diagnostic), cancellationToken As CancellationToken) As Task(Of IEnumerable(Of CodeAction)) Implements ICodeFixProvider.GetFixesAsync
    Dim root = Await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False)
    Dim diagnosticSpan = diagnostics.First().Location.SourceSpan
    Dim declarations = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf().OfType(Of SimpleNameSyntax)()
    If declarations.Any Then
      Dim ID_Node = declarations.First
      Dim Assignment_Statement = TryCast(ID_Node.Parent, AssignmentStatementSyntax)
      If Assignment_Statement Is Nothing Then Return {}
      Return {CodeAction.Create("Prefix with underscore", Function(c) Prefix_WithUnderscore(document, Assignment_Statement, c)),
        CodeAction.Create("Prefix with Me.", Function(c) Prefix_WithMeDot(document, Assignment_Statement, c))}
    End If
    Return {}
  End Function

  Private Async Function Prefix_WithUnderscore(document As Document, assignment_statement As AssignmentStatementSyntax, cancellationToken As CancellationToken) As Task(Of Solution)
    Dim LHS = DirectCast(assignment_statement.Left, SimpleNameSyntax)
    Dim _LHS = LHS.WithIdentifier(SyntaxFactory.Identifier("_" & LHS.Identifier.Text))
    Dim semanticModel = Await document.GetSemanticModelAsync(cancellationToken)
    Dim root = Await semanticModel.SyntaxTree.GetRootAsync 
    Dim tree = VisualBasicSyntaxTree.Create(root.ReplaceNode(LHS, _LHS))
    Dim newSolution = document.WithSyntaxRoot(Await tree.GetRootAsync)
    Return newSolution.Project.Solution
  End Function

  Private Async Function Prefix_WithMeDot(document As Document, assignment_statement As AssignmentStatementSyntax, cancellationToken As CancellationToken) As Task(Of Solution)
    Dim LHS = DirectCast(assignment_statement.Left, SimpleNameSyntax)
    Dim _LHS = SyntaxFactory.ExpressionStatement(
                 SyntaxFactory.InvocationExpression(
                   SyntaxFactory.SimpleMemberAccessExpression( SyntaxFactory.MeExpression,
                                                               SyntaxFactory.IdentifierName(LHS.Identifier.Text)
                                                              )))
    Dim ass = SyntaxFactory.SimpleAssignmentStatement(_LHS.Expression,assignment_statement.Right )
    Dim semanticModel = Await document.GetSemanticModelAsync(cancellationToken)
    Dim root = Await  semanticModel.SyntaxTree.GetRootAsync 
    Dim tree = VisualBasicSyntaxTree.Create(root.ReplaceNode(Of SyntaxNode)(assignment_statement, ass))
    Dim newSolution = document.WithSyntaxRoot(Await tree.GetRootAsync)
    Return newSolution.Project.Solution
  End Function
End Class

AdamSpeight2008 wrote Nov 3, 2014 at 1:20 AM

CS Implementation

AdamSpeight2008 wrote Nov 3, 2014 at 1:20 AM

VB Implementation

AdamSpeight2008 wrote Nov 4, 2014 at 1:29 AM

Visual Studio Gallery :- Constructor Misassignment

AdamSpeight2008 wrote Nov 9, 2014 at 12:02 AM

The following Blog Post contains both the VB.net and C# source code.