Skip to content

Commit

Permalink
Supports the use of static DI setup
Browse files Browse the repository at this point in the history
  • Loading branch information
NikolayPianikov committed Aug 25, 2024
1 parent 367ec47 commit a0d0347
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 32 deletions.
10 changes: 6 additions & 4 deletions src/Pure.DI.Core/Core/ApiInvocationProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ public void ProcessInvocation(
InvocationExpressionSyntax invocation,
string @namespace)
{
if (invocation.Expression is not MemberAccessExpressionSyntax memberAccess)
var name = invocation.Expression switch
{
return;
}
MemberAccessExpressionSyntax memberAccess when memberAccess.Kind() == SyntaxKind.SimpleMemberAccessExpression => memberAccess.Name,
IdentifierNameSyntax { Identifier.Text: nameof(DI.Setup) } identifierName => identifierName,
_ => default
};

var prevInvocation = invocation.DescendantNodes().FirstOrDefault(i => i is InvocationExpressionSyntax);
List<string> invocationComments;
Expand All @@ -38,7 +40,7 @@ public void ProcessInvocation(
.Where(trivia => trivia.IsKind(SyntaxKind.SingleLineCommentTrivia))).ToList();
}

switch (memberAccess.Name)
switch (name)
{
case IdentifierNameSyntax identifierName:
switch (identifierName.Identifier.Text)
Expand Down
52 changes: 24 additions & 28 deletions src/Pure.DI.Core/Core/MetadataSyntaxWalker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ internal sealed class MetadataSyntaxWalker(
CancellationToken cancellationToken)
: CSharpSyntaxWalker, IMetadataSyntaxWalker
{
private const string DISetup = $"{nameof(DI)}.{nameof(DI.Setup)}";
private readonly Stack<InvocationExpressionSyntax> _invocations = new();
private string _namespace = string.Empty;
private bool _isMetadata;

private SemanticModel? _semanticModel;

[SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1024:Symbols should be compared for equality")]
public void Visit(IMetadataVisitor metadataVisitor, in SyntaxUpdate update)
{
_semanticModel = update.SemanticModel;
Visit(update.Node);
var visitors = new List<InvocationVisitor>();
while (_invocations.TryPop(out var invocation))
Expand Down Expand Up @@ -82,36 +83,31 @@ public override void VisitNamespaceDeclaration(NamespaceDeclarationSyntax namesp
base.VisitNamespaceDeclaration(namespaceDeclaration);
}

private static string GetInvocationName(InvocationExpressionSyntax invocation) => GetName(invocation.Expression, 2);

private static string GetName(ExpressionSyntax expression, int deepness = int.MaxValue)
[SuppressMessage("ReSharper", "SuggestBaseTypeForParameter")]
private bool IsMetadata(InvocationExpressionSyntax invocation)
{
switch (expression)
foreach (var curInvocation in invocation.DescendantNodesAndSelf().OfType<InvocationExpressionSyntax>().Reverse())
{
case IdentifierNameSyntax identifierNameSyntax:
return identifierNameSyntax.Identifier.Text;

case MemberAccessExpressionSyntax memberAccess when memberAccess.IsKind(SyntaxKind.SimpleMemberAccessExpression):
switch (curInvocation.Expression)
{
var name = memberAccess.Name.Identifier.Text;
if (--deepness > 0)
{
var prefix = GetName(memberAccess.Expression, deepness);
return prefix == string.Empty ? name : $"{prefix}.{name}";
}

return name;
case IdentifierNameSyntax { Identifier.Text: nameof(DI.Setup) }:
case MemberAccessExpressionSyntax memberAccess
when memberAccess.Kind() == SyntaxKind.SimpleMemberAccessExpression
&& memberAccess.Name.Identifier.Text == nameof(DI.Setup)
&& (memberAccess.Expression is IdentifierNameSyntax { Identifier.Text: nameof(DI) }
|| memberAccess.Expression is MemberAccessExpressionSyntax firstMemberAccess && firstMemberAccess.Kind() == SyntaxKind.SimpleMemberAccessExpression && firstMemberAccess.Name.Identifier.Text == nameof(DI)):

if (_semanticModel?.GetTypeInfo(curInvocation) is { } typeInfo
&& (typeInfo.Type ?? typeInfo.ConvertedType) is { } type
&& type.ToDisplayString(NullableFlowState.None, SymbolDisplayFormat.FullyQualifiedFormat) == Names.ConfigurationInterfaceName)
{
return true;
}

break;
}

default:
return string.Empty;
}
}

[SuppressMessage("ReSharper", "SuggestBaseTypeForParameter")]
private static bool IsMetadata(InvocationExpressionSyntax invocation) =>
invocation
.DescendantNodesAndSelf()
.OfType<InvocationExpressionSyntax>()
.FirstOrDefault(i => GetInvocationName(i) == DISetup) is not null;
return false;
}
}
1 change: 1 addition & 0 deletions src/Pure.DI.Core/Core/Names.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ internal static class Names
public const string IAsyncDisposableInterfaceShortName = $"{nameof(System)}.IAsyncDisposable";
public const string ResolverInterfaceName = $"{ApiNamespace}{nameof(IResolver<object, object>)}";
public const string ContextInterfaceName = $"{ApiNamespace}{nameof(IContext)}";
public const string ConfigurationInterfaceName = $"{ApiNamespace}{nameof(IConfiguration)}";

// Attributes
public const string OrdinalAttributeName = $"{ApiNamespace}{nameof(OrdinalAttribute)}";
Expand Down
41 changes: 41 additions & 0 deletions tests/Pure.DI.IntegrationTests/SetupTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1918,4 +1918,45 @@ public static void Main()
result.Warnings.Count.ShouldBe(1);
result.Warnings.Count(i => i.Id == LogId.WarningMetadataDefect).ShouldBe(1);
}

[Fact]
public async Task ShouldSupportStaticSetupMethod()
{
// Given

// When
var result = await """
using System;
using Pure.DI;
using static Pure.DI.DI;
namespace Sample
{
interface IService {}
class Service: IService {}
static class Setup
{
private static void SetupComposition()
{
Setup(nameof(Composition))
.Bind<IService>().To<Service>()
.Root<IService>("Service");
}
}
public class Program
{
public static void Main()
{
var composition = new Composition();
Console.WriteLine(composition.Service != composition.Service);
}
}
}
""".RunAsync();

// Then
result.Success.ShouldBeTrue(result);
result.StdOut.ShouldBe(["True"], result);
}
}

0 comments on commit a0d0347

Please sign in to comment.