Skip to content

Commit 0db6006

Browse files
author
Andrin Meier
committed
implement function result value not used inspection (resolves #203)
1 parent 3be8453 commit 0db6006

11 files changed

+732
-418
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using Antlr4.Runtime;
2+
using Rubberduck.Parsing;
3+
using Rubberduck.Parsing.Grammar;
4+
using Rubberduck.UI;
5+
using Rubberduck.VBEditor;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using System.Text.RegularExpressions;
9+
10+
namespace Rubberduck.Inspections
11+
{
12+
public class ConvertToProcedureQuickFix : CodeInspectionQuickFix
13+
{
14+
private readonly IEnumerable<string> _returnStatements;
15+
16+
public ConvertToProcedureQuickFix(ParserRuleContext context, QualifiedSelection selection)
17+
: this(context, selection, new List<string>())
18+
{
19+
}
20+
21+
public ConvertToProcedureQuickFix(ParserRuleContext context, QualifiedSelection selection, IEnumerable<string> returnStatements)
22+
: base(context, selection, RubberduckUI.Inspections_ConvertFunctionToProcedure)
23+
{
24+
_returnStatements = returnStatements;
25+
}
26+
27+
public override void Fix()
28+
{
29+
var context = (VBAParser.FunctionStmtContext)Context;
30+
var visibility = context.visibility() == null ? string.Empty : context.visibility().GetText() + ' ';
31+
var name = ' ' + context.ambiguousIdentifier().GetText();
32+
var args = context.argList().GetText();
33+
var asType = context.asTypeClause() == null ? string.Empty : ' ' + context.asTypeClause().GetText();
34+
35+
var oldSignature = visibility + Tokens.Function + name + args + asType;
36+
var newSignature = visibility + Tokens.Sub + name + args;
37+
38+
var procedure = Context.GetText();
39+
string noReturnStatements = procedure;
40+
_returnStatements.ToList().ForEach(returnStatement =>
41+
noReturnStatements = Regex.Replace(noReturnStatements, @"[ \t\f]*" + returnStatement + @"[ \t\f]*\r?\n?", ""));
42+
var result = noReturnStatements.Replace(oldSignature, newSignature)
43+
.Replace(Tokens.End + ' ' + Tokens.Function, Tokens.End + ' ' + Tokens.Sub)
44+
.Replace(Tokens.Exit + ' ' + Tokens.Function, Tokens.Exit + ' ' + Tokens.Sub);
45+
46+
var module = Selection.QualifiedName.Component.CodeModule;
47+
var selection = Context.GetSelection();
48+
49+
module.DeleteLines(selection.StartLine, selection.LineCount);
50+
module.InsertLines(selection.StartLine, result);
51+
}
52+
}
53+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using Rubberduck.Parsing.Symbols;
4+
using Rubberduck.Parsing.VBA;
5+
using Rubberduck.Parsing.Grammar;
6+
using Antlr4.Runtime;
7+
using Rubberduck.UI;
8+
9+
namespace Rubberduck.Inspections
10+
{
11+
public sealed class FunctionReturnValueNotUsedInspection : InspectionBase
12+
{
13+
public FunctionReturnValueNotUsedInspection(RubberduckParserState state)
14+
: base(state)
15+
{
16+
Severity = CodeInspectionSeverity.Warning;
17+
}
18+
19+
public override string Description { get { return InspectionsUI.FunctionReturnValueNotUsedInspection; } }
20+
public override CodeInspectionType InspectionType { get { return CodeInspectionType.CodeQualityIssues; } }
21+
22+
public override IEnumerable<CodeInspectionResultBase> GetInspectionResults()
23+
{
24+
var functions = UserDeclarations.Where(function => function.DeclarationType == DeclarationType.Function).ToList();
25+
var returnValueNotUsedFunctions = functions.Where(function =>
26+
function.References.All(usage =>
27+
IsReturnStatement(function, usage) || IsAddressOfCall(usage) || IsCallWithoutAssignment(usage)));
28+
29+
var issues = returnValueNotUsedFunctions
30+
.Select(function =>
31+
new FunctionReturnValueNotUsedInspectionResult(
32+
this,
33+
function.Context,
34+
function.QualifiedName,
35+
function.References.Where(usage => IsReturnStatement(function, usage)).Select(usage => usage.Context.Parent.Parent.Parent.GetText())));
36+
37+
return issues;
38+
}
39+
40+
private bool IsAddressOfCall(IdentifierReference usage)
41+
{
42+
RuleContext current = usage.Context;
43+
while (current != null && !(current is VBAParser.VsAddressOfContext)) current = current.Parent;
44+
return current != null;
45+
}
46+
47+
private bool IsReturnStatement(Declaration function, IdentifierReference assignment)
48+
{
49+
return assignment.ParentScope == function.Scope;
50+
}
51+
52+
private bool IsCallWithoutAssignment(IdentifierReference usage)
53+
{
54+
return usage.Context.Parent != null && usage.Context.Parent is VBAParser.ICS_B_ProcedureCallContext;
55+
}
56+
}
57+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System.Collections.Generic;
2+
using Antlr4.Runtime;
3+
using Rubberduck.Parsing.Grammar;
4+
using Rubberduck.UI;
5+
using Rubberduck.VBEditor;
6+
using System.Text.RegularExpressions;
7+
using System.Linq;
8+
using System;
9+
using Rubberduck.Parsing;
10+
11+
namespace Rubberduck.Inspections
12+
{
13+
public class FunctionReturnValueNotUsedInspectionResult : CodeInspectionResultBase
14+
{
15+
private readonly IEnumerable<CodeInspectionQuickFix> _quickFixes;
16+
17+
public FunctionReturnValueNotUsedInspectionResult(IInspection inspection, ParserRuleContext context, QualifiedMemberName qualifiedName, IEnumerable<string> returnStatements)
18+
: base(inspection, string.Format(inspection.Description, qualifiedName.MemberName), qualifiedName.QualifiedModuleName, context)
19+
{
20+
_quickFixes = new[]
21+
{
22+
new ConvertToProcedureQuickFix(context, QualifiedSelection, returnStatements),
23+
};
24+
}
25+
26+
public override IEnumerable<CodeInspectionQuickFix> QuickFixes { get { return _quickFixes; } }
27+
}
28+
}

RetailCoder.VBE/Inspections/InspectionsUI.Designer.cs

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

RetailCoder.VBE/Inspections/InspectionsUI.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,4 +371,10 @@
371371
<data name="SelfAssignedDeclarationInspectionName" xml:space="preserve">
372372
<value>Self-assigned property</value>
373373
</data>
374+
<data name="FunctionReturnValueNotUsedInspectionName" xml:space="preserve">
375+
<value>Function return value is never used.</value>
376+
</data>
377+
<data name="FunctionReturnValueNotUsedInspection" xml:space="preserve">
378+
<value>Return value of function '{0}' is never used.</value>
379+
</data>
374380
</root>

RetailCoder.VBE/Inspections/NonReturningFunctionInspectionResult.cs

Lines changed: 1 addition & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Rubberduck.Parsing.Grammar;
66
using Rubberduck.UI;
77
using Rubberduck.VBEditor;
8+
using System.Text.RegularExpressions;
89

910
namespace Rubberduck.Inspections
1011
{
@@ -27,35 +28,4 @@ public NonReturningFunctionInspectionResult(IInspection inspection, string resul
2728

2829
public override IEnumerable<CodeInspectionQuickFix> QuickFixes { get { return _quickFixes; } }
2930
}
30-
31-
public class ConvertToProcedureQuickFix : CodeInspectionQuickFix
32-
{
33-
public ConvertToProcedureQuickFix(ParserRuleContext context, QualifiedSelection selection)
34-
: base(context, selection, RubberduckUI.Inspections_ConvertFunctionToProcedure)
35-
{
36-
}
37-
38-
public override void Fix()
39-
{
40-
var context = (VBAParser.FunctionStmtContext) Context;
41-
var visibility = context.visibility() == null ? string.Empty : context.visibility().GetText() + ' ';
42-
var name = ' ' + context.ambiguousIdentifier().GetText();
43-
var args = context.argList().GetText();
44-
var asType = context.asTypeClause() == null ? string.Empty : ' ' + context.asTypeClause().GetText();
45-
46-
var oldSignature = visibility + Tokens.Function + name + args + asType;
47-
var newSignature = visibility + Tokens.Sub + name + args;
48-
49-
var procedure = Context.GetText();
50-
var result = procedure.Replace(oldSignature, newSignature)
51-
.Replace(Tokens.End + ' ' + Tokens.Function, Tokens.End + ' ' + Tokens.Sub)
52-
.Replace(Tokens.Exit + ' ' + Tokens.Function, Tokens.Exit + ' ' + Tokens.Sub);
53-
54-
var module = Selection.QualifiedName.Component.CodeModule;
55-
var selection = Context.GetSelection();
56-
57-
module.DeleteLines(selection.StartLine, selection.LineCount);
58-
module.InsertLines(selection.StartLine, result);
59-
}
60-
}
6131
}

RetailCoder.VBE/Rubberduck.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,9 @@
299299
<DependentUpon>GroupingGrid.xaml</DependentUpon>
300300
</Compile>
301301
<Compile Include="Inspections\CodeInspectionQuickFix.cs" />
302+
<Compile Include="Inspections\ConvertToProcedureQuickFix.cs" />
303+
<Compile Include="Inspections\FunctionReturnValueNotUsedInspection.cs" />
304+
<Compile Include="Inspections\FunctionReturnValueNotUsedInspectionResult.cs" />
302305
<Compile Include="Inspections\SelfAssignedDeclarationInspection.cs" />
303306
<Compile Include="Inspections\SelfAssignedDeclarationInspectionResult.cs" />
304307
<Compile Include="Inspections\InspectionsUI.Designer.cs">
@@ -846,6 +849,7 @@
846849
<EmbeddedResource Include="Inspections\InspectionsUI.resx">
847850
<Generator>ResXFileCodeGenerator</Generator>
848851
<LastGenOutput>InspectionsUI1.Designer.cs</LastGenOutput>
852+
<SubType>Designer</SubType>
849853
</EmbeddedResource>
850854
<EmbeddedResource Include="UI\AboutWindow.resx">
851855
<DependentUpon>AboutWindow.cs</DependentUpon>

0 commit comments

Comments
 (0)