Skip to content

Commit e280deb

Browse files
committed
Stop using stale state in UnreachableCaseInspection
Also removes the special testing interface from ParseTreeValueVisitor.
1 parent 1c8f7a4 commit e280deb

File tree

9 files changed

+210
-157
lines changed

9 files changed

+210
-157
lines changed

Rubberduck.CodeAnalysis/Inspections/Concrete/UnreachableCaseInspection/ParseTreeValueVisitor.cs

Lines changed: 19 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,9 @@
1010
namespace Rubberduck.Inspections.Concrete.UnreachableCaseInspection
1111
{
1212
public interface IParseTreeValueVisitor : IParseTreeVisitor<IParseTreeVisitorResults>
13-
{
14-
event EventHandler<ValueResultEventArgs> OnValueResultCreated;
15-
}
16-
17-
public interface ITestParseTreeVisitor
18-
{
19-
void InjectValuedDeclarationEvaluator(Func<Declaration, (bool, string, string)> func);
20-
}
13+
{}
2114

22-
public class ParseTreeValueVisitor : IParseTreeValueVisitor, ITestParseTreeVisitor
15+
public class ParseTreeValueVisitor : IParseTreeValueVisitor
2316
{
2417
private class EnumMember
2518
{
@@ -29,28 +22,31 @@ public EnumMember(VBAParser.EnumerationStmt_ConstantContext constContext, long i
2922
Value = initValue;
3023
HasAssignment = constContext.children.Any(ch => ch.Equals(constContext.GetToken(VBAParser.EQ, 0)));
3124
}
32-
public VBAParser.EnumerationStmt_ConstantContext ConstantContext { set; get; }
25+
public VBAParser.EnumerationStmt_ConstantContext ConstantContext { get; }
3326
public long Value { set; get; }
34-
public bool HasAssignment { set; get; }
27+
public bool HasAssignment { get; }
3528
}
3629

37-
private IParseTreeVisitorResults _contextValues;
38-
private IParseTreeValueFactory _inspValueFactory;
39-
private IReadOnlyList<VBAParser.EnumerationStmtContext> _enumStmtContexts;
30+
private readonly IParseTreeVisitorResults _contextValues;
31+
private readonly IParseTreeValueFactory _inspValueFactory;
32+
private readonly IReadOnlyList<VBAParser.EnumerationStmtContext> _enumStmtContexts;
4033
private List<EnumMember> _enumMembers;
34+
private Func<Declaration, (bool, string, string)> _valueDeclarationEvaluator;
4135

42-
public ParseTreeValueVisitor(IParseTreeValueFactory valueFactory, IReadOnlyList<VBAParser.EnumerationStmtContext> allEnums, Func<ParserRuleContext, (bool success, IdentifierReference idRef)> idRefRetriever)
36+
public ParseTreeValueVisitor(
37+
IParseTreeValueFactory valueFactory,
38+
IReadOnlyList<VBAParser.EnumerationStmtContext> allEnums,
39+
Func<ParserRuleContext, (bool success, IdentifierReference idRef)> idRefRetriever,
40+
Func<Declaration, (bool, string, string)> valueDeclarationEvaluator = null)
4341
{
4442
_inspValueFactory = valueFactory;
4543
IdRefRetriever = idRefRetriever;
4644
_contextValues = new ParseTreeVisitorResults();
47-
OnValueResultCreated += _contextValues.OnNewValueResult;
4845
_enumStmtContexts = allEnums;
46+
_valueDeclarationEvaluator = valueDeclarationEvaluator ?? GetValuedDeclaration;
4947
}
5048

51-
private Func<ParserRuleContext, (bool success, IdentifierReference idRef)> IdRefRetriever { set; get; } = null;
52-
53-
public event EventHandler<ValueResultEventArgs> OnValueResultCreated;
49+
private Func<ParserRuleContext, (bool success, IdentifierReference idRef)> IdRefRetriever { get; }
5450

5551
public virtual IParseTreeVisitorResults Visit(IParseTree tree)
5652
{
@@ -85,7 +81,7 @@ public virtual IParseTreeVisitorResults VisitErrorNode(IErrorNode node)
8581

8682
private void StoreVisitResult(ParserRuleContext context, IParseTreeValue inspValue)
8783
{
88-
OnValueResultCreated(this, new ValueResultEventArgs(context, inspValue));
84+
_contextValues.AddIfNotPresent(context, inspValue);
8985
}
9086

9187
private bool HasResult(ParserRuleContext context)
@@ -141,7 +137,6 @@ private void Visit(VBAParser.LExprContext context)
141137
}
142138
else
143139
{
144-
var smplNameExprTypeName = string.Empty;
145140
var smplName = context.GetDescendent<VBAParser.SimpleNameExprContext>();
146141
if (TryGetIdentifierReferenceForContext(smplName, out IdentifierReference idRef))
147142
{
@@ -286,20 +281,6 @@ private bool TryGetLExprValue(VBAParser.LExprContext lExprContext, out string ex
286281
return false;
287282
}
288283

289-
private Func<Declaration, (bool, string, string)> _valueDeclarationEvaluator;
290-
private Func<Declaration, (bool, string, string)> ValuedDeclarationEvaluator
291-
{
292-
set
293-
{
294-
_valueDeclarationEvaluator = value;
295-
}
296-
get
297-
{
298-
return _valueDeclarationEvaluator ?? GetValuedDeclaration;
299-
}
300-
}
301-
302-
303284
private (bool IsType, string ExpressionValue, string TypeName) GetValuedDeclaration(Declaration declaration)
304285
{
305286
if (declaration is ValuedDeclaration valuedDeclaration)
@@ -321,7 +302,7 @@ private void GetContextValue(ParserRuleContext context, out string declaredTypeN
321302
expressionValue = rangeClauseIdentifierReference.IdentifierName;
322303
declaredTypeName = GetBaseTypeForDeclaration(declaration);
323304

324-
(bool IsValuedDeclaration, string ExpressionValue, string TypeName) = ValuedDeclarationEvaluator(declaration);
305+
(bool IsValuedDeclaration, string ExpressionValue, string TypeName) = _valueDeclarationEvaluator(declaration);
325306

326307
if( IsValuedDeclaration)
327308
{
@@ -334,7 +315,8 @@ private void GetContextValue(ParserRuleContext context, out string declaredTypeN
334315
declaredTypeName = Tokens.String;
335316
return;
336317
}
337-
else if (long.TryParse(expressionValue, out _))
318+
319+
if (long.TryParse(expressionValue, out _))
338320
{
339321
return;
340322
}
@@ -437,9 +419,6 @@ private static bool IsBinaryOpEvaluationContext<T>(T context)
437419
return false;
438420
}
439421

440-
public void InjectValuedDeclarationEvaluator( Func<Declaration, (bool, string, string)> func)
441-
=> ValuedDeclarationEvaluator = func;
442-
443422
private void LoadEnumMemberValues()
444423
{
445424
_enumMembers = new List<EnumMember>();
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Antlr4.Runtime;
4+
using Rubberduck.Parsing.Grammar;
5+
using Rubberduck.Parsing.Symbols;
6+
7+
namespace Rubberduck.Inspections.Concrete.UnreachableCaseInspection
8+
{
9+
public interface IParseTreeValueVisitorFactory
10+
{
11+
IParseTreeValueVisitor Create(IReadOnlyList<VBAParser.EnumerationStmtContext> allEnums, Func<ParserRuleContext, (bool success, IdentifierReference idRef)> idRefRetriever);
12+
}
13+
14+
public class ParseTreeValueVisitorFactory : IParseTreeValueVisitorFactory
15+
{
16+
private readonly IParseTreeValueFactory _valueFactory;
17+
18+
public ParseTreeValueVisitorFactory(IParseTreeValueFactory valueFactory)
19+
{
20+
_valueFactory = valueFactory;
21+
}
22+
23+
public IParseTreeValueVisitor Create(IReadOnlyList<VBAParser.EnumerationStmtContext> allEnums, Func<ParserRuleContext, (bool success, IdentifierReference idRef)> identifierReferenceRetriever)
24+
{
25+
return new ParseTreeValueVisitor(_valueFactory, allEnums, identifierReferenceRetriever);
26+
}
27+
}
28+
}

Rubberduck.CodeAnalysis/Inspections/Concrete/UnreachableCaseInspection/ParseTreeVisitorResults.cs

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Antlr4.Runtime;
22
using System;
33
using System.Collections.Generic;
4+
using System.Linq;
45

56
namespace Rubberduck.Inspections.Concrete.UnreachableCaseInspection
67
{
@@ -12,7 +13,7 @@ public interface IParseTreeVisitorResults
1213
string GetToken(ParserRuleContext context);
1314
bool Contains(ParserRuleContext context);
1415
bool TryGetValue(ParserRuleContext context, out IParseTreeValue value);
15-
void OnNewValueResult(object sender, ValueResultEventArgs e);
16+
void AddIfNotPresent(ParserRuleContext context, IParseTreeValue value);
1617
}
1718

1819
public class ParseTreeVisitorResults : IParseTreeVisitorResults
@@ -35,18 +36,15 @@ public IParseTreeValue GetValue(ParserRuleContext context)
3536

3637
public List<ParserRuleContext> GetChildResults(ParserRuleContext parent)
3738
{
38-
var results = new List<ParserRuleContext>();
39-
if (!(parent is null) || Contains(parent))
39+
if (parent is null)
4040
{
41-
foreach (var child in parent.children)
42-
{
43-
if (child is ParserRuleContext prCtxt && Contains(prCtxt))
44-
{
45-
results.Add(prCtxt);
46-
}
47-
}
41+
return new List<ParserRuleContext>();
4842
}
49-
return results;
43+
44+
return parent.children
45+
.OfType<ParserRuleContext>()
46+
.Where(Contains)
47+
.ToList();
5048
}
5149

5250
public string GetValueType(ParserRuleContext context)
@@ -69,11 +67,11 @@ public bool TryGetValue(ParserRuleContext context, out IParseTreeValue value)
6967
return _parseTreeValues.TryGetValue(context, out value);
7068
}
7169

72-
public void OnNewValueResult(object sender, ValueResultEventArgs e)
70+
public void AddIfNotPresent(ParserRuleContext context, IParseTreeValue value)
7371
{
74-
if (!_parseTreeValues.ContainsKey(e.Context))
72+
if (!_parseTreeValues.ContainsKey(context))
7573
{
76-
_parseTreeValues.Add(e.Context, e.Value);
74+
_parseTreeValues.Add(context, value);
7775
}
7876
}
7977
}

Rubberduck.CodeAnalysis/Inspections/Concrete/UnreachableCaseInspection/UnreachableCaseInspection.cs

Lines changed: 49 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -117,100 +117,95 @@ namespace Rubberduck.Inspections.Concrete.UnreachableCaseInspection
117117
public sealed class UnreachableCaseInspection : InspectionBase, IParseTreeInspection
118118
{
119119
private readonly IUnreachableCaseInspectorFactory _unreachableCaseInspectorFactory;
120-
private readonly IParseTreeValueFactory _valueFactory;
120+
private readonly IParseTreeValueVisitorFactory _parseTreeValueVisitorFactory;
121+
private readonly UnreachableCaseInspectionListener _listener;
121122

122-
private enum CaseInspectionResult { Unreachable, InherentlyUnreachable, MismatchType, Overflow, CaseElse };
123-
124-
private static readonly Dictionary<CaseInspectionResult, string> ResultMessages = new Dictionary<CaseInspectionResult, string>()
123+
public enum CaseInspectionResultType
125124
{
126-
[CaseInspectionResult.Unreachable] = InspectionResults.UnreachableCaseInspection_Unreachable,
127-
[CaseInspectionResult.InherentlyUnreachable] = InspectionResults.UnreachableCaseInspection_InherentlyUnreachable,
128-
[CaseInspectionResult.MismatchType] = InspectionResults.UnreachableCaseInspection_TypeMismatch,
129-
[CaseInspectionResult.Overflow] = InspectionResults.UnreachableCaseInspection_Overflow,
130-
[CaseInspectionResult.CaseElse] = InspectionResults.UnreachableCaseInspection_CaseElse
131-
};
125+
Unreachable,
126+
InherentlyUnreachable,
127+
MismatchType,
128+
Overflow,
129+
CaseElse
130+
}
132131

133-
public UnreachableCaseInspection(IDeclarationFinderProvider declarationFinderProvider)
132+
public UnreachableCaseInspection(IDeclarationFinderProvider declarationFinderProvider, IUnreachableCaseInspectionFactoryProvider factoryProvider)
134133
: base(declarationFinderProvider)
135134
{
136-
var factoryProvider = new UnreachableCaseInspectionFactoryProvider();
137-
138135
_unreachableCaseInspectorFactory = factoryProvider.CreateIUnreachableInspectorFactory();
139-
_valueFactory = factoryProvider.CreateIParseTreeValueFactory();
136+
_parseTreeValueVisitorFactory = factoryProvider.CreateParseTreeValueVisitorFactory();
137+
_listener = new UnreachableCaseInspectionListener();
140138
}
141139

142140
public CodeKind TargetKindOfCode => CodeKind.CodePaneCode;
143141

144-
public IInspectionListener Listener { get; } =
145-
new UnreachableCaseInspectionListener();
146-
147-
private ParseTreeVisitorResults ValueResults { get; } = new ParseTreeVisitorResults();
142+
public IInspectionListener Listener => _listener;
148143

149144
protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
150145
{
151146
var finder = DeclarationFinderProvider.DeclarationFinder;
147+
var enumStmts = _listener.EnumerationStmtContexts();
148+
var parseTreeValueVisitor = CreateParseTreeValueVisitor(enumStmts, GetIdentifierReferenceForContext);
152149

153150
return finder.UserDeclarations(DeclarationType.Module)
154151
.Where(module => module != null)
155-
.SelectMany(module => DoGetInspectionResults(module.QualifiedModuleName, finder))
152+
.SelectMany(module => DoGetInspectionResults(module.QualifiedModuleName, finder, parseTreeValueVisitor))
156153
.ToList();
157154
}
158155

159156
protected override IEnumerable<IInspectionResult> DoGetInspectionResults(QualifiedModuleName module)
160157
{
161158
var finder = DeclarationFinderProvider.DeclarationFinder;
162-
return DoGetInspectionResults(module, finder);
159+
var enumStmts = _listener.EnumerationStmtContexts();
160+
var parseTreeValueVisitor = CreateParseTreeValueVisitor(enumStmts, GetIdentifierReferenceForContext);
161+
return DoGetInspectionResults(module, finder, parseTreeValueVisitor);
163162
}
164163

165-
private IEnumerable<IInspectionResult> DoGetInspectionResults(QualifiedModuleName module, DeclarationFinder finder)
164+
private IEnumerable<IInspectionResult> DoGetInspectionResults(QualifiedModuleName module, DeclarationFinder finder, IParseTreeValueVisitor parseTreeValueVisitor)
166165
{
167166
var qualifiedSelectCaseStmts = Listener.Contexts(module)
168167
// ignore filtering here to make the search space smaller
169168
.Where(result => !result.IsIgnoringInspectionResultFor(finder, AnnotationName));
170169

171-
ParseTreeValueVisitor.OnValueResultCreated += ValueResults.OnNewValueResult;
172-
173170
return qualifiedSelectCaseStmts
174-
.SelectMany(ResultsForContext)
171+
.SelectMany(context => ResultsForContext(context, finder, parseTreeValueVisitor))
175172
.ToList();
176173
}
177174

178-
private IEnumerable<IInspectionResult> ResultsForContext(QualifiedContext<ParserRuleContext> qualifiedSelectCaseStmt)
175+
private IEnumerable<IInspectionResult> ResultsForContext(QualifiedContext<ParserRuleContext> qualifiedSelectCaseStmt, DeclarationFinder finder, IParseTreeValueVisitor parseTreeValueVisitor)
179176
{
180-
qualifiedSelectCaseStmt.Context.Accept(ParseTreeValueVisitor);
181-
var selectCaseInspector = _unreachableCaseInspectorFactory.Create((VBAParser.SelectCaseStmtContext)qualifiedSelectCaseStmt.Context, ValueResults, _valueFactory, GetVariableTypeName);
177+
var contextValues = qualifiedSelectCaseStmt.Context.Accept(parseTreeValueVisitor);
178+
var selectCaseInspector = _unreachableCaseInspectorFactory.Create((VBAParser.SelectCaseStmtContext)qualifiedSelectCaseStmt.Context, contextValues, GetVariableTypeName);
182179

183-
selectCaseInspector.InspectForUnreachableCases();
180+
var results = selectCaseInspector.InspectForUnreachableCases();
184181

185-
return selectCaseInspector
186-
.UnreachableCases
187-
.Select(uc => CreateInspectionResult(qualifiedSelectCaseStmt, uc, ResultMessages[CaseInspectionResult.Unreachable]))
188-
.Concat(selectCaseInspector
189-
.MismatchTypeCases
190-
.Select(mm => CreateInspectionResult(qualifiedSelectCaseStmt, mm, ResultMessages[CaseInspectionResult.MismatchType])))
191-
.Concat(selectCaseInspector
192-
.OverflowCases
193-
.Select(mm => CreateInspectionResult(qualifiedSelectCaseStmt, mm, ResultMessages[CaseInspectionResult.Overflow])))
194-
.Concat(selectCaseInspector
195-
.InherentlyUnreachableCases
196-
.Select(mm => CreateInspectionResult(qualifiedSelectCaseStmt, mm, ResultMessages[CaseInspectionResult.InherentlyUnreachable])))
197-
.Concat(selectCaseInspector
198-
.UnreachableCaseElseCases
199-
.Select(ce => CreateInspectionResult(qualifiedSelectCaseStmt, ce, ResultMessages[CaseInspectionResult.CaseElse])))
182+
return results
183+
.Select(resultTpl => CreateInspectionResult(qualifiedSelectCaseStmt, resultTpl.context, resultTpl.resultType))
200184
.ToList();
201185
}
202186

203-
private IParseTreeValueVisitor _parseTreeValueVisitor;
204-
public IParseTreeValueVisitor ParseTreeValueVisitor
187+
private IInspectionResult CreateInspectionResult(QualifiedContext<ParserRuleContext> selectStmt, ParserRuleContext unreachableBlock, CaseInspectionResultType resultType)
205188
{
206-
get
189+
return CreateInspectionResult(selectStmt, unreachableBlock, ResultMessage(resultType));
190+
}
191+
192+
//This cannot be a dictionary because the strings have to change after a change in the selected language.
193+
private static string ResultMessage(CaseInspectionResultType resultType)
194+
{
195+
switch (resultType)
207196
{
208-
if (_parseTreeValueVisitor is null)
209-
{
210-
var listener = (UnreachableCaseInspectionListener)Listener;
211-
_parseTreeValueVisitor = CreateParseTreeValueVisitor(_valueFactory, listener.EnumerationStmtContexts(), GetIdentifierReferenceForContext);
212-
}
213-
return _parseTreeValueVisitor;
197+
case CaseInspectionResultType.Unreachable:
198+
return InspectionResults.UnreachableCaseInspection_Unreachable;
199+
case CaseInspectionResultType.InherentlyUnreachable:
200+
return InspectionResults.UnreachableCaseInspection_InherentlyUnreachable;
201+
case CaseInspectionResultType.MismatchType:
202+
return InspectionResults.UnreachableCaseInspection_TypeMismatch;
203+
case CaseInspectionResultType.Overflow:
204+
return InspectionResults.UnreachableCaseInspection_Overflow;
205+
case CaseInspectionResultType.CaseElse:
206+
return InspectionResults.UnreachableCaseInspection_CaseElse;
207+
default:
208+
throw new ArgumentOutOfRangeException(nameof(resultType), resultType, null);
214209
}
215210
}
216211

@@ -221,8 +216,8 @@ private IInspectionResult CreateInspectionResult(QualifiedContext<ParserRuleCont
221216
new QualifiedContext<ParserRuleContext>(selectStmt.ModuleName, unreachableBlock));
222217
}
223218

224-
public static IParseTreeValueVisitor CreateParseTreeValueVisitor(IParseTreeValueFactory valueFactory, IReadOnlyList<VBAParser.EnumerationStmtContext> allEnums, Func<ParserRuleContext, (bool success, IdentifierReference idRef)> func)
225-
=> new ParseTreeValueVisitor(valueFactory, allEnums, func);
219+
public IParseTreeValueVisitor CreateParseTreeValueVisitor(IReadOnlyList<VBAParser.EnumerationStmtContext> allEnums, Func<ParserRuleContext, (bool success, IdentifierReference idRef)> func)
220+
=> _parseTreeValueVisitorFactory.Create(allEnums, func);
226221

227222
//Method is used as a delegate to avoid propagating RubberduckParserState beyond this class
228223
private (bool success, IdentifierReference idRef) GetIdentifierReferenceForContext(ParserRuleContext context)

0 commit comments

Comments
 (0)