This repository was archived by the owner on Sep 24, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 273
Expand file tree
/
Copy pathSomeOfEnumValuesWasNotHandledInSwitchStatementIssue.cs
More file actions
159 lines (131 loc) · 5.12 KB
/
SomeOfEnumValuesWasNotHandledInSwitchStatementIssue.cs
File metadata and controls
159 lines (131 loc) · 5.12 KB
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
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.Refactoring;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
[IssueDescription("Some of enum values was not handled in switch statement",
Description = "Some of enum values was not handled in switch statement.",
Category = IssueCategories.CodeQualityIssues,
Severity = Severity.Warning)]
public class SomeOfEnumValuesWasNotHandledInSwitchStatementIssue: GatherVisitorCodeIssueProvider
{
internal static readonly string EnableCheckComment = "Check exhaustiveness";
internal static bool IsExhaustivenessCheckEnabled(SwitchStatement switchStatement)
{
var comment = switchStatement.Parent.GetChildByRole(Roles.Comment);
return comment != null && comment.Content.Trim() == EnableCheckComment;
}
internal static SwitchData BuildSwitchData(SwitchStatement switchStatement, BaseRefactoringContext context)
{
return new SwitchDataBuilder(switchStatement, context).Build();
}
protected override IGatherVisitor CreateVisitor(BaseRefactoringContext context)
{
return new GatherVisitor(context);
}
internal class SwitchData
{
public readonly IType EnumType;
public readonly IEnumerable<MemberReferenceExpression> LabelsExpressions;
public SwitchData(IType enumType, IEnumerable<MemberReferenceExpression> labelsExpressions)
{
EnumType = enumType;
LabelsExpressions = labelsExpressions.ToArray();
}
}
class SwitchDataBuilder
{
readonly SwitchStatement _switchStatement;
readonly BaseRefactoringContext _context;
public SwitchDataBuilder(SwitchStatement switchStatement, BaseRefactoringContext context)
{
_switchStatement = switchStatement;
_context = context;
}
public SwitchData Build()
{
var enumType = GetEnumType();
if (enumType == null)
return null;
var labelsExpressions = GatherCaseLabelsExpressions();
if (!AreAllExpressionsHasEnumType(labelsExpressions, enumType))
return null;
return new SwitchData(enumType, labelsExpressions.Cast<MemberReferenceExpression>());
}
IType GetEnumType()
{
var resolveResult = _context.Resolve(_switchStatement.Expression);
return resolveResult.Type.Kind == TypeKind.Enum ? resolveResult.Type : null;
}
IEnumerable<Expression> GatherCaseLabelsExpressions()
{
var labels = _switchStatement.SwitchSections.SelectMany(_ => _.CaseLabels);
var nonDefaultLabels = labels.Where(_ => !_.Expression.IsNull);
return nonDefaultLabels.Select(_ => _.Expression);
}
bool AreAllExpressionsHasEnumType(IEnumerable<Expression> expressions, IType type)
{
var resolveResults = expressions.Select(_ => _context.Resolve(_)).ToArray();
return resolveResults.Any() && resolveResults.All(_ => _.Type == type);
}
}
class GatherVisitor: GatherVisitorBase<SomeOfEnumValuesWasNotHandledInSwitchStatementIssue>
{
public GatherVisitor(BaseRefactoringContext ctx) : base(ctx)
{
}
public override void VisitSwitchStatement(SwitchStatement switchStatement)
{
base.VisitSwitchStatement(switchStatement);
if (IsExhaustivenessCheckEnabled(switchStatement)) {
var switchData = BuildSwitchData(switchStatement, ctx);
if (switchData != null) {
var missingValues = GetMissingEnumValues(switchData).ToArray();
if (missingValues.Any())
AddIssue(new CodeIssue(
switchStatement.SwitchToken,
"Some of enum values was not handled",
"Handle missing values",
script => GenerateMissingCasesForMissingValues(script, switchStatement, missingValues)
));
}
}
}
static IEnumerable<IField> GetMissingEnumValues(SwitchData switchData)
{
var handledValues = switchData.LabelsExpressions.Select(_ => _.MemberName).ToArray();
var allValues = switchData.EnumType.GetFields(_ => _.IsConst && _.IsPublic);
return allValues.Where(_ => !handledValues.Contains(_.Name));
}
static void GenerateMissingCasesForMissingValues(Script script, SwitchStatement switchStatement, IEnumerable<IField> values)
{
var astType = new SimpleType(values.First().Type.Name);
var newSwitchStatement = (SwitchStatement)switchStatement.Clone();
var previousSection = GetDefaultSection(newSwitchStatement);
foreach (var value in values.Reverse()) {
var newSection = new SwitchSection {
CaseLabels = {
new CaseLabel(new MemberReferenceExpression(astType.Clone(), value.Name))
},
Statements = {
new ThrowStatement(new ObjectCreateExpression(new SimpleType("System.NotImplementedException")))
}
};
if (previousSection != null)
newSwitchStatement.SwitchSections.InsertBefore(previousSection, newSection);
else
newSwitchStatement.SwitchSections.Add(newSection);
previousSection = newSection;
}
script.Replace(switchStatement, newSwitchStatement);
}
static SwitchSection GetDefaultSection(SwitchStatement switchStatement)
{
var sections = switchStatement.SwitchSections;
return sections.FirstOrDefault(s => s.CaseLabels.Any(l => l.Expression.IsNull));
}
}
}
}