//Prevent static usage of OldAndBrokenServiceLocator //For example, OldAndBrokenServiceLocator.Instance.Get<IFoo>() private void PreventOldAndBrokenUsage( SyntaxNodeAnalysisContext context, ImmutableArray <INamedTypeSymbol> disallowedTypes, TypeWhitelist typeWhitelist ) { if (!(context.Node is IdentifierNameSyntax syntax)) { return; } if (!IdentifierIsOfDisallowedType(context.SemanticModel, disallowedTypes, syntax)) { return; } var parentClasses = context.Node.Ancestors().OfType <TypeDeclarationSyntax>(); var parentSymbols = parentClasses.Select(c => context.SemanticModel.GetDeclaredSymbol(c)).ToImmutableArray(); if (parentSymbols.Any(s => Attributes.DIFramework.IsDefined(s))) { //Classes in the DI Framework are allowed to use locators and activators return; } if (_excludeKnownProblems && parentSymbols.Any(typeWhitelist.Contains)) { return; } context.ReportDiagnostic( Diagnostic.Create(Diagnostics.OldAndBrokenLocatorIsObsolete, syntax.GetLocation()) ); }
public void RegisterServiceLocatorAnalyzer(CompilationStartAnalysisContext context) { // Cache some important type lookups var locatorType = context.Compilation.GetTypeByMetadataName("D2L.LP.Extensibility.Activation.Domain.OldAndBrokenServiceLocator"); var factoryType = context.Compilation.GetTypeByMetadataName("D2L.LP.Extensibility.Activation.Domain.OldAndBrokenServiceLocatorFactory"); var activatorType = context.Compilation.GetTypeByMetadataName("D2L.LP.Extensibility.Activation.Domain.IObjectActivator"); var customActivatorType = context.Compilation.GetTypeByMetadataName("D2L.LP.Extensibility.Activation.Domain.ICustomObjectActivator"); // If those type lookups failed then OldAndBrokenServiceLocator // cannot resolve and we don't need to register our analyzer. if (locatorType == null || locatorType.Kind == SymbolKind.ErrorType) { return; } if (factoryType == null || factoryType.Kind == SymbolKind.ErrorType) { return; } ImmutableArray <INamedTypeSymbol> disallowedTypes = ImmutableArray.Create( locatorType, factoryType, activatorType, customActivatorType ); TypeWhitelist typeWhitelist = TypeWhitelist.CreateFromAnalyzerOptions( whitelistFileName: "OldAndBrokenServiceLocatorWhitelist.txt", analyzerOptions: context.Options ); //Prevent static usage of OldAndBrokenServiceLocator //For example, OldAndBrokenServiceLocator.Instance.Get<IFoo>() context.RegisterSyntaxNodeAction( ctx => PreventOldAndBrokenUsage( ctx, disallowedTypes, typeWhitelist ), SyntaxKind.IdentifierName ); context.RegisterSymbolAction( ctx => PreventUnnecessaryWhitelisting( ctx, disallowedTypes, typeWhitelist ), SymbolKind.NamedType ); }
private void PreventUnnecessaryWhitelisting( SymbolAnalysisContext context, ImmutableArray <INamedTypeSymbol> disallowedTypes, TypeWhitelist typeWhitelist ) { if (!(context.Symbol is INamedTypeSymbol namedType)) { return; } if (!typeWhitelist.Contains(namedType)) { return; } Location diagnosticLocation = null; foreach (var syntaxRef in namedType.DeclaringSyntaxReferences) { var typeSyntax = syntaxRef.GetSyntax(context.CancellationToken) as TypeDeclarationSyntax; diagnosticLocation = diagnosticLocation ?? typeSyntax.Identifier.GetLocation(); SemanticModel model = context.Compilation.GetSemanticModel(typeSyntax.SyntaxTree); bool usesDisallowedTypes = typeSyntax .DescendantNodes() .OfType <IdentifierNameSyntax>() .Any(syntax => IdentifierIsOfDisallowedType(model, disallowedTypes, syntax)); if (usesDisallowedTypes) { return; } } if (diagnosticLocation != null) { typeWhitelist.ReportEntryAsUnnecesary( entry: namedType, location: diagnosticLocation, report: context.ReportDiagnostic ); } }