forked from akimboyko/MetaProgramming
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Roslyn-ReturnNull.linq
141 lines (126 loc) · 5.07 KB
/
Roslyn-ReturnNull.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
<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>
<Namespace>Roslyn.Compilers.Common</Namespace>
<Namespace>Roslyn.Compilers.CSharp</Namespace>
<Namespace>Roslyn.Services</Namespace>
<Namespace>System.Collections.Concurrent</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;
IEnumerable<ReturnNull> returnNulls;
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 and semantic model asynchronously in parallel
// for all documents from all projects
returnNulls =
origianlSolution
.Projects
.AsParallel()
.AsUnordered()
.WithCancellation(cancellationToken)
.SelectMany(project => project.Documents)
.Select(document => document.GetSyntaxRootAsync(cancellationToken))
// calculate complexity for all methods in parallel
.SelectMany(syntaxRootAsync =>
FindReturnNull(syntaxRootAsync, cancellationToken).Result)
.ToArray();
// throw an exception if more then 1 minute passed since start
cancellationToken.ThrowIfCancellationRequested();
}
stopwatch.Stop();
stopwatch.Elapsed.Dump("Elapsed time");
// show results
returnNulls
.GroupBy(returnNull => returnNull.FilePath)
.OrderBy(returnNullByFilePath => returnNullByFilePath.Key)
.Select(returnNullByFilePath => new
{
FilePath = returnNullByFilePath.Key,
ReturnNulls =
returnNullByFilePath
.OrderBy(returnNull => returnNull.SourceLine)
.Select(returnNull => new { returnNull.SourceLine, returnNull.SourcesSample })
})
.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 `return null;`
private static readonly Func<StatementSyntax, bool> returnNullStatement =
PredicateBuilder
.True<StatementSyntax>()
// all return statements
.And(s => s is ReturnStatementSyntax)
// with expression
.And(s => (s as ReturnStatementSyntax).Expression != null)
// with expression "null"
.And(s => (s as ReturnStatementSyntax).Expression.Kind == SyntaxKind.NullLiteralExpression)
.Compile();
// process descendant nodes of syntaxRoot
private static async Task<IEnumerable<ReturnNull>> FindReturnNull(
Task<CommonSyntaxNode> syntaxRootAsync,
CancellationToken cancellationToken)
{
return
(await syntaxRootAsync)
.DescendantNodes()
.OfType<ReturnStatementSyntax>()
.Where(returnNull => returnNullStatement(returnNull))
.Select(returnNull =>
new ReturnNull
{
TypeIdentifier = GetParentSyntax<TypeDeclarationSyntax>(returnNull).Identifier.ValueText,
SourcesSample = returnNull.ToString(),
FilePath = returnNull.GetLocation().SourceTree.FilePath,
SourceLine = returnNull
.GetLocation().SourceTree
.GetLineSpan(returnNull.Span, true, cancellationToken)
.StartLinePosition.Line + 1
});
}
private class ReturnNull
{
public string TypeIdentifier { get; set; }
public string SourcesSample { get; set; }
public string FilePath { get; set; }
public int SourceLine { get; set; }
}
private static TDeclarationSyntax GetParentSyntax<TDeclarationSyntax>(SyntaxNode statementSyntax)
where TDeclarationSyntax : MemberDeclarationSyntax
{
SyntaxNode statement = statementSyntax;
while(statement != null && !(statement is TDeclarationSyntax))
{
statement = statement.Parent;
}
if(statement == null || !(statement is TDeclarationSyntax))
{
throw new Exception(string.Format("Can't find parent {0} node", typeof(TDeclarationSyntax)));
}
return (TDeclarationSyntax)statement;
}