Skip to content

Commit 89212c4

Browse files
committed
Created UnhandledOnErrorResumeNext inspection
1 parent 0df8107 commit 89212c4

File tree

8 files changed

+742
-3
lines changed

8 files changed

+742
-3
lines changed
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using Antlr4.Runtime;
4+
using Rubberduck.Inspections.Abstract;
5+
using Rubberduck.Inspections.Results;
6+
using Rubberduck.Parsing;
7+
using Rubberduck.Parsing.Grammar;
8+
using Rubberduck.Parsing.Inspections;
9+
using Rubberduck.Parsing.Inspections.Abstract;
10+
using Rubberduck.Parsing.Inspections.Resources;
11+
using Rubberduck.Parsing.VBA;
12+
using Rubberduck.VBEditor;
13+
14+
namespace Rubberduck.Inspections.Concrete
15+
{
16+
public class UnhandledOnErrorResumeNextInspection : ParseTreeInspectionBase
17+
{
18+
private readonly Dictionary<QualifiedContext<ParserRuleContext>, string> _errorHandlerLabelsMap =
19+
new Dictionary<QualifiedContext<ParserRuleContext>, string>();
20+
private readonly Dictionary<QualifiedContext<ParserRuleContext>, VBAParser.ModuleBodyElementContext> _bodyElementContextsMap =
21+
new Dictionary<QualifiedContext<ParserRuleContext>, VBAParser.ModuleBodyElementContext>();
22+
23+
public UnhandledOnErrorResumeNextInspection(RubberduckParserState state,
24+
CodeInspectionSeverity defaultSeverity = CodeInspectionSeverity.Warning) : base(state, defaultSeverity)
25+
{
26+
Listener = new OnErrorStatementListener(_errorHandlerLabelsMap, _bodyElementContextsMap);
27+
}
28+
29+
public override CodeInspectionType InspectionType => CodeInspectionType.CodeQualityIssues;
30+
31+
public override IInspectionListener Listener { get; }
32+
33+
protected override IEnumerable<IInspectionResult> DoGetInspectionResults()
34+
{
35+
return Listener.Contexts
36+
.Where(result => !IsIgnoringInspectionResultFor(result.ModuleName, result.Context.Start.Line))
37+
.Select(result =>
38+
{
39+
dynamic properties = new PropertyBag();
40+
properties.Label = _errorHandlerLabelsMap[result];
41+
properties.BodyElement = _bodyElementContextsMap[result];
42+
43+
return new QualifiedContextInspectionResult(this, InspectionsUI.UnhandledOnErrorResumeNextInspectionResultFormat, result, properties);
44+
});
45+
}
46+
}
47+
48+
public class OnErrorStatementListener : VBAParserBaseListener, IInspectionListener
49+
{
50+
private readonly List<QualifiedContext<ParserRuleContext>> _contexts = new List<QualifiedContext<ParserRuleContext>>();
51+
private readonly List<QualifiedContext<ParserRuleContext>> _unhandledContexts = new List<QualifiedContext<ParserRuleContext>>();
52+
private readonly List<string> _errorHandlerLabels = new List<string>();
53+
private readonly Dictionary<QualifiedContext<ParserRuleContext>, string> _errorHandlerLabelsMap;
54+
private readonly Dictionary<QualifiedContext<ParserRuleContext>, VBAParser.ModuleBodyElementContext> _bodyElementContextsMap;
55+
56+
private const string LabelPrefix = "ErrorHandler";
57+
58+
public OnErrorStatementListener(Dictionary<QualifiedContext<ParserRuleContext>, string> errorHandlerLabelsMap,
59+
Dictionary<QualifiedContext<ParserRuleContext>, VBAParser.ModuleBodyElementContext> bodyElementContextsMap)
60+
{
61+
_errorHandlerLabelsMap = errorHandlerLabelsMap;
62+
_bodyElementContextsMap = bodyElementContextsMap;
63+
}
64+
65+
public IReadOnlyList<QualifiedContext<ParserRuleContext>> Contexts => _contexts;
66+
67+
public void ClearContexts()
68+
{
69+
_contexts.Clear();
70+
}
71+
72+
public QualifiedModuleName CurrentModuleName { get; set; }
73+
74+
public override void ExitModuleBodyElement(VBAParser.ModuleBodyElementContext context)
75+
{
76+
if (_unhandledContexts.Any())
77+
{
78+
var labelIndex = -1;
79+
80+
foreach (var errorContext in _unhandledContexts)
81+
{
82+
_bodyElementContextsMap.Add(errorContext, context);
83+
84+
labelIndex++;
85+
var labelSuffix = labelIndex == 0 ? "" : labelIndex.ToString();
86+
87+
while (_errorHandlerLabels.Contains($"{LabelPrefix.ToLower()}{labelSuffix}"))
88+
{
89+
labelIndex++;
90+
labelSuffix = labelIndex == 0 ? "" : labelIndex.ToString();
91+
}
92+
93+
_errorHandlerLabelsMap.Add(errorContext, $"{LabelPrefix}{labelSuffix}");
94+
}
95+
96+
_contexts.AddRange(_unhandledContexts);
97+
98+
_unhandledContexts.Clear();
99+
_errorHandlerLabels.Clear();
100+
}
101+
}
102+
103+
public override void ExitOnErrorStmt(VBAParser.OnErrorStmtContext context)
104+
{
105+
if (context.RESUME() != null)
106+
{
107+
_unhandledContexts.Add(new QualifiedContext<ParserRuleContext>(CurrentModuleName, context));
108+
}
109+
else if (context.GOTO() != null)
110+
{
111+
_unhandledContexts.Clear();
112+
}
113+
}
114+
115+
public override void ExitIdentifierStatementLabel(VBAParser.IdentifierStatementLabelContext context)
116+
{
117+
var labelText = context.unrestrictedIdentifier().identifier().untypedIdentifier().identifierValue().IDENTIFIER().GetText();
118+
if (labelText.ToLower().StartsWith(LabelPrefix.ToLower()))
119+
{
120+
_errorHandlerLabels.Add(labelText.ToLower());
121+
}
122+
}
123+
}
124+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using Rubberduck.Inspections.Abstract;
2+
using Rubberduck.Inspections.Concrete;
3+
using Rubberduck.Parsing.Grammar;
4+
using Rubberduck.Parsing.Inspections.Abstract;
5+
using Rubberduck.Parsing.Inspections.Resources;
6+
using Rubberduck.Parsing.VBA;
7+
8+
namespace Rubberduck.Inspections.QuickFixes
9+
{
10+
public class RestoreErrorHandlingQuickFix : QuickFixBase
11+
{
12+
private readonly RubberduckParserState _state;
13+
14+
public RestoreErrorHandlingQuickFix(RubberduckParserState state)
15+
: base(typeof(UnhandledOnErrorResumeNextInspection))
16+
{
17+
_state = state;
18+
}
19+
20+
public override void Fix(IInspectionResult result)
21+
{
22+
var rewriter = _state.GetRewriter(result.QualifiedSelection.QualifiedName);
23+
var context = (VBAParser.OnErrorStmtContext)result.Context;
24+
25+
rewriter.Replace(context.RESUME(), Tokens.GoTo);
26+
rewriter.Replace(context.NEXT(), result.Properties.Label);
27+
28+
var exitStatement = "Exit ";
29+
VBAParser.BlockContext block;
30+
VBAParser.ModuleBodyElementContext bodyElementContext = result.Properties.BodyElement;
31+
32+
if (bodyElementContext.propertyGetStmt() != null)
33+
{
34+
exitStatement += "Property";
35+
block = bodyElementContext.propertyGetStmt().block();
36+
}
37+
else if (bodyElementContext.propertyLetStmt() != null)
38+
{
39+
exitStatement += "Property";
40+
block = bodyElementContext.propertyLetStmt().block();
41+
}
42+
else if (bodyElementContext.propertySetStmt() != null)
43+
{
44+
exitStatement += "Property";
45+
block = bodyElementContext.propertySetStmt().block();
46+
}
47+
else if (bodyElementContext.functionStmt() != null)
48+
{
49+
exitStatement += "Function";
50+
block = bodyElementContext.functionStmt().block();
51+
}
52+
else
53+
{
54+
exitStatement += "Sub";
55+
block = bodyElementContext.subStmt().block();
56+
}
57+
58+
var errorHandlerSubroutine = $@"
59+
{exitStatement}
60+
{result.Properties.Label}:
61+
If Err.Number > 0 Then 'TODO: handle specific error
62+
Err.Clear
63+
Resume Next
64+
End If
65+
";
66+
67+
rewriter.InsertAfter(block.Stop.TokenIndex, errorHandlerSubroutine);
68+
}
69+
70+
public override string Description(IInspectionResult result) => InspectionsUI.UnhandledOnErrorResumeNextInspectionQuickFix;
71+
72+
public override bool CanFixInProcedure => true;
73+
public override bool CanFixInModule => true;
74+
public override bool CanFixInProject => true;
75+
}
76+
}

Rubberduck.Inspections/Rubberduck.Inspections.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
<Compile Include="Concrete\LineLabelNotUsedInspection.cs" />
7575
<Compile Include="Concrete\IntegerDataTypeInspection.cs" />
7676
<Compile Include="Concrete\ShadowedDeclarationInspection.cs" />
77+
<Compile Include="Concrete\UnhandledOnErrorResumeNextInspection.cs" />
7778
<Compile Include="ParseTreeListeners\AttributeAnnotationListener.cs" />
7879
<Compile Include="Concrete\ConstantNotUsedInspection.cs" />
7980
<Compile Include="Concrete\DefaultProjectNameInspection.cs" />
@@ -125,6 +126,7 @@
125126
<Compile Include="Abstract\QuickFixBase.cs" />
126127
<Compile Include="QuickFixes\RemoveStopKeywordQuickFix.cs" />
127128
<Compile Include="QuickFixes\ReplaceObsoleteErrorStatementQuickFix.cs" />
129+
<Compile Include="QuickFixes\RestoreErrorHandlingQuickFix.cs" />
128130
<Compile Include="QuickFixes\SpecifyExplicitByRefModifierQuickFix.cs" />
129131
<Compile Include="QuickFixes\ChangeProcedureToFunctionQuickFix.cs" />
130132
<Compile Include="QuickFixes\ConvertToProcedureQuickFix.cs" />

Rubberduck.Parsing/Inspections/Resources/InspectionsUI.resx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ If the parameter can be null, ignore this inspection result; passing a null valu
542542
<value>Component '{0}' uses 'Option Base 1'</value>
543543
</data>
544544
<data name="UntypedFunctionUsageInspectionResultFormat" xml:space="preserve">
545-
<value>Replace function `{0}` with existing typed function</value>
545+
<value>Replace function '{0}' with existing typed function</value>
546546
</data>
547547
<data name="VariableTypeNotDeclaredInspectionResultFormat" xml:space="preserve">
548548
<value>{0} '{1}' is implicitly 'Variant'</value>
@@ -890,4 +890,16 @@ If the parameter can be null, ignore this inspection result; passing a null valu
890890
<data name="ObsoleteErrorSyntaxInspectionName" xml:space="preserve">
891891
<value>Use of 'Error' statement</value>
892892
</data>
893+
<data name="UnhandledOnErrorResumeNextInspectionMeta" xml:space="preserve">
894+
<value>Error handling should be restored after using 'On Error Resume Next'.</value>
895+
</data>
896+
<data name="UnhandledOnErrorResumeNextInspectionName" xml:space="preserve">
897+
<value>Unhandled 'On Error Resume Next'</value>
898+
</data>
899+
<data name="UnhandledOnErrorResumeNextInspectionQuickFix" xml:space="preserve">
900+
<value>Introduce error handling subroutine</value>
901+
</data>
902+
<data name="UnhandledOnErrorResumeNextInspectionResultFormat" xml:space="preserve">
903+
<value>Errors are ignored but never handled again</value>
904+
</data>
893905
</root>

Rubberduck.Parsing/Inspections/Resources/InspectionsUI1.Designer.cs

Lines changed: 37 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)