public override async Task <CommandResult> ExecuteAsync(ProjectOrSolution projectOrSolution, CancellationToken cancellationToken = default) { AssemblyResolver.Register(); ImmutableArray <UnusedSymbolInfo> allUnusedSymbols; if (projectOrSolution.IsProject) { Project project = projectOrSolution.AsProject(); allUnusedSymbols = await AnalyzeProject(project, cancellationToken); } else { Solution solution = projectOrSolution.AsSolution(); ImmutableArray <UnusedSymbolInfo> .Builder unusedSymbols = null; foreach (Project project in FilterProjects(solution, Options, s => s .GetProjectDependencyGraph() .GetTopologicallySortedProjects(cancellationToken) .ToImmutableArray())) { cancellationToken.ThrowIfCancellationRequested(); ImmutableArray <UnusedSymbolInfo> unusedSymbols2 = await AnalyzeProject(project, cancellationToken); if (unusedSymbols2.Any()) { (unusedSymbols ?? (unusedSymbols = ImmutableArray.CreateBuilder <UnusedSymbolInfo>())).AddRange(unusedSymbols2); } } allUnusedSymbols = unusedSymbols?.ToImmutableArray() ?? ImmutableArray <UnusedSymbolInfo> .Empty; } if (allUnusedSymbols.Any()) { WriteLine(Verbosity.Normal); Dictionary <UnusedSymbolKind, int> countByKind = allUnusedSymbols .GroupBy(f => UnusedSymbolFinder.GetUnusedSymbolKind(f.Symbol)) .OrderByDescending(f => f.Count()) .ThenBy(f => f.Key) .ToDictionary(f => f.Key, f => f.Count()); int maxCountLength = countByKind.Sum(f => f.Value.ToString().Length); foreach (KeyValuePair <UnusedSymbolKind, int> kvp in countByKind) { WriteLine($"{kvp.Value.ToString().PadLeft(maxCountLength)} {kvp.Key.ToString().ToLowerInvariant()} symbols", Verbosity.Normal); } } WriteLine(Verbosity.Minimal); WriteLine($"{allUnusedSymbols.Length} unused {((allUnusedSymbols.Length == 1) ? "symbol" : "symbols")} found", ConsoleColor.Green, Verbosity.Minimal); WriteLine(Verbosity.Minimal); return(CommandResult.Success); }
private async Task <ImmutableArray <UnusedSymbolInfo> > AnalyzeProject(Project project, CancellationToken cancellationToken) { WriteLine($"Analyze '{project.Name}'", Verbosity.Minimal); Compilation compilation = await project.GetCompilationAsync(cancellationToken); INamedTypeSymbol generatedCodeAttribute = compilation.GetTypeByMetadataName("System.CodeDom.Compiler.GeneratedCodeAttribute"); ImmutableHashSet <ISymbol> ignoredSymbols = Options.IgnoredSymbols .Select(f => DocumentationCommentId.GetFirstSymbolForDeclarationId(f, compilation)) .Where(f => f != null) .ToImmutableHashSet(); return(await UnusedSymbolFinder.FindUnusedSymbolsAsync(project, compilation, Predicate, ignoredSymbols, cancellationToken : cancellationToken).ConfigureAwait(false)); bool Predicate(ISymbol symbol) { return((UnusedSymbolKinds & GetUnusedSymbolKinds(symbol)) != 0 && IsVisible(symbol) && (!Options.IgnoreObsolete || !symbol.HasAttribute(MetadataNames.System_ObsoleteAttribute)) && (Options.IncludeGeneratedCode || !GeneratedCodeUtility.IsGeneratedCode(symbol, generatedCodeAttribute, MefWorkspaceServices.Default.GetService <ISyntaxFactsService>(project.Language).IsComment, cancellationToken))); } }