You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/Analyzers/CONTRIBUTING.md
+184Lines changed: 184 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -43,6 +43,190 @@ The following resources are useful to start developing Roslyn Analyzers and unde
43
43
-[Roslyn analyzers and code-aware library for ImmutableArrays](https://learn.microsoft.com/en-us/visualstudio/extensibility/roslyn-analyzers-and-code-aware-library-for-immutablearrays?view=vs-2022)
Many of the orchestration analyzers need to understand which methods in a codebase are orchestration entry points and which methods are invoked by orchestrations (directly or indirectly). This section documents how orchestrations are discovered and how method call chains are analyzed.
49
+
50
+
#### How Orchestrations Are Discovered
51
+
52
+
The `OrchestrationAnalyzer<TOrchestrationVisitor>` base class provides a unified framework for discovering orchestrations across three different patterns:
53
+
54
+
##### 1. Durable Functions Orchestrations
55
+
56
+
Durable Functions orchestrations are discovered by looking for methods with the following characteristics:
57
+
- The method has a parameter decorated with `[OrchestrationTrigger]` attribute from `Microsoft.Azure.Functions.Worker` namespace
58
+
- The method or class has a `[Function]` attribute with a function name
The analyzer registers a `SyntaxNodeAction` for `SyntaxKind.MethodDeclaration` and checks if the method symbol contains the `OrchestrationTriggerAttribute` in any of its parameters.
70
+
71
+
##### 2. TaskOrchestrator Orchestrations
72
+
73
+
TaskOrchestrator orchestrations are discovered by looking for classes that:
74
+
- Implement the `ITaskOrchestrator` interface, or
75
+
- Inherit from the `TaskOrchestrator<TInput, TOutput>` base class
The analyzer registers a `SyntaxNodeAction` for `SyntaxKind.ClassDeclaration` and checks if the class implements `ITaskOrchestrator`. It then looks for methods that have a `TaskOrchestrationContext` parameter.
89
+
90
+
##### 3. Func Orchestrations
91
+
92
+
Func orchestrations are discovered by looking for calls to `DurableTaskRegistry.AddOrchestratorFunc()` extension methods where an orchestration is registered as a lambda or method reference.
The analyzer registers an `OperationAction` for `OperationKind.Invocation` and checks if:
104
+
- The invocation returns a `DurableTaskRegistry` type
105
+
- The method name is `AddOrchestratorFunc`
106
+
- The `orchestrator` parameter is a delegate (lambda or method reference)
107
+
108
+
#### How Method Probing Works
109
+
110
+
The `MethodProbeOrchestrationVisitor` class implements recursive method call analysis to detect violations in methods invoked by orchestrations. This is crucial because non-deterministic code can exist not just in the orchestration entry point, but also in helper methods called by the orchestration.
111
+
112
+
##### Probing Algorithm
113
+
114
+
1.**Entry Point**: When an orchestration is discovered, the visitor starts analyzing from the orchestration's root method.
115
+
116
+
2.**Recursive Traversal**: For each method:
117
+
- The method is added to a dictionary that tracks which orchestrations invoke it
118
+
- All `InvocationExpressionSyntax` nodes within the method body are examined
119
+
- For each invocation, the target method is identified via semantic analysis
120
+
- The target method is recursively analyzed with the same orchestration name
121
+
122
+
3.**Cycle Detection**: The analyzer maintains a `ConcurrentDictionary<IMethodSymbol, ConcurrentBag<string>>` that maps methods to the orchestrations that invoke them. Before analyzing a method for a specific orchestration, it checks if that combination has already been processed to prevent infinite recursion.
123
+
124
+
4.**Cross-Tree Analysis**: When a called method is defined in a different syntax tree (e.g., in another file or partial class), the analyzer obtains the correct `SemanticModel` for that syntax tree to continue analysis.
125
+
126
+
##### Probing Capabilities
127
+
128
+
The method probing can follow:
129
+
-**Direct method calls**: `Helper()` where `Helper` is a concrete method
-**Async methods**: Methods returning `Task` or `ValueTask`
134
+
-**Lambda expressions**: Inline lambdas and local functions within orchestrations
135
+
-**Method references**: Delegates created from method references
136
+
-**Partial classes**: Methods defined across multiple files via partial class declarations
137
+
-**Recursive methods**: Protected against infinite loops via cycle detection
138
+
139
+
##### Probing Limitations
140
+
141
+
The method probing **cannot** follow method calls in the following scenarios:
142
+
143
+
1.**Interface Method Calls**: When calling a method through an interface reference, the analyzer cannot determine which implementation will be invoked at runtime.
6.**Dependency Injection**: When methods are invoked on instances resolved via DI containers, the concrete type is not known at analysis time.
184
+
185
+
```cs
186
+
// Will NOT be analyzed if injected
187
+
publicMyOrchestrator(IServiceservice)
188
+
{
189
+
service.Process(); // analyzer cannot determine concrete type
190
+
}
191
+
```
192
+
193
+
##### Diagnostic Messages
194
+
195
+
When a violation is detected in a helper method, the diagnostic message explicitly indicates:
196
+
- The **method** where the violation occurs
197
+
- The **specific violation** (e.g., `System.DateTime.Now`)
198
+
- The **orchestration** that invokes the method
199
+
200
+
**Example diagnostic:**
201
+
```
202
+
"The method 'HelperMethod' uses 'System.DateTime.Now' that may cause non-deterministic behavior when invoked from orchestration 'MyOrchestrator'"
203
+
```
204
+
205
+
This makes it clear that the issue is not with `HelperMethod` itself, but with its invocation from an orchestration context.
206
+
207
+
##### Consistency Across Analyzers
208
+
209
+
All orchestration analyzers that extend `OrchestrationAnalyzer<TOrchestrationVisitor>` should use the same method probing behavior by deriving their visitor from `MethodProbeOrchestrationVisitor`. This ensures consistent behavior across all orchestration-related diagnostics.
0 commit comments