Skip to content

Commit 7343692

Browse files
CopilotYunchuWang
andcommitted
Refactor code based on code review feedback
Extract helper methods IsActivityMethod, IsOrchestratorMethod, and ImplementsInterface to reduce code duplication. Move system assembly names to static readonly collections. Optimize LINQ chain in ScanReferencedAssemblies to use simple foreach loop. Co-authored-by: YunchuWang <[email protected]>
1 parent 8b7b23e commit 7343692

File tree

1 file changed

+94
-30
lines changed

1 file changed

+94
-30
lines changed

src/Analyzers/Activities/FunctionNotFoundAnalyzer.cs

Lines changed: 94 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
namespace Microsoft.DurableTask.Analyzers.Activities;
1212

13+
/// <summary>
14+
/// Analyzer that detects calls to non-existent activities and sub-orchestrations.
1315
/// <summary>
1416
/// Analyzer that detects calls to non-existent activities and sub-orchestrations.
1517
/// </summary>
@@ -26,6 +28,22 @@ public sealed class FunctionNotFoundAnalyzer : DiagnosticAnalyzer
2628
/// </summary>
2729
public const string SubOrchestrationNotFoundDiagnosticId = "DURABLE2004";
2830

31+
// System assemblies to skip when scanning referenced assemblies for performance
32+
static readonly HashSet<string> SystemAssemblyNames =
33+
[
34+
"mscorlib",
35+
"System",
36+
"netstandard"
37+
];
38+
39+
static readonly string[] SystemAssemblyPrefixes =
40+
[
41+
"System.",
42+
"Microsoft.CodeAnalysis",
43+
"Microsoft.CSharp",
44+
"Microsoft.VisualBasic"
45+
];
46+
2947
static readonly LocalizableString ActivityNotFoundTitle = new LocalizableResourceString(nameof(Resources.ActivityNotFoundAnalyzerTitle), Resources.ResourceManager, typeof(Resources));
3048
static readonly LocalizableString ActivityNotFoundMessageFormat = new LocalizableResourceString(nameof(Resources.ActivityNotFoundAnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources));
3149

@@ -129,19 +147,13 @@ public override void Initialize(AnalysisContext context)
129147
}
130148

131149
// Check for Activity defined via [ActivityTrigger]
132-
if (knownSymbols.ActivityTriggerAttribute != null &&
133-
methodSymbol.ContainsAttributeInAnyMethodArguments(knownSymbols.ActivityTriggerAttribute) &&
134-
knownSymbols.FunctionNameAttribute != null &&
135-
methodSymbol.TryGetSingleValueFromAttribute(knownSymbols.FunctionNameAttribute, out string functionName))
150+
if (IsActivityMethod(methodSymbol, knownSymbols, out string functionName))
136151
{
137152
activityNames.Add(functionName);
138153
}
139154

140155
// Check for Orchestrator defined via [OrchestrationTrigger]
141-
if (knownSymbols.FunctionOrchestrationAttribute != null &&
142-
methodSymbol.ContainsAttributeInAnyMethodArguments(knownSymbols.FunctionOrchestrationAttribute) &&
143-
knownSymbols.FunctionNameAttribute != null &&
144-
methodSymbol.TryGetSingleValueFromAttribute(knownSymbols.FunctionNameAttribute, out string orchestratorFunctionName))
156+
if (IsOrchestratorMethod(methodSymbol, knownSymbols, out string orchestratorFunctionName))
145157
{
146158
orchestratorNames.Add(orchestratorFunctionName);
147159
}
@@ -173,7 +185,7 @@ public override void Initialize(AnalysisContext context)
173185

174186
// Check for ITaskOrchestrator implementations (class-based orchestrators)
175187
if (knownSymbols.TaskOrchestratorInterface != null &&
176-
classSymbol.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, knownSymbols.TaskOrchestratorInterface)))
188+
ImplementsInterface(classSymbol, knownSymbols.TaskOrchestratorInterface))
177189
{
178190
orchestratorNames.Add(classSymbol.Name);
179191
}
@@ -281,6 +293,49 @@ public override void Initialize(AnalysisContext context)
281293
return constant.Value?.ToString();
282294
}
283295

296+
static bool IsActivityMethod(IMethodSymbol methodSymbol, KnownTypeSymbols knownSymbols, out string functionName)
297+
{
298+
functionName = string.Empty;
299+
300+
if (knownSymbols.ActivityTriggerAttribute == null ||
301+
!methodSymbol.ContainsAttributeInAnyMethodArguments(knownSymbols.ActivityTriggerAttribute))
302+
{
303+
return false;
304+
}
305+
306+
if (knownSymbols.FunctionNameAttribute == null ||
307+
!methodSymbol.TryGetSingleValueFromAttribute(knownSymbols.FunctionNameAttribute, out functionName))
308+
{
309+
return false;
310+
}
311+
312+
return true;
313+
}
314+
315+
static bool IsOrchestratorMethod(IMethodSymbol methodSymbol, KnownTypeSymbols knownSymbols, out string functionName)
316+
{
317+
functionName = string.Empty;
318+
319+
if (knownSymbols.FunctionOrchestrationAttribute == null ||
320+
!methodSymbol.ContainsAttributeInAnyMethodArguments(knownSymbols.FunctionOrchestrationAttribute))
321+
{
322+
return false;
323+
}
324+
325+
if (knownSymbols.FunctionNameAttribute == null ||
326+
!methodSymbol.TryGetSingleValueFromAttribute(knownSymbols.FunctionNameAttribute, out functionName))
327+
{
328+
return false;
329+
}
330+
331+
return true;
332+
}
333+
334+
static bool ImplementsInterface(INamedTypeSymbol typeSymbol, INamedTypeSymbol interfaceSymbol)
335+
{
336+
return typeSymbol.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, interfaceSymbol));
337+
}
338+
284339
static bool ClassOverridesMethod(INamedTypeSymbol classSymbol, IMethodSymbol methodToFind)
285340
{
286341
INamedTypeSymbol? baseType = classSymbol;
@@ -308,14 +363,21 @@ static void ScanReferencedAssemblies(
308363
{
309364
// Scan all referenced assemblies for activities and orchestrators
310365
// Skip system assemblies for performance
311-
foreach (IAssemblySymbol assembly in compilation.References
312-
.Select(r => compilation.GetAssemblyOrModuleSymbol(r))
313-
.OfType<IAssemblySymbol>()
314-
.Where(a => !IsSystemAssembly(a)))
366+
foreach (MetadataReference reference in compilation.References)
315367
{
316368
cancellationToken.ThrowIfCancellationRequested();
317369

318-
// Get all types in the assembly
370+
if (compilation.GetAssemblyOrModuleSymbol(reference) is not IAssemblySymbol assembly)
371+
{
372+
continue;
373+
}
374+
375+
if (IsSystemAssembly(assembly))
376+
{
377+
continue;
378+
}
379+
380+
// Scan this assembly
319381
ScanNamespaceForFunctions(
320382
assembly.GlobalNamespace,
321383
knownSymbols,
@@ -330,13 +392,21 @@ static bool IsSystemAssembly(IAssemblySymbol assembly)
330392
{
331393
// Skip well-known system assemblies to improve performance
332394
string assemblyName = assembly.Name;
333-
return assemblyName == "mscorlib" ||
334-
assemblyName == "System" ||
335-
assemblyName == "netstandard" ||
336-
assemblyName.StartsWith("System.", StringComparison.Ordinal) ||
337-
assemblyName.StartsWith("Microsoft.CodeAnalysis", StringComparison.Ordinal) ||
338-
assemblyName.StartsWith("Microsoft.CSharp", StringComparison.Ordinal) ||
339-
assemblyName.StartsWith("Microsoft.VisualBasic", StringComparison.Ordinal);
395+
396+
if (SystemAssemblyNames.Contains(assemblyName))
397+
{
398+
return true;
399+
}
400+
401+
foreach (string prefix in SystemAssemblyPrefixes)
402+
{
403+
if (assemblyName.StartsWith(prefix, StringComparison.Ordinal))
404+
{
405+
return true;
406+
}
407+
}
408+
409+
return false;
340410
}
341411

342412
static void ScanNamespaceForFunctions(
@@ -365,7 +435,7 @@ static void ScanNamespaceForFunctions(
365435

366436
// Check for ITaskOrchestrator implementations (class-based orchestrators)
367437
if (knownSymbols.TaskOrchestratorInterface != null &&
368-
typeSymbol.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, knownSymbols.TaskOrchestratorInterface)))
438+
ImplementsInterface(typeSymbol, knownSymbols.TaskOrchestratorInterface))
369439
{
370440
orchestratorNames.Add(typeSymbol.Name);
371441
}
@@ -381,19 +451,13 @@ static void ScanNamespaceForFunctions(
381451
}
382452

383453
// Check for Activity defined via [ActivityTrigger]
384-
if (knownSymbols.ActivityTriggerAttribute != null &&
385-
methodSymbol.ContainsAttributeInAnyMethodArguments(knownSymbols.ActivityTriggerAttribute) &&
386-
knownSymbols.FunctionNameAttribute != null &&
387-
methodSymbol.TryGetSingleValueFromAttribute(knownSymbols.FunctionNameAttribute, out string functionName))
454+
if (IsActivityMethod(methodSymbol, knownSymbols, out string functionName))
388455
{
389456
activityNames.Add(functionName);
390457
}
391458

392459
// Check for Orchestrator defined via [OrchestrationTrigger]
393-
if (knownSymbols.FunctionOrchestrationAttribute != null &&
394-
methodSymbol.ContainsAttributeInAnyMethodArguments(knownSymbols.FunctionOrchestrationAttribute) &&
395-
knownSymbols.FunctionNameAttribute != null &&
396-
methodSymbol.TryGetSingleValueFromAttribute(knownSymbols.FunctionNameAttribute, out string orchestratorFunctionName))
460+
if (IsOrchestratorMethod(methodSymbol, knownSymbols, out string orchestratorFunctionName))
397461
{
398462
orchestratorNames.Add(orchestratorFunctionName);
399463
}

0 commit comments

Comments
 (0)