From 14a8abf8455b1b054646152989edda18fc925e62 Mon Sep 17 00:00:00 2001
From: Allan Targino <13934447+allantargino@users.noreply.github.com>
Date: Mon, 6 May 2024 10:22:04 -0300
Subject: [PATCH 1/3] azure function orchestration bindings analyzer
---
src/Analyzers/AnalyzerReleases.Unshipped.md | 1 +
.../OtherBindingsOrchestrationAnalyzer.cs | 73 ++++++++++++++++
src/Analyzers/Resources.resx | 6 ++
.../OtherBindingsOrchestrationAnalyzer.cs | 86 +++++++++++++++++++
4 files changed, 166 insertions(+)
create mode 100644 src/Analyzers/Functions/Orchestration/OtherBindingsOrchestrationAnalyzer.cs
create mode 100644 test/Analyzers.Tests/Functions/Orchestration/OtherBindingsOrchestrationAnalyzer.cs
diff --git a/src/Analyzers/AnalyzerReleases.Unshipped.md b/src/Analyzers/AnalyzerReleases.Unshipped.md
index bf14e3b2..690cd6a4 100644
--- a/src/Analyzers/AnalyzerReleases.Unshipped.md
+++ b/src/Analyzers/AnalyzerReleases.Unshipped.md
@@ -9,6 +9,7 @@ DURABLE0001 | Orchestration | Warning | DateTimeOrchestrationAnalyzer
DURABLE0002 | Orchestration | Warning | GuidOrchestrationAnalyzer
DURABLE0003 | Orchestration | Warning | DelayOrchestrationAnalyzer
DURABLE0007 | Orchestration | Warning | CancellationTokenOrchestrationAnalyzer
+DURABLE0008 | Orchestration | Warning | OtherBindingsOrchestrationAnalyzer
DURABLE1001 | Attribute Binding | Error | OrchestrationTriggerBindingAnalyzer
DURABLE1002 | Attribute Binding | Error | DurableClientBindingAnalyzer
DURABLE1003 | Attribute Binding | Error | EntityTriggerBindingAnalyzer
\ No newline at end of file
diff --git a/src/Analyzers/Functions/Orchestration/OtherBindingsOrchestrationAnalyzer.cs b/src/Analyzers/Functions/Orchestration/OtherBindingsOrchestrationAnalyzer.cs
new file mode 100644
index 00000000..9092e25a
--- /dev/null
+++ b/src/Analyzers/Functions/Orchestration/OtherBindingsOrchestrationAnalyzer.cs
@@ -0,0 +1,73 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.DurableTask.Analyzers.Orchestration;
+using static Microsoft.DurableTask.Analyzers.Functions.Orchestration.OtherBindingsOrchestrationAnalyzer;
+
+namespace Microsoft.DurableTask.Analyzers.Functions.Orchestration;
+
+///
+/// Analyzer that reports a warning when a Durable Function Orchestration has parameters bindings other than OrchestrationTrigger.
+///
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
+class OtherBindingsOrchestrationAnalyzer : OrchestrationAnalyzer
+{
+ ///
+ /// Diagnostic ID supported for the analyzer.
+ ///
+ public const string DiagnosticId = "DURABLE0008";
+
+ static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.OtherBindingsOrchestrationAnalyzerTitle), Resources.ResourceManager, typeof(Resources));
+ static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.OtherBindingsOrchestrationAnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources));
+
+ static readonly DiagnosticDescriptor Rule = new(
+ DiagnosticId,
+ Title,
+ MessageFormat,
+ AnalyzersCategories.Orchestration,
+ DiagnosticSeverity.Warning,
+ isEnabledByDefault: true);
+
+ ///
+ public override ImmutableArray SupportedDiagnostics => [Rule];
+
+ ///
+ /// Visitor that inspects Durable Functions's method signatures for parameters binding other than OrchestrationTrigger.
+ ///
+ public sealed class OtherBindingsOrchestrationOrchestrationVisitor : OrchestrationVisitor
+ {
+ ImmutableArray bannedBindings;
+
+ ///
+ public override bool Initialize()
+ {
+ List candidateSymbols = [
+ this.KnownTypeSymbols.DurableClientAttribute,
+ this.KnownTypeSymbols.EntityTriggerAttribute,
+ ];
+
+ // filter out null values, since some of them may not be available during compilation
+ this.bannedBindings = candidateSymbols.Where(s => s != null).ToImmutableArray();
+
+ return this.bannedBindings.Length > 0;
+ }
+
+ ///
+ public override void VisitDurableFunction(SemanticModel sm, MethodDeclarationSyntax methodSyntax, IMethodSymbol methodSymbol, string orchestrationName, Action reportDiagnostic)
+ {
+ foreach (IParameterSymbol parameter in methodSymbol.Parameters)
+ {
+ IEnumerable attributesSymbols = parameter.GetAttributes().Select(att => att.AttributeClass);
+
+ if (attributesSymbols.Any(att => att != null && this.bannedBindings.Contains(att, SymbolEqualityComparer.Default)))
+ {
+ reportDiagnostic(RoslynExtensions.BuildDiagnostic(Rule, parameter, orchestrationName));
+ }
+ }
+ }
+ }
+}
diff --git a/src/Analyzers/Resources.resx b/src/Analyzers/Resources.resx
index 5e5da2cf..7f3481a0 100644
--- a/src/Analyzers/Resources.resx
+++ b/src/Analyzers/Resources.resx
@@ -159,4 +159,10 @@
CancellationToken should not be used as an orchestrator function parameter
+
+ Orchestration '{0}' is using multiple bindings
+
+
+ OrchestrationTrigger methods must not use any other bindings
+
\ No newline at end of file
diff --git a/test/Analyzers.Tests/Functions/Orchestration/OtherBindingsOrchestrationAnalyzer.cs b/test/Analyzers.Tests/Functions/Orchestration/OtherBindingsOrchestrationAnalyzer.cs
new file mode 100644
index 00000000..9c048eab
--- /dev/null
+++ b/test/Analyzers.Tests/Functions/Orchestration/OtherBindingsOrchestrationAnalyzer.cs
@@ -0,0 +1,86 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using Microsoft.CodeAnalysis.Testing;
+using Microsoft.DurableTask.Analyzers.Functions.Orchestration;
+using VerifyCS = Microsoft.DurableTask.Analyzers.Tests.Verifiers.CSharpAnalyzerVerifier;
+
+namespace Microsoft.DurableTask.Analyzers.Tests.Functions.Orchestration;
+
+public class OtherBindingsOrchestrationAnalyzerTests
+{
+ [Fact]
+ public async Task EmptyCodeHasNoDiag()
+ {
+ string code = @"";
+
+ await VerifyCS.VerifyDurableTaskAnalyzerAsync(code);
+ }
+
+ [Fact]
+ public async Task DurableFunctionOrchestrationWithNoBannedBindingHasNoDiag()
+ {
+ string code = Wrapper.WrapDurableFunctionOrchestration(@"
+[Function(""Run"")]
+void Method([OrchestrationTrigger] TaskOrchestrationContext context)
+{
+}
+");
+
+ await VerifyCS.VerifyDurableTaskAnalyzerAsync(code);
+ }
+
+ [Fact]
+ public async Task DurableFunctionOrchestrationUsingDurableClientHasDiag()
+ {
+ string code = Wrapper.WrapDurableFunctionOrchestration(@"
+[Function(""Run"")]
+void Method([OrchestrationTrigger] TaskOrchestrationContext context, {|#0:[DurableClient] DurableTaskClient client|})
+{
+}
+");
+
+ DiagnosticResult expected = BuildDiagnostic().WithLocation(0).WithArguments("Run");
+
+ await VerifyCS.VerifyDurableTaskAnalyzerAsync(code, expected);
+ }
+
+ [Fact]
+ public async Task DurableFunctionOrchestrationUsingEntityTriggerHasDiag()
+ {
+ string code = Wrapper.WrapDurableFunctionOrchestration(@"
+[Function(""Run"")]
+void Method([OrchestrationTrigger] TaskOrchestrationContext context, {|#0:[EntityTrigger] TaskEntityDispatcher dispatcher|})
+{
+}
+");
+
+ DiagnosticResult expected = BuildDiagnostic().WithLocation(0).WithArguments("Run");
+
+ await VerifyCS.VerifyDurableTaskAnalyzerAsync(code, expected);
+ }
+
+
+ [Fact]
+ public async Task DurableFunctionOrchestrationUsingMultipleBannedBindingsHasDiag()
+ {
+ string code = Wrapper.WrapDurableFunctionOrchestration(@"
+[Function(""Run"")]
+void Method([OrchestrationTrigger] TaskOrchestrationContext context,
+{|#0:[EntityTrigger] TaskEntityDispatcher dispatcher|},
+{|#1:[DurableClient] DurableTaskClient client|})
+{
+}
+");
+
+ DiagnosticResult[] expected = Enumerable.Range(0, 2).Select(
+ i => BuildDiagnostic().WithLocation(i).WithArguments("Run")).ToArray();
+
+ await VerifyCS.VerifyDurableTaskAnalyzerAsync(code, expected);
+ }
+
+ static DiagnosticResult BuildDiagnostic()
+ {
+ return VerifyCS.Diagnostic(OtherBindingsOrchestrationAnalyzer.DiagnosticId);
+ }
+}
From fa4f30a560e83772d4c8dcd233c0627b6d240f2c Mon Sep 17 00:00:00 2001
From: Allan Targino <13934447+allantargino@users.noreply.github.com>
Date: Mon, 6 May 2024 16:35:45 -0300
Subject: [PATCH 2/3] fixing nullability warning
---
.../Orchestration/OtherBindingsOrchestrationAnalyzer.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/Analyzers/Functions/Orchestration/OtherBindingsOrchestrationAnalyzer.cs b/src/Analyzers/Functions/Orchestration/OtherBindingsOrchestrationAnalyzer.cs
index 9092e25a..495d8469 100644
--- a/src/Analyzers/Functions/Orchestration/OtherBindingsOrchestrationAnalyzer.cs
+++ b/src/Analyzers/Functions/Orchestration/OtherBindingsOrchestrationAnalyzer.cs
@@ -45,13 +45,13 @@ public sealed class OtherBindingsOrchestrationOrchestrationVisitor : Orchestrati
///
public override bool Initialize()
{
- List candidateSymbols = [
+ List candidateSymbols = [
this.KnownTypeSymbols.DurableClientAttribute,
this.KnownTypeSymbols.EntityTriggerAttribute,
];
// filter out null values, since some of them may not be available during compilation
- this.bannedBindings = candidateSymbols.Where(s => s != null).ToImmutableArray();
+ this.bannedBindings = candidateSymbols.Where(s => s != null).ToImmutableArray()!;
return this.bannedBindings.Length > 0;
}
From a571908cbcc4f82cbd2072ceb6e66329eec1874a Mon Sep 17 00:00:00 2001
From: Allan Targino <13934447+allantargino@users.noreply.github.com>
Date: Mon, 6 May 2024 17:11:11 -0300
Subject: [PATCH 3/3] Update
src/Analyzers/Functions/Orchestration/OtherBindingsOrchestrationAnalyzer.cs
Co-authored-by: Varshitha Bachu
---
.../Orchestration/OtherBindingsOrchestrationAnalyzer.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Analyzers/Functions/Orchestration/OtherBindingsOrchestrationAnalyzer.cs b/src/Analyzers/Functions/Orchestration/OtherBindingsOrchestrationAnalyzer.cs
index 495d8469..6cea911d 100644
--- a/src/Analyzers/Functions/Orchestration/OtherBindingsOrchestrationAnalyzer.cs
+++ b/src/Analyzers/Functions/Orchestration/OtherBindingsOrchestrationAnalyzer.cs
@@ -36,7 +36,7 @@ class OtherBindingsOrchestrationAnalyzer : OrchestrationAnalyzer SupportedDiagnostics => [Rule];
///
- /// Visitor that inspects Durable Functions's method signatures for parameters binding other than OrchestrationTrigger.
+ /// Visitor that inspects Durable Functions' method signatures for parameters binding other than OrchestrationTrigger.
///
public sealed class OtherBindingsOrchestrationOrchestrationVisitor : OrchestrationVisitor
{