private static IEnumerable <Diagnostic> GetDiagnostics(SymbolUsageCollector usageCollector, ISet <ISymbol> removableSymbols, string accessibility, BidirectionalDictionary <ISymbol, SyntaxNode> fieldLikeSymbols) { var unusedSymbols = removableSymbols .Except(usageCollector.UsedSymbols) .Where(symbol => !MentionedInDebuggerDisplay(symbol)) .ToHashSet(); var propertiesWithUnusedAccessor = removableSymbols .Intersect(usageCollector.UsedSymbols) .OfType <IPropertySymbol>() .Where(usageCollector.PropertyAccess.ContainsKey) .Where(symbol => !MentionedInDebuggerDisplay(symbol)); return(GetDiagnosticsForMembers(unusedSymbols, accessibility, fieldLikeSymbols) .Concat(propertiesWithUnusedAccessor.SelectMany(propertySymbol => GetDiagnosticsForProperty(propertySymbol, usageCollector.PropertyAccess)))); bool MentionedInDebuggerDisplay(ISymbol symbol) => usageCollector.DebuggerDisplayValues.Any(value => value.Contains(symbol.Name)); }
protected override void Initialize(SonarAnalysisContext context) { context.RegisterCompilationStartAction( c => { // Collect potentially removable internal types from the project to evaluate when // the compilation is over, depending on whether InternalsVisibleTo attribute is present // or not. var removableInternalTypes = new ConcurrentBag <ISymbol>(); // Collect here all named types from the project to look for internal member usages. var allNamedTypes = new ConcurrentBag <INamedTypeSymbol>(); c.RegisterSymbolAction( cc => { var namedType = (INamedTypeSymbol)cc.Symbol; if (!namedType.IsClassOrStruct() || namedType.ContainingType != null || namedType.DerivesFromAny(IgnoredTypes)) { return; } // Collect all symbols to try to look for used internal members when the compilation ends allNamedTypes.Add(namedType); // Collect symbols of private members that could potentially be removed var removableSymbolsCollector = new RemovableSymbolCollector(c.Compilation.GetSemanticModel); VisitDeclaringReferences(namedType, removableSymbolsCollector); // Keep the removable internal types for when the compilation ends foreach (var internalSymbol in removableSymbolsCollector.InternalSymbols.OfType <INamedTypeSymbol>()) { removableInternalTypes.Add(internalSymbol); } var usageCollector = new SymbolUsageCollector( c.Compilation.GetSemanticModel, removableSymbolsCollector.PrivateSymbols.Select(s => s.Name).ToHashSet()); VisitDeclaringReferences(namedType, usageCollector); var diagnostics = GetDiagnostics(usageCollector, removableSymbolsCollector.PrivateSymbols, "private", removableSymbolsCollector.FieldLikeSymbols); foreach (var diagnostic in diagnostics) { cc.ReportDiagnosticIfNonGenerated(diagnostic, cc.Compilation); } }, SymbolKind.NamedType); c.RegisterCompilationEndAction( cc => { var foundInternalsVisibleTo = cc.Compilation.Assembly .GetAttributes(KnownType.System_Runtime_CompilerServices_InternalsVisibleToAttribute) .Any(); if (foundInternalsVisibleTo || removableInternalTypes.Count == 0) { return; } var usageCollector = new SymbolUsageCollector( c.Compilation.GetSemanticModel, removableInternalTypes.Select(s => s.Name).ToHashSet()); foreach (var symbol in allNamedTypes) { VisitDeclaringReferences(symbol, usageCollector); } var diagnostics = GetDiagnostics(usageCollector, removableInternalTypes.ToHashSet(), "internal", new BidirectionalDictionary <ISymbol, SyntaxNode>()); foreach (var diagnostic in diagnostics) { cc.ReportDiagnosticIfNonGenerated(diagnostic, cc.Compilation); } }); }); }