Retro.SourceGeneratorUtilities
0.1.2
dotnet add package Retro.SourceGeneratorUtilities --version 0.1.2
NuGet\Install-Package Retro.SourceGeneratorUtilities -Version 0.1.2
<PackageReference Include="Retro.SourceGeneratorUtilities" Version="0.1.2" />
<PackageVersion Include="Retro.SourceGeneratorUtilities" Version="0.1.2" />
<PackageReference Include="Retro.SourceGeneratorUtilities" />
paket add Retro.SourceGeneratorUtilities --version 0.1.2
#r "nuget: Retro.SourceGeneratorUtilities, 0.1.2"
#:package Retro.SourceGeneratorUtilities@0.1.2
#addin nuget:?package=Retro.SourceGeneratorUtilities&version=0.1.2
#tool nuget:?package=Retro.SourceGeneratorUtilities&version=0.1.2
Retro.SourceGeneratorUtilities
This repo contains a number of useful utilities for usage within a source generator.
Attribute Data Models
The primary thing this package provides is the ability to create data models for your attributes. This is done by
using the [AttributeInfoType]
attribute.
Say we have the following attribute defined:
public enum ServiceScope {
Singleton,
Scoped,
Transient
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, AllowMultiple = true)]
public class DependencyAttribute(Type type, ServiceScope scope) : Attribute {
public Type Type { get; } = type;
public ServiceScope Scope { get; } = scope;
public string? Key { get; init; }
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, AllowMultiple = true)]
public class SingletonAttribute(Type serviceType) : DependencyAttribute(serviceType, ServiceScope.Singleton);
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, AllowMultiple = true)]
public class SingletonAttribute<TService>() : SingletonAttribute(typeof(TService));
We could then define a model that follows:
[AttributeInfoType<DependencyAttribute>]
public record DependencyOverview(ITypeSymbol Type, ServiceScope Scope) {
public string? Key { get; init; }
}
[AttributeInfoType<SingletonAttribute>]
public record SingletonOverview(ITypeSymbol Type) : DependencyOverview(Type, ServiceScope.Singleton);
[AttributeInfoType(typeof(SingletonAttribute<>))]
public record SingletonOneParamOverview(ITypeSymbol Type) : SingletonOverview(Type);
The way the matching is done is by validating that the model type has the same constructor signatures as attribute
as well as all the same settable properties (requiring either the set or init keyword). With only caveat is that all
instances of Type
must be replaced with ITypeSymbol
and if modeling a generic attribute the model must be
non-generic and contain additional constructor arguments of type ITypeSymbol
at the beginning of the constructor
for each generic type parameter.
By doing so you get generated code that looks something like this:
public static class DependencyOverviewExtensions {
public static DependencyOverview GetDependencyOverview(this AttributeData data) {
return data.TryGetDependencyOverview(out var info) ? info : throw new InvalidOperationException("Cannot create Info");
}
public static bool TryGetDependencyOverview(this AttributeData data, [NotNullWhen(true)] out DependencyOverview? info) {
var args = data.ConstructorArguments;
if (data.AttributeClass is null) {
info = null;
return false;
}
if (!data.AttributeClass.IsAssignableFrom(typeof(Retro.SourceGeneratorUtilities.Generator.Sample.Attributes.SingletonAttribute))) {
if (Retro.SourceGeneratorUtilities.Generator.Sample.Model.SingletonOverviewExtensions.TryGetSingletonOverview(data, out var childInfo)) {
info = childInfo;
return true;
} else {
info = null;
return false;
}
}
if (!data.AttributeClass.IsSameType(typeof(Retro.SourceGeneratorUtilities.Generator.Sample.Attributes.DependencyAttribute))) {
info = null;
return false;
}
if (data.HasMatchingConstructor(typeof(Microsoft.CodeAnalysis.ITypeSymbol), typeof(Retro.SourceGeneratorUtilities.Generator.Sample.Attributes.ServiceScope))) {
var namedArguments = data.NamedArguments.ToDictionary();
info = new DependencyOverview(data.ConstructorArguments[0].GetTypedValue<Microsoft.CodeAnalysis.ITypeSymbol>(), data.ConstructorArguments[0].GetTypedValue<Retro.SourceGeneratorUtilities.Generator.Sample.Attributes.ServiceScope>()) {
Key = namedArguments.TryGetValue("Key", out var valueKey) ? valueKey.GetTypedValue<string?>() : default,
};
return true;
}
info = null;
return false;
}
public static IEnumerable<DependencyOverview> GetDependencyOverviews(this IEnumerable<AttributeData> attributeDatas) {
foreach (var data in attributeDatas) {
if (data.TryGetDependencyOverview(out var info)) {
yield return info;
}
}
}
}
public static class SingletonOverviewExtensions {
public static SingletonOverview GetSingletonOverview(this AttributeData data) {
return data.TryGetSingletonOverview(out var info) ? info : throw new InvalidOperationException("Cannot create Info");
}
public static bool TryGetSingletonOverview(this AttributeData data, [NotNullWhen(true)] out SingletonOverview? info) {
var args = data.ConstructorArguments;
if (data.AttributeClass is null) {
info = null;
return false;
}
if (!data.AttributeClass.IsAssignableFrom(typeof(Retro.SourceGeneratorUtilities.Generator.Sample.Attributes.SingletonAttribute<>))) {
if (Retro.SourceGeneratorUtilities.Generator.Sample.Model.SingletonOneParamOverviewExtensions.TryGetSingletonOneParamOverview(data, out var childInfo)) {
info = childInfo;
return true;
} else {
info = null;
return false;
}
}
if (!data.AttributeClass.IsSameType(typeof(Retro.SourceGeneratorUtilities.Generator.Sample.Attributes.SingletonAttribute))) {
info = null;
return false;
}
if (data.HasMatchingConstructor(typeof(Microsoft.CodeAnalysis.ITypeSymbol))) {
var namedArguments = data.NamedArguments.ToDictionary();
info = new SingletonOverview(data.ConstructorArguments[0].GetTypedValue<Microsoft.CodeAnalysis.ITypeSymbol>()) {
Key = namedArguments.TryGetValue("Key", out var valueKey) ? valueKey.GetTypedValue<string?>() : default,
};
return true;
}
info = null;
return false;
}
public static IEnumerable<SingletonOverview> GetSingletonOverviews(this IEnumerable<AttributeData> attributeDatas) {
foreach (var data in attributeDatas) {
if (data.TryGetSingletonOverview(out var info)) {
yield return info;
}
}
}
}
public static class SingletonOneParamOverviewExtensions {
public static SingletonOneParamOverview GetSingletonOneParamOverview(this AttributeData data) {
return data.TryGetSingletonOneParamOverview(out var info) ? info : throw new InvalidOperationException("Cannot create Info");
}
public static bool TryGetSingletonOneParamOverview(this AttributeData data, [NotNullWhen(true)] out SingletonOneParamOverview? info) {
var args = data.ConstructorArguments;
if (data.AttributeClass is null) {
info = null;
return false;
}
if (!data.AttributeClass.IsSameType(typeof(Retro.SourceGeneratorUtilities.Generator.Sample.Attributes.SingletonAttribute<>))) {
info = null;
return false;
}
if (data.HasMatchingConstructor(typeof(Microsoft.CodeAnalysis.ITypeSymbol))) {
var namedArguments = data.NamedArguments.ToDictionary();
info = new SingletonOneParamOverview(data.ConstructorArguments[0].GetTypedValue<Microsoft.CodeAnalysis.ITypeSymbol>()) {
Key = namedArguments.TryGetValue("Key", out var valueKey) ? valueKey.GetTypedValue<string?>() : default,
};
return true;
}
info = null;
return false;
}
public static IEnumerable<SingletonOneParamOverview> GetSingletonOneParamOverviews(this IEnumerable<AttributeData> attributeDatas) {
foreach (var data in attributeDatas) {
if (data.TryGetSingletonOneParamOverview(out var info)) {
yield return info;
}
}
}
}
Attributions
Construction and tools icons created by Dewi Sari - Flaticon
Learn more about Target Frameworks and .NET Standard.
-
.NETStandard 2.0
- Microsoft.CodeAnalysis.CSharp (>= 4.13.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Initial Release