public static List <IMethodSymbol> FindConfigureMethods(StartupSymbols symbols, IAssemblySymbol assembly) { var visitor = new ConfigureMethodVisitor(symbols); visitor.Visit(assembly); return(visitor._methods); }
public async Task FindConfigureMethods_AtDifferentScopes() { // Arrange var expected = new string[] { "global::ANamespace.Nested.Startup.Configure", "global::ANamespace.Nested.Startup.NestedStartup.Configure", "global::ANamespace.Startup.ConfigureDevelopment", "global::ANamespace.Startup.NestedStartup.ConfigureTest", "global::Another.AnotherStartup.Configure", "global::GlobalStartup.Configure", }; var compilation = await CreateCompilationAsync("Startup"); var symbols = new StartupSymbols(compilation); // Act var results = ConfigureMethodVisitor.FindConfigureMethods(symbols, compilation.Assembly); // Assert var actual = results .Select(m => m.ContainingType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + "." + m.Name) .OrderBy(s => s) .ToArray(); Assert.Equal(expected, actual); }
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()); }
public void FindConfigureMethods_AtDifferentScopes() { // Arrange var source = @" using Microsoft.AspNetCore.Builder; public class GlobalStartup { public void Configure(IApplicationBuilder app) { } } namespace Another { public class AnotherStartup { public void Configure(IApplicationBuilder app) { } } } namespace ANamespace { public class Startup { public void ConfigureDevelopment(IApplicationBuilder app) { } public class NestedStartup { public void ConfigureTest(IApplicationBuilder app) { } } } } namespace ANamespace.Nested { public class Startup { public void Configure(IApplicationBuilder app) { } public class NestedStartup { public void Configure(IApplicationBuilder app) { } } } }"; var expected = new string[] { "global::ANamespace.Nested.Startup.Configure", "global::ANamespace.Nested.Startup.NestedStartup.Configure", "global::ANamespace.Startup.ConfigureDevelopment", "global::ANamespace.Startup.NestedStartup.ConfigureTest", "global::Another.AnotherStartup.Configure", "global::GlobalStartup.Configure", }; var compilation = TestCompilation.Create(source); var symbols = new StartupSymbols(compilation); // Act var results = ConfigureMethodVisitor.FindConfigureMethods(symbols, compilation.Assembly); // Assert var actual = results .Select(m => m.ContainingType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + "." + m.Name) .OrderBy(s => s) .ToArray(); Assert.Equal(expected, actual); }