| File: StartupFacts.cs | Web Access |
| Project: src\aspnetcore\src\Analyzers\Analyzers\src\Microsoft.AspNetCore.Analyzers.csproj (Microsoft.AspNetCore.Analyzers) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using Microsoft.CodeAnalysis; namespace Microsoft.AspNetCore.Analyzers; internal static class StartupFacts { public static bool IsStartupClass(StartupSymbols symbols, INamedTypeSymbol type) { if (symbols == null) { throw new ArgumentNullException(nameof(symbols)); } if (type == null) { throw new ArgumentNullException(nameof(type)); } // It's not good enough to just look for a method called ConfigureServices or Configure as a heuristic. // ConfigureServices might not appear in trivial cases, and Configure might be named ConfigureDevelopment // or something similar. // // Additionally, a startup class could be called anything and wired up explicitly. // // Since we already are analyzing the symbol it should be cheap to do a pass over the members. var members = type.GetMembers(); for (var i = 0; i < members.Length; i++) { if (members[i] is IMethodSymbol method && (IsConfigureServices(symbols, method) || IsConfigure(symbols, method))) { return true; } } return false; } // Based on StartupLoader. The philosophy is that we want to do analysis only on things // that would be recognized as a ConfigureServices method to avoid false positives. // // The ConfigureServices method follows the naming pattern `Configure{Environment?}Services` (ignoring case). // The ConfigureServices method must be public. // The ConfigureServices method can be instance or static. // The ConfigureServices method cannot have other parameters besides IServiceCollection. // // The ConfigureServices method does not actually need to accept IServiceCollection // but we exclude that case because a ConfigureServices method that doesn't accept an // IServiceCollection can't do anything interesting to analysis. public static bool IsConfigureServices(StartupSymbols symbols, IMethodSymbol symbol) { if (symbol == null) { throw new ArgumentNullException(nameof(symbol)); } if (symbol.DeclaredAccessibility != Accessibility.Public) { return false; } if (symbol.Name == null || !symbol.Name.StartsWith(SymbolNames.ConfigureServicesMethodPrefix, StringComparison.OrdinalIgnoreCase) || !symbol.Name.EndsWith(SymbolNames.ConfigureServicesMethodSuffix, StringComparison.OrdinalIgnoreCase)) { return false; } if (symbol.Parameters.Length != 1) { return false; } return SymbolEqualityComparer.Default.Equals(symbol.Parameters[0].Type, symbols.IServiceCollection); } // Based on StartupLoader. The philosophy is that we want to do analysis only on things // that would be recognized as a Configure method to avoid false positives. // // The Configure method follows the naming pattern `Configure{Environment?}` (ignoring case). // The Configure method must be public. // The Configure method can be instance or static. // The Configure method *can* have other parameters besides IApplicationBuilder. // // The Configure method does not actually need to accept IApplicationBuilder // but we exclude that case because a Configure method that doesn't accept an // IApplicationBuilder can't do anything interesting to analysis. public static bool IsConfigure(StartupSymbols symbols, IMethodSymbol symbol) { if (symbol == null) { throw new ArgumentNullException(nameof(symbol)); } if (symbol.DeclaredAccessibility != Accessibility.Public) { return false; } if (symbol.Name == null || !symbol.Name.StartsWith(SymbolNames.ConfigureMethodPrefix, StringComparison.OrdinalIgnoreCase)) { return false; } // IApplicationBuilder can appear in any parameter, but must appear. for (var i = 0; i < symbol.Parameters.Length; i++) { if (SymbolEqualityComparer.Default.Equals(symbol.Parameters[i].Type, symbols.IApplicationBuilder)) { return true; } } return false; } // Based on the three known gestures for including SignalR in a ConfigureMethod: // UseSignalR() // middleware // MapHub<>() // endpoint routing // MapBlazorHub() // server-side blazor // // To be slightly less brittle, we don't look at the exact symbols and instead just look // at method names in here. We're NOT worried about false negatives, because all of these // cases contain words like SignalR or Hub. public static bool IsSignalRConfigureMethodGesture(IMethodSymbol symbol) { if (symbol == null) { throw new ArgumentNullException(nameof(symbol)); } // UseSignalR has been removed in 5.0, but we should probably still check for it in this analyzer in case the user // installs it into a pre-5.0 app. if (string.Equals(symbol.Name, SymbolNames.SignalRAppBuilderExtensions.UseSignalRMethodName, StringComparison.Ordinal) || string.Equals(symbol.Name, SymbolNames.HubEndpointRouteBuilderExtensions.MapHubMethodName, StringComparison.Ordinal) || string.Equals(symbol.Name, SymbolNames.ComponentEndpointRouteBuilderExtensions.MapBlazorHubMethodName, StringComparison.Ordinal)) { return true; } return false; } }