Skip to content

Commit d83f061

Browse files
Fix invalidsetup metadata detection
1 parent 16d5bcc commit d83f061

5 files changed

Lines changed: 173 additions & 7 deletions

File tree

src/Pure.DI.Core/Core/Metadata.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
namespace Pure.DI.Core;
22

3-
sealed class Metadata
3+
sealed class Metadata(ISemantic semantic)
44
: IMetadata
55
{
6-
public bool IsMetadata(SyntaxNode node, SemanticModel? semanticModel, CancellationToken cancellationToken)
6+
private const string IConfigurationTypeName = $"{Names.GeneratorName}.{nameof(IConfiguration)}";
7+
8+
public bool IsMetadata(SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken)
79
{
810
if (node is not InvocationExpressionSyntax invocation)
911
{
@@ -23,7 +25,8 @@ public bool IsMetadata(SyntaxNode node, SemanticModel? semanticModel, Cancellati
2325
case MemberAccessExpressionSyntax memberAccess
2426
when memberAccess.Kind() == SyntaxKind.SimpleMemberAccessExpression
2527
&& memberAccess.Name.Identifier.Text == nameof(DI.Setup):
26-
return true;
28+
var returnType = semantic.TryGetTypeSymbol<ITypeSymbol>(semanticModel, node);
29+
return returnType?.ToString() == IConfigurationTypeName;
2730
}
2831
}
2932

src/Pure.DI.Core/Core/MetadataWalker.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public void Visit(IMetadataVisitor metadataVisitor, in SyntaxUpdate update)
8989
public override void VisitInvocationExpression(InvocationExpressionSyntax invocation)
9090
{
9191
cancellationToken.ThrowIfCancellationRequested();
92-
if (_isMetadata || metadata.IsMetadata(invocation, _semanticModel, cancellationToken))
92+
if (_isMetadata || metadata.IsMetadata(invocation, _semanticModel!, cancellationToken))
9393
{
9494
_invocations.Push(invocation);
9595
}

src/Pure.DI.Core/Core/Semantic.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Pure.DI.Core;
44

5+
using System.Reflection;
56
using System.Runtime.CompilerServices;
67
using Microsoft.CodeAnalysis.Operations;
78

@@ -100,8 +101,8 @@ when literalExpression.IsKind(SyntaxKind.DefaultLiteralExpression)
100101
|| literalExpression.IsKind(SyntaxKind.NullLiteralExpression):
101102
return default;
102103

103-
case LiteralExpressionSyntax literalExpression:
104-
return (T?)literalExpression.Token.Value;
104+
case LiteralExpressionSyntax { Token.Value: T value }:
105+
return value;
105106

106107
// Simple member access expressions for enums (no semantic model needed)
107108
case MemberAccessExpressionSyntax memberAccess when memberAccess.IsKind(SyntaxKind.SimpleMemberAccessExpression):

src/Pure.DI.Core/IMetadata.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22

33
public interface IMetadata
44
{
5-
bool IsMetadata(SyntaxNode node, SemanticModel? semanticModel, CancellationToken cancellationToken);
5+
bool IsMetadata(SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken);
66
}

tests/Pure.DI.IntegrationTests/SetupTests.cs

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,60 @@ public static void Main()
223223
result.Success.ShouldBeTrue(result);
224224
}
225225

226+
[Fact]
227+
public async Task ShouldFilterInvalidSetup()
228+
{
229+
// Given
230+
231+
// When
232+
var result = await """
233+
using Pure.DI;
234+
235+
namespace Sample
236+
{
237+
interface IDependency { }
238+
239+
class Dependency : IDependency { }
240+
241+
class Service
242+
{
243+
public Service(IDependency dependency)
244+
{
245+
}
246+
}
247+
248+
static class SetupComposition
249+
{
250+
static void Setup(int i, string name) {}
251+
252+
private static void InvalidSetup()
253+
{
254+
Setup(123, "Composition");
255+
}
256+
257+
private static void ValidSetup()
258+
{
259+
DI.Setup("Composition")
260+
.Bind<IDependency>().To<Dependency>()
261+
.Root<Service>("Root");
262+
}
263+
}
264+
265+
public class Program
266+
{
267+
public static void Main()
268+
{
269+
var composition = new Composition();
270+
var service = composition.Root;
271+
}
272+
}
273+
}
274+
""".RunAsync();
275+
276+
// Then
277+
result.Success.ShouldBeTrue(result);
278+
}
279+
226280
[Fact]
227281
public async Task ShouldGenerateSpecifiedRootType()
228282
{
@@ -4122,4 +4176,112 @@ public static void Main()
41224176
// Then
41234177
result.Success.ShouldBeTrue(result);
41244178
}
4179+
4180+
[Fact]
4181+
public async Task ShouldHandleNameofWithDependsOnEnumParameter()
4182+
{
4183+
// Given
4184+
4185+
// When
4186+
var result = await """
4187+
using System;
4188+
using Pure.DI;
4189+
#pragma warning disable CS8618
4190+
#pragma warning disable CS0649
4191+
4192+
namespace Sample
4193+
{
4194+
public class ClockConfig : IClockConfig
4195+
{
4196+
public string? Value { get; set; }
4197+
}
4198+
4199+
public interface IClockConfig
4200+
{
4201+
string? Value { get; set; }
4202+
}
4203+
4204+
public class ClockService
4205+
{
4206+
public ClockService(IClockConfig config)
4207+
{
4208+
}
4209+
}
4210+
4211+
public class ClocksComposition
4212+
{
4213+
private ClockConfig? clockConfig = null;
4214+
4215+
void Setup() => DI.Setup(kind: CompositionKind.Internal)
4216+
.Bind<IClockConfig>().To(_ => clockConfig!)
4217+
.Singleton<ClockService>();
4218+
}
4219+
4220+
public partial class Scope
4221+
{
4222+
void Setup() => DI.Setup()
4223+
.DependsOn(nameof(ClocksComposition), SetupContextKind.Members)
4224+
.Root<ClockService>(nameof(ClockManager));
4225+
}
4226+
4227+
public partial class ClockManager
4228+
{
4229+
}
4230+
4231+
public class Program
4232+
{
4233+
public static void Main()
4234+
{
4235+
var composition = new Scope();
4236+
}
4237+
}
4238+
}
4239+
""".RunAsync();
4240+
4241+
// Then
4242+
result.Success.ShouldBeTrue(result);
4243+
}
4244+
4245+
[Fact]
4246+
public async Task ShouldHandleNameofExpressionWithEnumParameter()
4247+
{
4248+
// Given
4249+
4250+
// When
4251+
var result = await """
4252+
using System;
4253+
using Pure.DI;
4254+
4255+
namespace Sample
4256+
{
4257+
interface IService { }
4258+
class Service : IService { }
4259+
4260+
internal class Composition1
4261+
{
4262+
void Setup() => DI.Setup(kind: CompositionKind.Internal)
4263+
.Bind<IService>().To<Service>();
4264+
}
4265+
4266+
internal partial class Composition2
4267+
{
4268+
void Setup() => DI.Setup()
4269+
.DependsOn(nameof(Composition1), SetupContextKind.Members)
4270+
.Root<IService>("Root");
4271+
}
4272+
4273+
public class Program
4274+
{
4275+
public static void Main()
4276+
{
4277+
var composition = new Composition2();
4278+
var service = composition.Root;
4279+
}
4280+
}
4281+
}
4282+
""".RunAsync();
4283+
4284+
// Then
4285+
result.Success.ShouldBeTrue(result);
4286+
}
41254287
}

0 commit comments

Comments
 (0)