Exemplo n.º 1
0
        /// <summary>
        /// Processes the provided <paramref name="project"/>, and computes the provided <paramref name="metrics"/>.
        /// </summary>
        /// <param name="parameters">The inspection parameters provided by the user.</param>
        /// <param name="project">The project to inspect.</param>
        /// <param name="metrics">The metrics to compute.</param>
        /// <param name="ct">A cancellation token.</param>
        private async Task <ProjectInspectionResult> ProcessAsync(InspectionParameters parameters, Project project, IList <IMetric> metrics, CancellationToken ct)
        {
            _logger.LogVerbose($"Inspecting project: {project.Name}");

            var compilation = await project.GetCompilationAsync(ct);

            if (compilation is null)
            {
                throw new InspectionException($"Failed to compile project: {project.Name}");
            }

            var registry = new Registry();
            var resolver = new PackageResolver(_logger);
            var results  = new List <PackageInspectionResult>();

            var documents  = project.Documents.ToImmutableHashSet();
            var exclusions = NamespaceExclusionList.CreateFromParameters(parameters, project);

            await resolver.CreatePackageGraph(project, exclusions, ct);

            var memberLookupTable = await GetMemberAccessLookupTable(compilation, exclusions, ct);

            using (var portableExecutableLoadContext = new PortableExecutableLoadContext(project, _logger))
            {
                foreach (var executable in portableExecutableLoadContext.GetExecutables(exclusions))
                {
                    var package = resolver.CreatePackage(executable);

                    registry.AddPackage(package);

                    foreach (var type in executable.ExportedTypes)
                    {
                        var compilationType = GetCompilationType(compilation, type);

                        if (compilationType is null)
                        {
                            continue;
                        }

                        var isAdded = await AddConstructorReferences(project.Solution, documents, package, registry, compilationType, ct);

                        if (isAdded)
                        {
                            await AddMemberReferences(project.Solution, documents, package, registry, compilationType, memberLookupTable, ct);
                        }
                    }

                    results.Add(ComputeMetrics(project, compilation, package, metrics, registry));
                }
            }

            results.AddRange(AddMissingExplicitPackages(resolver, exclusions, results));

            return(ProjectInspectionResult.Ok(project, results));
        }
Exemplo n.º 2
0
        /// <summary>
        /// Adds any packages discovered from the NuGet package graph that was not part of any
        /// references in the code.
        /// </summary>
        private static IEnumerable <PackageInspectionResult> AddMissingExplicitPackages(PackageResolver resolver, NamespaceExclusionList exclusions, List <PackageInspectionResult> results)
        {
            foreach (var package in resolver.GetPackages())
            {
                if (exclusions.IsExcluded(package.Name))
                {
                    continue;
                }

                if (results.Any(result => result.Package.Equals(package)))
                {
                    continue;
                }

                yield return(new PackageInspectionResult(package, new List <IMetricResult?>()));
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Gets all member access syntax tokens from the Roslyn compilation syntax trees, and maps
        /// them into a lookup table.
        /// </summary>
        /// <param name="compilation">The Roslyn compilation of the project source-files.</param>
        /// <param name="exclusions">The list of namespace excluded from the inspection.</param>
        /// <param name="ct">A cancellation token.</param>
        private static async Task <IReadOnlyDictionary <INamespaceSymbol, ICollection <ISymbol> > > GetMemberAccessLookupTable(Compilation compilation, NamespaceExclusionList exclusions, CancellationToken ct)
        {
            var symbols = new List <ISymbol>();

            foreach (var syntaxTree in compilation.SyntaxTrees)
            {
                var root = await syntaxTree.GetRootAsync(ct);

                var model = compilation.GetSemanticModel(syntaxTree);

                var intermediate = root
                                   .DescendantNodes()
                                   .OfType <MemberAccessExpressionSyntax>()
                                   .Select(node => model.GetSymbolInfo(node, ct).Symbol)
                                   .Where(symbol => symbol != null)
                                   .Where(symbol => !exclusions.IsExcluded(symbol !.ContainingNamespace));

                symbols.AddRange(intermediate !);
            }

            var comparer = SymbolEqualityComparer.Default;
            var lookup   = new Dictionary <INamespaceSymbol, ICollection <ISymbol> >(comparer);

            foreach (var group in symbols.GroupBy(symbol => symbol.ContainingNamespace, comparer))
            {
                lookup.Add((INamespaceSymbol)group.Key !, group.ToList());
            }

            return(lookup);
        }