/// <summary> /// Creates an exclusion list based on the inspection parameters and the project being inspected. The /// project itself and any project-references are also added to the list. /// </summary> /// <param name="parameters">The inspection parameters provided by the user.</param> /// <param name="project">The project being inspected.</param> public static NamespaceExclusionList CreateFromParameters(InspectionParameters parameters, Project project) { var namespaces = parameters.ExcludedNamespaces.ToList(); // Add references to type in the project itself to the exclusions if (project.DefaultNamespace != null) { var projectRootNs = project.DefaultNamespace; namespaces.Add(projectRootNs); // Stem the root ns var offset = projectRootNs.IndexOf('.'); if (offset > 0 && offset < projectRootNs.Length) { namespaces.Add(projectRootNs.Substring(0, offset)); } } // Add referenced projects in the same solution to the exclusions foreach (var reference in project.ProjectReferences) { var referencedProject = project.Solution.GetProject(reference.ProjectId); if (referencedProject?.DefaultNamespace != null) { namespaces.Add(referencedProject.DefaultNamespace); } } return(new NamespaceExclusionList(namespaces)); }
/// <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)); }
/// <summary> /// Instantiates all metrics specified by the <paramref name="parameters"/>. /// </summary> /// <param name="parameters">The inspection parameters provided by the user.</param> public IEnumerable <IMetric> CreateMetricInstances(InspectionParameters parameters) { if (parameters.Metrics.Count < 1) { foreach (var factory in _factories.Values) { yield return(factory()); } } else { var selected = _factories .Where(kvp => parameters.Metrics.Contains(kvp.Key)) .Select(kvp => kvp.Value); foreach (var factory in selected) { yield return(factory()); } } }
/// <summary> /// Inspects the provided <paramref name="file"/>. If the file is a solution (.sln) file all /// non-excluded projects are inspected. If the file is a project (.csproj) file that project is /// inspected. /// </summary> /// <param name="file">An absolute file path to either a solution (.sln) or project (.csproj) file.</param> /// <param name="parameters">The inspection parameters provided by the user.</param> /// <param name="ct">A cancellation token.</param> /// <exception cref="InspectionException">This exception is thrown if the inspection process encounters an error.</exception> public async IAsyncEnumerable <ProjectInspectionResult> InspectAsync(FileSystemInfo file, InspectionParameters parameters, [EnumeratorCancellation] CancellationToken ct) { MSBuildLocator.RegisterDefaults(); _logger.LogInformation("Initializing"); using (var workspace = MSBuildWorkspace.Create()) { workspace.LoadMetadataForReferencedProjects = true; var context = new InspectionContext(file, workspace, parameters); var metrics = context.CreateMetricInstances(context.Parameters).ToList(); if (file.HasExtension(".csproj")) { _logger.LogVerbose($"Inspecting project: {file}"); yield return(await InspectProject(context, metrics, ct)); } else if (file.HasExtension(".sln")) { _logger.LogVerbose($"Inspecting solution: {file}"); await foreach (var result in InspectSolution(context, metrics, ct)) { yield return(result); } } else { throw new InspectionException("The specified file path is not a reference to a solution or project file"); } } }
public InspectionContext(FileSystemInfo file, MSBuildWorkspace workspace, InspectionParameters parameters) { File = file; Workspace = workspace; Parameters = parameters; }