forked from akimboyko/MetaProgramming
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Roslyn-CyclomaticComplexityRx.linq
143 lines (130 loc) · 5.8 KB
/
Roslyn-CyclomaticComplexityRx.linq
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
<Query Kind="Program">
<Reference><RuntimeDirectory>\System.Runtime.dll</Reference>
<Reference><RuntimeDirectory>\System.Threading.Tasks.dll</Reference>
<NuGetReference Prerelease="true">Microsoft.Bcl.Immutable</NuGetReference>
<NuGetReference>Roslyn.Services.CSharp</NuGetReference>
<NuGetReference>Rx-Core</NuGetReference>
<NuGetReference>Rx-Linq</NuGetReference>
<NuGetReference>Rx-Main</NuGetReference>
<NuGetReference>Rx-PlatformServices</NuGetReference>
<Namespace>Roslyn.Compilers.Common</Namespace>
<Namespace>Roslyn.Compilers.CSharp</Namespace>
<Namespace>Roslyn.Services</Namespace>
<Namespace>System.Collections.Concurrent</Namespace>
<Namespace>System.Reactive</Namespace>
<Namespace>System.Reactive.Concurrency</Namespace>
<Namespace>System.Reactive.Joins</Namespace>
<Namespace>System.Reactive.Linq</Namespace>
<Namespace>System.Reactive.Subjects</Namespace>
<Namespace>System.Reactive.Threading.Tasks</Namespace>
<Namespace>System.Threading.Tasks</Namespace>
<IncludePredicateBuilder>true</IncludePredicateBuilder>
</Query>
void Main()
{
// prepare cancellationToken for async operations
var cancellationTokenSource = new CancellationTokenSource();
cancellationTokenSource.CancelAfter(TimeSpan.FromMinutes(1));
var cancellationToken = cancellationTokenSource.Token;
var complexityBag = new ConcurrentBag<Complexity>();
var stopwatch = new Stopwatch();
stopwatch.Start();
// load workspace, i.e. solution from Visual Studio
using(var workspace = Workspace.LoadSolution(solutionPath))
{
// save a reference to original state
var origianlSolution = workspace.CurrentSolution;
// build syntax root asynchronously for all documents from all projects
var syntaxRootObservable =
origianlSolution
.Projects
.AsParallel()
.AsUnordered()
.WithCancellation(cancellationToken)
.SelectMany(project => project.Documents)
.Select(document => document.GetSyntaxRootAsync(cancellationToken))
.ToObservable();
// calculate complexity for all methods using Rx Observables
syntaxRootObservable
.Subscribe(
syntaxRootAsync =>
CalculateComplexity(syntaxRootAsync, complexityBag, cancellationToken),
cancellationToken);
// throw an exception if more then 1 minute passed since start
cancellationToken.ThrowIfCancellationRequested();
}
stopwatch.Stop();
stopwatch.Elapsed.Dump("Elapsed time");
// show results
complexityBag
.GroupBy(complexity => complexity.FilePath)
.OrderByDescending(@group => @group.Sum(complexity => complexity.nStatementSyntax))
.Select(@group => @group
.OrderByDescending(complexity => complexity.nStatementSyntax))
.Dump();
}
// cloc info about hibernate-core-master from github
// 5680 text files.
// 5515 unique files.
// 997 files ignored.
//
//http://cloc.sourceforge.net v 1.58 T=50.0 s (108.4 files/s, 15539.8 lines/s)
//-------------------------------------------------------------------------------
//Language files blank comment code
//-------------------------------------------------------------------------------
//C# 4007 61226 44433 341068
//MSBuild scripts 6 0 42 5899
const string solutionPath =
@"D:\temp\nhibernate-core-master\src\NHibernate.Everything.sln";
// statements for independent paths through a program's source code
private static readonly Func<StatementSyntax, bool> cyclomaticComplexityStatements =
PredicateBuilder
.False<StatementSyntax>()
.Or(s => s is DoStatementSyntax)
.Or(s => s is ForEachStatementSyntax)
.Or(s => s is ForStatementSyntax)
.Or(s => s is IfStatementSyntax)
.Or(s => s is SwitchStatementSyntax)
.Or(s => s is UsingStatementSyntax)
.Or(s => s is WhileStatementSyntax)
.Compile();
// process descendant nodes of syntaxRoot
private static async void CalculateComplexity(
Task<CommonSyntaxNode> syntaxRootAsync,
ConcurrentBag<Complexity> complexityBag,
CancellationToken cancellationToken)
{
Array.ForEach(
(await syntaxRootAsync)
.DescendantNodes()
.OfType<MethodDeclarationSyntax>()
.Select(methodDeclaration =>
new Complexity
{
TypeIdentifier = ((TypeDeclarationSyntax)methodDeclaration.Parent).Identifier.ValueText,
MethodIdentifier = methodDeclaration.Identifier.ValueText,
SourcesSample = methodDeclaration.ToString(),
nStatementSyntax = methodDeclaration.DescendantNodes()
.OfType<StatementSyntax>()
.Where(cyclomaticComplexityStatements)
.Count() + 1,
FilePath = methodDeclaration.GetLocation().SourceTree.FilePath,
SourceLine = methodDeclaration.GetLocation().SourceTree.GetLineSpan(methodDeclaration.Span, true, cancellationToken).StartLinePosition.Line
})
.Where(complexity => complexity.nStatementSyntax > 10)
.ToArray(),
complexity =>
{
complexityBag.Add(complexity);
cancellationToken.ThrowIfCancellationRequested();
});
}
private class Complexity
{
public string TypeIdentifier { get; set; }
public string MethodIdentifier { get; set; }
public string SourcesSample { get; set; }
public int nStatementSyntax { get; set; }
public string FilePath { get; set; }
public int SourceLine { get; set; }
}