Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Analyzer to detect missing ASP.NET Core integration registration #1917

Merged
merged 48 commits into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
6301376
Analyzer
surgupta-msft Sep 20, 2023
c50dfe9
Add analyzer
surgupta-msft Sep 20, 2023
2d5b70b
Fix test
surgupta-msft Sep 20, 2023
4819a5d
Adding docs for Analyzer
surgupta-msft Sep 20, 2023
047edbb
Added tests for aspnet analyzer
surgupta-msft Sep 20, 2023
d9c1665
Add more tests and code cleanup
surgupta-msft Sep 20, 2023
cb41c98
Addressing PR feedback
surgupta-msft Sep 21, 2023
2bb5684
Adding check for Main method
surgupta-msft Sep 25, 2023
5309d78
Merge branch 'main' into surgupta/aspnet-analyzer
surgupta-msft Sep 25, 2023
bfd7b3a
Updated analyzer check
surgupta-msft Sep 27, 2023
fb93b38
Making Analyzer as part of AspNet package
surgupta-msft Sep 27, 2023
ba7f0b0
Add analyzer as part of AspNet project
surgupta-msft Oct 3, 2023
18edbcf
Update sdk analyzer diagnostics
surgupta-msft Oct 3, 2023
b097472
Updating version
surgupta-msft Oct 3, 2023
23f0aac
Minor updates
surgupta-msft Oct 3, 2023
7d460f3
Minor csproj cleanup
surgupta-msft Oct 3, 2023
1b35d92
Adding a new project for AspNet analyzers
surgupta-msft Oct 4, 2023
6d2b485
Fix formatting in csproj file
surgupta-msft Oct 4, 2023
a2ba972
Removing unecessary check
surgupta-msft Oct 4, 2023
eff85a8
Merge branch 'main' into surgupta/aspnet-analyzer
surgupta-msft Oct 4, 2023
a03e4a2
Test fix
surgupta-msft Oct 4, 2023
90827fa
Revert "Test fix"
surgupta-msft Oct 4, 2023
7868a53
Test fix
surgupta-msft Oct 4, 2023
aa2adfc
Moving aspnet projects in a folder
surgupta-msft Oct 6, 2023
1cbf4f7
Revert "Moving aspnet projects in a folder"
surgupta-msft Oct 6, 2023
b9f2351
Fix typo
surgupta-msft Oct 6, 2023
0fcd2fd
Set diagnostic warning
surgupta-msft Oct 6, 2023
ba6aa87
Adding new tests project for aspnet core
surgupta-msft Oct 6, 2023
55c1d3e
Added release notes
surgupta-msft Oct 6, 2023
c7377e4
Updated analyzer rule doc
surgupta-msft Oct 6, 2023
1f847ca
Removing tools files
surgupta-msft Oct 10, 2023
6b81e33
Rebase with main
surgupta-msft Oct 17, 2023
a0cf107
PR cleanup
surgupta-msft Oct 17, 2023
028bfce
PR cleanup
surgupta-msft Oct 17, 2023
226e3b4
Format csproj
surgupta-msft Oct 17, 2023
7e00672
Revert "Format csproj"
surgupta-msft Oct 17, 2023
b3b2a09
build fix
surgupta-msft Oct 17, 2023
1e40912
Rebase with main
surgupta-msft Oct 18, 2023
2da778f
Update sln with project
surgupta-msft Oct 18, 2023
54ae68d
Update sln
surgupta-msft Oct 18, 2023
7b0e909
Update sln
surgupta-msft Oct 18, 2023
4b0c3ce
Updating name to ASP.Net Core Integration
surgupta-msft Oct 18, 2023
6b84c1d
Merge branch 'main' into surgupta/aspnet-analyzer
surgupta-msft Oct 24, 2023
4d66267
Updated condition to generate diagnostic
surgupta-msft Oct 24, 2023
529c4c8
Minor cleanup
surgupta-msft Oct 24, 2023
03f59bc
Docs update, minor test update
surgupta-msft Oct 24, 2023
20abb2f
Narrowing symbol location
surgupta-msft Oct 24, 2023
24bcd68
address PR comments
surgupta-msft Oct 25, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions DotNetWorker.sln
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DependentAssemblyWithFuncti
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Worker.Extensions.Http.AspNetCore.Tests", "test\extensions\Worker.Extensions.Http.AspNetCore.Tests\Worker.Extensions.Http.AspNetCore.Tests.csproj", "{D8E79B53-9A44-46CC-9D7A-E9494FC8CAF4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Worker.Extensions.Http.AspNetCore.Analyzers", "extensions\Worker.Extensions.Http.AspNetCore.Analyzers\Worker.Extensions.Http.AspNetCore.Analyzers.csproj", "{7B6C2920-7A02-43B2-8DA0-7B76B9FAFC6B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -334,6 +336,10 @@ Global
{AB6E1E7A-0D2C-4086-9487-566887C1E753}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AB6E1E7A-0D2C-4086-9487-566887C1E753}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AB6E1E7A-0D2C-4086-9487-566887C1E753}.Release|Any CPU.Build.0 = Release|Any CPU
{7B6C2920-7A02-43B2-8DA0-7B76B9FAFC6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7B6C2920-7A02-43B2-8DA0-7B76B9FAFC6B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7B6C2920-7A02-43B2-8DA0-7B76B9FAFC6B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7B6C2920-7A02-43B2-8DA0-7B76B9FAFC6B}.Release|Any CPU.Build.0 = Release|Any CPU
{D8E79B53-9A44-46CC-9D7A-E9494FC8CAF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D8E79B53-9A44-46CC-9D7A-E9494FC8CAF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D8E79B53-9A44-46CC-9D7A-E9494FC8CAF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -397,6 +403,7 @@ Global
{D2F67410-9933-42E8-B04A-E17634D83A30} = {9D6603BD-7EA2-4D11-A69C-0D9E01317FD6}
{AB6E1E7A-0D2C-4086-9487-566887C1E753} = {B5821230-6E0A-4535-88A9-ED31B6F07596}
{D8E79B53-9A44-46CC-9D7A-E9494FC8CAF4} = {AA4D318D-101B-49E7-A4EC-B34165AEDB33}
{7B6C2920-7A02-43B2-8DA0-7B76B9FAFC6B} = {A7B4FF1E-3DF7-4F28-9333-D0961CDDF702}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {497D2ED4-A13E-4BCA-8D29-F30CA7D0EA4A}
Expand Down
25 changes: 25 additions & 0 deletions docs/analyzer-rules/AZFW0014.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# AZFW0011: Missing Registration for ASP.NET Core Integration

| | Value |
|-|-|
| **Rule ID** |AZFW00014|
| **Category** |[Usage]|
| **Severity** |Error|

## Cause

This rule is triggered when worker using the ASP.NET Core Integration uses `ConfigureFunctionsWorkerDefaults()`.

## Rule description

When using the ASP.NET Core Integration, the worker must configure the worker using the `ConfigureFunctionsWebApplication()` method. This rule will be violated when the worker uses `ConfigureFunctionsWorkerDefaults()`.


## How to fix violations
surgupta-msft marked this conversation as resolved.
Show resolved Hide resolved

Replace usage of `ConfigureFunctionsWorkerDefaults` with `ConfigureFunctionsWebApplication` method in the Function App. Refer to [public documentation](https://learn.microsoft.com/en-us/azure/azure-functions/dotnet-isolated-process-guide#aspnet-core-integration) for guidance on ASP.NET Core Integration.


## When to suppress

It is okay to suppress this if `ConfigureFunctionsWebApplication` is called indirectly through a different API.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using Microsoft.CodeAnalysis;

namespace Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore
{
internal class DiagnosticDescriptors
{
public const string Usage = "Usage";

private static DiagnosticDescriptor Create(string id, string title, string messageFormat, string category, DiagnosticSeverity severity)
{
var helpLink = $"https://aka.ms/azfw-rules?ruleid={id}";
return new DiagnosticDescriptor(id, title, messageFormat, category, severity, isEnabledByDefault: true, helpLinkUri: helpLink);
}

public static DiagnosticDescriptor CorrectRegistrationExpectedInAspNetIntegration { get; }
= Create(id: "AZFW0014", title: "Missing expected registration of ASP.NET Core Integration services", messageFormat: "The registration for method '{0}' is expected for ASP.NET Core Integration.",
category: Usage, severity: DiagnosticSeverity.Error);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore
{
/// <summary>
/// Analyzer to verify whether expected registration is present for ASP.NET Core Integration.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class RegistrationExpectedInASPNetIntegration : DiagnosticAnalyzer
surgupta-msft marked this conversation as resolved.
Show resolved Hide resolved
{
/// Diagnostics supported by the analyzer
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(DiagnosticDescriptors.CorrectRegistrationExpectedInAspNetIntegration);

private const string ExpectedRegistrationMethod = "ConfigureFunctionsWebApplication";
private const string IncorrectRegistrationMethod = "ConfigureFunctionsWorkerDefaults";

/// Initialization method
public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);
context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method);
}

private static void AnalyzeMethod(SymbolAnalysisContext context)
{
var symbol = (IMethodSymbol)context.Symbol;

if (!IsMainMethod(symbol))
{
return;
}

var syntaxReference = symbol.DeclaringSyntaxReferences.FirstOrDefault();
var root = syntaxReference?.SyntaxTree.GetRoot();
surgupta-msft marked this conversation as resolved.
Show resolved Hide resolved
var methodCallExpressions = root?.DescendantNodes().OfType<InvocationExpressionSyntax>();

var expectedMethodInvocationPresent = methodCallExpressions?.Any(invocation => (invocation.Expression as MemberAccessExpressionSyntax)?.Name.Identifier.ValueText == ExpectedRegistrationMethod);
surgupta-msft marked this conversation as resolved.
Show resolved Hide resolved

var incorrectMethodCallExpressions = methodCallExpressions?.Where(invocation => (invocation.Expression as MemberAccessExpressionSyntax)?.Name.Identifier.ValueText == IncorrectRegistrationMethod);
var incorrectMethodInvocationPresent = incorrectMethodCallExpressions?.Any();

if (!(bool)incorrectMethodInvocationPresent)
surgupta-msft marked this conversation as resolved.
Show resolved Hide resolved
{
return;
}

//Finding exact location of method call
Location location = GetSymbolLocation(root, incorrectMethodCallExpressions);

if (!(bool)expectedMethodInvocationPresent)
{
var diagnostic = Diagnostic.Create(DiagnosticDescriptors.CorrectRegistrationExpectedInAspNetIntegration, location, ExpectedRegistrationMethod);
context.ReportDiagnostic(diagnostic);
}
}

// Checks if a method symbol is a Main method. This also checks for implicit main in top-level statements
private static bool IsMainMethod(IMethodSymbol symbol)
{
return symbol?.IsStatic == true && symbol.Name switch
{
"Main" => true,
"$Main" => true,
"<Main>$" => true,
_ => false
};
}

private static Location GetSymbolLocation(SyntaxNode root, IEnumerable<InvocationExpressionSyntax> methodCallExpressions)
{
Location location = Location.None;

if (methodCallExpressions != null)
{
var lineSpan = methodCallExpressions.FirstOrDefault().GetLocation().SourceSpan;
var node = root.DescendantNodes(lineSpan)
.First(n => lineSpan.Contains(n.FullSpan)).DescendantNodes()
.OfType<IdentifierNameSyntax>().FirstOrDefault(c => c.Identifier.Text == IncorrectRegistrationMethod);

location = node.GetLocation();
}

return location;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<VersionPrefix>1.0.0</VersionPrefix>
<OutputType>Library</OutputType>
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
<IncludeBuildOutput>false</IncludeBuildOutput>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageId>Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore.Analyzers</PackageId>
surgupta-msft marked this conversation as resolved.
Show resolved Hide resolved
surgupta-msft marked this conversation as resolved.
Show resolved Hide resolved
<Description>This package provides development time code analysis for ASP.NET Core extensions for .NET isolated functions.</Description>
<AssemblyName>Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore.Analyzers</AssemblyName>
<RootNamespace>Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore.Analyzers</RootNamespace>
<Nullable>disable</Nullable>
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);_AddAnalyzersToOutput</TargetsForTfmSpecificContentInPackage>
</PropertyGroup>

<Import Project="..\..\build\Common.props" />

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.11.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="1.1.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="1.1.1" />
</ItemGroup>

<Target Name="_AddAnalyzersToOutput">
<ItemGroup>
<TfmSpecificPackageFile Include="$(OutputPath)\Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore.Analyzers.dll" PackagePath="analyzers/dotnet/cs" />
</ItemGroup>
</Target>
</Project>
4 changes: 4 additions & 0 deletions extensions/Worker.Extensions.Http.AspNetCore/release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@
### Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore <version>

- New overload added to `ConfigureFunctionsWebApplication` to take a `HostBuilderContext` (#1925). Thank you @vmcbaptista

### Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore.Analyzers

- Analyzer to detect missing ASP.NET Core Integration registration (#1917)
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<ProjectReference Include="..\..\..\src\DotNetWorker\DotNetWorker.csproj" />
<ProjectReference Include="..\..\Worker.Extensions.Abstractions\src\Worker.Extensions.Abstractions.csproj" />
<ProjectReference Include="..\..\Worker.Extensions.Http\src\Worker.Extensions.Http.csproj" />
<ProjectReference Include="..\..\Worker.Extensions.Http.AspNetCore.Analyzers\Worker.Extensions.Http.AspNetCore.Analyzers.csproj" />
</ItemGroup>

</Project>
Loading