diff --git a/SemDiff.Core/DataStore.cs b/SemDiff.Core/DataStore.cs deleted file mode 100644 index 22ea133..0000000 --- a/SemDiff.Core/DataStore.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) 2015 semdiffdotnet. Distributed under the MIT License. -// See LICENSE file or opensource.org/licenses/MIT. -using Microsoft.CodeAnalysis; -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; - -namespace SemDiff.Core -{ - /// - /// Simplifies the retrieval of syntax trees based on the repo they are in and allows updating - /// the tree without relocating the repo based on the path - /// - internal class DataStore - { - //At the highest level we have a dictionary that maps the assembly - //name to info about the repository and it's files -#pragma warning disable CC0052 // Make field readonly - - private ImmutableDictionary store = - ImmutableDictionary.Empty; - -#pragma warning restore CC0052 // Make field readonly - - public ImmutableDictionary Store => store; - - public RepoFileSyntaxTreeInfo InterlockedAddOrUpdate(string assembly, - IEnumerable trees, Func getRepoFunc) - { - return ImmutableInterlocked.AddOrUpdate(ref store, - assembly, s => InUpdate(trees, getRepoFunc), - (s, r) => InUpdate(trees, getRepoFunc, r)); - } - - private static RepoFileSyntaxTreeInfo InUpdate(IEnumerable trees, - Func getRepoFunc, RepoFileSyntaxTreeInfo? previous = null) - { - return trees.Aggregate(previous ?? RepoFileSyntaxTreeInfo.Empty, - (rfsti, tree) => rfsti.AddOrUpdateSyntaxTree(tree, getRepoFunc)); - } - - //This is basically a customized tuple that contains two dictionaries that - //ultimately maps the Repo to all the syntax trees - internal struct RepoFileSyntaxTreeInfo - { - //This maps from the (absolute) path of a file to the SyntaxTree that represents it - public ImmutableDictionary FileSyntaxTreeLookup; - - //This maps from our repositories to a list of all the files (under version control), - //the string represents the absolute path of the file locally - public ImmutableDictionary> RepoFileLookup; - - public static RepoFileSyntaxTreeInfo Empty { get; } = new RepoFileSyntaxTreeInfo - { - RepoFileLookup = ImmutableDictionary>.Empty, - FileSyntaxTreeLookup = ImmutableDictionary.Empty, - }; - - public IEnumerable Repos => RepoFileLookup.Keys; - - public RepoFileSyntaxTreeInfo AddOrUpdateSyntaxTree(SyntaxTree tree, Func getRepoFunc) - { - //Note that if the file isn't in a repo it will be in - //the FileSyntaxTreeLookup, but it will not be in the repo - return new RepoFileSyntaxTreeInfo - { - FileSyntaxTreeLookup = FileSyntaxTreeLookup.SetItem(tree.FilePath, tree), - //Use the presence in the File Syntax Lookup as an indicator of if we have already found the repo - RepoFileLookup = FileSyntaxTreeLookup.ContainsKey(tree.FilePath) - ? RepoFileLookup - : AddFileToRepo(RepoFileLookup, getRepoFunc?.Invoke(tree), tree.FilePath) - }; - } - - public IEnumerable GetTreesForRepo(Repo repo) - { - var fileTreeLookup = FileSyntaxTreeLookup; //Makes the compiler happy about the following closure - return RepoFileLookup[repo].Select(s => fileTreeLookup[s]); - } - - private static ImmutableDictionary> AddFileToRepo( - ImmutableDictionary> initial, Repo repo, string filePath) - { - return repo != null - ? initial.ContainsKey(repo) - ? initial.SetItem(repo, initial[repo].Add(filePath)) - : initial.Add(repo, ImmutableList.Empty.Add(filePath)) - : initial; - } - } - } -} \ No newline at end of file diff --git a/SemDiff.Core/Extensions.cs b/SemDiff.Core/Extensions.cs index 55a8966..789e252 100644 --- a/SemDiff.Core/Extensions.cs +++ b/SemDiff.Core/Extensions.cs @@ -278,5 +278,32 @@ public static IEnumerable> Map(this IEnumerable sourcel, IE { return source; } + + internal static Dictionary> GetRepoAndTrees(this Compilation comp, Func getRepo) + { + if (getRepo == null) + throw new InvalidOperationException(); + + var dictionary = new Dictionary>(); + foreach (var t in comp.SyntaxTrees) + { + var repo = getRepo(t); + if (repo == null) + { + continue; //Not a part of a repo + } + if (dictionary.ContainsKey(repo)) + { + dictionary[repo].Add(t); + } + else + { + var list = new List(); + list.Add(t); + dictionary[repo] = list; + } + } + return dictionary; + } } } \ No newline at end of file diff --git a/SemDiff.Core/SemDiff.Core.csproj b/SemDiff.Core/SemDiff.Core.csproj index 2c43c2c..6167e1e 100644 --- a/SemDiff.Core/SemDiff.Core.csproj +++ b/SemDiff.Core/SemDiff.Core.csproj @@ -137,7 +137,6 @@ - diff --git a/SemDiff.Core/SemDiffAnalyzer.cs b/SemDiff.Core/SemDiffAnalyzer.cs index d3e8756..e08150f 100644 --- a/SemDiff.Core/SemDiffAnalyzer.cs +++ b/SemDiff.Core/SemDiffAnalyzer.cs @@ -5,6 +5,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using SemDiff.Core.Exceptions; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.IO; @@ -22,8 +23,9 @@ namespace SemDiff.Core [DiagnosticAnalyzer(LanguageNames.CSharp)] public class SemDiffAnalyzer : DiagnosticAnalyzer { + private static readonly ConcurrentDictionary _treePathLookup = new ConcurrentDictionary(); + private static int libGit2SharpNativePathSet; public override ImmutableArray SupportedDiagnostics => Diagnostics.Supported; - internal static DataStore Store { get; } = new DataStore(); /// /// Called once at session start to register actions in the analysis context. @@ -40,9 +42,7 @@ public override void Initialize(AnalysisContext context) context.RegisterCompilationAction(OnCompilation); } - private int libGit2SharpNativePathSet; - - private void SetupLibGit2Sharp() + private static void SetupLibGit2Sharp() { // It is important to only set the native library path before they are accessed for the first time. // Otherwise exceptions will be thrown. So this code is only executed once. @@ -71,6 +71,13 @@ private void SetupLibGit2Sharp() } } + private static Repo GetRepo(SyntaxTree tree) + { + return _treePathLookup.AddOrUpdate(tree.FilePath, + p => Repo.GetRepoFor(p), + (p, o) => o ?? Repo.GetRepoFor(p)); //Always check again if null + } + private static void OnCompilation(CompilationAnalysisContext context) { var diags = OnCompilationAsync(context.Compilation).Result; @@ -85,9 +92,8 @@ private async static Task> OnCompilationAsync(Compilatio Logger.Trace($"Entering {nameof(OnCompilationAsync)}: {comp.AssemblyName}"); try { - var data = Store.InterlockedAddOrUpdate(comp.AssemblyName, comp.SyntaxTrees, GetRepo); - var repos = data.Repos; - foreach (var repo in repos) + var data = comp.GetRepoAndTrees(GetRepo); + foreach (var repo in data.Keys) { try { @@ -121,11 +127,12 @@ private async static Task> OnCompilationAsync(Compilatio } var diagnostics = new List(); - foreach (var r in repos) + foreach (var rt in data) { - foreach (var t in data.GetTreesForRepo(r)) + var repo = rt.Key; + foreach (var t in rt.Value) //Foreach tree { - diagnostics.AddRange(Analyze(comp.GetSemanticModel(t), r)); + diagnostics.AddRange(Analyze(comp.GetSemanticModel(t), repo)); } } return diagnostics; @@ -141,8 +148,6 @@ private async static Task> OnCompilationAsync(Compilatio } } - private static Repo GetRepo(SyntaxTree tree) => Repo.GetRepoFor(tree.FilePath); - private static IEnumerable Analyze(SemanticModel semanticModel, Repo repo) { Logger.Trace($"Entering {nameof(Analyze)}: {semanticModel?.SyntaxTree?.FilePath}");