-
Notifications
You must be signed in to change notification settings - Fork 10k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixes: #9281 - implement SignalR detection
This change adds detection of various SignalR configure-related calls to the startup analysis infrastructure. Also adds a shim that VS is going to call into to analyze the project pre-publish.
- Loading branch information
Showing
14 changed files
with
501 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Operations; | ||
|
||
namespace Microsoft.AspNetCore.Analyzers | ||
{ | ||
internal static class CompilationFeatureDetector | ||
{ | ||
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(); | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Collections.Generic; | ||
using Microsoft.CodeAnalysis; | ||
|
||
namespace Microsoft.AspNetCore.Analyzers | ||
{ | ||
internal class ConfigureMethodVisitor : SymbolVisitor | ||
{ | ||
public static List<IMethodSymbol> FindConfigureMethods(StartupSymbols symbols, IAssemblySymbol assembly) | ||
{ | ||
var visitor = new ConfigureMethodVisitor(symbols); | ||
visitor.Visit(assembly); | ||
return visitor._types; | ||
} | ||
|
||
private readonly StartupSymbols _symbols; | ||
private readonly List<IMethodSymbol> _types; | ||
|
||
private ConfigureMethodVisitor(StartupSymbols symbols) | ||
{ | ||
_symbols = symbols; | ||
_types = new List<IMethodSymbol>(); | ||
} | ||
|
||
public override void VisitAssembly(IAssemblySymbol symbol) | ||
{ | ||
Visit(symbol.GlobalNamespace); | ||
} | ||
|
||
public override void VisitNamespace(INamespaceSymbol symbol) | ||
{ | ||
foreach (var type in symbol.GetTypeMembers()) | ||
{ | ||
Visit(type); | ||
} | ||
|
||
foreach (var @namespace in symbol.GetNamespaceMembers()) | ||
{ | ||
Visit(@namespace); | ||
} | ||
} | ||
|
||
public override void VisitNamedType(INamedTypeSymbol symbol) | ||
{ | ||
foreach (var member in symbol.GetMembers()) | ||
{ | ||
Visit(member); | ||
} | ||
} | ||
|
||
public override void VisitMethod(IMethodSymbol symbol) | ||
{ | ||
if (StartupFacts.IsConfigure(_symbols, symbol)) | ||
{ | ||
_types.Add(symbol); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
namespace Microsoft.AspNetCore.Analyzers | ||
{ | ||
internal static class WellKnownFeatures | ||
{ | ||
public static readonly string SignalR = nameof(SignalR); | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
src/Analyzers/Analyzers/test/CompilationFeatureDetectorTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Analyzers.TestFiles.CompilationFeatureDetectorTest; | ||
using Microsoft.CodeAnalysis; | ||
using Xunit; | ||
|
||
namespace Microsoft.AspNetCore.Analyzers | ||
{ | ||
public class CompilationFeatureDetectorTest : AnalyzerTestBase | ||
{ | ||
[Fact] | ||
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); | ||
} | ||
|
||
[Theory] | ||
[InlineData(nameof(StartupWithUseSignalR))] | ||
[InlineData(nameof(StartupWithMapHub))] | ||
[InlineData(nameof(StartupWithMapBlazorHub))] | ||
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)); | ||
} | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
src/Analyzers/Analyzers/test/ConfigureMethodVisitorTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Microsoft.CodeAnalysis; | ||
using Xunit; | ||
|
||
namespace Microsoft.AspNetCore.Analyzers | ||
{ | ||
public class ConfigureMethodVisitorTest : AnalyzerTestBase | ||
{ | ||
[Fact] | ||
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); | ||
} | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
...lyzers/Analyzers/test/TestFiles/CompilationFeatureDetectorTest/StartupWithMapBlazorHub.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using Microsoft.AspNetCore.Builder; | ||
|
||
namespace Microsoft.AspNetCore.Analyzers.TestFiles.CompilationFeatureDetectorTest | ||
{ | ||
public class StartupWithMapBlazorHub | ||
{ | ||
public void Configure(IApplicationBuilder app) | ||
{ | ||
app.UseRouting(); | ||
|
||
app.UseEndpoints(endpoints => | ||
{ | ||
endpoints.MapBlazorHub(); | ||
}); | ||
} | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
src/Analyzers/Analyzers/test/TestFiles/CompilationFeatureDetectorTest/StartupWithMapHub.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
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 | ||
{ | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
...nalyzers/Analyzers/test/TestFiles/CompilationFeatureDetectorTest/StartupWithNoFeatures.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
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"); | ||
}); | ||
} | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
...nalyzers/Analyzers/test/TestFiles/CompilationFeatureDetectorTest/StartupWithUseSignalR.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using Microsoft.AspNetCore.Builder; | ||
|
||
namespace Microsoft.AspNetCore.Analyzers.TestFiles.CompilationFeatureDetectorTest | ||
{ | ||
public class StartupWithUseSignalR | ||
{ | ||
public void Configure(IApplicationBuilder app) | ||
{ | ||
app.UseSignalR(routes => | ||
{ | ||
}); | ||
} | ||
} | ||
} |
Oops, something went wrong.