Skip to content

Commit d44bd53

Browse files
committed
Implemented VariableNotAssignedInspetion
1 parent 1578de1 commit d44bd53

File tree

9 files changed

+214
-69
lines changed

9 files changed

+214
-69
lines changed
Lines changed: 104 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Collections.Generic;
22
using System.Linq;
33
using Antlr4.Runtime;
4+
using Microsoft.Vbe.Interop;
45
using Rubberduck.Extensions;
56
using Rubberduck.VBA;
67
using Rubberduck.VBA.Grammar;
@@ -9,49 +10,107 @@
910

1011
namespace Rubberduck.Inspections
1112
{
12-
//public class VariableNotAssignedInspection : IInspection
13-
//{
14-
// public VariableNotAssignedInspection()
15-
// {
16-
// Severity = CodeInspectionSeverity.Error;
17-
// }
18-
19-
// public string Name { get { return InspectionNames.VariableNotAssigned; } }
20-
// public CodeInspectionType InspectionType { get { return CodeInspectionType.CodeQualityIssues; } }
21-
// public CodeInspectionSeverity Severity { get; set; }
22-
23-
// public IEnumerable<CodeInspectionResultBase> GetInspectionResults(IEnumerable<VBComponentParseResult> parseResult)
24-
// {
25-
// var parseResults = parseResult.ToList();
26-
27-
// /*
28-
// *
29-
// *
30-
// */
31-
32-
// var assignments = parseResults.Select(result =>
33-
// new
34-
// {
35-
// Module = result.Component,
36-
// ParseTree = result.ParseTree.GetContexts<VariableAssignmentListener, VisualBasic6Parser.VariableCallStmtContext>(new VariableAssignmentListener())
37-
// }).ToList();
38-
39-
// foreach (var result in parseResults)
40-
// {
41-
// var module = result; // to avoid access to modified closure in below lambdas
42-
// var declarations = result.ParseTree.GetContexts<DeclarationListener, ParserRuleContext>(new DeclarationListener()).ToList();
43-
// var localAssigns = assignments.Where(assign => assign.Module.QualifiedName().Equals(module.QualifiedName));
44-
45-
// var variables = declarations.Where(declaration => declaration is VisualBasic6Parser.VariableSubStmtContext)
46-
// .Cast<VisualBasic6Parser.VariableSubStmtContext>()
47-
// .Where(variable => assignments.Where(a => a.Module.QualifiedName() != ).All(assigned => assigned.ambiguousIdentifier().GetText() != variable.ambiguousIdentifier().GetText()))
48-
// .Select(variable => new VariableNotAssignedInspetionResult(Name, Severity, variable, module.QualifiedName));
49-
50-
// foreach (var variable in variables)
51-
// {
52-
// yield return variable;
53-
// }
54-
// }
55-
// }
56-
//}
13+
public class VariableNotAssignedInspection : IInspection
14+
{
15+
public VariableNotAssignedInspection()
16+
{
17+
Severity = CodeInspectionSeverity.Error;
18+
}
19+
20+
public string Name { get { return InspectionNames.VariableNotAssigned; } }
21+
public CodeInspectionType InspectionType { get { return CodeInspectionType.CodeQualityIssues; } }
22+
public CodeInspectionSeverity Severity { get; set; }
23+
24+
public IEnumerable<CodeInspectionResultBase> GetInspectionResults(IEnumerable<VBComponentParseResult> parseResult)
25+
{
26+
var parseResults = parseResult.ToList();
27+
28+
// publics & globals delared at module-scope in standard modules:
29+
var globals =
30+
parseResults.Select(result =>
31+
new
32+
{
33+
Name = result.QualifiedName,
34+
Globals = result.ParseTree.GetContexts<DeclarationSectionListener, ParserRuleContext>(new DeclarationSectionListener())
35+
.OfType<VisualBasic6Parser.VariableStmtContext>()
36+
.Where(context =>
37+
context.visibility() != null &&
38+
context.visibility().GetText() != Tokens.Private)
39+
.SelectMany(context => context.variableListStmt().variableSubStmt()
40+
.Select(variable => variable.ambiguousIdentifier()))
41+
})
42+
.SelectMany(module => module.Globals.Select(global =>
43+
new
44+
{
45+
Name = module.Name,
46+
Global = global
47+
})).ToList();
48+
49+
var assignedGlobals = new List<VisualBasic6Parser.AmbiguousIdentifierContext>();
50+
var unassignedDeclarations = new List<CodeInspectionResultBase>();
51+
52+
foreach (var result in parseResults)
53+
{
54+
// module-scoped in this module:
55+
var declarations = result.ParseTree.GetContexts<DeclarationSectionListener, ParserRuleContext>(new DeclarationSectionListener())
56+
.OfType<VisualBasic6Parser.VariableSubStmtContext>()
57+
.Where(variable => globals.All(global => global.Global.GetText() != variable.GetText()))
58+
.ToList();
59+
var procedures = result.ParseTree.GetContexts<ProcedureListener, ParserRuleContext>(new ProcedureListener()).ToList();
60+
61+
// fetch & scope all assignments:
62+
var assignments = procedures.SelectMany(
63+
procedure => procedure.GetContexts<VariableAssignmentListener, VisualBasic6Parser.AmbiguousIdentifierContext>(new VariableAssignmentListener())
64+
.Select(context => new
65+
{
66+
Scope = new QualifiedMemberName(result.QualifiedName, ((dynamic)procedure).ambiguousIdentifier().GetText()),
67+
Name = context.GetText()
68+
}));
69+
70+
// fetch & scope all procedure-scoped declarations:
71+
var locals = procedures.SelectMany(
72+
procedure => procedure.GetContexts<DeclarationListener, ParserRuleContext>(new DeclarationListener())
73+
.OfType<VisualBasic6Parser.VariableSubStmtContext>()
74+
.Select(context => new
75+
{
76+
Context = context,
77+
Scope = new QualifiedMemberName(result.QualifiedName, ((dynamic)procedure).ambiguousIdentifier().GetText()),
78+
Name = context.ambiguousIdentifier().GetText()
79+
}));
80+
81+
// identify unassigned module-scoped declarations:
82+
unassignedDeclarations.AddRange(
83+
declarations.Select(d => d.ambiguousIdentifier())
84+
.Where(d => assignments.All(a => a.Name != d.GetText()))
85+
.Select(identifier => new VariableNotAssignedInspetionResult(Name, Severity, identifier, result.QualifiedName)));
86+
87+
// identify unassigned procedure-scoped declarations:
88+
unassignedDeclarations.AddRange(
89+
locals.Where(local => assignments.All(a => a.Name != local.Name))
90+
.Select(identifier => new VariableNotAssignedInspetionResult(Name, Severity, identifier.Context.ambiguousIdentifier(), result.QualifiedName)));
91+
92+
// identify globals assigned in this module:
93+
assignedGlobals.AddRange(globals.Where(global => assignments.Any(a => a.Name == global.Global.GetText()))
94+
.Select(global => global.Global));
95+
}
96+
97+
// identify unassigned globals:
98+
var assignedIdentifiers = assignedGlobals.Select(assigned => assigned.GetText());
99+
var unassignedGlobals = globals.Where(global => !assignedIdentifiers.Contains(global.Global.GetText()))
100+
.Select(identifier => new VariableNotAssignedInspetionResult(Name, Severity, identifier.Global, identifier.Name));
101+
unassignedDeclarations.AddRange(unassignedGlobals);
102+
103+
return unassignedDeclarations;
104+
}
105+
106+
private IEnumerable<VisualBasic6Parser.AmbiguousIdentifierContext> FindUnassignedLocals(ParserRuleContext procedureContext, IEnumerable<VisualBasic6Parser.AmbiguousIdentifierContext> globals)
107+
{
108+
return procedureContext.GetRuleContexts<VisualBasic6Parser.VariableSubStmtContext>()
109+
.Select(variable => variable.ambiguousIdentifier())
110+
.Union(globals)
111+
.Where(identifier =>
112+
procedureContext.GetContexts<VariableAssignmentListener, VisualBasic6Parser.AmbiguousIdentifierContext>(new VariableAssignmentListener())
113+
.All(assignment => assignment.GetText() != identifier.GetText()));
114+
}
115+
}
57116
}

RetailCoder.VBE/Rubberduck.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@
132132
<Compile Include="VBA\ParseTreeListeners\ProcedureListener.cs" />
133133
<Compile Include="VBA\ParseTreeListeners\ProcedureNameListener.cs" />
134134
<Compile Include="VBA\ParseTreeListeners\PublicSubListener.cs" />
135+
<Compile Include="VBA\ParseTreeListeners\ScopedDeclaration.cs" />
135136
<Compile Include="VBA\ParseTreeListeners\TreeViewListener.cs" />
136137
<Compile Include="VBA\ParseTreeListeners\VariableAssignmentListener.cs" />
137138
<Compile Include="VBA\QualifiedContext.cs" />

RetailCoder.VBE/UI/CodeInspections/CodeInspectionsDockablePresenter.cs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,23 @@ private async void RefreshAsync()
7878
var code = (_parser.Parse(VBE.ActiveVBProject)).ToList();
7979

8080
var results = new ConcurrentBag<CodeInspectionResultBase>();
81-
var inspections = _inspections.Where(inspection => inspection.Severity != CodeInspectionSeverity.DoNotShow);
82-
Parallel.ForEach(inspections, inspection =>
81+
var inspections = _inspections.Where(inspection => inspection.Severity != CodeInspectionSeverity.DoNotShow)
82+
.Select(inspection =>
83+
new Task(() =>
84+
{
85+
var result = inspection.GetInspectionResults(code);
86+
foreach (var inspectionResult in result)
87+
{
88+
results.Add(inspectionResult);
89+
}
90+
})).ToArray();
91+
92+
foreach (var inspection in inspections)
8393
{
84-
var result = inspection.GetInspectionResults(code);
85-
foreach (var inspectionResult in result)
86-
{
87-
results.Add(inspectionResult);
88-
}
89-
});
94+
inspection.Start();
95+
}
96+
97+
Task.WaitAll(inspections);
9098

9199
_results = results.ToList();
92100
Control.SetContent(_results.Select(item => new CodeInspectionResultGridViewItem(item)).OrderBy(item => item.Component).ThenBy(item => item.Line));

RetailCoder.VBE/UI/Refactorings/ExtractMethod/ExtractedParameter.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
using System.Runtime.InteropServices;
21
using Rubberduck.VBA;
32

43
namespace Rubberduck.UI.Refactorings.ExtractMethod
54
{
6-
[ComVisible(true)]
75
public class ExtractedParameter
86
{
97
public enum PassedBy

RetailCoder.VBE/VBA/AntlrExtensions.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System.Collections.Generic;
2+
using Antlr4.Runtime;
23
using Antlr4.Runtime.Tree;
4+
using Rubberduck.VBA.Grammar;
35
using Rubberduck.VBA.ParseTreeListeners;
46

57
namespace Rubberduck.VBA
@@ -18,5 +20,15 @@ public static IEnumerable<TContext> GetContexts<TListener, TContext>(this IParse
1820

1921
return listener.Members;
2022
}
23+
24+
/// <summary>
25+
/// Assuming the specified identifier is in scope,
26+
/// returns <c>true</c> if its name matches that of the used variable.
27+
/// </summary>
28+
/// <returns></returns>
29+
public static bool IsIdentifierUsage(this VisualBasic6Parser.VariableCallStmtContext usage, VisualBasic6Parser.AmbiguousIdentifierContext identifier)
30+
{
31+
return usage.ambiguousIdentifier().GetText() == identifier.GetText();
32+
}
2133
}
2234
}

RetailCoder.VBE/VBA/ParseTreeListeners/DeclarationListener.cs

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,65 @@
11
using System.Collections.Generic;
22
using Antlr4.Runtime;
3-
using Rubberduck.Inspections;
43
using Rubberduck.VBA.Grammar;
54

65
namespace Rubberduck.VBA.ParseTreeListeners
76
{
8-
9-
public class ScopedDeclaration<TContext>
10-
where TContext : ParserRuleContext
7+
public class DeclarationSectionListener : DeclarationListener
118
{
12-
private readonly TContext _context;
9+
private bool _insideProcedure;
10+
11+
public override void EnterVariableStmt(VisualBasic6Parser.VariableStmtContext context)
12+
{
13+
if (!_insideProcedure)
14+
{
15+
base.EnterVariableStmt(context);
16+
}
17+
}
18+
19+
public override void EnterConstStmt(VisualBasic6Parser.ConstStmtContext context)
20+
{
21+
if (!_insideProcedure)
22+
{
23+
base.EnterConstStmt(context);
24+
}
25+
}
26+
27+
public override void EnterArg(VisualBasic6Parser.ArgContext context)
28+
{
29+
return;
30+
}
31+
32+
public override void EnterSubStmt(VisualBasic6Parser.SubStmtContext context)
33+
{
34+
_insideProcedure = true;
35+
}
36+
37+
public override void EnterFunctionStmt(VisualBasic6Parser.FunctionStmtContext context)
38+
{
39+
_insideProcedure = true;
40+
}
1341

14-
public ScopedDeclaration(TContext context)
42+
public override void EnterPropertyGetStmt(VisualBasic6Parser.PropertyGetStmtContext context)
1543
{
16-
_context = context;
44+
_insideProcedure = true;
1745
}
1846

19-
public TContext Context { get { return _context; } }
47+
public override void EnterPropertyLetStmt(VisualBasic6Parser.PropertyLetStmtContext context)
48+
{
49+
_insideProcedure = true;
50+
}
51+
52+
public override void ExitPropertySetStmt(VisualBasic6Parser.PropertySetStmtContext context)
53+
{
54+
_insideProcedure = true;
55+
}
2056
}
2157

2258
public class DeclarationListener : VisualBasic6BaseListener, IExtensionListener<ParserRuleContext>
2359
{
2460
private readonly IList<ParserRuleContext> _members = new List<ParserRuleContext>();
2561
public IEnumerable<ParserRuleContext> Members { get { return _members; } }
2662

27-
28-
2963
public override void EnterVariableStmt(VisualBasic6Parser.VariableStmtContext context)
3064
{
3165
_members.Add(context);

RetailCoder.VBE/VBA/ParseTreeListeners/IExtensionListener.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
namespace Rubberduck.VBA.ParseTreeListeners
55
{
66
public interface IExtensionListener<out TContext>
7+
where TContext : class
78
{
89
IEnumerable<TContext> Members { get; }
910
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using Antlr4.Runtime;
2+
using Rubberduck.Inspections;
3+
4+
namespace Rubberduck.VBA.ParseTreeListeners
5+
{
6+
public class ScopedDeclaration<TContext>
7+
where TContext : ParserRuleContext
8+
{
9+
private readonly TContext _context;
10+
private readonly QualifiedMemberName _scope;
11+
12+
public ScopedDeclaration(TContext context, QualifiedModuleName scope)
13+
:this(context, new QualifiedMemberName(scope, string.Empty))
14+
{
15+
}
16+
17+
public ScopedDeclaration(TContext context, QualifiedMemberName scope)
18+
{
19+
_context = context;
20+
_scope = scope;
21+
}
22+
23+
public TContext Context { get { return _context; } }
24+
public QualifiedMemberName Scope { get { return _scope; } }
25+
}
26+
}

RetailCoder.VBE/VBA/ParseTreeListeners/VariableAssignmentListener.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
using System.Collections.Generic;
2+
using System.Linq;
23
using Rubberduck.VBA.Grammar;
34

45
namespace Rubberduck.VBA.ParseTreeListeners
56
{
6-
public class VariableAssignmentListener : VisualBasic6BaseListener, IExtensionListener<VisualBasic6Parser.VariableCallStmtContext>
7+
public class VariableAssignmentListener : VisualBasic6BaseListener, IExtensionListener<VisualBasic6Parser.AmbiguousIdentifierContext>
78
{
8-
private readonly IList<VisualBasic6Parser.VariableCallStmtContext> _members = new List<VisualBasic6Parser.VariableCallStmtContext>();
9-
public IEnumerable<VisualBasic6Parser.VariableCallStmtContext> Members { get { return _members; } }
9+
private readonly IList<VisualBasic6Parser.AmbiguousIdentifierContext> _members = new List<VisualBasic6Parser.AmbiguousIdentifierContext>();
10+
public IEnumerable<VisualBasic6Parser.AmbiguousIdentifierContext> Members { get { return _members; } }
11+
12+
public override void EnterForNextStmt(VisualBasic6Parser.ForNextStmtContext context)
13+
{
14+
_members.Add(context.ambiguousIdentifier().First());
15+
}
1016

1117
public override void EnterVariableCallStmt(VisualBasic6Parser.VariableCallStmtContext context)
1218
{
1319
if (context.Parent.Parent.Parent is VisualBasic6Parser.LetStmtContext)
1420
{
15-
_members.Add(context);
21+
_members.Add(context.ambiguousIdentifier());
1622
}
1723
}
1824
}

0 commit comments

Comments
 (0)