public void AnalyzeSymbol(SymbolAnalysisContext context) { Debug.Assert(context.Symbol.Kind == SymbolKind.NamedType); Debug.Assert(StartupFacts.IsStartupClass(_context.StartupSymbols, (INamedTypeSymbol)context.Symbol)); var type = (INamedTypeSymbol)context.Symbol; var optionsAnalysis = _context.GetRelatedSingletonAnalysis <OptionsAnalysis>(type); // Find the middleware analysis foreach of the Configure methods defined by this class and validate. // // Note that this doesn't attempt to handle inheritance scenarios. foreach (var middlewareAnalysis in _context.GetRelatedAnalyses <MiddlewareAnalysis>(type)) { foreach (var middlewareItem in middlewareAnalysis.Middleware) { if (middlewareItem.UseMethod.Name == "UseMvc" || middlewareItem.UseMethod.Name == "UseMvcWithDefaultRoute") { // Report a diagnostic if it's unclear that the user turned off Endpoint Routing. if (!OptionsFacts.IsEndpointRoutingExplicitlyDisabled(optionsAnalysis)) { context.ReportDiagnostic(Diagnostic.Create( StartupAnalzyer.Diagnostics.UnsupportedUseMvcWithEndpointRouting, middlewareItem.Operation.Syntax.GetLocation(), middlewareItem.UseMethod.Name, optionsAnalysis.ConfigureServicesMethod.Name)); } } } } }
public override void VisitMethod(IMethodSymbol symbol) { if (StartupFacts.IsConfigure(_symbols, symbol)) { _methods.Add(symbol); } }
private void OnCompilationStart(CompilationStartAnalysisContext context) { var symbols = new StartupSymbols(context.Compilation); // Don't run analyzer if ASP.NET Core types cannot be found if (!symbols.HasRequiredSymbols) { return; } context.RegisterSymbolStartAction(context => { var type = (INamedTypeSymbol)context.Symbol; if (!StartupFacts.IsStartupClass(symbols, type)) { // Not a startup class, nothing to do. return; } // This analyzer fans out a bunch of jobs. The context will capture the results of doing analysis // on the startup code, so that other analyzers that run later can examine them. var builder = new StartupAnalysisBuilder(this, symbols); var services = new ServicesAnalyzer(builder); var options = new OptionsAnalyzer(builder); var middleware = new MiddlewareAnalyzer(builder); context.RegisterOperationBlockStartAction(context => { if (context.OwningSymbol.Kind != SymbolKind.Method) { return; } var method = (IMethodSymbol)context.OwningSymbol; if (StartupFacts.IsConfigureServices(symbols, method)) { OnConfigureServicesMethodFound(method); services.AnalyzeConfigureServices(context); options.AnalyzeConfigureServices(context); } if (StartupFacts.IsConfigure(symbols, method)) { OnConfigureMethodFound(method); middleware.AnalyzeConfigureMethod(context); } }); // Run after analyses have had a chance to finish to add diagnostics. context.RegisterSymbolEndAction(context => { var analysis = builder.Build(); new UseMvcAnalyzer(analysis).AnalyzeSymbol(context); new BuildServiceProviderValidator(analysis).AnalyzeSymbol(context); }); }, SymbolKind.NamedType); }
public async Task DetectFeaturesAsync_FindsNoFeatures() { // Arrange var source = @" using Microsoft.AspNetCore.Builder; namespace Microsoft.AspNetCore.Analyzers.TestFiles.CompilationFeatureDetectorTest { public class StartupWithNoFeatures { public void Configure(IApplicationBuilder app) { app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapFallbackToFile(""index.html""); }); } } }"; var compilation = TestCompilation.Create(source); var symbols = new StartupSymbols(compilation); var type = (INamedTypeSymbol)compilation.GetSymbolsWithName("StartupWithNoFeatures").Single(); Assert.True(StartupFacts.IsStartupClass(symbols, type)); // Act var features = await CompilationFeatureDetector.DetectFeaturesAsync(compilation); // Assert Assert.Empty(features); }
public void AnalyzeSymbol(SymbolAnalysisContext context) { Debug.Assert(context.Symbol.Kind == SymbolKind.NamedType); Debug.Assert(StartupFacts.IsStartupClass(_context.StartupSymbols, (INamedTypeSymbol)context.Symbol)); var type = (INamedTypeSymbol)context.Symbol; var middlwareAnalyses = _context.GetRelatedAnalyses <MiddlewareAnalysis>(type); foreach (var middlewareAnalsysis in middlwareAnalyses) { for (var i = 0; i < middlewareAnalsysis.Middleware.Length; i++) { var middlewareItem = middlewareAnalsysis.Middleware[i]; if (MiddlewareHappensAfterMap.TryGetValue(middlewareItem.UseMethod.Name, out var cannotComeAfter)) { for (var j = i; j < middlewareAnalsysis.Middleware.Length; j++) { var candidate = middlewareAnalsysis.Middleware[j]; if (string.Equals(cannotComeAfter, candidate.UseMethod.Name, StringComparison.Ordinal)) { // Found the other middleware after current one. This is an error. context.ReportDiagnostic(Diagnostic.Create( StartupAnalzyer.Diagnostics.MiddlewareInvalidOrder, candidate.Operation.Syntax.GetLocation(), middlewareItem.UseMethod.Name, candidate.UseMethod.Name)); } } } } } }
public void AnalyzeSymbol(SymbolAnalysisContext context) { Debug.Assert(context.Symbol.Kind == SymbolKind.NamedType); Debug.Assert(StartupFacts.IsStartupClass(_context.StartupSymbols, (INamedTypeSymbol)context.Symbol)); var type = (INamedTypeSymbol)context.Symbol; // Find the services analysis for each of the ConfigureServices methods defined by this class. // // There should just be one. var servicesAnalysis = _context.GetRelatedSingletonAnalysis <ServicesAnalysis>(type); var occluded = new HashSet <string>(); foreach (var entry in servicesAnalysis.Services) { occluded.Add(entry.UseMethod.Name); if (ServicesMap.TryGetValue(entry.UseMethod.Name, out var additional)) { foreach (var item in additional) { occluded.Add(item); } } } // Find the middleware analysis for each of the Configure methods defined by this class and validate. // // Note that this doesn't attempt to handle inheritance scenarios. foreach (var middlewareAnalsysis in _context.GetRelatedAnalyses <MiddlewareAnalysis>(type)) { foreach (var middlewareItem in middlewareAnalsysis.Middleware) { if (MiddlewareMap.TryGetValue(middlewareItem.UseMethod.Name, out var requiredServices)) { foreach (var requiredService in requiredServices) { if (!occluded.Contains(requiredService)) { context.ReportDiagnostic(Diagnostic.Create( StartupAnalzyer.Diagnostics.MiddlewareMissingRequiredServices, middlewareItem.Operation.Syntax.GetLocation(), middlewareItem.UseMethod.Name, requiredService, servicesAnalysis.ConfigureServicesMethod.Name)); } } } } } }
public void IsStartupClass_RejectsNotStartupClass(string source) { // Arrange var compilation = TestCompilation.Create(TestSources[source]); var symbols = new StartupSymbols(compilation); var type = (INamedTypeSymbol)compilation.GetSymbolsWithName(source).Single(); // Act var result = StartupFacts.IsStartupClass(symbols, type); // Assert Assert.False(result); }
public async Task IsStartupClass_RejectsNotStartupClass(string source) { // Arrange var compilation = await CreateCompilationAsync(source); var symbols = new StartupSymbols(compilation); var type = (INamedTypeSymbol)compilation.GetSymbolsWithName(source).Single(); // Act var result = StartupFacts.IsStartupClass(symbols, type); // Assert Assert.False(result); }
public async Task DetectFeaturesAsync_FindsSignalR(string source) { // Arrange var compilation = await CreateCompilationAsync(source); var symbols = new StartupSymbols(compilation); var type = (INamedTypeSymbol)compilation.GetSymbolsWithName(source).Single(); Assert.True(StartupFacts.IsStartupClass(symbols, type)); // Act var features = await CompilationFeatureDetector.DetectFeaturesAsync(compilation); // Assert Assert.Collection(features, f => Assert.Equal(WellKnownFeatures.SignalR, f)); }
public async Task DetectFeaturesAsync_FindsNoFeatures() { // Arrange var compilation = await CreateCompilationAsync(nameof(StartupWithNoFeatures)); var symbols = new StartupSymbols(compilation); var type = (INamedTypeSymbol)compilation.GetSymbolsWithName(nameof(StartupWithNoFeatures)).Single(); Assert.True(StartupFacts.IsStartupClass(symbols, type)); // Act var features = await CompilationFeatureDetector.DetectFeaturesAsync(compilation); // Assert Assert.Empty(features); }
public void IsConfigure_RejectsNonConfigureMethod(string source, string methodName) { // Arrange var compilation = TestCompilation.Create(TestSources[source]); var symbols = new StartupSymbols(compilation); var type = (INamedTypeSymbol)compilation.GetSymbolsWithName(source).Single(); var methods = type.GetMembers(methodName).Cast <IMethodSymbol>(); foreach (var method in methods) { // Act var result = StartupFacts.IsConfigure(symbols, method); // Assert Assert.False(result); } }
public async Task IsConfigure_FindsConfigureMethod(string source, string methodName) { // Arrange var compilation = await CreateCompilationAsync(source); var symbols = new StartupSymbols(compilation); var type = (INamedTypeSymbol)compilation.GetSymbolsWithName(source).Single(); var methods = type.GetMembers(methodName).Cast <IMethodSymbol>(); foreach (var method in methods) { // Act var result = StartupFacts.IsConfigure(symbols, method); // Assert Assert.True(result); } }
public static async Task <IImmutableSet <string> > DetectFeaturesAsync( Compilation compilation, CancellationToken cancellationToken = default) { var symbols = new StartupSymbols(compilation); if (!symbols.HasRequiredSymbols) { // Cannot find ASP.NET Core types. return(ImmutableHashSet <string> .Empty); } var features = ImmutableHashSet.CreateBuilder <string>(); // Find configure methods in the project's assembly var configureMethods = ConfigureMethodVisitor.FindConfigureMethods(symbols, compilation.Assembly); for (var i = 0; i < configureMethods.Count; i++) { var configureMethod = configureMethods[i]; // Handles the case where a method is using partial definitions. We don't expect this to occur, but still handle it correctly. var syntaxReferences = configureMethod.DeclaringSyntaxReferences; for (var j = 0; j < syntaxReferences.Length; j++) { var semanticModel = compilation.GetSemanticModel(syntaxReferences[j].SyntaxTree); var syntax = await syntaxReferences[j].GetSyntaxAsync(cancellationToken).ConfigureAwait(false); var operation = semanticModel.GetOperation(syntax); // Look for a call to one of the SignalR gestures that applies to the Configure method. if (operation .Descendants() .OfType <IInvocationOperation>() .Any(op => StartupFacts.IsSignalRConfigureMethodGesture(op.TargetMethod))) { features.Add(WellKnownFeatures.SignalR); } } } return(features.ToImmutable()); }
private void AnalyzeStartupMethods(OperationBlockStartAnalysisContext context, StartupSymbols symbols, ConcurrentBag <StartupComputedAnalysis> analyses) { if (!IsStartupFile(context)) { return; } if (context.OwningSymbol.Kind != SymbolKind.Method) { return; } var startupAnalysisContext = new StartupAnalysisContext(context, symbols); var method = (IMethodSymbol)context.OwningSymbol; if (StartupFacts.IsConfigureServices(symbols, method)) { for (var i = 0; i < ConfigureServicesMethodAnalysisFactories.Length; i++) { var analysis = ConfigureServicesMethodAnalysisFactories[i].Invoke(startupAnalysisContext); analyses.Add(analysis); OnAnalysisStarted(analysis); } OnConfigureServicesMethodFound(method); } if (StartupFacts.IsConfigure(symbols, method)) { for (var i = 0; i < ConfigureMethodAnalysisFactories.Length; i++) { var analysis = ConfigureMethodAnalysisFactories[i].Invoke(startupAnalysisContext); analyses.Add(analysis); OnAnalysisStarted(analysis); } OnConfigureMethodFound(method); } }
public async Task DetectFeatureAsync_StartupWithMapHub_FindsSignalR() { var source = @" using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.SignalR; namespace Microsoft.AspNetCore.Analyzers.TestFiles.CompilationFeatureDetectorTest { public class StartupWithMapHub { public void Configure(IApplicationBuilder app) { app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapHub<MyHub>("" / test""); }); } } public class MyHub : Hub { } } "; var compilation = TestCompilation.Create(source); var symbols = new StartupSymbols(compilation); var type = (INamedTypeSymbol)compilation.GetSymbolsWithName("StartupWithMapHub").Single(); Assert.True(StartupFacts.IsStartupClass(symbols, type)); // Act var features = await CompilationFeatureDetector.DetectFeaturesAsync(compilation); // Assert Assert.Collection(features, f => Assert.Equal(WellKnownFeatures.SignalR, f)); }
public void AnalyzeSymbol(SymbolAnalysisContext context) { Debug.Assert(context.Symbol.Kind == SymbolKind.NamedType); Debug.Assert(StartupFacts.IsStartupClass(_context.StartupSymbols, (INamedTypeSymbol)context.Symbol)); var type = (INamedTypeSymbol)context.Symbol; foreach (var serviceAnalysis in _context.GetRelatedAnalyses <ServicesAnalysis>(type)) { foreach (var serviceItem in serviceAnalysis.Services) { if (serviceItem.UseMethod.Name == "BuildServiceProvider") { context.ReportDiagnostic(Diagnostic.Create( StartupAnalyzer.Diagnostics.BuildServiceProviderShouldNotCalledInConfigureServicesMethod, serviceItem.Operation.Syntax.GetLocation(), serviceItem.UseMethod.Name, serviceAnalysis.ConfigureServicesMethod.Name)); } } } }
public void AnalyzeSymbol(SymbolAnalysisContext context) { Debug.Assert(context.Symbol.Kind == SymbolKind.NamedType); Debug.Assert(StartupFacts.IsStartupClass(_context.StartupSymbols, (INamedTypeSymbol)context.Symbol)); var type = (INamedTypeSymbol)context.Symbol; foreach (var middlewareAnalysis in _context.GetRelatedAnalyses <MiddlewareAnalysis>(type)) { MiddlewareItem?useAuthorizationItem = default; MiddlewareItem?useRoutingItem = default; MiddlewareItem?useEndpoint = default; var length = middlewareAnalysis.Middleware.Length; for (var i = length - 1; i >= 0; i--) { var middlewareItem = middlewareAnalysis.Middleware[i]; var middleware = middlewareItem.UseMethod.Name; if (middleware == "UseAuthorization") { if (useRoutingItem != null && useAuthorizationItem == null) { // This looks like // // app.UseAuthorization(); // ... // app.UseRouting(); // app.UseEndpoints(...); context.ReportDiagnostic(Diagnostic.Create( StartupAnalyzer.Diagnostics.IncorrectlyConfiguredAuthorizationMiddleware, middlewareItem.Operation.Syntax.GetLocation(), middlewareItem.UseMethod.Name)); } useAuthorizationItem = middlewareItem; } else if (middleware == "UseEndpoints") { if (useAuthorizationItem != null) { // This configuration looks like // // app.UseRouting(); // app.UseEndpoints(...); // ... // app.UseAuthorization(); // context.ReportDiagnostic(Diagnostic.Create( StartupAnalyzer.Diagnostics.IncorrectlyConfiguredAuthorizationMiddleware, useAuthorizationItem.Operation.Syntax.GetLocation(), middlewareItem.UseMethod.Name)); } useEndpoint = middlewareItem; } else if (middleware == "UseRouting") { if (useEndpoint is null) { // We're likely here because the middleware uses an expression chain e.g. // app.UseRouting() // .UseAuthorization() // .UseEndpoints(..)); // This analyzer expects MiddlewareItem instances to appear in the order in which they appear in source // which unfortunately isn't true for chained calls (the operations appear in reverse order). // We'll avoid doing any analysis in this event and rely on the runtime guardrails. // We'll use https://github.com/dotnet/aspnetcore/issues/16648 to track addressing this in a future milestone return; } useRoutingItem = middlewareItem; } } } }
private void OnCompilationStart(CompilationStartAnalysisContext context) { var symbols = new StartupSymbols(context.Compilation); // Don't run analyzer if ASP.NET Core types cannot be found if (!symbols.HasRequiredSymbols) { return; } var entryPoint = context.Compilation.GetEntryPoint(context.CancellationToken); context.RegisterSymbolStartAction(context => { var type = (INamedTypeSymbol)context.Symbol; if (!StartupFacts.IsStartupClass(symbols, type) && !SymbolEqualityComparer.Default.Equals(entryPoint?.ContainingType, type)) { // Not a startup class, nothing to do. return; } // This analyzer fans out a bunch of jobs. The context will capture the results of doing analysis // on the startup code, so that other analyzers that run later can examine them. var builder = new StartupAnalysisBuilder(this, symbols); var services = new ServicesAnalyzer(builder); var options = new OptionsAnalyzer(builder); var middleware = new MiddlewareAnalyzer(builder); context.RegisterOperationBlockStartAction(context => { if (context.OwningSymbol.Kind != SymbolKind.Method) { return; } var method = (IMethodSymbol)context.OwningSymbol; var isConfigureServices = StartupFacts.IsConfigureServices(symbols, method); if (isConfigureServices) { OnConfigureServicesMethodFound(method); } // In the future we can consider looking at more methods, but for now limit to Main, implicit Main, and Configure* methods var isMain = SymbolEqualityComparer.Default.Equals(entryPoint, context.OwningSymbol); if (isConfigureServices || isMain) { services.AnalyzeConfigureServices(context); options.AnalyzeConfigureServices(context); } var isConfigure = StartupFacts.IsConfigure(symbols, method); if (isConfigure) { OnConfigureMethodFound(method); } if (isConfigure || isMain) { middleware.AnalyzeConfigureMethod(context); } }); // Run after analyses have had a chance to finish to add diagnostics. context.RegisterSymbolEndAction(context => { var analysis = builder.Build(); new UseMvcAnalyzer(analysis).AnalyzeSymbol(context); new BuildServiceProviderAnalyzer(analysis).AnalyzeSymbol(context); new UseAuthorizationAnalyzer(analysis).AnalyzeSymbol(context); }); }, SymbolKind.NamedType); }
public void AnalyzeSymbol(SymbolAnalysisContext context) { Debug.Assert(context.Symbol.Kind == SymbolKind.NamedType); Debug.Assert(StartupFacts.IsStartupClass(_context.StartupSymbols, (INamedTypeSymbol)context.Symbol)); var type = (INamedTypeSymbol)context.Symbol; foreach (var middlewareAnalysis in _context.GetRelatedAnalyses <MiddlewareAnalysis>(type)) { MiddlewareItem?useAuthorizationItem = default; MiddlewareItem?useRoutingItem = default; var length = middlewareAnalysis.Middleware.Length; for (var i = length - 1; i >= 0; i--) { var middlewareItem = middlewareAnalysis.Middleware[i]; var middleware = middlewareItem.UseMethod.Name; if (middleware == "UseAuthorization") { if (useRoutingItem != null && useAuthorizationItem == null) { // This looks like // // app.UseAuthorization(); // ... // app.UseRouting(); // app.UseEndpoints(...); context.ReportDiagnostic(Diagnostic.Create( StartupAnalyzer.Diagnostics.IncorrectlyConfiguredAuthorizationMiddleware, middlewareItem.Operation.Syntax.GetLocation(), middlewareItem.UseMethod.Name)); } useAuthorizationItem = middlewareItem; } else if (middleware == "UseEndpoints") { if (useAuthorizationItem != null) { // This configuration looks like // // app.UseRouting(); // app.UseEndpoints(...); // ... // app.UseAuthorization(); // context.ReportDiagnostic(Diagnostic.Create( StartupAnalyzer.Diagnostics.IncorrectlyConfiguredAuthorizationMiddleware, useAuthorizationItem.Operation.Syntax.GetLocation(), middlewareItem.UseMethod.Name)); } } else if (middleware == "UseRouting") { useRoutingItem = middlewareItem; } } } }