feat(workflow): analyzer recognizes both StellaOps and Ablera workflow namespaces
The vendored copy of StellaOps.Workflow in Serdica uses a parallel namespace (Ablera.Serdica.Workflow.Abstractions). The analyzer now looks up well-known types in both namespaces and treats both assembly-name prefixes (StellaOps.Workflow.* and Ablera.Serdica.Workflow.*) as trusted leaves. Activation still requires the Abstractions assembly to be in the compilation; absent either namespace's IDeclarativeWorkflow<T>, the analyzer is a no-op. 20/20 analyzer tests still pass.
This commit is contained in:
@@ -1,17 +1,35 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Immutable;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
|
|
||||||
namespace StellaOps.Workflow.Analyzer;
|
namespace StellaOps.Workflow.Analyzer;
|
||||||
|
|
||||||
internal sealed class WorkflowWellKnownTypes
|
internal sealed class WorkflowWellKnownTypes
|
||||||
{
|
{
|
||||||
public const string TrustedAssemblyPrefix = "StellaOps.Workflow";
|
private static readonly string[] TrustedAssemblyPrefixes =
|
||||||
|
{
|
||||||
|
"StellaOps.Workflow",
|
||||||
|
"Ablera.Serdica.Workflow",
|
||||||
|
};
|
||||||
|
|
||||||
public INamedTypeSymbol DeclarativeWorkflowOfT { get; }
|
private static readonly string[] DeclarativeWorkflowMetadataNames =
|
||||||
public INamedTypeSymbol WorkflowSpecOfT { get; }
|
{
|
||||||
|
"StellaOps.Workflow.Abstractions.IDeclarativeWorkflow`1",
|
||||||
|
"Ablera.Serdica.Workflow.Abstractions.IDeclarativeWorkflow`1",
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly string[] WorkflowSpecMetadataNames =
|
||||||
|
{
|
||||||
|
"StellaOps.Workflow.Abstractions.WorkflowSpec`1",
|
||||||
|
"Ablera.Serdica.Workflow.Abstractions.WorkflowSpec`1",
|
||||||
|
};
|
||||||
|
|
||||||
|
public ImmutableArray<INamedTypeSymbol> DeclarativeWorkflowOfT { get; }
|
||||||
|
public ImmutableArray<INamedTypeSymbol> WorkflowSpecOfT { get; }
|
||||||
|
|
||||||
private WorkflowWellKnownTypes(
|
private WorkflowWellKnownTypes(
|
||||||
INamedTypeSymbol declarativeWorkflowOfT,
|
ImmutableArray<INamedTypeSymbol> declarativeWorkflowOfT,
|
||||||
INamedTypeSymbol workflowSpecOfT)
|
ImmutableArray<INamedTypeSymbol> workflowSpecOfT)
|
||||||
{
|
{
|
||||||
DeclarativeWorkflowOfT = declarativeWorkflowOfT;
|
DeclarativeWorkflowOfT = declarativeWorkflowOfT;
|
||||||
WorkflowSpecOfT = workflowSpecOfT;
|
WorkflowSpecOfT = workflowSpecOfT;
|
||||||
@@ -19,27 +37,53 @@ internal sealed class WorkflowWellKnownTypes
|
|||||||
|
|
||||||
public static WorkflowWellKnownTypes? TryResolve(Compilation compilation)
|
public static WorkflowWellKnownTypes? TryResolve(Compilation compilation)
|
||||||
{
|
{
|
||||||
var declarativeWorkflowOfT = compilation.GetTypeByMetadataName(
|
var declarativeBuilder = ImmutableArray.CreateBuilder<INamedTypeSymbol>();
|
||||||
"StellaOps.Workflow.Abstractions.IDeclarativeWorkflow`1");
|
foreach (var name in DeclarativeWorkflowMetadataNames)
|
||||||
var workflowSpecOfT = compilation.GetTypeByMetadataName(
|
{
|
||||||
"StellaOps.Workflow.Abstractions.WorkflowSpec`1");
|
var t = compilation.GetTypeByMetadataName(name);
|
||||||
if (declarativeWorkflowOfT is null || workflowSpecOfT is null)
|
if (t is not null)
|
||||||
|
{
|
||||||
|
declarativeBuilder.Add(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (declarativeBuilder.Count == 0)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new WorkflowWellKnownTypes(declarativeWorkflowOfT, workflowSpecOfT);
|
var specBuilder = ImmutableArray.CreateBuilder<INamedTypeSymbol>();
|
||||||
|
foreach (var name in WorkflowSpecMetadataNames)
|
||||||
|
{
|
||||||
|
var t = compilation.GetTypeByMetadataName(name);
|
||||||
|
if (t is not null)
|
||||||
|
{
|
||||||
|
specBuilder.Add(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (specBuilder.Count == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new WorkflowWellKnownTypes(
|
||||||
|
declarativeBuilder.ToImmutable(),
|
||||||
|
specBuilder.ToImmutable());
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsDeclarativeWorkflowImplementation(INamedTypeSymbol type)
|
public bool IsDeclarativeWorkflowImplementation(INamedTypeSymbol type)
|
||||||
{
|
{
|
||||||
foreach (var iface in type.AllInterfaces)
|
foreach (var iface in type.AllInterfaces)
|
||||||
{
|
{
|
||||||
if (iface.IsGenericType &&
|
if (!iface.IsGenericType)
|
||||||
SymbolEqualityComparer.Default.Equals(
|
|
||||||
iface.ConstructedFrom, DeclarativeWorkflowOfT))
|
|
||||||
{
|
{
|
||||||
return true;
|
continue;
|
||||||
|
}
|
||||||
|
foreach (var knownIface in DeclarativeWorkflowOfT)
|
||||||
|
{
|
||||||
|
if (SymbolEqualityComparer.Default.Equals(iface.ConstructedFrom, knownIface))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -47,15 +91,18 @@ internal sealed class WorkflowWellKnownTypes
|
|||||||
|
|
||||||
public bool IsSpecProperty(IPropertySymbol property)
|
public bool IsSpecProperty(IPropertySymbol property)
|
||||||
{
|
{
|
||||||
if (property.Type is not INamedTypeSymbol typed)
|
if (property.Type is not INamedTypeSymbol typed || !typed.IsGenericType)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!typed.IsGenericType)
|
foreach (var knownSpec in WorkflowSpecOfT)
|
||||||
{
|
{
|
||||||
return false;
|
if (SymbolEqualityComparer.Default.Equals(typed.ConstructedFrom, knownSpec))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return SymbolEqualityComparer.Default.Equals(typed.ConstructedFrom, WorkflowSpecOfT);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsTrustedAssembly(IAssemblySymbol? assembly)
|
public static bool IsTrustedAssembly(IAssemblySymbol? assembly)
|
||||||
@@ -65,8 +112,18 @@ internal sealed class WorkflowWellKnownTypes
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
var name = assembly.Name;
|
var name = assembly.Name;
|
||||||
return name != null &&
|
if (string.IsNullOrEmpty(name))
|
||||||
(name.Equals(TrustedAssemblyPrefix, System.StringComparison.Ordinal) ||
|
{
|
||||||
name.StartsWith(TrustedAssemblyPrefix + ".", System.StringComparison.Ordinal));
|
return false;
|
||||||
|
}
|
||||||
|
foreach (var prefix in TrustedAssemblyPrefixes)
|
||||||
|
{
|
||||||
|
if (name.Equals(prefix, StringComparison.Ordinal) ||
|
||||||
|
name.StartsWith(prefix + ".", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user