-
-
Notifications
You must be signed in to change notification settings - Fork 260
/
Copy pathUseCompoundAssignmentAnalyzer.cs
162 lines (126 loc) · 6.07 KB
/
UseCompoundAssignmentAnalyzer.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Copyright (c) .NET Foundation and Contributors. 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;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Roslynator.CSharp.Syntax;
namespace Roslynator.CSharp.Analysis;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class UseCompoundAssignmentAnalyzer : BaseDiagnosticAnalyzer
{
private static ImmutableArray<DiagnosticDescriptor> _supportedDiagnostics;
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
{
get
{
if (_supportedDiagnostics.IsDefault)
{
Immutable.InterlockedInitialize(
ref _supportedDiagnostics,
DiagnosticRules.UseCompoundAssignment,
DiagnosticRules.UseCompoundAssignmentFadeOut);
}
return _supportedDiagnostics;
}
}
public override void Initialize(AnalysisContext context)
{
base.Initialize(context);
context.RegisterSyntaxNodeAction(
c =>
{
if (DiagnosticRules.UseCompoundAssignment.IsEffective(c))
AnalyzeSimpleAssignment(c);
},
SyntaxKind.SimpleAssignmentExpression);
context.RegisterCompilationStartAction(startContext =>
{
if (((CSharpCompilation)startContext.Compilation).LanguageVersion >= LanguageVersion.CSharp8)
{
startContext.RegisterSyntaxNodeAction(
c =>
{
if (DiagnosticRules.UseCompoundAssignment.IsEffective(c))
AnalyzeCoalesceExpression(c);
},
SyntaxKind.CoalesceExpression);
}
});
}
private static void AnalyzeSimpleAssignment(SyntaxNodeAnalysisContext context)
{
var assignmentExpression = (AssignmentExpressionSyntax)context.Node;
SimpleAssignmentExpressionInfo assignmentInfo = SyntaxInfo.SimpleAssignmentExpressionInfo(assignmentExpression);
if (!assignmentInfo.Success)
return;
if (assignmentExpression.IsParentKind(
SyntaxKind.ObjectInitializerExpression,
SyntaxKind.WithInitializerExpression))
{
return;
}
ExpressionSyntax right = assignmentInfo.Right;
if (!CanBeReplacedWithCompoundAssignment(right))
return;
BinaryExpressionInfo binaryInfo = SyntaxInfo.BinaryExpressionInfo((BinaryExpressionSyntax)right);
if (!binaryInfo.Success)
return;
if (!CSharpFactory.AreEquivalent(assignmentInfo.Left, binaryInfo.Left))
return;
var binaryExpression = (BinaryExpressionSyntax)right;
DiagnosticHelpers.ReportDiagnostic(context, DiagnosticRules.UseCompoundAssignment, assignmentExpression);
DiagnosticHelpers.ReportNode(context, DiagnosticRules.UseCompoundAssignmentFadeOut, binaryExpression.Left);
bool CanBeReplacedWithCompoundAssignment(ExpressionSyntax expression)
{
switch (expression.Kind())
{
case SyntaxKind.AddExpression:
case SyntaxKind.SubtractExpression:
case SyntaxKind.MultiplyExpression:
case SyntaxKind.DivideExpression:
case SyntaxKind.ModuloExpression:
case SyntaxKind.BitwiseAndExpression:
case SyntaxKind.ExclusiveOrExpression:
case SyntaxKind.BitwiseOrExpression:
case SyntaxKind.LeftShiftExpression:
case SyntaxKind.RightShiftExpression:
return true;
case SyntaxKind.CoalesceExpression:
{
return ((CSharpCompilation)context.Compilation).LanguageVersion >= LanguageVersion.CSharp8
&& !((BinaryExpressionSyntax)expression).Right.IsKind(SyntaxKind.ThrowExpression);
}
}
return false;
}
}
internal static string GetCompoundAssignmentOperatorText(BinaryExpressionSyntax binaryExpression)
{
SyntaxKind compoundAssignmentKind = CSharpFacts.GetCompoundAssignmentKind(binaryExpression.Kind());
SyntaxKind compoundAssignmentOperatorKind = CSharpFacts.GetCompoundAssignmentOperatorKind(compoundAssignmentKind);
return SyntaxFacts.GetText(compoundAssignmentOperatorKind);
}
private static void AnalyzeCoalesceExpression(SyntaxNodeAnalysisContext context)
{
var coalesceExpression = (BinaryExpressionSyntax)context.Node;
BinaryExpressionInfo binaryExpressionInfo = SyntaxInfo.BinaryExpressionInfo(coalesceExpression, walkDownParentheses: false);
if (!binaryExpressionInfo.Success)
return;
ExpressionSyntax right = binaryExpressionInfo.Right;
if (right is not ParenthesizedExpressionSyntax parenthesizedExpression)
return;
ExpressionSyntax expression = parenthesizedExpression.Expression;
if (!expression.IsKind(SyntaxKind.SimpleAssignmentExpression))
return;
SimpleAssignmentExpressionInfo assignmentInfo = SyntaxInfo.SimpleAssignmentExpressionInfo((AssignmentExpressionSyntax)expression);
if (!assignmentInfo.Success)
return;
if (!CSharpFactory.AreEquivalent(binaryExpressionInfo.Left, assignmentInfo.Left))
return;
DiagnosticHelpers.ReportDiagnostic(context, DiagnosticRules.UseCompoundAssignment, coalesceExpression);
DiagnosticHelpers.ReportToken(context, DiagnosticRules.UseCompoundAssignmentFadeOut, parenthesizedExpression.OpenParenToken);
DiagnosticHelpers.ReportNode(context, DiagnosticRules.UseCompoundAssignmentFadeOut, assignmentInfo.Left);
DiagnosticHelpers.ReportToken(context, DiagnosticRules.UseCompoundAssignmentFadeOut, parenthesizedExpression.CloseParenToken);
}
}