Skip to content

Commit d256eaf

Browse files
committed
Adds checks for literal control characters
1 parent 8d59ac9 commit d256eaf

File tree

1 file changed

+76
-25
lines changed

1 file changed

+76
-25
lines changed

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

Lines changed: 76 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,14 @@ public interface IParseTreeValueVisitor : IParseTreeVisitor<IParseTreeVisitorRes
1414
event EventHandler<ValueResultEventArgs> OnValueResultCreated;
1515
}
1616

17-
public class ParseTreeValueVisitor : IParseTreeValueVisitor
17+
public interface ITestParseTreeVisitor
18+
{
19+
bool IsVBStringConstantToLiteral(string token, out string literalValue);
20+
bool IsNonPrintingControlCharacter(string token);
21+
void InjectValuedDeclarationEvaluator(Func<Declaration, (bool, string, string)> func);
22+
}
23+
24+
public class ParseTreeValueVisitor : IParseTreeValueVisitor, ITestParseTreeVisitor
1825
{
1926
private class EnumMember
2027
{
@@ -34,18 +41,18 @@ public EnumMember(VBAParser.EnumerationStmt_ConstantContext constContext, long i
3441
private List<VBAParser.EnumerationStmtContext> _enumStmtContexts;
3542
private List<EnumMember> _enumMembers;
3643

37-
private static List<KeyValuePair<string, string>> _vbStringConstants = new List<KeyValuePair<string, string>>()
44+
private static Dictionary<string, string> _vbStringConstants = new Dictionary<string, string>()
3845
{
39-
new KeyValuePair<string, string>(Tokens.vbBack, ((char)8).ToString()),
40-
new KeyValuePair<string, string>(Tokens.vbCr, ((char)13).ToString()),
41-
new KeyValuePair<string, string>(Tokens.vbCrLf, ((char)13).ToString() + ((char)10).ToString()),
42-
new KeyValuePair<string, string>(Tokens.vbLf, ((char)10).ToString()),
43-
new KeyValuePair<string, string>(Tokens.vbFormFeed, ((char)12).ToString()),
44-
new KeyValuePair<string, string>(Tokens.vbNewLine, Environment.NewLine),
45-
new KeyValuePair<string, string>(Tokens.vbNullChar, ((char)0).ToString()),
46-
new KeyValuePair<string, string>(Tokens.vbNullString, null),
47-
new KeyValuePair<string, string>(Tokens.vbTab, ((char)9).ToString()),
48-
new KeyValuePair<string, string>(Tokens.vbVerticalTab, ((char)11).ToString()),
46+
[Tokens.vbBack] = ((char)8).ToString(),
47+
[Tokens.vbCr] = ((char)13).ToString(),
48+
[Tokens.vbCrLf] = ((char)13).ToString() + ((char)10).ToString(),
49+
[Tokens.vbLf] = ((char)10).ToString(),
50+
[Tokens.vbFormFeed] = ((char)12).ToString(),
51+
[Tokens.vbNewLine] = Environment.NewLine,
52+
[Tokens.vbNullChar] = ((char)0).ToString(),
53+
[Tokens.vbNullString] = null,
54+
[Tokens.vbTab] =((char)9).ToString(),
55+
[Tokens.vbVerticalTab] =((char)11).ToString(),
4956
};
5057

5158
public ParseTreeValueVisitor(IParseTreeValueFactory valueFactory, List<VBAParser.EnumerationStmtContext> allEnums, Func<ParserRuleContext, (bool success, IdentifierReference idRef)> idRefRetriever)
@@ -55,8 +62,6 @@ public ParseTreeValueVisitor(IParseTreeValueFactory valueFactory, List<VBAParser
5562
_contextValues = new ParseTreeVisitorResults();
5663
OnValueResultCreated += _contextValues.OnNewValueResult;
5764
_enumStmtContexts = allEnums;
58-
_enumMembers = new List<EnumMember>();
59-
LoadEnumMemberValues();
6065
}
6166

6267
private Func<ParserRuleContext, (bool success, IdentifierReference idRef)> IdRefRetriever { set; get; } = null;
@@ -289,6 +294,30 @@ private bool TryGetLExprValue(VBAParser.LExprContext lExprContext, out string ex
289294
return false;
290295
}
291296

297+
private Func<Declaration, (bool, string, string)> _valueDeclarationEvaluator;
298+
private Func<Declaration, (bool, string, string)> ValuedDeclarationEvaluator
299+
{
300+
set
301+
{
302+
_valueDeclarationEvaluator = value;
303+
}
304+
get
305+
{
306+
return _valueDeclarationEvaluator ?? GetValuedDeclaration;
307+
}
308+
}
309+
310+
311+
private (bool IsType, string ExpressionValue, string TypeName) GetValuedDeclaration(Declaration declaration)
312+
{
313+
if (declaration is ValuedDeclaration valuedDeclaration)
314+
{
315+
var typeName = GetBaseTypeForDeclaration(declaration);
316+
return (true, valuedDeclaration.Expression, typeName);
317+
}
318+
return (false, null, null);
319+
}
320+
292321
private void GetContextValue(ParserRuleContext context, out string declaredTypeName, out string expressionValue)
293322
{
294323
expressionValue = context.GetText();
@@ -300,20 +329,25 @@ private void GetContextValue(ParserRuleContext context, out string declaredTypeN
300329
expressionValue = rangeClauseIdentifierReference.IdentifierName;
301330
declaredTypeName = GetBaseTypeForDeclaration(declaration);
302331

303-
if (declaration is ValuedDeclaration valuedDeclaration)
332+
(bool IsValuedDeclaration, string ExpressionValue, string TypeName) = ValuedDeclarationEvaluator(declaration);
333+
334+
if( IsValuedDeclaration)
304335
{
305-
expressionValue = valuedDeclaration.Expression;
306-
declaredTypeName = GetBaseTypeForDeclaration(declaration);
307-
if (IsVBStringConstant(expressionValue))
336+
expressionValue = ExpressionValue;
337+
declaredTypeName = TypeName;
338+
339+
if (IsVBStringConstantToLiteral(expressionValue, out string constLiteral))
308340
{
309-
//Returning here ensures the typename is correct,
310-
//but only identical (copy/paste) equivalence involving
311-
//constants like vbNewLine will be flagged
312341
declaredTypeName = Tokens.String;
342+
expressionValue = constLiteral;
313343
return;
314344
}
315-
316-
if (long.TryParse(expressionValue, out _))
345+
else if (IsNonPrintingControlCharacter(expressionValue))
346+
{
347+
declaredTypeName = Tokens.String;
348+
return;
349+
}
350+
else if (long.TryParse(expressionValue, out _))
317351
{
318352
return;
319353
}
@@ -329,6 +363,10 @@ private void GetContextValue(ParserRuleContext context, out string declaredTypeN
329363
expressionValue = GetConstantContextValueToken(declaration.Context);
330364
if (expressionValue.Equals(string.Empty))
331365
{
366+
if (_enumMembers is null)
367+
{
368+
LoadEnumMemberValues();
369+
}
332370
var enumValue = _enumMembers.SingleOrDefault(dt => dt.ConstantContext == declaration.Context);
333371
expressionValue = enumValue?.Value.ToString() ?? string.Empty;
334372
}
@@ -412,11 +450,24 @@ private static bool IsBinaryOpEvaluationContext<T>(T context)
412450
return false;
413451
}
414452

415-
private static bool IsVBStringConstant(string candidate)
416-
=> _vbStringConstants.Exists(kv => kv.Key.Equals(candidate));
453+
public bool IsVBStringConstantToLiteral(string candidate, out string literal)
454+
{
455+
return _vbStringConstants.TryGetValue(candidate, out literal);
456+
}
457+
458+
public bool IsNonPrintingControlCharacter(string controlChar)
459+
{
460+
return controlChar != null && _vbStringConstants.ContainsValue(controlChar);
461+
}
462+
463+
public void InjectValuedDeclarationEvaluator( Func<Declaration, (bool, string, string)> func)
464+
{
465+
ValuedDeclarationEvaluator = func;
466+
}
417467

418468
private void LoadEnumMemberValues()
419469
{
470+
_enumMembers = new List<EnumMember>();
420471
foreach (var enumStmt in _enumStmtContexts)
421472
{
422473
long enumAssignedValue = -1;

0 commit comments

Comments
 (0)