예제 #1
0
        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);
     }
 }
예제 #3
0
        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);
        }
예제 #4
0
        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));
                            }
                        }
                    }
                }
            }
        }
예제 #7
0
        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);
        }
예제 #8
0
        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);
        }
예제 #9
0
        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));
        }
예제 #10
0
        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);
        }
예제 #11
0
        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);
            }
        }
예제 #12
0
        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());
        }
예제 #14
0
        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);
            }
        }
예제 #15
0
        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;
                    }
                }
            }
        }
예제 #18
0
        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);
        }
예제 #19
0
        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;
                    }
                }
            }
        }