Skip to content

Commit 3109432

Browse files
committed
Adds Stop keyword inspection and quick fix. Closes #3266
1 parent e240d2f commit 3109432

File tree

7 files changed

+317
-1
lines changed

7 files changed

+317
-1
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Antlr4.Runtime;
5+
using Antlr4.Runtime.Misc;
6+
using Rubberduck.Inspections.Abstract;
7+
using Rubberduck.Inspections.Results;
8+
using Rubberduck.Parsing;
9+
using Rubberduck.Parsing.Grammar;
10+
using Rubberduck.Parsing.Inspections.Abstract;
11+
using Rubberduck.Parsing.Inspections.Resources;
12+
using Rubberduck.Parsing.VBA;
13+
using Rubberduck.VBEditor;
14+
15+
namespace Rubberduck.Inspections.Concrete
16+
{
17+
public sealed class StopKeywordInspection : ParseTreeInspectionBase
18+
{
19+
public StopKeywordInspection(RubberduckParserState state)
20+
: base(state, CodeInspectionSeverity.Suggestion) { }
21+
22+
public override Type Type => typeof(StopKeywordInspection);
23+
24+
public override CodeInspectionType InspectionType => CodeInspectionType.CodeQualityIssues;
25+
26+
public override IInspectionListener Listener { get; } =
27+
new StopKeywordListener();
28+
29+
public override IEnumerable<IInspectionResult> GetInspectionResults()
30+
{
31+
return Listener.Contexts
32+
.Where(result => !IsIgnoringInspectionResultFor(result.ModuleName, result.Context.Start.Line))
33+
.Select(result => new QualifiedContextInspectionResult(this,
34+
InspectionsUI.StopKeywordInspectionResultFormat,
35+
result));
36+
}
37+
38+
public class StopKeywordListener : VBAParserBaseListener, IInspectionListener
39+
{
40+
private readonly List<QualifiedContext<ParserRuleContext>> _contexts = new List<QualifiedContext<ParserRuleContext>>();
41+
public IReadOnlyList<QualifiedContext<ParserRuleContext>> Contexts => _contexts;
42+
43+
public QualifiedModuleName CurrentModuleName { get; set; }
44+
45+
public void ClearContexts()
46+
{
47+
_contexts.Clear();
48+
}
49+
50+
public override void ExitStopStmt([NotNull] VBAParser.StopStmtContext context)
51+
{
52+
_contexts.Add(new QualifiedContext<ParserRuleContext>(CurrentModuleName, context));
53+
}
54+
}
55+
}
56+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Rubberduck.Inspections.Concrete;
5+
using Rubberduck.Parsing.Inspections.Abstract;
6+
using Rubberduck.Parsing.Inspections.Resources;
7+
using Rubberduck.Parsing.VBA;
8+
9+
namespace Rubberduck.Inspections.QuickFixes
10+
{
11+
public sealed class RemoveStopKeywordQuickFix : IQuickFix
12+
{
13+
private readonly RubberduckParserState _state;
14+
private static readonly HashSet<Type> _supportedInspections = new HashSet<Type>
15+
{
16+
typeof(StopKeywordInspection)
17+
};
18+
19+
public RemoveStopKeywordQuickFix(RubberduckParserState state)
20+
{
21+
_state = state;
22+
}
23+
24+
public IReadOnlyCollection<Type> SupportedInspections => _supportedInspections.ToList();
25+
26+
public void Fix(IInspectionResult result)
27+
{
28+
var rewriter = _state.GetRewriter(result.QualifiedSelection.QualifiedName);
29+
rewriter.Remove(result.Context);
30+
}
31+
32+
public string Description(IInspectionResult result)
33+
{
34+
return InspectionsUI.RemoveStopKeywordQuickFix;
35+
}
36+
37+
public bool CanFixInProcedure => false;
38+
public bool CanFixInModule => false;
39+
public bool CanFixInProject => false;
40+
}
41+
}

Rubberduck.Inspections/Rubberduck.Inspections.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
<Compile Include="Abstract\ParseTreeInspectionBase.cs" />
5858
<Compile Include="Concrete\ApplicationWorksheetFunctionInspection.cs" />
5959
<Compile Include="Concrete\AssignedByValParameterInspection.cs" />
60+
<Compile Include="Concrete\StopKeywordInspection.cs" />
6061
<Compile Include="Concrete\LineLabelNotUsedInspection.cs" />
6162
<Compile Include="Concrete\IntegerDataTypeInspection.cs" />
6263
<Compile Include="ParseTreeListeners\AttributeAnnotationListener.cs" />
@@ -107,6 +108,7 @@
107108
<Compile Include="QuickFixes\AssignedByValParameterMakeLocalCopyQuickFix.cs" />
108109
<Compile Include="QuickFixes\ChangeDimToPrivateQuickFix.cs" />
109110
<Compile Include="QuickFixes\ChangeIntegerToLongQuickFix.cs" />
111+
<Compile Include="QuickFixes\RemoveStopKeywordQuickFix.cs" />
110112
<Compile Include="QuickFixes\SpecifyExplicitByRefModifierQuickFix.cs" />
111113
<Compile Include="QuickFixes\ChangeProcedureToFunctionQuickFix.cs" />
112114
<Compile Include="QuickFixes\ConvertToProcedureQuickFix.cs" />

Rubberduck.Parsing/Inspections/Resources/InspectionsUI.resx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,4 +790,17 @@ If the parameter can be null, ignore this inspection result; passing a null valu
790790
<data name="IntegerDataTypeQuickFix" xml:space="preserve">
791791
<value>Change declaration type to Long</value>
792792
</data>
793+
<data name="RemoveStopKeywordQuickFix" xml:space="preserve">
794+
<value>Remove usage of the 'stop' keyword</value>
795+
</data>
796+
<data name="StopKeywordInspectionMeta" xml:space="preserve">
797+
<value>The 'stop' keyword halts execution and brings up the debugger. Avoid its usage.</value>
798+
</data>
799+
<data name="StopKeywordInspectionName" xml:space="preserve">
800+
<value>Stop keyword</value>
801+
</data>
802+
<data name="StopKeywordInspectionResultFormat" xml:space="preserve">
803+
<value>Stop keyword detected</value>
804+
<comment>{0} Property name</comment>
805+
</data>
793806
</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.
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
using System.Linq;
2+
using Microsoft.VisualStudio.TestTools.UnitTesting;
3+
using RubberduckTests.Mocks;
4+
using System.Threading;
5+
using Rubberduck.Inspections.Concrete;
6+
using Rubberduck.Inspections.QuickFixes;
7+
using Rubberduck.Parsing.Inspections.Resources;
8+
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
9+
10+
namespace RubberduckTests.Inspections
11+
{
12+
[TestClass]
13+
public class StopKeywordInspectionTests
14+
{
15+
[TestMethod]
16+
[TestCategory("Inspections")]
17+
public void StopKeyword_ReturnsResult()
18+
{
19+
const string inputCode =
20+
@"Sub Foo()
21+
Stop
22+
End Sub";
23+
24+
IVBComponent component;
25+
var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out component);
26+
var state = MockParser.CreateAndParse(vbe.Object);
27+
28+
var inspection = new StopKeywordInspection(state);
29+
var inspector = InspectionsHelper.GetInspector(inspection);
30+
var inspectionResults = inspector.FindIssuesAsync(state, CancellationToken.None).Result;
31+
32+
Assert.AreEqual(1, inspectionResults.Count());
33+
}
34+
35+
[TestMethod]
36+
[TestCategory("Inspections")]
37+
public void NoStopKeyword_NoResult()
38+
{
39+
var inputCode =
40+
@"Sub Foo()
41+
End Sub";
42+
43+
IVBComponent component;
44+
var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out component);
45+
var state = MockParser.CreateAndParse(vbe.Object);
46+
47+
var inspection = new StopKeywordInspection(state);
48+
var inspector = InspectionsHelper.GetInspector(inspection);
49+
var inspectionResults = inspector.FindIssuesAsync(state, CancellationToken.None).Result;
50+
51+
Assert.IsFalse(inspectionResults.Any());
52+
}
53+
54+
[TestMethod]
55+
[TestCategory("Inspections")]
56+
public void StopKeyword_Ignored_DoesNotReturnResult()
57+
{
58+
var inputCode =
59+
@"Sub Foo()
60+
'@Ignore StopKeyword
61+
Stop
62+
End Sub";
63+
64+
IVBComponent component;
65+
var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out component);
66+
var state = MockParser.CreateAndParse(vbe.Object);
67+
68+
var inspection = new ObsoleteCallStatementInspection(state);
69+
var inspector = InspectionsHelper.GetInspector(inspection);
70+
var inspectionResults = inspector.FindIssuesAsync(state, CancellationToken.None).Result;
71+
72+
Assert.IsFalse(inspectionResults.Any());
73+
}
74+
75+
[TestMethod]
76+
[TestCategory("Inspections")]
77+
public void StopKeywords_Ignored_ReturnsCorrectResults()
78+
{
79+
var inputCode =
80+
@"Sub Foo()
81+
Dim d As Integer
82+
d = 0
83+
Stop
84+
85+
d = 1
86+
87+
'@Ignore StopKeyword
88+
Stop
89+
End Sub";
90+
91+
IVBComponent component;
92+
var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out component);
93+
var state = MockParser.CreateAndParse(vbe.Object);
94+
95+
var inspection = new StopKeywordInspection(state);
96+
var inspector = InspectionsHelper.GetInspector(inspection);
97+
var inspectionResults = inspector.FindIssuesAsync(state, CancellationToken.None).Result;
98+
99+
Assert.AreEqual(1, inspectionResults.Count());
100+
Assert.AreEqual(4, inspectionResults.First().QualifiedSelection.Selection.StartLine);
101+
}
102+
103+
[TestMethod]
104+
[TestCategory("Inspections")]
105+
public void StopKeyword_QuickFixWorks_RemoveKeyword()
106+
{
107+
var inputCode =
108+
@"Sub Foo()
109+
Stop
110+
End Sub";
111+
112+
var expectedCode =
113+
@"Sub Foo()
114+
115+
End Sub";
116+
117+
IVBComponent component;
118+
var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out component);
119+
var state = MockParser.CreateAndParse(vbe.Object);
120+
121+
var inspection = new StopKeywordInspection(state);
122+
var inspector = InspectionsHelper.GetInspector(inspection);
123+
var inspectionResults = inspector.FindIssuesAsync(state, CancellationToken.None).Result;
124+
125+
new RemoveOptionBaseStatementQuickFix(state).Fix(inspectionResults.First());
126+
Assert.AreEqual(expectedCode, state.GetRewriter(component).GetText());
127+
}
128+
129+
[TestMethod]
130+
[TestCategory("Inspections")]
131+
public void StopKeyword_QuickFixWorks_RemoveKeyword_InstructionSeparator()
132+
{
133+
var inputCode = "Sub Foo(): Stop: End Sub";
134+
135+
var expectedCode = "Sub Foo(): : End Sub";
136+
137+
IVBComponent component;
138+
var vbe = MockVbeBuilder.BuildFromSingleStandardModule(inputCode, out component);
139+
var state = MockParser.CreateAndParse(vbe.Object);
140+
141+
var inspection = new StopKeywordInspection(state);
142+
var inspector = InspectionsHelper.GetInspector(inspection);
143+
var inspectionResults = inspector.FindIssuesAsync(state, CancellationToken.None).Result;
144+
145+
new RemoveStopKeywordQuickFix(state).Fix(inspectionResults.First());
146+
Assert.AreEqual(expectedCode, state.GetRewriter(component).GetText());
147+
}
148+
149+
[TestMethod]
150+
[TestCategory("Inspections")]
151+
public void InspectionType()
152+
{
153+
var inspection = new StopKeywordInspection(null);
154+
Assert.AreEqual(CodeInspectionType.CodeQualityIssues, inspection.InspectionType);
155+
}
156+
157+
[TestMethod]
158+
[TestCategory("Inspections")]
159+
public void InspectionName()
160+
{
161+
const string inspectionName = nameof(StopKeywordInspection);
162+
var inspection = new StopKeywordInspection(null);
163+
164+
Assert.AreEqual(inspectionName, inspection.Name);
165+
}
166+
}
167+
}

RubberduckTests/RubberduckTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@
107107
<Compile Include="Inspections\IgnoreOnceQuickFixTests.cs" />
108108
<Compile Include="Inspections\ImplicitActiveWorkbookReferenceInspectionTests.cs" />
109109
<Compile Include="Inspections\MemberNotOnInterfaceInspectionTests.cs" />
110+
<Compile Include="Inspections\StopKeywordInspectionTests.cs" />
110111
<Compile Include="Inspections\OptionBaseZeroInspectionTests.cs" />
111112
<Compile Include="Inspections\PassParameterByReferenceQuickFixTests.cs" />
112113
<Compile Include="Inspections\QuickFixProviderTests.cs" />

0 commit comments

Comments
 (0)