Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
07c7767
refactor(source-generators): simplify tuple access and variable namin…
j-d-ha Dec 25, 2025
03534cc
refactor(source-generators): rename internal classes and methods for …
j-d-ha Dec 25, 2025
f3b11be
feat(source-generators): introduce WellKnownTypes and supporting exte…
j-d-ha Dec 25, 2025
6000ae7
feat(source-generators): expand WellKnownTypes with additional type m…
j-d-ha Dec 25, 2025
f88da6b
feat(source-generators): introduce `HandlerSyntaxProvider` for unifie…
j-d-ha Dec 25, 2025
de0a922
refactor(source-generators): streamline `GeneratorContext` and handle…
j-d-ha Dec 25, 2025
0dd9df2
feat(source-generators): add extensibility to `HigherOrderMethodInfo`…
j-d-ha Dec 25, 2025
883c494
refactor(source-generators): rename `GetAsGlobal` to `ToGloballyQuali…
j-d-ha Dec 25, 2025
911c560
feat(source-generators): add support for parameter assignment extract…
j-d-ha Dec 26, 2025
8351899
refactor(source-generators): streamline parameter extraction in `MapH…
j-d-ha Dec 26, 2025
9703df3
refactor(source-generators): enhance parameter assignment handling in…
j-d-ha Dec 26, 2025
275fdfa
feat(source-generators): add `ParameterInfo2` and enhance parameter h…
j-d-ha Dec 26, 2025
97a0507
refactor(source-generators): simplify DI-based parameter handling in …
j-d-ha Dec 26, 2025
2f8a0aa
refactor(source-generators): simplify event handling logic in `MapHan…
j-d-ha Dec 26, 2025
16e5095
refactor(tests): remove redundant comments in snapshot test files
j-d-ha Dec 26, 2025
4579afd
refactor(source-generators): remove `Result` and simplify parameter h…
j-d-ha Dec 26, 2025
3495891
refactor(source-generators): extract method-related logic into `Metho…
j-d-ha Dec 26, 2025
9feeba9
feat(source-generators): refactor `MapHandler` generation logic to si…
j-d-ha Dec 27, 2025
5fd9284
refactor(source-generators): comment out unused code to improve clari…
j-d-ha Dec 27, 2025
aaafc18
feat(source-generators): enhance `MapHandler` logic and improve nulla…
j-d-ha Dec 27, 2025
0746be7
refactor(source-generators): remove `IsResponseTypeBool` from `Higher…
j-d-ha Dec 27, 2025
9f1eeec
feat(source-generators): enhance `MapHandlerParameterInfo` and diagno…
j-d-ha Dec 27, 2025
93c06a6
feat(source-generators): enhance `MapHandlerParameterInfo` and improv…
j-d-ha Dec 27, 2025
09bc180
refactor(source-generators): simplify and enhance parameter and metho…
j-d-ha Dec 27, 2025
e39aa9f
refactor(source-generators): refactor `ReportMultipleEvents` for impr…
j-d-ha Dec 27, 2025
693e641
feat(source-generators): introduce `CollectDiagnosticResults` for str…
j-d-ha Dec 27, 2025
06d225f
feat(source-generators): replace `HigherOrderMethodInfo` with `Invoca…
j-d-ha Dec 27, 2025
b2f6b49
feat(source-generators): enhance diagnostic aggregation and refactor …
j-d-ha Dec 27, 2025
444d863
feat(source-generators): refactor and replace `InvocationMethodInfo` …
j-d-ha Dec 27, 2025
221bca8
feat(source-generators): refactor return type handling and improve li…
j-d-ha Dec 27, 2025
8f514c5
feat(source-generators): refactor lifecycle handling and cleanup unus…
j-d-ha Dec 27, 2025
1a937f0
fix(source-generators): correct lifecycle method return type document…
j-d-ha Dec 27, 2025
0c7c30e
refactor(source-generators): remove `GloballyQualifiedType` from `Lif…
j-d-ha Dec 27, 2025
6271d39
feat(source-generators): enhance `OnShutdown` lifecycle handling and …
j-d-ha Dec 27, 2025
b7cdb0c
feat(minimal-lambda): enable `OnInit` lifecycle for output formatting…
j-d-ha Dec 28, 2025
769eb0b
refactor(source-generators): simplify `Generate` methods and remove c…
j-d-ha Dec 28, 2025
1503153
refactor(source-generators): simplify template rendering and enhance …
j-d-ha Dec 28, 2025
561c37e
refactor(source-generators): remove unused syntax providers and relat…
j-d-ha Dec 28, 2025
22f37d1
feat(source-generators): add support for `ILambdaMiddleware` in `UseM…
j-d-ha Dec 28, 2025
63f6950
refactor(source-generators): replace generated code attribute logic w…
j-d-ha Dec 28, 2025
4f9972b
refactor(source-generators): remove unused models and consolidate log…
j-d-ha Dec 29, 2025
ef657ca
refactor(source-generators): inline `Generate` logic and remove unuse…
j-d-ha Dec 29, 2025
fc1fb3e
refactor(source-generators): inline `Generate` logic and improve `Use…
j-d-ha Dec 29, 2025
bdaf37b
refactor(source-generators): simplify `ClassInfo.Create` and improve …
j-d-ha Dec 29, 2025
185fc52
refactor(source-generators): rename and refactor models for middlewar…
j-d-ha Dec 31, 2025
ff9f06e
refactor(tests): remove commented-out `MiddlewareParameterInfo` diagn…
j-d-ha Dec 31, 2025
1fa903c
feat(source-generators): add `AllFromServices` property and refactor …
j-d-ha Dec 31, 2025
eb55e0a
refactor(source-generators): replace `MiddlewareParameterInfo` with `…
j-d-ha Dec 31, 2025
f3215e8
feat(source-generators): enhance parameter handling and resolution logic
j-d-ha Jan 1, 2026
d76a819
refactor(source-generators): remove unused models and simplify parame…
j-d-ha Jan 1, 2026
3f5263c
feat(source-generators): enhance middleware diagnostics and improve t…
j-d-ha Jan 1, 2026
2a4cdbe
refactor(source-generators): update `DiagnosticInfo` to use `record` …
j-d-ha Jan 2, 2026
afdb0c9
refactor(source-generators): replace type symbol extension methods wi…
j-d-ha Jan 6, 2026
b354ea8
refactor(source-generators): remove `UseMiddlewareTSource` class and …
j-d-ha Jan 6, 2026
1384e33
refactor(source-generators): streamline enumerable diagnostic results…
j-d-ha Jan 6, 2026
111a0d3
refactor(source-generators): simplify `InterceptableLocationInfo` usa…
j-d-ha Jan 6, 2026
ea7adb8
refactor(source-generators): combine generic type constraints in `Fun…
j-d-ha Jan 7, 2026
8baf1d4
refactor(source-generators): clean up templates, diagnostics, and unu…
j-d-ha Jan 7, 2026
bd9bba9
refactor(source-generators): remove redundant extension methods and c…
j-d-ha Jan 7, 2026
a2e2916
refactor(source-generators): remove redundant WellKnownTypes extensio…
j-d-ha Jan 7, 2026
3853c8a
refactor(source-generators): rename extension classes and streamline …
j-d-ha Jan 7, 2026
df56b32
refactor(source-generators): enhance middleware diagnostics and strea…
j-d-ha Jan 7, 2026
f5d69d8
refactor(source-generators): simplify diagnostics collection in `Midd…
j-d-ha Jan 7, 2026
f4c2d89
refactor(source-generators): reorganize `Models` folder structure for…
j-d-ha Jan 7, 2026
70d5e10
refactor(source-generators): update namespace structure to improve cl…
j-d-ha Jan 7, 2026
4c11679
refactor(source-generators): simplify attribute handling and refactor…
j-d-ha Jan 8, 2026
7451cc4
Merge branch 'main' into feature/source-gen-logic-to-be-simpler
j-d-ha Jan 8, 2026
ff33957
refactor(source-generators): simplify location handling in `UseMiddle…
j-d-ha Jan 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions MinimalLambda.sln.DotSettings

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using LayeredCraft.SourceGeneratorTools.Types;
using Microsoft.CodeAnalysis;
using MinimalLambda.SourceGenerators.Models;

Expand All @@ -12,90 +11,30 @@ internal static List<Diagnostic> GenerateDiagnostics(CompilationInfo compilation
{
var diagnostics = new List<Diagnostic>();

var delegateInfos = compilationInfo.MapHandlerInvocationInfos;

// Validate parameters
foreach (var invocationInfo in delegateInfos)
// check for multiple parameters that use the `[FromEvent]` attribute
if (
invocationInfo.DelegateInfo.Parameters.Count(p => p.Source == ParameterSource.Event)
> 1
)
diagnostics.AddRange(
invocationInfo
.DelegateInfo.Parameters.Where(p => p.Source == ParameterSource.Event)
.Select(p =>
Diagnostic.Create(
Diagnostics.MultipleParametersUseAttribute,
p.LocationInfo?.ToLocation(),
AttributeConstants.FromEventAttribute
)
)
);

// check for invalid keyed service usage - MapHandler
diagnostics.AddRange(
compilationInfo.MapHandlerInvocationInfos.GenerateKeyedServiceKeyDiagnostics()
compilationInfo
.MapHandlerInvocationInfos.SelectMany(m => m.DiagnosticInfos)
.Select(d => d.ToDiagnostic())
);

// check for invalid keyed service usage - OnShutdown
diagnostics.AddRange(
compilationInfo.OnShutdownInvocationInfos.GenerateKeyedServiceKeyDiagnostics()
compilationInfo
.OnInitInvocationInfos.SelectMany(m => m.DiagnosticInfos)
.Select(d => d.ToDiagnostic())
);

foreach (var useMiddlewareTInfo in compilationInfo.UseMiddlewareTInfos)
{
// ensure middleware class is concrete
if (useMiddlewareTInfo.ClassInfo.TypeKind is "interface" or "abstract class")
{
diagnostics.Add(
Diagnostic.Create(
Diagnostics.MustBeConcreteType,
useMiddlewareTInfo.GenericTypeArgumentLocation?.ToLocation(),
useMiddlewareTInfo.ClassInfo.ShortName
)
);
}
diagnostics.AddRange(
compilationInfo
.OnShutdownInvocationInfos.SelectMany(m => m.DiagnosticInfos)
.Select(d => d.ToDiagnostic())
);

// validate that middleware class constructors only use `[MiddlewareConstructor]` once
diagnostics.AddRange(
useMiddlewareTInfo
.ClassInfo.ConstructorInfos.Where(c =>
c.AttributeInfos.Any(a =>
a.FullName == AttributeConstants.MiddlewareConstructor
)
)
.Skip(1)
.Select(c =>
Diagnostic.Create(
Diagnostics.MultipleConstructorsWithAttribute,
c.AttributeInfos.First(a =>
a.FullName == AttributeConstants.MiddlewareConstructor
)
.LocationInfo?.ToLocation(),
AttributeConstants.MiddlewareConstructor
)
)
);
}
diagnostics.AddRange(
compilationInfo
.UseMiddlewareTInfos.SelectMany(m => m.DiagnosticInfos)
.Select(d => d.ToDiagnostic())
);

return diagnostics;
}

private static Diagnostic[] GenerateKeyedServiceKeyDiagnostics(
this EquatableArray<HigherOrderMethodInfo> methodNameInfos
) =>
methodNameInfos
.SelectMany(onShutdownInvocationInfo =>
onShutdownInvocationInfo.DelegateInfo.Parameters
)
.Where(parameterInfo => parameterInfo.KeyedServiceKey is { DisplayValue: null })
.Select(parameterInfo =>
Diagnostic.Create(
Diagnostics.InvalidAttributeArgument,
parameterInfo.KeyedServiceKey!.Value.LocationInfo?.ToLocation(),
parameterInfo.KeyedServiceKey.Value.Type
)
)
.ToArray();
}
40 changes: 40 additions & 0 deletions src/MinimalLambda.SourceGenerators/Diagnostics/DiagnosticInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using LayeredCraft.SourceGeneratorTools.Utilities;
using Microsoft.CodeAnalysis;

namespace MinimalLambda.SourceGenerators.Models;

internal sealed record DiagnosticInfo(
DiagnosticDescriptor DiagnosticDescriptor,
LocationInfo? LocationInfo = null,
params object?[] MessageArgs
)
{
public bool Equals(DiagnosticInfo? other) =>
other is not null
&& Equals(DiagnosticDescriptor.Id, other.DiagnosticDescriptor.Id)
&& Equals(LocationInfo, other.LocationInfo);

public override int GetHashCode() => HashCode.Combine(DiagnosticDescriptor, LocationInfo);
}

internal static class DiagnosticInfoExtensions
{
extension(DiagnosticInfo diagnosticInfo)
{
internal static DiagnosticInfo Create(
DiagnosticDescriptor diagnosticDescriptor,
LocationInfo? locationInfo,
object?[] messageArgs
) => new(diagnosticDescriptor, locationInfo, messageArgs);

internal Diagnostic ToDiagnostic() =>
Diagnostic.Create(
diagnosticInfo.DiagnosticDescriptor,
diagnosticInfo.LocationInfo?.ToLocation(),
diagnosticInfo.MessageArgs
);

internal void ReportDiagnostic(SourceProductionContext context) =>
context.ReportDiagnostic(diagnosticInfo.ToDiagnostic());
}
}
54 changes: 54 additions & 0 deletions src/MinimalLambda.SourceGenerators/Diagnostics/DiagnosticResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using Microsoft.CodeAnalysis;
using MinimalLambda.SourceGenerators.Models;

// ReSharper disable MemberCanBePrivate.Global

namespace MinimalLambda.SourceGenerators;

internal sealed class DiagnosticResult<T>
{
internal bool IsSuccess { get; }
internal T? Value { get; }
internal DiagnosticInfo? Error { get; }

private DiagnosticResult(bool isSuccess, T? value, DiagnosticInfo? error)
{
IsSuccess = isSuccess;
Value = value;
Error = error;
}

public static DiagnosticResult<T> Success(T value) => new(true, value, null);

public static implicit operator DiagnosticResult<T>(T value) => Success(value);

public static DiagnosticResult<T> Failure(DiagnosticInfo error) => new(false, default, error);

public static DiagnosticResult<T> Failure(
DiagnosticDescriptor diagnosticDescriptor,
LocationInfo? locationInfo = null,
params object?[] messageArgs
) => new(false, default, new DiagnosticInfo(diagnosticDescriptor, locationInfo, messageArgs));

public DiagnosticResult<TNew> Map<TNew>(Func<T, TNew> map) =>
IsSuccess
? DiagnosticResult<TNew>.Success(map(Value!))
: DiagnosticResult<TNew>.Failure(Error!);

public DiagnosticResult<TNew> Bind<TNew>(Func<T, DiagnosticResult<TNew>> bind) =>
IsSuccess ? bind(Value!) : DiagnosticResult<TNew>.Failure(Error!);

public TResult Match<TResult>(
Func<T, TResult> onSuccess,
Func<DiagnosticInfo, TResult> onFailure
) => IsSuccess ? onSuccess(Value!) : onFailure(Error!);

public void Switch(Action<T> onSuccess, Action<DiagnosticInfo> onFailure)
{
if (IsSuccess)
onSuccess(Value!);
else
onFailure(Error!);
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis;
using MinimalLambda.SourceGenerators.Models;

namespace MinimalLambda.SourceGenerators;
namespace MinimalLambda.SourceGenerators.Emitters;

internal static class LambdaHostOutputGenerator
internal static class MinimalLambdaEmitter
{
internal static string GeneratedCodeAttribute
internal static readonly Lazy<string> GeneratedCodeAttribute = new(() =>
{
get
{
if (field is null)
{
var assembly = Assembly.GetExecutingAssembly();
var generatorName = assembly.GetName().Name;
var generatorVersion = assembly.GetName().Version.ToString();

field =
$"""[global::System.CodeDom.Compiler.GeneratedCode("{generatorName}", "{generatorVersion}")]""";
}
var assembly = Assembly.GetExecutingAssembly();
var generatorName = assembly.GetName().Name;
var generatorVersion = assembly.GetName().Version;

return field;
}
}
return $"""[global::System.CodeDom.Compiler.GeneratedCode("{generatorName}", "{generatorVersion}")]""";
});

internal static void Generate(SourceProductionContext context, CompilationInfo compilationInfo)
{
Expand All @@ -41,7 +33,10 @@ internal static void Generate(SourceProductionContext context, CompilationInfo c

List<string?> outputs =
[
CommonSources.Generate(),
TemplateHelper.Render(
GeneratorConstants.InterceptsLocationAttributeTemplateFile,
new { GeneratedCodeAttribute }
),
"""
namespace MinimalLambda.Generated
{
Expand All @@ -59,37 +54,50 @@ namespace MinimalLambda.Generated
// if MapHandler calls found, generate the source code.
if (compilationInfo.MapHandlerInvocationInfos.Count >= 1)
outputs.Add(
MapHandlerSources.Generate(
compilationInfo.MapHandlerInvocationInfos,
compilationInfo.BuilderInfos
TemplateHelper.Render(
GeneratorConstants.LambdaHostMapHandlerExtensionsTemplateFile,
new
{
GeneratedCodeAttribute,
MapHandlerCalls = compilationInfo.MapHandlerInvocationInfos,
}
)
);

// add UseMiddleware<T> interceptors
if (compilationInfo.UseMiddlewareTInfos.Count >= 1)
outputs.Add(UseMiddlewareTSource.Generate(compilationInfo.UseMiddlewareTInfos));
outputs.Add(
TemplateHelper.Render(
GeneratorConstants.UseMiddlewareTTemplateFile,
new { GeneratedCodeAttribute, Calls = compilationInfo.UseMiddlewareTInfos }
)
);

// add OnInit interceptors
if (compilationInfo.OnInitInvocationInfos.Count >= 1)
outputs.Add(
GenericHandlerSources.Generate(
compilationInfo.OnInitInvocationInfos,
"OnInit",
"bool",
"true",
"ILambdaOnInitBuilder"
TemplateHelper.Render(
GeneratorConstants.GenericHandlerTemplateFile,
new
{
Name = compilationInfo.OnInitInvocationInfos.First().MethodType,
Calls = compilationInfo.OnInitInvocationInfos,
GeneratedCodeAttribute,
}
)
);

// add OnShutdown interceptors
if (compilationInfo.OnShutdownInvocationInfos.Count >= 1)
outputs.Add(
GenericHandlerSources.Generate(
compilationInfo.OnShutdownInvocationInfos,
"OnShutdown",
null,
null,
"ILambdaOnShutdownBuilder"
TemplateHelper.Render(
GeneratorConstants.GenericHandlerTemplateFile,
new
{
Name = compilationInfo.OnShutdownInvocationInfos.First().MethodType,
Calls = compilationInfo.OnShutdownInvocationInfos,
GeneratedCodeAttribute,
}
)
);

Expand Down
Loading
Loading