Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
103 changes: 103 additions & 0 deletions ServiceScan.SourceGenerator.Tests/CustomHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,109 @@ public static partial IServiceCollection ProcessServices(this IServiceCollection
Assert.Equal(expected, results.GeneratedTrees[1].ToString());
}

[Fact]
public void AddMultipleCustomHandlerAttributesWithDifferentCustomHandler()
{
var source = $$"""
using ServiceScan.SourceGenerator;

namespace GeneratorTests;

public static partial class ServicesExtensions
{
[GenerateServiceRegistrations(AssignableTo = typeof(IFirstService), CustomHandler = nameof(HandleFirstType))]
[GenerateServiceRegistrations(AssignableTo = typeof(ISecondService), CustomHandler = nameof(HandleSecondType))]
public static partial void ProcessServices();

private static void HandleFirstType<T>() => System.Console.WriteLine("First:" + typeof(T).Name);
private static void HandleSecondType<T>() => System.Console.WriteLine("Second:" + typeof(T).Name);
}
""";

var services =
"""
namespace GeneratorTests;

public interface IFirstService { }
public interface ISecondService { }
public class MyService1 : IFirstService { }
public class MyService2 : ISecondService { }
""";

var compilation = CreateCompilation(source, services);

var results = CSharpGeneratorDriver
.Create(_generator)
.RunGenerators(compilation)
.GetRunResult();

var expected = $$"""
namespace GeneratorTests;

public static partial class ServicesExtensions
{
public static partial void ProcessServices()
{
HandleFirstType<global::GeneratorTests.MyService1>();
HandleSecondType<global::GeneratorTests.MyService2>();
}
}
""";
Assert.Equal(expected, results.GeneratedTrees[1].ToString());
}


[Fact]
public void AddMultipleCustomHandlerAttributesWithSameCustomHandler()
{
var source = $$"""
using ServiceScan.SourceGenerator;

namespace GeneratorTests;

public static partial class ServicesExtensions
{
[GenerateServiceRegistrations(AssignableTo = typeof(IFirstService), CustomHandler = nameof(HandleType))]
[GenerateServiceRegistrations(AssignableTo = typeof(ISecondService), CustomHandler = nameof(HandleType))]
public static partial void ProcessServices();

private static void HandleType<T>() => System.Console.WriteLine(typeof(T).Name);
}
""";

var services =
"""
namespace GeneratorTests;

public interface IFirstService { }
public interface ISecondService { }
public class MyService1 : IFirstService { }
public class MyService2 : ISecondService { }
""";

var compilation = CreateCompilation(source, services);

var results = CSharpGeneratorDriver
.Create(_generator)
.RunGenerators(compilation)
.GetRunResult();

var expected = $$"""
namespace GeneratorTests;

public static partial class ServicesExtensions
{
public static partial void ProcessServices()
{
HandleType<global::GeneratorTests.MyService1>();
HandleType<global::GeneratorTests.MyService2>();
}
}
""";
Assert.Equal(expected, results.GeneratedTrees[1].ToString());
}


private static Compilation CreateCompilation(params string[] source)
{
var path = Path.GetDirectoryName(typeof(object).Assembly.Location)!;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ public partial class DependencyInjectionGenerator
if (!method.IsPartialDefinition)
return Diagnostic.Create(NotPartialDefinition, method.Locations[0]);

var hasCustomHandler = false;
var attributeData = new AttributeModel[context.Attributes.Length];
var attributeData = context.Attributes.Select(a => AttributeModel.Create(a, method)).ToArray();
var hasCustomHandlers = attributeData.Any(a => a.CustomHandler != null);

for (var i = 0; i < context.Attributes.Length; i++)
{
var attribute = AttributeModel.Create(context.Attributes[i], method);
Expand All @@ -26,9 +27,8 @@ public partial class DependencyInjectionGenerator
if (!attribute.HasSearchCriteria)
return Diagnostic.Create(MissingSearchCriteria, attribute.Location);

hasCustomHandler |= attribute.CustomHandler != null;
if (hasCustomHandler && context.Attributes.Length != 1)
return Diagnostic.Create(OnlyOneCustomHandlerAllowed, attribute.Location);
if (hasCustomHandlers && attribute.CustomHandler == null)
return Diagnostic.Create(CantMixRegularAndCustomHandlerRegistrations, attribute.Location);

if (attribute.KeySelector != null)
{
Expand Down Expand Up @@ -72,7 +72,7 @@ public partial class DependencyInjectionGenerator
return null;
}

if (!hasCustomHandler)
if (!hasCustomHandlers)
{
var serviceCollectionType = context.SemanticModel.Compilation.GetTypeByMetadataName("Microsoft.Extensions.DependencyInjection.IServiceCollection");

Expand Down
8 changes: 4 additions & 4 deletions ServiceScan.SourceGenerator/DiagnosticDescriptors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,72 +4,72 @@

public static class DiagnosticDescriptors
{
public static readonly DiagnosticDescriptor NotPartialDefinition = new("DI0001",

Check warning on line 7 in ServiceScan.SourceGenerator/DiagnosticDescriptors.cs

View workflow job for this annotation

GitHub Actions / build

Enable analyzer release tracking for the analyzer project containing rule 'DI0001' (https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)
"Method is not partial",
"Method with GenerateServiceRegistrations attribute must have partial modifier",
"Usage",
DiagnosticSeverity.Error,
true);

public static readonly DiagnosticDescriptor WrongReturnType = new("DI0002",

Check warning on line 14 in ServiceScan.SourceGenerator/DiagnosticDescriptors.cs

View workflow job for this annotation

GitHub Actions / build

Enable analyzer release tracking for the analyzer project containing rule 'DI0002' (https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)
"Wrong return type",
"Method with GenerateServiceRegistrations attribute must return void or IServiceCollection",
"Usage",
DiagnosticSeverity.Error,
true);

public static readonly DiagnosticDescriptor WrongMethodParameters = new("DI0003",

Check warning on line 21 in ServiceScan.SourceGenerator/DiagnosticDescriptors.cs

View workflow job for this annotation

GitHub Actions / build

Enable analyzer release tracking for the analyzer project containing rule 'DI0003' (https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)
"Wrong method parameters",
"Method with GenerateServiceRegistrations attribute must have a single IServiceCollection parameter",
"Usage",
DiagnosticSeverity.Error,
true);

public static readonly DiagnosticDescriptor MissingSearchCriteria = new("DI0004",

Check warning on line 28 in ServiceScan.SourceGenerator/DiagnosticDescriptors.cs

View workflow job for this annotation

GitHub Actions / build

Enable analyzer release tracking for the analyzer project containing rule 'DI0004' (https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)
"Missing search criteria",
"GenerateServiceRegistrations must have at least one search criteria",
"Usage",
DiagnosticSeverity.Error,
true);

public static readonly DiagnosticDescriptor NoMatchingTypesFound = new("DI0005",

Check warning on line 35 in ServiceScan.SourceGenerator/DiagnosticDescriptors.cs

View workflow job for this annotation

GitHub Actions / build

Enable analyzer release tracking for the analyzer project containing rule 'DI0005' (https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)
"No matching types found",
"There are no types matching attribute's search criteria",
"Usage",
DiagnosticSeverity.Warning,
true);

public static readonly DiagnosticDescriptor KeySelectorMethodHasIncorrectSignature = new("DI0007",

Check warning on line 42 in ServiceScan.SourceGenerator/DiagnosticDescriptors.cs

View workflow job for this annotation

GitHub Actions / build

Enable analyzer release tracking for the analyzer project containing rule 'DI0007' (https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)
"Provided KeySelector method has incorrect signature",
"KeySelector should have non-void return type, and either be generic with no parameters, or non-generic with a single Type parameter",
"Usage",
DiagnosticSeverity.Error,
true);

public static readonly DiagnosticDescriptor OnlyOneCustomHandlerAllowed = new("DI0008",
"Only one GenerateServiceRegistrations attribute is allowed when CustomHandler used",
"Only one GenerateServiceRegistrations attribute is allowed when CustomHandler used",
public static readonly DiagnosticDescriptor CantMixRegularAndCustomHandlerRegistrations = new("DI0008",

Check warning on line 49 in ServiceScan.SourceGenerator/DiagnosticDescriptors.cs

View workflow job for this annotation

GitHub Actions / build

Enable analyzer release tracking for the analyzer project containing rule 'DI0008' (https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)
"It's not allowed to mix GenerateServiceRegistrations attributes with and without CustomHandler on the same method",
"It's not allowed to mix GenerateServiceRegistrations attributes with and without CustomHandler on the same method",
"Usage",
DiagnosticSeverity.Error,
true);

public static readonly DiagnosticDescriptor WrongReturnTypeForCustomHandler = new("DI0009",

Check warning on line 56 in ServiceScan.SourceGenerator/DiagnosticDescriptors.cs

View workflow job for this annotation

GitHub Actions / build

Enable analyzer release tracking for the analyzer project containing rule 'DI0009' (https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)
"Wrong return type",
"Method with CustomHandler must return void or 'this' parameter type",
"Usage",
DiagnosticSeverity.Error,
true);

public static readonly DiagnosticDescriptor CustomHandlerMethodNotFound = new("DI0012",

Check warning on line 63 in ServiceScan.SourceGenerator/DiagnosticDescriptors.cs

View workflow job for this annotation

GitHub Actions / build

Enable analyzer release tracking for the analyzer project containing rule 'DI0012' (https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)
"Provided CustomHandler method is not found",
"CustomHandler parameter should point to a static method in the class",
"Usage",
DiagnosticSeverity.Error,
true);

public static readonly DiagnosticDescriptor CustomHandlerMethodHasIncorrectSignature = new("DI0011",

Check warning on line 70 in ServiceScan.SourceGenerator/DiagnosticDescriptors.cs

View workflow job for this annotation

GitHub Actions / build

Enable analyzer release tracking for the analyzer project containing rule 'DI0011' (https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)
"Provided CustomHandler method has incorrect signature",
"CustomHandler method must be generic, and must have the same parameters as the method with an attribute",
"CustomHandler method must be generic, and must have the same parameters as the method with the attribute",
"Usage",
DiagnosticSeverity.Error,
true);
Expand Down