Created
November 7, 2016 15:03
-
-
Save svick/571b8e678e41d61517ca3dad64598974 to your computer and use it in GitHub Desktop.
Counting underscores in lambdas on GitHub
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.IO; | |
using System.Linq; | |
using System.Threading.Tasks; | |
using System.Threading.Tasks.Dataflow; | |
using Microsoft.CodeAnalysis; | |
using Microsoft.CodeAnalysis.CSharp; | |
using Microsoft.CodeAnalysis.CSharp.Syntax; | |
using Microsoft.CodeAnalysis.Text; | |
namespace UnderscoreLambdasGithub | |
{ | |
public class Program | |
{ | |
public static void Main() | |
{ | |
var capacityOptions = new ExecutionDataflowBlockOptions { BoundedCapacity = 1 }; | |
var cloneBlock = new TransformBlock<string, string>(repo => Clone(repo), capacityOptions); | |
var getFilesBlock = new TransformManyBlock<string, string>(path => GetFiles(path), capacityOptions); | |
var parseBlock = new TransformBlock<string, LambdaData>(file => Parse(file), capacityOptions); | |
LambdaData lambdaDataSummary = new LambdaData(); | |
var summarizeBlock = new ActionBlock<LambdaData>(ld => lambdaDataSummary.Add(ld)); | |
var linkOptions = new DataflowLinkOptions { PropagateCompletion = true }; | |
cloneBlock.LinkTo(getFilesBlock, linkOptions); | |
getFilesBlock.LinkTo(parseBlock, linkOptions); | |
parseBlock.LinkTo(summarizeBlock, linkOptions); | |
var sendDataTask = SendData(cloneBlock); | |
summarizeBlock.Completion.Wait(); | |
sendDataTask.Wait(); | |
Print(lambdaDataSummary); | |
} | |
private static void Print(LambdaData lambdaData) | |
{ | |
Console.WriteLine($"single lambdas: {lambdaData.TotalSingleLambdasCount}, multi lambdas: {lambdaData.TotalMultiLambdasCount}"); | |
Console.WriteLine("Single lambdas:"); | |
Print(lambdaData.SingleLambdas); | |
Console.WriteLine("Multi lambdas, one parameter:"); | |
Print(lambdaData.MultiLambdasOneParameter); | |
Console.WriteLine("Multi lambdas, multi parameters:"); | |
Print(lambdaData.MultiLambdasMultiParameters); | |
} | |
private static void Print(Dictionary<string, int> dictionary) | |
{ | |
Console.WriteLine($"Total: {dictionary.Values.Sum()}"); | |
var data = dictionary.OrderByDescending(kvp => kvp.Value).Take(10); | |
foreach (var kvp in data) | |
{ | |
Console.WriteLine($"{kvp.Key}\t{kvp.Value}"); | |
} | |
} | |
private static readonly string BasePath = @"C:\Users\Svick\AppData\Local\Temp\UnderscoreLambdasGithub\"; | |
private static async Task SendData(ITargetBlock<string> targetBlock) | |
{ | |
foreach (var repo in Repos) | |
{ | |
await targetBlock.SendAsync(repo); | |
} | |
targetBlock.Complete(); | |
} | |
private static LambdaData Parse(string file) | |
{ | |
Console.WriteLine($"Parsing {file.Substring(BasePath.Length)}."); | |
var lambdaData = new LambdaData(); | |
var syntaxTree = CSharpSyntaxTree.ParseText(SourceText.From(File.OpenRead(file))); | |
var semanticModel = CSharpCompilation.Create(null).AddSyntaxTrees(syntaxTree).GetSemanticModel(syntaxTree); | |
var lambdas = syntaxTree.GetCompilationUnitRoot().DescendantNodes().OfType<LambdaExpressionSyntax>(); | |
foreach (var lambda in lambdas) | |
{ | |
var names = lambda.DescendantNodes().OfType<NameSyntax>().Select(name => semanticModel.GetSymbolInfo(name).Symbol).Where(s => s != null).ToList(); | |
var simpleLambda = lambda as SimpleLambdaExpressionSyntax; | |
if (simpleLambda != null) | |
{ | |
var paramaterSymbol = semanticModel.GetDeclaredSymbol(simpleLambda.Parameter); | |
SingleLambda(lambdaData, names, paramaterSymbol); | |
} | |
var complexLambda = lambda as ParenthesizedLambdaExpressionSyntax; | |
if (complexLambda != null) | |
{ | |
var parameterSymbols = complexLambda.ParameterList.Parameters | |
.Select(param => semanticModel.GetDeclaredSymbol(param)) | |
.ToList(); | |
if (parameterSymbols.Count == 1) | |
{ | |
SingleLambda(lambdaData, names, parameterSymbols.Single()); | |
} | |
else | |
{ | |
MultiLambda(lambdaData, names, parameterSymbols); | |
} | |
} | |
} | |
return lambdaData; | |
} | |
private static void MultiLambda(LambdaData lambdaData, List<ISymbol> names, List<IParameterSymbol> parameterSymbols) | |
{ | |
lambdaData.MultiLambda(); | |
var missingSymbols = | |
(from parameterSymbol in parameterSymbols | |
where !names.Contains(parameterSymbol) | |
select parameterSymbol.Name).ToList(); | |
switch (missingSymbols.Count) | |
{ | |
case 0: | |
break; | |
case 1: | |
lambdaData.MultiLambdaOneUnused(missingSymbols.Single()); | |
break; | |
default: | |
lambdaData.MultiLambdaMultiUnused(string.Join(", ", missingSymbols)); | |
break; | |
} | |
} | |
private static void SingleLambda(LambdaData lambdaData, List<ISymbol> names, IParameterSymbol parameterSymbol) | |
{ | |
lambdaData.SingleLambda(); | |
if (!names.Contains(parameterSymbol)) | |
{ | |
lambdaData.SingleLambdaUnused(parameterSymbol.Name); | |
} | |
} | |
private static IEnumerable<string> GetFiles(string path) | |
=> Directory.EnumerateFiles(path, "*.cs", SearchOption.AllDirectories); | |
private static string Clone(string repo) | |
{ | |
string gitUrl = $"https://github.com/{repo}.git"; | |
string path = Path.Combine(BasePath, repo.Split('/')[1]); | |
if (Directory.Exists(path)) | |
Console.WriteLine($"Directory for {repo} already exists."); | |
else | |
Process.Start("git", $"clone {gitUrl} {path} --depth 1").WaitForExit(); | |
return path; | |
} | |
// https://github.com/trending/csharp on 2016-11-01 | |
private static readonly string[] Repos = | |
{ | |
"Mrs4s/BaiduPanDownload", | |
"shadowsocks/shadowsocks-windows", | |
"Microsoft/aspnet-api-versioning", | |
"lolp1/Overlay.NET", | |
"mxgmn/WaveFunctionCollapse", | |
"dotnet-state-machine/stateless", | |
"telerik/JustDecompileEngine", | |
"anakic/Jot", | |
"JimBobSquarePants/ImageSharp", | |
"Redth/PushSharp", | |
"aspnet/Mvc", | |
"aspnet/EntityFramework", | |
"HangfireIO/Hangfire", | |
"google/sandbox-attacksurface-analysis-tools", | |
"serilog/serilog", | |
"dotnet/roslyn", | |
"thestonefox/VRTK", | |
"Microsoft/UWPCommunityToolkit", | |
"Unity-Technologies/PostProcessing", | |
"PowerShell/PowerShell", | |
"StackExchange/dapper-dot-net", | |
"Reactive-Extensions/Rx.NET", | |
"neuecc/UniRx", | |
"JFrogDev/project-examples", | |
"aspnet/Razor" | |
}; | |
} | |
class LambdaData | |
{ | |
public int TotalSingleLambdasCount { get; private set; } | |
public int TotalMultiLambdasCount { get; private set; } | |
public Dictionary<string, int> SingleLambdas { get; } = new Dictionary<string, int>(); | |
public Dictionary<string, int> MultiLambdasOneParameter { get; } = new Dictionary<string, int>(); | |
public Dictionary<string, int> MultiLambdasMultiParameters { get; } = new Dictionary<string, int>(); | |
public void SingleLambda() => TotalSingleLambdasCount++; | |
public void MultiLambda() => TotalMultiLambdasCount++; | |
private static void Increment<T>(Dictionary<T, int> dictionary, T key, int added = 1) | |
{ | |
int value; | |
dictionary.TryGetValue(key, out value); | |
dictionary[key] = value + added; | |
} | |
public void SingleLambdaUnused(string name) => Increment(SingleLambdas, name); | |
public void MultiLambdaOneUnused(string name) => Increment(MultiLambdasOneParameter, name); | |
public void MultiLambdaMultiUnused(string name) => Increment(MultiLambdasMultiParameters, name); | |
private static void Add(Dictionary<string, int> thisDictionary, Dictionary<string, int> otherDictionary) | |
{ | |
foreach (var kvp in otherDictionary) | |
{ | |
Increment(thisDictionary, kvp.Key, kvp.Value); | |
} | |
} | |
public void Add(LambdaData other) | |
{ | |
this.TotalSingleLambdasCount += other.TotalSingleLambdasCount; | |
this.TotalMultiLambdasCount += other.TotalMultiLambdasCount; | |
Add(this.SingleLambdas, other.SingleLambdas); | |
Add(this.MultiLambdasOneParameter, other.MultiLambdasOneParameter); | |
Add(this.MultiLambdasMultiParameters, other.MultiLambdasMultiParameters); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"version": "1.0.0-*", | |
"buildOptions": { | |
"emitEntryPoint": true | |
}, | |
"dependencies": { | |
"Microsoft.CodeAnalysis.CSharp": "1.3.2", | |
"Microsoft.NETCore.App": { | |
"type": "platform", | |
"version": "1.0.0" | |
} | |
}, | |
"frameworks": { | |
"netcoreapp1.0": {} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment