Skip to content

Commit ea2ef2a

Browse files
committed
Add finer graining to metricsResults and return IEnumerable instead of Task
1 parent 030345e commit ea2ef2a

File tree

4 files changed

+115
-56
lines changed

4 files changed

+115
-56
lines changed

RetailCoder.VBE/Navigation/CodeMetrics/CodeMetricsResult.cs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
using System;
1+
using Rubberduck.Parsing.Symbols;
2+
using Rubberduck.VBEditor;
3+
using System;
24
using System.Collections.Generic;
35
using System.Linq;
4-
using System.Text;
5-
using System.Threading.Tasks;
66

77
namespace Rubberduck.Navigation.CodeMetrics
88
{
@@ -21,11 +21,36 @@ public CodeMetricsResult(int lines, int cyclomaticComplexity, int nesting, IEnum
2121
CyclomaticComplexity = cyclomaticComplexity + childScopeMetric.CyclomaticComplexity;
2222
MaximumNesting = Math.Max(nesting, childScopeMetric.MaximumNesting);
2323
}
24-
25-
// possibly refer to a selection?
24+
2625
public int Lines { get; private set; }
2726
public int CyclomaticComplexity { get; private set; }
2827
public int MaximumNesting { get; private set; }
2928

3029
}
30+
31+
public struct MemberMetricsResult
32+
{
33+
public Declaration Member { get; private set; }
34+
public CodeMetricsResult Result { get; private set; }
35+
36+
public MemberMetricsResult(Declaration member, IEnumerable<CodeMetricsResult> contextResults)
37+
{
38+
Member = member;
39+
Result = new CodeMetricsResult(0, 0, 0, contextResults);
40+
}
41+
}
42+
43+
public struct ModuleMetricsResult
44+
{
45+
public QualifiedModuleName ModuleName { get; private set; }
46+
public CodeMetricsResult Result { get; private set; }
47+
public IReadOnlyDictionary<Declaration, CodeMetricsResult> MemberResults { get; private set; }
48+
49+
public ModuleMetricsResult(QualifiedModuleName moduleName, IEnumerable<MemberMetricsResult> memberMetricsResult, IEnumerable<CodeMetricsResult> nonMemberResults)
50+
{
51+
ModuleName = moduleName;
52+
MemberResults = memberMetricsResult.ToDictionary(mmr => mmr.Member, mmr => mmr.Result);
53+
Result = new CodeMetricsResult(0, 0, 0, nonMemberResults.Concat(MemberResults.Values));
54+
}
55+
}
3156
}

RetailCoder.VBE/Navigation/CodeMetrics/ICodeMetricsAnalyst.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ namespace Rubberduck.Navigation.CodeMetrics
1010
{
1111
public interface ICodeMetricsAnalyst
1212
{
13-
Task<CodeMetricsResult> GetResult(RubberduckParserState state, CancellationToken token);
13+
IEnumerable<ModuleMetricsResult> ModuleMetrics(RubberduckParserState state, CancellationToken token);
1414
}
1515
}

RetailCoder.VBE/Navigation/CodeMetrics/ParseTreeMetricsAnalyst.cs

Lines changed: 58 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,50 +10,63 @@
1010
using Rubberduck.Parsing.Grammar;
1111
using Rubberduck.VBEditor;
1212
using Antlr4.Runtime.Misc;
13+
using Rubberduck.Parsing.Symbols;
1314

1415
namespace Rubberduck.Navigation.CodeMetrics
1516
{
1617
public class ParseTreeMetricsAnalyst : ICodeMetricsAnalyst
1718
{
18-
public async Task<CodeMetricsResult> GetResult(RubberduckParserState state, CancellationToken token)
19+
public IEnumerable<ModuleMetricsResult> ModuleMetrics(RubberduckParserState state, CancellationToken token)
1920
{
2021
if (state == null || !state.AllUserDeclarations.Any())
2122
{
22-
return new CodeMetricsResult();
23+
yield break;
2324
}
24-
return await Task.Run(() =>
25-
{
26-
var trees = state.ParseTrees;
27-
var results = new List<CodeMetricsResult>();
2825

29-
foreach (var moduleTree in trees)
26+
var trees = state.ParseTrees;
27+
var results = new List<CodeMetricsResult>();
28+
29+
foreach (var moduleTree in trees)
30+
{
31+
if (token.IsCancellationRequested)
3032
{
31-
if (token.IsCancellationRequested)
32-
{
33-
return new CodeMetricsResult();
34-
}
35-
// FIXME rewrite as visitor. That should make subtrees easier and allow us to expand metrics
36-
var cmListener = new CodeMetricsListener(moduleTree.Key);
37-
ParseTreeWalker.Default.Walk(cmListener, moduleTree.Value);
38-
results.Add(cmListener.GetMetricsResult());
33+
yield break;
3934
}
40-
return new CodeMetricsResult(0, 0, 0, results);
41-
});
35+
yield return GetModuleResult(moduleTree.Key, moduleTree.Value, state.DeclarationFinder);
36+
};
37+
}
38+
39+
public ModuleMetricsResult GetModuleResult(RubberduckParserState state, QualifiedModuleName qmn)
40+
{
41+
return GetModuleResult(qmn, state.GetParseTree(qmn), state.DeclarationFinder);
4242
}
4343

44+
private ModuleMetricsResult GetModuleResult(QualifiedModuleName qmn, IParseTree moduleTree, DeclarationFinder declarationFinder)
45+
{
46+
// Consider rewrite as visitor? That should make subtrees easier and allow us to expand metrics
47+
var cmListener = new CodeMetricsListener(declarationFinder);
48+
ParseTreeWalker.Default.Walk(cmListener, moduleTree);
49+
return cmListener.GetMetricsResult(qmn);
50+
}
51+
52+
4453
private class CodeMetricsListener : VBAParserBaseListener
4554
{
46-
private QualifiedModuleName qmn;
55+
private Declaration currentMember;
56+
private readonly DeclarationFinder _finder;
4757
private List<CodeMetricsResult> results = new List<CodeMetricsResult>();
58+
private List<CodeMetricsResult> moduleResults = new List<CodeMetricsResult>();
4859

49-
public CodeMetricsListener(QualifiedModuleName qmn)
60+
private List<MemberMetricsResult> memberResults = new List<MemberMetricsResult>();
61+
62+
public CodeMetricsListener(DeclarationFinder finder)
5063
{
51-
this.qmn = qmn;
64+
_finder = finder;
5265
}
5366

5467
public override void EnterEndOfLine([NotNull] VBAParser.EndOfLineContext context)
5568
{
56-
results.Add(new CodeMetricsResult(1, 0, 0));
69+
(currentMember == null ? moduleResults : results).Add(new CodeMetricsResult(1, 0, 0));
5770
}
5871

5972
public override void EnterIfStmt([NotNull] VBAParser.IfStmtContext context)
@@ -88,12 +101,34 @@ public override void EnterSubStmt([NotNull] VBAParser.SubStmtContext context)
88101
{
89102
// this is the default path through the sub
90103
results.Add(new CodeMetricsResult(0, 1, 0));
104+
105+
// if First borks, we got a bigger problems
106+
currentMember = _finder.DeclarationsWithType(DeclarationType.Procedure).Where(d => d.Context == context).First();
107+
}
108+
109+
public override void ExitSubStmt([NotNull] VBAParser.SubStmtContext context)
110+
{
111+
// well, we're done here
112+
memberResults.Add(new MemberMetricsResult(currentMember, results));
113+
results = new List<CodeMetricsResult>(); // reinitialize to drop results
114+
currentMember = null;
91115
}
92116

93117
public override void EnterFunctionStmt([NotNull] VBAParser.FunctionStmtContext context)
94118
{
95119
// this is the default path through the function
96120
results.Add(new CodeMetricsResult(0, 1, 0));
121+
122+
// if First borks, we got bigger problems
123+
currentMember = _finder.DeclarationsWithType(DeclarationType.Function).Where(d => d.Context == context).First();
124+
}
125+
126+
public override void ExitFunctionStmt([NotNull] VBAParser.FunctionStmtContext context)
127+
{
128+
// well, we're done here
129+
memberResults.Add(new MemberMetricsResult(currentMember, results));
130+
results = new List<CodeMetricsResult>(); // reinitialize to drop results
131+
currentMember = null;
97132
}
98133

99134
public override void EnterBlockStmt([NotNull] VBAParser.BlockStmtContext context)
@@ -106,9 +141,9 @@ public override void EnterBlockStmt([NotNull] VBAParser.BlockStmtContext context
106141

107142
// FIXME also check if we need to do something about `mandatoryLineContinuation`?
108143

109-
internal CodeMetricsResult GetMetricsResult()
144+
internal ModuleMetricsResult GetMetricsResult(QualifiedModuleName qmn)
110145
{
111-
return new CodeMetricsResult(0, 0, 0, results);
146+
return new ModuleMetricsResult(qmn, memberResults, moduleResults);
112147
}
113148
}
114149
}

RubberduckTests/Stats/ParseTreeMetricsAnalystTests.cs

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ public void EmptyModule_HasMetricsZeroed()
2929
{
3030
var code = @"";
3131
var state = MockParser.ParseString(code, out var qmn);
32-
var metrics = cut.GetResult(state, cts.Token).Result;
33-
Assert.AreEqual(new CodeMetricsResult(), metrics);
32+
var metrics = cut.ModuleMetrics(state, cts.Token).First();
33+
Assert.AreEqual(new CodeMetricsResult(), metrics.Result);
3434
}
3535

3636
[TestMethod]
@@ -42,8 +42,8 @@ Sub NoCode()
4242
End Sub
4343
";
4444
var state = MockParser.ParseString(code, out var _);
45-
var metrics = cut.GetResult(state, cts.Token).Result;
46-
Assert.AreEqual(1, metrics.CyclomaticComplexity);
45+
var metrics = cut.ModuleMetrics(state, cts.Token).First();
46+
Assert.AreEqual(1, metrics.Result.CyclomaticComplexity);
4747
}
4848

4949
[TestMethod]
@@ -56,8 +56,8 @@ End Function
5656
";
5757

5858
var state = MockParser.ParseString(code, out var _);
59-
var metrics = cut.GetResult(state, cts.Token).Result;
60-
Assert.AreEqual(1, metrics.CyclomaticComplexity);
59+
var metrics = cut.ModuleMetrics(state, cts.Token).First();
60+
Assert.AreEqual(1, metrics.Result.CyclomaticComplexity);
6161
}
6262

6363
[TestMethod]
@@ -74,8 +74,8 @@ public void ModuleHas_AsManyLines_AsPhysicalLines()
7474
var code = builder.ToString();
7575

7676
var state = MockParser.ParseString(code, out var _);
77-
var metric = cut.GetResult(state, cts.Token).Result;
78-
Assert.AreEqual(lineCount, metric.Lines);
77+
var metric = cut.ModuleMetrics(state, cts.Token).First();
78+
Assert.AreEqual(lineCount, metric.Result.Lines);
7979
}
8080
}
8181

@@ -90,8 +90,8 @@ End If
9090
End Sub
9191
";
9292
var state = MockParser.ParseString(code, out var _);
93-
var metrics = cut.GetResult(state, cts.Token).Result;
94-
Assert.AreEqual(2, metrics.CyclomaticComplexity);
93+
var metrics = cut.ModuleMetrics(state, cts.Token).First();
94+
Assert.AreEqual(2, metrics.Result.CyclomaticComplexity);
9595
}
9696

9797
[TestMethod]
@@ -106,8 +106,8 @@ End If
106106
End Sub
107107
";
108108
var state = MockParser.ParseString(code, out var _);
109-
var metrics = cut.GetResult(state, cts.Token).Result;
110-
Assert.AreEqual(2, metrics.CyclomaticComplexity);
109+
var metrics = cut.ModuleMetrics(state, cts.Token).First();
110+
Assert.AreEqual(2, metrics.Result.CyclomaticComplexity);
111111
}
112112

113113
[TestMethod]
@@ -122,8 +122,8 @@ End If
122122
End Sub
123123
";
124124
var state = MockParser.ParseString(code, out var _);
125-
var metrics = cut.GetResult(state, cts.Token).Result;
126-
Assert.AreEqual(3, metrics.CyclomaticComplexity);
125+
var metrics = cut.ModuleMetrics(state, cts.Token).First();
126+
Assert.AreEqual(3, metrics.Result.CyclomaticComplexity);
127127
}
128128

129129
[TestMethod]
@@ -139,8 +139,8 @@ End If
139139
End Sub
140140
";
141141
var state = MockParser.ParseString(code, out var _);
142-
var metrics = cut.GetResult(state, cts.Token).Result;
143-
Assert.AreEqual(3, metrics.CyclomaticComplexity);
142+
var metrics = cut.ModuleMetrics(state, cts.Token).First();
143+
Assert.AreEqual(3, metrics.Result.CyclomaticComplexity);
144144
}
145145

146146
[TestMethod]
@@ -156,8 +156,8 @@ End If
156156
End Sub
157157
";
158158
var state = MockParser.ParseString(code, out var _);
159-
var metrics = cut.GetResult(state, cts.Token).Result;
160-
Assert.AreEqual(3, metrics.CyclomaticComplexity);
159+
var metrics = cut.ModuleMetrics(state, cts.Token).First();
160+
Assert.AreEqual(3, metrics.Result.CyclomaticComplexity);
161161
}
162162

163163
[TestMethod]
@@ -172,8 +172,8 @@ Next stuff
172172
End Sub
173173
";
174174
var state = MockParser.ParseString(code, out var _);
175-
var metrics = cut.GetResult(state, cts.Token).Result;
176-
Assert.AreEqual(2, metrics.CyclomaticComplexity);
175+
var metrics = cut.ModuleMetrics(state, cts.Token).First();
176+
Assert.AreEqual(2, metrics.Result.CyclomaticComplexity);
177177
}
178178

179179
[TestMethod]
@@ -189,8 +189,8 @@ Next i
189189
End Sub
190190
";
191191
var state = MockParser.ParseString(code, out var _);
192-
var metrics = cut.GetResult(state, cts.Token).Result;
193-
Assert.AreEqual(2, metrics.CyclomaticComplexity);
192+
var metrics = cut.ModuleMetrics(state, cts.Token).First();
193+
Assert.AreEqual(2, metrics.Result.CyclomaticComplexity);
194194
}
195195

196196
[TestMethod]
@@ -205,8 +205,8 @@ End Select
205205
End Sub
206206
";
207207
var state = MockParser.ParseString(code, out var _);
208-
var metrics = cut.GetResult(state, cts.Token).Result;
209-
Assert.AreEqual(1, metrics.CyclomaticComplexity);
208+
var metrics = cut.ModuleMetrics(state, cts.Token).First();
209+
Assert.AreEqual(1, metrics.Result.CyclomaticComplexity);
210210
}
211211

212212
[TestMethod]
@@ -228,9 +228,8 @@ End Select
228228
End Sub
229229
";
230230
var state = MockParser.ParseString(code, out var _);
231-
var metrics = cut.GetResult(state, cts.Token).Result;
232-
Assert.AreEqual(blockCount + 1, metrics.CyclomaticComplexity);
233-
231+
var metrics = cut.ModuleMetrics(state, cts.Token).First();
232+
Assert.AreEqual(blockCount + 1, metrics.Result.CyclomaticComplexity);
234233
}
235234
}
236235
}

0 commit comments

Comments
 (0)