From 5b52278ce69443efc04a169cd38e0ae2da0f2cf2 Mon Sep 17 00:00:00 2001 From: hadashiA Date: Wed, 29 May 2024 19:57:42 +0900 Subject: [PATCH] Fix the algorithm to target generated type --- .../SyntaxCollector.cs | 48 +- .../VContainer.SourceGenerator.Roslyn3.csproj | 1 + .../VContainerSourceGenerator.cs | 362 ++------------ .../WorkItem.cs | 46 +- VContainer.SourceGenerator/Analyzer.cs | 106 ++++ VContainer.SourceGenerator/Emitter.cs | 306 ++++++++++++ .../ReferenceSymbols.cs | 2 + VContainer.SourceGenerator/TypeMeta.cs | 17 +- .../UsingDirectiveMeta.cs | 32 ++ .../VContainerIncrementalSourceGenerator.cs | 467 ++++-------------- .../UserSettings/Layouts/default-2021.dwlt | 38 +- 11 files changed, 654 insertions(+), 771 deletions(-) create mode 100644 VContainer.SourceGenerator/Analyzer.cs create mode 100644 VContainer.SourceGenerator/Emitter.cs create mode 100644 VContainer.SourceGenerator/UsingDirectiveMeta.cs diff --git a/VContainer.SourceGenerator.Roslyn3/SyntaxCollector.cs b/VContainer.SourceGenerator.Roslyn3/SyntaxCollector.cs index 0e9ceafe..a70f7fe0 100644 --- a/VContainer.SourceGenerator.Roslyn3/SyntaxCollector.cs +++ b/VContainer.SourceGenerator.Roslyn3/SyntaxCollector.cs @@ -1,38 +1,44 @@ -using System; using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace VContainer.SourceGenerator +namespace VContainer.SourceGenerator; + +class SyntaxCollector : ISyntaxReceiver { - class SyntaxCollector : ISyntaxReceiver - { - public List Log { get; } = new(); - public List WorkItems { get; } = new(); + public List Log { get; } = new(); + public List WorkItems { get; } = new(); - public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + if (syntaxNode.IsKind(SyntaxKind.ClassDeclaration)) { - if (IsCandidateType(syntaxNode)) + if (syntaxNode is ClassDeclarationSyntax classDeclarationSyntax) { - WorkItems.Add(new WorkItem((TypeDeclarationSyntax)syntaxNode)); + if (!classDeclarationSyntax.Modifiers.Any(modifier => modifier.IsKind(SyntaxKind.AbstractKeyword) || + modifier.IsKind(SyntaxKind.StaticKeyword))) + { + WorkItems.Add(new WorkItem(classDeclarationSyntax)); + } } } - - static bool IsCandidateType(SyntaxNode syntax) + else if (syntaxNode.IsKind(SyntaxKind.InvocationExpression)) { - if (syntax is not ClassDeclarationSyntax classDeclarationSyntax) - { - return false; - } - - if (classDeclarationSyntax.Modifiers.Any(modifier => modifier.IsKind(SyntaxKind.AbstractKeyword) || - modifier.IsKind(SyntaxKind.StaticKeyword))) + if (syntaxNode is InvocationExpressionSyntax + { + Expression: MemberAccessExpressionSyntax + { + Expression: IdentifierNameSyntax + } memberAccess + } invocationExpressionSyntax) { - return false; + if (memberAccess.Name.Identifier.Text.StartsWith("Register")) + { + WorkItems.Add(new WorkItem(invocationExpressionSyntax)); + } } - return true; } } -} \ No newline at end of file +} diff --git a/VContainer.SourceGenerator.Roslyn3/VContainer.SourceGenerator.Roslyn3.csproj b/VContainer.SourceGenerator.Roslyn3/VContainer.SourceGenerator.Roslyn3.csproj index 25031b9d..a47c53a1 100644 --- a/VContainer.SourceGenerator.Roslyn3/VContainer.SourceGenerator.Roslyn3.csproj +++ b/VContainer.SourceGenerator.Roslyn3/VContainer.SourceGenerator.Roslyn3.csproj @@ -9,6 +9,7 @@ true true true + ROSLYN3 diff --git a/VContainer.SourceGenerator.Roslyn3/VContainerSourceGenerator.cs b/VContainer.SourceGenerator.Roslyn3/VContainerSourceGenerator.cs index 5bb61524..80fb785f 100644 --- a/VContainer.SourceGenerator.Roslyn3/VContainerSourceGenerator.cs +++ b/VContainer.SourceGenerator.Roslyn3/VContainerSourceGenerator.cs @@ -1,337 +1,63 @@ -using System.Linq; -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; -namespace VContainer.SourceGenerator +namespace VContainer.SourceGenerator; + +[Generator(LanguageNames.CSharp)] +public class VContainerSourceGenerator : ISourceGenerator { - [Generator(LanguageNames.CSharp)] - public class VContainerSourceGenerator : ISourceGenerator + public void Initialize(GeneratorInitializationContext context) { - public void Initialize(GeneratorInitializationContext context) - { - context.RegisterForSyntaxNotifications(() => new SyntaxCollector()); - } - - public void Execute(GeneratorExecutionContext context) - { - var moduleName = context.Compilation.SourceModule.Name; - if (moduleName.StartsWith("UnityEngine.")) return; - if (moduleName.StartsWith("UnityEditor.")) return; - if (moduleName.StartsWith("Unity.")) return; - if (moduleName.StartsWith("VContainer.") && !moduleName.Contains("Test")) return; - - var references = ReferenceSymbols.Create(context.Compilation); - if (references is null) return; - - var codeWriter = new CodeWriter(); - var syntaxCollector = (SyntaxCollector)context.SyntaxReceiver!; - foreach (var workItem in syntaxCollector.WorkItems) - { - var typeMeta = workItem.Analyze(in context, references); - if (typeMeta is null) continue; - - if (TryEmitGeneratedInjector(typeMeta, codeWriter, references, in context)) - { - var fullType = typeMeta.FullTypeName - .Replace("global::", "") - .Replace("<", "_") - .Replace(">", "_"); - context.AddSource($"{fullType}GeneratedInjector.g.cs", codeWriter.ToString()); - } - codeWriter.Clear(); - } - } + context.RegisterForSyntaxNotifications(() => new SyntaxCollector()); + } - static bool TryEmitGeneratedInjector( - TypeMeta typeMeta, - CodeWriter codeWriter, - ReferenceSymbols references, - in GeneratorExecutionContext context) + public void Execute(GeneratorExecutionContext context) + { + var moduleName = context.Compilation.SourceModule.Name; + if (moduleName.StartsWith("UnityEngine.")) return; + if (moduleName.StartsWith("UnityEditor.")) return; + if (moduleName.StartsWith("Unity.")) return; + if (moduleName.StartsWith("VContainer.") && !moduleName.Contains("Test")) return; + + var references = ReferenceSymbols.Create(context.Compilation); + if (references is null) return; + + var codeWriter = new CodeWriter(); + var syntaxCollector = (SyntaxCollector)context.SyntaxReceiver!; + foreach (var workItem in syntaxCollector.WorkItems) { - if (typeMeta.IsNested()) + if (workItem.TypeDeclarationSyntax is { } typeDeclarationSyntax) { - if (typeMeta.ExplicitInjectable) + var semanticModel = context.Compilation.GetSemanticModel(typeDeclarationSyntax.SyntaxTree); + var typeDeclarationCandidate = new TypeDeclarationCandidate(typeDeclarationSyntax, semanticModel); + if (typeDeclarationCandidate.Analyze(references) is { } typeMeta) { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.NestedNotSupported, - typeMeta.Syntax.Identifier.GetLocation(), - typeMeta.Symbol.Name)); + Execute(typeMeta, codeWriter, references, in context); + codeWriter.Clear(); } - return false; } - - if (typeMeta.Symbol.IsAbstract) + else if (workItem.RegisterInvocationSyntax is { } registerInvocationSyntax) { - if (typeMeta.ExplicitInjectable) + var semanticModel = context.Compilation.GetSemanticModel(registerInvocationSyntax.SyntaxTree); + var registerInvocationCandidate = new RegisterInvocationCandidate(registerInvocationSyntax, semanticModel); + var typeMetas = registerInvocationCandidate.Analyze(references); + foreach (var typeMeta in typeMetas) { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.AbstractNotAllow, - typeMeta.Syntax.Identifier.GetLocation(), - typeMeta.TypeName)); + Execute(typeMeta, codeWriter, references, in context); + codeWriter.Clear(); } - return false; - } - - if (typeMeta.IsGenerics) - { - return false; // TODO: - } - - codeWriter.AppendLine("using System;"); - codeWriter.AppendLine("using System.Collections.Generic;"); - codeWriter.AppendLine("using VContainer;"); - codeWriter.AppendLine(); - - var ns = typeMeta.Symbol.ContainingNamespace; - if (!ns.IsGlobalNamespace) - { - codeWriter.AppendLine($"namespace {ns}"); - codeWriter.BeginBlock(); } + } + } - var typeName = typeMeta.TypeName + static void Execute(TypeMeta typeMeta, CodeWriter codeWriter, ReferenceSymbols referenceSymbols, in GeneratorExecutionContext context) + { + if (Emitter.TryEmitGeneratedInjector(typeMeta, codeWriter, referenceSymbols, in context)) + { + var fullType = typeMeta.FullTypeName .Replace("global::", "") .Replace("<", "_") .Replace(">", "_"); - - var generateTypeName = $"{typeName}GeneratedInjector"; - using (codeWriter.BeginBlockScope($"class {generateTypeName} : IInjector")) - { - if (!TryEmitCreateInstanceMethod(typeMeta, codeWriter, references, in context)) - { - return false; - } - - codeWriter.AppendLine(); - - if (!TryEmitInjectMethod(typeMeta, codeWriter, in context)) - { - return false; - } - } - - if (!ns.IsGlobalNamespace) - { - codeWriter.EndBlock(); - } - - return true; - } - - static bool TryEmitInjectMethod( - TypeMeta typeMeta, - CodeWriter codeWriter, - in GeneratorExecutionContext context) - { - using (codeWriter.BeginBlockScope( - "public void Inject(object instance, IObjectResolver resolver, IReadOnlyList parameters)")) - { - if (typeMeta.InjectFields.Count <= 0 && - typeMeta.InjectProperties.Count <= 0 && - typeMeta.InjectMethods.Count <= 0) - { - codeWriter.AppendLine("return;"); - return true; - } - - codeWriter.AppendLine($"var __x = ({typeMeta.TypeName})instance;"); - - var error = false; - - // verify field - foreach (var fieldSymbol in typeMeta.InjectFields) - { - if (!fieldSymbol.CanBeCallFromInternal()) - { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.PrivateFieldNotSupported, - fieldSymbol.Locations.FirstOrDefault() ?? typeMeta.Syntax.GetLocation(), - fieldSymbol.Name)); - error = true; - } - - if (fieldSymbol.Type is ITypeParameterSymbol) - { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.GenericsNotSupported, - fieldSymbol.Locations.FirstOrDefault() ?? typeMeta.Syntax.GetLocation(), - fieldSymbol.Name)); - error = true; - } - } - - // verify property - foreach (var propSymbol in typeMeta.InjectProperties) - { - if (!propSymbol.CanBeCallFromInternal()) - { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.PrivatePropertyNotSupported, - propSymbol.Locations.FirstOrDefault() ?? typeMeta.Syntax.GetLocation(), - propSymbol.Name)); - error = true; - } - - if (propSymbol.Type is ITypeParameterSymbol) - { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.GenericsNotSupported, - propSymbol.Locations.FirstOrDefault() ?? typeMeta.Syntax.GetLocation(), - propSymbol.Name)); - error = true; - } - } - - // verify method - if (typeMeta.InjectMethods.Count > 1) - { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.GenericsNotSupported, - typeMeta.InjectMethods.First().Locations.FirstOrDefault() ?? typeMeta.Syntax.GetLocation(), - typeMeta.InjectMethods.First().Name)); - error = true; - } - - foreach (var methodSymbol in typeMeta.InjectMethods) - { - if (!methodSymbol.CanBeCallFromInternal()) - { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.PrivateMethodNotSupported, - methodSymbol.Locations.FirstOrDefault() ?? typeMeta.Syntax.GetLocation(), - methodSymbol.Name)); - error = true; - } - if (methodSymbol.Arity > 0) - { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.GenericsNotSupported, - methodSymbol.Locations.FirstOrDefault() ?? typeMeta.Syntax.GetLocation(), - methodSymbol.Name)); - error = true; - } - } - - if (error) - { - return false; - } - - foreach (var fieldSymbol in typeMeta.InjectFields) - { - var fieldTypeName = fieldSymbol.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - - codeWriter.AppendLine($"__x.{fieldSymbol.Name} = ({fieldTypeName})resolver.ResolveOrParameter(typeof({fieldTypeName}), \"{fieldSymbol.Name}\", parameters);"); - } - - foreach (var propSymbol in typeMeta.InjectProperties) - { - var propTypeName = propSymbol.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - - codeWriter.AppendLine($"__x.{propSymbol.Name} = ({propTypeName})resolver.ResolveOrParameter(typeof({propTypeName}), \"{propSymbol.Name}\", parameters);"); - } - - foreach (var methodSymbol in typeMeta.InjectMethods) - { - var parameters = methodSymbol.Parameters - .Select(param => - { - var paramType = - param.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - var paramName = param.Name; - return (paramType, paramName); - }) - .ToArray(); - - foreach (var (paramType, paramName) in parameters) - { - codeWriter.AppendLine( - $"var __{paramName} = resolver.ResolveOrParameter(typeof({paramType}), \"{paramName}\", parameters);"); - } - - var arguments = parameters.Select(x => $"({x.paramType})__{x.paramName}"); - codeWriter.AppendLine($"__x.{methodSymbol.Name}({string.Join(", ", arguments)});"); - } - return true; - } - } - - static bool TryEmitCreateInstanceMethod( - TypeMeta typeMeta, - CodeWriter codeWriter, - ReferenceSymbols references, - in GeneratorExecutionContext context) - { - if (typeMeta.ExplictInjectConstructors.Count > 1) - { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.MultipleCtorAttributeNotSupported, - typeMeta.Syntax.Identifier.GetLocation(), - typeMeta.TypeName)); - return false; - } - - var constructorSymbol = typeMeta.ExplictInjectConstructors.Count == 1 - ? typeMeta.ExplictInjectConstructors.First() - : typeMeta.Constructors.OrderByDescending(ctor => ctor.Parameters.Length).FirstOrDefault(); - - if (constructorSymbol != null) - { - if (!constructorSymbol.CanBeCallFromInternal()) - { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.PrivateConstructorNotSupported, - typeMeta.Syntax.Identifier.GetLocation(), - typeMeta.TypeName)); - return false; - } - - if (constructorSymbol.Arity > 0) - { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.GenericsNotSupported, - typeMeta.Syntax.Identifier.GetLocation(), - typeMeta.TypeName)); - return false; - } - } - - using (codeWriter.BeginBlockScope("public object CreateInstance(IObjectResolver resolver, IReadOnlyList parameters)")) - { - if (references.UnityEngineComponent != null && - typeMeta.InheritsFrom(references.UnityEngineComponent)) - { - codeWriter.AppendLine($"throw new NotSupportedException(\"UnityEngine.Component:{typeMeta.TypeName} cannot be `new`\");"); - return true; - } - if (constructorSymbol is null) - { - codeWriter.AppendLine($"var __instance = new {typeMeta.TypeName}();"); - codeWriter.AppendLine("Inject(__instance, resolver, parameters);"); - codeWriter.AppendLine("return __instance;"); - return true; - } - var parameters = constructorSymbol.Parameters - .Select(param => - { - var paramType = - param.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - var paramName = param.Name; - return (paramType, paramName); - }) - .ToArray(); - - foreach (var (paramType, paramName) in parameters) - { - codeWriter.AppendLine( - $"var __{paramName} = resolver.ResolveOrParameter(typeof({paramType}), \"{paramName}\", parameters);"); - } - - var arguments = parameters.Select(x => $"({x.paramType})__{x.paramName}"); - codeWriter.AppendLine($"var __instance = new {typeMeta.TypeName}({string.Join(", ", arguments)});"); - codeWriter.AppendLine("Inject(__instance, resolver, parameters);"); - codeWriter.AppendLine("return __instance;"); - } - return true; + context.AddSource($"{fullType}GeneratedInjector.g.cs", codeWriter.ToString()); } } -} +} \ No newline at end of file diff --git a/VContainer.SourceGenerator.Roslyn3/WorkItem.cs b/VContainer.SourceGenerator.Roslyn3/WorkItem.cs index 88b3fe67..ab95a516 100644 --- a/VContainer.SourceGenerator.Roslyn3/WorkItem.cs +++ b/VContainer.SourceGenerator.Roslyn3/WorkItem.cs @@ -1,39 +1,19 @@ -using System.Linq; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace VContainer.SourceGenerator -{ - class WorkItem - { - public TypeDeclarationSyntax Syntax { get; } +namespace VContainer.SourceGenerator; - public WorkItem(TypeDeclarationSyntax syntax) - { - Syntax = syntax; - } - - public TypeMeta? Analyze(in GeneratorExecutionContext context, ReferenceSymbols references) - { - var semanticModel = context.Compilation.GetSemanticModel(Syntax.SyntaxTree); - var symbol = semanticModel.GetDeclaredSymbol(Syntax, context.CancellationToken); - if (symbol is not INamedTypeSymbol typeSymbol) - { - return null; - } - var isAttribute = typeSymbol.GetAllBaseTypes().Any(x => SymbolEqualityComparer.Default.Equals(x, references.AttributeBase)); - if (isAttribute) - { - return null; - } +class WorkItem +{ + public TypeDeclarationSyntax? TypeDeclarationSyntax { get; } + public InvocationExpressionSyntax? RegisterInvocationSyntax { get; } - var injectIgnore = symbol.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, references.VContainerInjectIgnoreAttribute)); - if (injectIgnore) - { - return null; - } + public WorkItem(TypeDeclarationSyntax typeDeclarationSyntax) + { + TypeDeclarationSyntax = typeDeclarationSyntax; + } - return new TypeMeta(Syntax, typeSymbol, references); - } + public WorkItem(InvocationExpressionSyntax registerInvocationCandidate) + { + RegisterInvocationSyntax = registerInvocationCandidate; } -} +} \ No newline at end of file diff --git a/VContainer.SourceGenerator/Analyzer.cs b/VContainer.SourceGenerator/Analyzer.cs new file mode 100644 index 00000000..f1cbc25a --- /dev/null +++ b/VContainer.SourceGenerator/Analyzer.cs @@ -0,0 +1,106 @@ +using System.Collections.Generic; +using System.Threading; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace VContainer.SourceGenerator; + +static class Analyzer +{ + public static TypeMeta? AnalyzeTypeSymbol( + ITypeSymbol symbol, + ReferenceSymbols referenceSymbols, + TypeDeclarationSyntax? syntax = null, + CancellationToken cancellation = default) + { + if (symbol is not INamedTypeSymbol typeSymbol) + { + return null; + } + if (typeSymbol.TypeKind is TypeKind.Interface or TypeKind.Struct or TypeKind.Enum) + { + return null; + } + if (typeSymbol.IsAbstract || typeSymbol.IsStatic) + { + return null; + } + + if (typeSymbol.ContainingModule.Name is "VContainer" or "VContainer.Standalone") + { + return null; + } + + foreach (var baseTypeSymbol in typeSymbol.GetAllBaseTypes()) + { + if (SymbolEqualityComparer.Default.Equals(baseTypeSymbol, referenceSymbols.AttributeBase)) + { + return null; + } + } + + foreach (var attributeData in typeSymbol.GetAttributes()) + { + if (attributeData.AttributeClass != null) + { + // Ignore + if (SymbolEqualityComparer.Default.Equals( + attributeData.AttributeClass, + referenceSymbols.VContainerInjectIgnoreAttribute)) + { + return null; + } + } + + } + return new TypeMeta(typeSymbol, referenceSymbols, syntax); + } +} + +record struct TypeDeclarationCandidate(TypeDeclarationSyntax Syntax, SemanticModel SemanticModel) +{ + public TypeMeta? Analyze(ReferenceSymbols referenceSymbols, CancellationToken cancellation = default) + { + var symbol = SemanticModel.GetDeclaredSymbol(Syntax); + if (symbol is ITypeSymbol typeSymbol) + return Analyzer.AnalyzeTypeSymbol(typeSymbol, referenceSymbols, Syntax); + return null; + } +} + +record struct RegisterInvocationCandidate(InvocationExpressionSyntax Syntax, SemanticModel SemanticModel) +{ + public IEnumerable Analyze(ReferenceSymbols referenceSymbols, CancellationToken cancellation = default) + { + var symbol = SemanticModel.GetSymbolInfo(Syntax).Symbol; + if (symbol is IMethodSymbol methodSymbol) + { + var typeSymbol = methodSymbol.ReceiverType; + if (SymbolEqualityComparer.Default.Equals(typeSymbol, referenceSymbols.ContainerBuilderInterface)) + { + if (methodSymbol.Arity > 0) + { + foreach (var typeArgument in methodSymbol.TypeArguments) + { + var typeMeta = Analyzer.AnalyzeTypeSymbol(typeArgument, referenceSymbols, cancellation: cancellation); + if (typeMeta != null) + { + yield return typeMeta; + } + } + } + else + { + foreach (var p in methodSymbol.Parameters) + { + var typeMeta = Analyzer.AnalyzeTypeSymbol(p.Type, referenceSymbols, cancellation: cancellation); + if (typeMeta != null) + { + yield return typeMeta; + } + } + } + } + } + } +} \ No newline at end of file diff --git a/VContainer.SourceGenerator/Emitter.cs b/VContainer.SourceGenerator/Emitter.cs new file mode 100644 index 00000000..29373bab --- /dev/null +++ b/VContainer.SourceGenerator/Emitter.cs @@ -0,0 +1,306 @@ +using System.Linq; +using Microsoft.CodeAnalysis; +#if ROSLYN3 +using SourceProductionContext = Microsoft.CodeAnalysis.GeneratorExecutionContext; +#endif + +namespace VContainer.SourceGenerator; + +static class Emitter +{ + public static bool TryEmitGeneratedInjector( + TypeMeta typeMeta, + CodeWriter codeWriter, + ReferenceSymbols references, + in SourceProductionContext context) + { + if (typeMeta.IsNested()) + { + if (typeMeta.ExplicitInjectable) + { + context.ReportDiagnostic(Diagnostic.Create( + DiagnosticDescriptors.NestedNotSupported, + typeMeta.GetLocation(), + typeMeta.Symbol.Name)); + } + return false; + } + + if (typeMeta.Symbol.IsAbstract) + { + if (typeMeta.ExplicitInjectable) + { + context.ReportDiagnostic(Diagnostic.Create( + DiagnosticDescriptors.AbstractNotAllow, + typeMeta.GetLocation(), + typeMeta.TypeName)); + } + return false; + } + + if (typeMeta.IsGenerics) + { + return false; // TODO: + } + + codeWriter.AppendLine("using System;"); + codeWriter.AppendLine("using System.Collections.Generic;"); + codeWriter.AppendLine("using VContainer;"); + codeWriter.AppendLine(); + + var ns = typeMeta.Symbol.ContainingNamespace; + if (!ns.IsGlobalNamespace) + { + codeWriter.AppendLine($"namespace {ns}"); + codeWriter.BeginBlock(); + } + + var typeName = typeMeta.TypeName + .Replace("global::", "") + .Replace("<", "_") + .Replace(">", "_"); + + var generateTypeName = $"{typeName}GeneratedInjector"; + + codeWriter.AppendLine("[Preserve]"); + using (codeWriter.BeginBlockScope($"class {generateTypeName} : IInjector")) + { + codeWriter.AppendLine(); + if (!TryEmitCreateInstanceMethod(typeMeta, codeWriter, references, in context)) + { + return false; + } + + codeWriter.AppendLine(); + + if (!TryEmitInjectMethod(typeMeta, codeWriter, in context)) + { + return false; + } + } + + if (!ns.IsGlobalNamespace) + { + codeWriter.EndBlock(); + } + + return true; + } + + static bool TryEmitInjectMethod( + TypeMeta typeMeta, + CodeWriter codeWriter, + in SourceProductionContext context) + { + using (codeWriter.BeginBlockScope( + "public void Inject(object instance, IObjectResolver resolver, IReadOnlyList parameters)")) + { + if (typeMeta.InjectFields.Count <= 0 && + typeMeta.InjectProperties.Count <= 0 && + typeMeta.InjectMethods.Count <= 0) + { + codeWriter.AppendLine("return;"); + return true; + } + + codeWriter.AppendLine($"var __x = ({typeMeta.TypeName})instance;"); + + var error = false; + + // verify field + foreach (var fieldSymbol in typeMeta.InjectFields) + { + if (!fieldSymbol.CanBeCallFromInternal()) + { + context.ReportDiagnostic(Diagnostic.Create( + DiagnosticDescriptors.PrivateFieldNotSupported, + fieldSymbol.Locations.FirstOrDefault() ?? typeMeta.GetLocation(), + fieldSymbol.Name)); + error = true; + } + + if (fieldSymbol.Type is ITypeParameterSymbol) + { + context.ReportDiagnostic(Diagnostic.Create( + DiagnosticDescriptors.GenericsNotSupported, + fieldSymbol.Locations.FirstOrDefault() ?? typeMeta.GetLocation(), + fieldSymbol.Name)); + error = true; + } + } + + // verify property + foreach (var propSymbol in typeMeta.InjectProperties) + { + if (!propSymbol.CanBeCallFromInternal()) + { + context.ReportDiagnostic(Diagnostic.Create( + DiagnosticDescriptors.PrivatePropertyNotSupported, + propSymbol.Locations.FirstOrDefault() ?? typeMeta.GetLocation(), + propSymbol.Name)); + error = true; + } + + if (propSymbol.Type is ITypeParameterSymbol) + { + context.ReportDiagnostic(Diagnostic.Create( + DiagnosticDescriptors.GenericsNotSupported, + propSymbol.Locations.FirstOrDefault() ?? typeMeta.GetLocation(), + propSymbol.Name)); + error = true; + } + } + + // verify method + if (typeMeta.InjectMethods.Count > 1) + { + context.ReportDiagnostic(Diagnostic.Create( + DiagnosticDescriptors.GenericsNotSupported, + typeMeta.InjectMethods.First().Locations.FirstOrDefault() ?? typeMeta.GetLocation(), + typeMeta.InjectMethods.First().Name)); + error = true; + } + + foreach (var methodSymbol in typeMeta.InjectMethods) + { + if (!methodSymbol.CanBeCallFromInternal()) + { + context.ReportDiagnostic(Diagnostic.Create( + DiagnosticDescriptors.PrivateMethodNotSupported, + methodSymbol.Locations.FirstOrDefault() ?? typeMeta.GetLocation(), + methodSymbol.Name)); + error = true; + } + if (methodSymbol.Arity > 0) + { + context.ReportDiagnostic(Diagnostic.Create( + DiagnosticDescriptors.GenericsNotSupported, + methodSymbol.Locations.FirstOrDefault() ?? typeMeta.GetLocation(), + methodSymbol.Name)); + error = true; + } + } + + if (error) + { + return false; + } + + foreach (var fieldSymbol in typeMeta.InjectFields) + { + var fieldTypeName = fieldSymbol.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + + codeWriter.AppendLine($"__x.{fieldSymbol.Name} = ({fieldTypeName})resolver.ResolveOrParameter(typeof({fieldTypeName}), \"{fieldSymbol.Name}\", parameters);"); + } + + foreach (var propSymbol in typeMeta.InjectProperties) + { + var propTypeName = propSymbol.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + + codeWriter.AppendLine($"__x.{propSymbol.Name} = ({propTypeName})resolver.ResolveOrParameter(typeof({propTypeName}), \"{propSymbol.Name}\", parameters);"); + } + + foreach (var methodSymbol in typeMeta.InjectMethods) + { + var parameters = methodSymbol.Parameters + .Select(param => + { + var paramType = + param.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + var paramName = param.Name; + return (paramType, paramName); + }) + .ToArray(); + + foreach (var (paramType, paramName) in parameters) + { + codeWriter.AppendLine( + $"var __{paramName} = resolver.ResolveOrParameter(typeof({paramType}), \"{paramName}\", parameters);"); + } + + var arguments = parameters.Select(x => $"({x.paramType})__{x.paramName}"); + codeWriter.AppendLine($"__x.{methodSymbol.Name}({string.Join(", ", arguments)});"); + } + return true; + } + } + + public static bool TryEmitCreateInstanceMethod( + TypeMeta typeMeta, + CodeWriter codeWriter, + ReferenceSymbols references, + in SourceProductionContext context) + { + if (typeMeta.ExplictInjectConstructors.Count > 1) + { + context.ReportDiagnostic(Diagnostic.Create( + DiagnosticDescriptors.MultipleCtorAttributeNotSupported, + typeMeta.GetLocation(), + typeMeta.TypeName)); + return false; + } + + var constructorSymbol = typeMeta.ExplictInjectConstructors.Count == 1 + ? typeMeta.ExplictInjectConstructors.First() + : typeMeta.Constructors.OrderByDescending(ctor => ctor.Parameters.Length).FirstOrDefault(); + + if (constructorSymbol != null) + { + if (!constructorSymbol.CanBeCallFromInternal()) + { + context.ReportDiagnostic(Diagnostic.Create( + DiagnosticDescriptors.PrivateConstructorNotSupported, + typeMeta.GetLocation(), + typeMeta.TypeName)); + return false; + } + + if (constructorSymbol.Arity > 0) + { + context.ReportDiagnostic(Diagnostic.Create( + DiagnosticDescriptors.GenericsNotSupported, + typeMeta.GetLocation(), + typeMeta.TypeName)); + return false; + } + } + + using (codeWriter.BeginBlockScope("public object CreateInstance(IObjectResolver resolver, IReadOnlyList parameters)")) + { + if (references.UnityEngineComponent != null && + typeMeta.InheritsFrom(references.UnityEngineComponent)) + { + codeWriter.AppendLine($"throw new NotSupportedException(\"UnityEngine.Component:{typeMeta.TypeName} cannot be `new`\");"); + return true; + } + if (constructorSymbol is null) + { + codeWriter.AppendLine($"var __instance = new {typeMeta.TypeName}();"); + codeWriter.AppendLine("Inject(__instance, resolver, parameters);"); + codeWriter.AppendLine("return __instance;"); + return true; + } + var parameters = constructorSymbol.Parameters + .Select(param => + { + var paramType = + param.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + var paramName = param.Name; + return (paramType, paramName); + }) + .ToArray(); + + foreach (var (paramType, paramName) in parameters) + { + codeWriter.AppendLine( + $"var __{paramName} = resolver.ResolveOrParameter(typeof({paramType}), \"{paramName}\", parameters);"); + } + + var arguments = parameters.Select(x => $"({x.paramType})__{x.paramName}"); + codeWriter.AppendLine($"var __instance = new {typeMeta.TypeName}({string.Join(", ", arguments)});"); + codeWriter.AppendLine("Inject(__instance, resolver, parameters);"); + codeWriter.AppendLine("return __instance;"); + } + return true; + } +} diff --git a/VContainer.SourceGenerator/ReferenceSymbols.cs b/VContainer.SourceGenerator/ReferenceSymbols.cs index e892dcf8..671a3b71 100644 --- a/VContainer.SourceGenerator/ReferenceSymbols.cs +++ b/VContainer.SourceGenerator/ReferenceSymbols.cs @@ -12,6 +12,7 @@ public class ReferenceSymbols return new ReferenceSymbols { + ContainerBuilderInterface = compilation.GetTypeByMetadataName("VContainer.IContainerBuilder")!, VContainerInjectAttribute = injectAttribute, VContainerInjectIgnoreAttribute = compilation.GetTypeByMetadataName("VContainer.InjectIgnoreAttribute")!, AttributeBase = compilation.GetTypeByMetadataName("System.Attribute")!, @@ -19,6 +20,7 @@ public class ReferenceSymbols }; } + public INamedTypeSymbol ContainerBuilderInterface { get; private set; } = default!; public INamedTypeSymbol VContainerInjectAttribute { get; private set; } = default!; public INamedTypeSymbol VContainerInjectIgnoreAttribute { get; private set; } = default!; public INamedTypeSymbol AttributeBase { get; private set; } = default!; diff --git a/VContainer.SourceGenerator/TypeMeta.cs b/VContainer.SourceGenerator/TypeMeta.cs index 190b8b94..b26edd27 100644 --- a/VContainer.SourceGenerator/TypeMeta.cs +++ b/VContainer.SourceGenerator/TypeMeta.cs @@ -7,7 +7,6 @@ namespace VContainer.SourceGenerator; class TypeMeta { - public TypeDeclarationSyntax Syntax { get; } public INamedTypeSymbol Symbol { get; } public string TypeName { get; } public string FullTypeName { get; } @@ -21,13 +20,14 @@ class TypeMeta public bool IsGenerics => Symbol.Arity > 0; - ReferenceSymbols references; + readonly ReferenceSymbols references; + readonly TypeDeclarationSyntax? syntax; - public TypeMeta(TypeDeclarationSyntax syntax, INamedTypeSymbol symbol, ReferenceSymbols references) + public TypeMeta(INamedTypeSymbol symbol, ReferenceSymbols references, TypeDeclarationSyntax? syntax = null) { - Syntax = syntax; Symbol = symbol; this.references = references; + this.syntax = syntax; TypeName = symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); FullTypeName = symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); @@ -44,6 +44,13 @@ public TypeMeta(TypeDeclarationSyntax syntax, INamedTypeSymbol symbol, Reference InjectMethods.Count > 0; } + public Location GetLocation() + { + return syntax?.Identifier.GetLocation() ?? + Symbol.Locations.FirstOrDefault() ?? + Location.None; + } + public bool InheritsFrom(INamedTypeSymbol baseSymbol) { var baseName = baseSymbol.ToString(); @@ -109,6 +116,6 @@ IReadOnlyList GetInjectMethods() public bool IsNested() { - return Syntax.Parent is TypeDeclarationSyntax; + return Symbol.ContainingType != null; } } \ No newline at end of file diff --git a/VContainer.SourceGenerator/UsingDirectiveMeta.cs b/VContainer.SourceGenerator/UsingDirectiveMeta.cs new file mode 100644 index 00000000..f1564372 --- /dev/null +++ b/VContainer.SourceGenerator/UsingDirectiveMeta.cs @@ -0,0 +1,32 @@ +using System; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +readonly struct UsingDirectiveMeta : IEquatable +{ + public readonly bool UseVContainer; + public readonly string? AliasName; + + public UsingDirectiveMeta(UsingDirectiveSyntax usingDirective) + { + UseVContainer = usingDirective.Alias == null; + AliasName = UseVContainer ? "" : usingDirective.Alias?.Name.ToString(); + } + + public bool Equals(UsingDirectiveMeta other) + { + return UseVContainer == other.UseVContainer && AliasName == other.AliasName; + } + + public override bool Equals(object? obj) + { + return obj is UsingDirectiveMeta other && Equals(other); + } + + public override int GetHashCode() + { + unchecked + { + return (UseVContainer.GetHashCode() * 397) ^ (AliasName != null ? AliasName.GetHashCode() : 0); + } + } +} \ No newline at end of file diff --git a/VContainer.SourceGenerator/VContainerIncrementalSourceGenerator.cs b/VContainer.SourceGenerator/VContainerIncrementalSourceGenerator.cs index 56a79084..cd400edc 100644 --- a/VContainer.SourceGenerator/VContainerIncrementalSourceGenerator.cs +++ b/VContainer.SourceGenerator/VContainerIncrementalSourceGenerator.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; using System.Linq; -using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -12,12 +10,69 @@ public class VContainerIncrementalSourceGenerator : IIncrementalGenerator { public void Initialize(IncrementalGeneratorInitializationContext context) { - var provider = context.SyntaxProvider + // Assembly filter + var vcontainerReferenceValueProvider = context.CompilationProvider + .Select((compilation, cancellation) => + { + if (compilation.AssemblyName?.StartsWith("VContainer") == true && + !compilation.AssemblyName.Contains("Test")) + { + return false; + } + + if (compilation.AssemblyName?.StartsWith("UnityEngine.") == true || + compilation.AssemblyName?.StartsWith("Unity.") == true) + { + return false; + } + + foreach (var referencedAssemblyName in compilation.ReferencedAssemblyNames) + { + if (referencedAssemblyName.Name.StartsWith("VContainer")) + return true; + } + return false; + }); + + // Find Types based on Register* methods + var registerInvocations = context.SyntaxProvider .CreateSyntaxProvider( - (s, _) => + (s, cancellation) => { - if (s is not ClassDeclarationSyntax syntax) - return false; + if (s.IsKind(SyntaxKind.InvocationExpression)) + { + if (s is InvocationExpressionSyntax + { + Expression: MemberAccessExpressionSyntax + { + Expression: IdentifierNameSyntax + } memberAccess + }) + { + if (memberAccess.Name.Identifier.Text.StartsWith("Register")) + { + return true; + } + } + } + return false; + }, + (ctx, cancellation) => ctx) + .Combine(vcontainerReferenceValueProvider) + .Where(tuple => tuple.Right) + .Select((tuple, cancellation) => + { + var invocationExpressionSyntax = (InvocationExpressionSyntax)tuple.Left.Node; + var semanticModel = tuple.Left.SemanticModel; + return new RegisterInvocationCandidate(invocationExpressionSyntax, semanticModel); + }); + + // Find types by explicit [Inject] + var typeDeclarations = context.SyntaxProvider + .CreateSyntaxProvider((s, cancellation) => + { + if (!s.IsKind(SyntaxKind.ClassDeclaration)) return false; + if (s is not ClassDeclarationSyntax syntax) return false; if (syntax.Modifiers.Any(modifier => modifier.IsKind(SyntaxKind.AbstractKeyword) || modifier.IsKind(SyntaxKind.StaticKeyword))) @@ -27,22 +82,24 @@ public void Initialize(IncrementalGeneratorInitializationContext context) return true; }, - (ctx, cancellation) => Analyze(ctx, cancellation)) - .Where(x => x.HasValue) - .WithComparer(Comparer.Instance); + (ctx, cancellation) => ctx) + .Combine(vcontainerReferenceValueProvider) + .Where(tuple => tuple.Right) + .Select((tuple, cancellatio) => + { + return new TypeDeclarationCandidate((TypeDeclarationSyntax)tuple.Left.Node, tuple.Left.SemanticModel); + }); // Generate the source code. context.RegisterSourceOutput( - context.CompilationProvider.Combine(provider.Collect()), - (sourceProductionContext, t) => + context.CompilationProvider + .Combine(typeDeclarations.Collect()) + .Combine(registerInvocations.Collect()), + (sourceProductionContext, tuple) => { - var (compilation, list) = t; - - var moduleName = compilation.SourceModule.Name; - if (moduleName.StartsWith("UnityEngine.")) return; - if (moduleName.StartsWith("UnityEditor.")) return; - if (moduleName.StartsWith("Unity.")) return; - if (moduleName.StartsWith("VContainer.") && !moduleName.Contains("Test")) return; + var compilation = tuple.Left.Left; + var typeDeclarationCandidates = tuple.Left.Right; + var registerInvocationCandidates = tuple.Right; var references = ReferenceSymbols.Create(compilation); if (references is null) @@ -52,15 +109,25 @@ public void Initialize(IncrementalGeneratorInitializationContext context) var codeWriter = new CodeWriter(); - foreach (var x in list) + var typeMetas = typeDeclarationCandidates + .Select(x => x.Analyze(references)) + .Where(x => x != null && + (x.ExplictInjectConstructors.Count > 0 || + x.InjectFields.Count > 0 || + x.InjectProperties.Count > 0 || + x.InjectMethods.Count > 0)); + + var typeMetasFromRegister = registerInvocationCandidates + .SelectMany(x => x.Analyze(references)); + + foreach (var typeMeta in typeMetas + .Concat(typeMetasFromRegister) + .Where(x => x != null) + .DistinctBy(x => x!.Symbol, SymbolEqualityComparer.Default)) { - var (syntax, symbol) = x!.Value; - - var typeMeta = new TypeMeta(syntax, symbol, references); - - if (TryEmitGeneratedInjector(typeMeta, codeWriter, references, in sourceProductionContext)) + if (Emitter.TryEmitGeneratedInjector(typeMeta!, codeWriter, references, in sourceProductionContext)) { - var fullType = typeMeta.FullTypeName + var fullType = typeMeta!.FullTypeName .Replace("global::", "") .Replace("<", "_") .Replace(">", "_"); @@ -70,354 +137,4 @@ public void Initialize(IncrementalGeneratorInitializationContext context) } }); } - - static (TypeDeclarationSyntax, INamedTypeSymbol)? Analyze( - GeneratorSyntaxContext context, - CancellationToken cancellation) - { - if (context.Node is not TypeDeclarationSyntax syntax) - { - return null; - } - - var symbol = context.SemanticModel.GetDeclaredSymbol(context.Node, cancellation); - if (symbol is not INamedTypeSymbol typeSymbol) - { - return null; - } - - foreach (var baseTypeSymbol in typeSymbol.GetAllBaseTypes()) - { - var name = baseTypeSymbol.ToDisplayString(); - // Ignore - if (name == "System.Attributes") - { - return null; - } - } - - foreach (var attributeListSyntax in syntax.AttributeLists) - { - foreach (var attributeSyntax in attributeListSyntax.Attributes) - { - if (context.SemanticModel.GetSymbolInfo(attributeSyntax).Symbol is not IMethodSymbol attributeSymbol) - continue; - - var attributeName = attributeSymbol.ContainingType.ToDisplayString(); - - // Ignore - if (attributeName == "VContainer.InjectIgnoreAttribute") - return null; - } - } - return (syntax, typeSymbol); - } - - static bool TryEmitGeneratedInjector( - TypeMeta typeMeta, - CodeWriter codeWriter, - ReferenceSymbols references, - in SourceProductionContext context) - { - if (typeMeta.IsNested()) - { - if (typeMeta.ExplicitInjectable) - { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.NestedNotSupported, - typeMeta.Syntax.Identifier.GetLocation(), - typeMeta.Symbol.Name)); - } - return false; - } - - if (typeMeta.Symbol.IsAbstract) - { - if (typeMeta.ExplicitInjectable) - { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.AbstractNotAllow, - typeMeta.Syntax.Identifier.GetLocation(), - typeMeta.TypeName)); - } - return false; - } - - if (typeMeta.IsGenerics) - { - return false; // TODO: - } - - codeWriter.AppendLine("using System;"); - codeWriter.AppendLine("using System.Collections.Generic;"); - codeWriter.AppendLine("using VContainer;"); - codeWriter.AppendLine(); - - var ns = typeMeta.Symbol.ContainingNamespace; - if (!ns.IsGlobalNamespace) - { - codeWriter.AppendLine($"namespace {ns}"); - codeWriter.BeginBlock(); - } - - var typeName = typeMeta.TypeName - .Replace("global::", "") - .Replace("<", "_") - .Replace(">", "_"); - - var generateTypeName = $"{typeName}GeneratedInjector"; - using (codeWriter.BeginBlockScope($"class {generateTypeName} : IInjector")) - { - if (!TryEmitCreateInstanceMethod(typeMeta, codeWriter, references, in context)) - { - return false; - } - - codeWriter.AppendLine(); - - if (!TryEmitInjectMethod(typeMeta, codeWriter, in context)) - { - return false; - } - } - - if (!ns.IsGlobalNamespace) - { - codeWriter.EndBlock(); - } - - return true; - } - - static bool TryEmitInjectMethod( - TypeMeta typeMeta, - CodeWriter codeWriter, - in SourceProductionContext context) - { - using (codeWriter.BeginBlockScope( - "public void Inject(object instance, IObjectResolver resolver, IReadOnlyList parameters)")) - { - if (typeMeta.InjectFields.Count <= 0 && - typeMeta.InjectProperties.Count <= 0 && - typeMeta.InjectMethods.Count <= 0) - { - codeWriter.AppendLine("return;"); - return true; - } - - codeWriter.AppendLine($"var __x = ({typeMeta.TypeName})instance;"); - - var error = false; - - // verify field - foreach (var fieldSymbol in typeMeta.InjectFields) - { - if (!fieldSymbol.CanBeCallFromInternal()) - { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.PrivateFieldNotSupported, - fieldSymbol.Locations.FirstOrDefault() ?? typeMeta.Syntax.GetLocation(), - fieldSymbol.Name)); - error = true; - } - - if (fieldSymbol.Type is ITypeParameterSymbol) - { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.GenericsNotSupported, - fieldSymbol.Locations.FirstOrDefault() ?? typeMeta.Syntax.GetLocation(), - fieldSymbol.Name)); - error = true; - } - } - - // verify property - foreach (var propSymbol in typeMeta.InjectProperties) - { - if (!propSymbol.CanBeCallFromInternal()) - { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.PrivatePropertyNotSupported, - propSymbol.Locations.FirstOrDefault() ?? typeMeta.Syntax.GetLocation(), - propSymbol.Name)); - error = true; - } - - if (propSymbol.Type is ITypeParameterSymbol) - { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.GenericsNotSupported, - propSymbol.Locations.FirstOrDefault() ?? typeMeta.Syntax.GetLocation(), - propSymbol.Name)); - error = true; - } - } - - // verify method - if (typeMeta.InjectMethods.Count > 1) - { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.GenericsNotSupported, - typeMeta.InjectMethods.First().Locations.FirstOrDefault() ?? typeMeta.Syntax.GetLocation(), - typeMeta.InjectMethods.First().Name)); - error = true; - } - - foreach (var methodSymbol in typeMeta.InjectMethods) - { - if (!methodSymbol.CanBeCallFromInternal()) - { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.PrivateMethodNotSupported, - methodSymbol.Locations.FirstOrDefault() ?? typeMeta.Syntax.GetLocation(), - methodSymbol.Name)); - error = true; - } - if (methodSymbol.Arity > 0) - { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.GenericsNotSupported, - methodSymbol.Locations.FirstOrDefault() ?? typeMeta.Syntax.GetLocation(), - methodSymbol.Name)); - error = true; - } - } - - if (error) - { - return false; - } - - foreach (var fieldSymbol in typeMeta.InjectFields) - { - var fieldTypeName = fieldSymbol.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - - codeWriter.AppendLine($"__x.{fieldSymbol.Name} = ({fieldTypeName})resolver.ResolveOrParameter(typeof({fieldTypeName}), \"{fieldSymbol.Name}\", parameters);"); - } - - foreach (var propSymbol in typeMeta.InjectProperties) - { - var propTypeName = propSymbol.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - - codeWriter.AppendLine($"__x.{propSymbol.Name} = ({propTypeName})resolver.ResolveOrParameter(typeof({propTypeName}), \"{propSymbol.Name}\", parameters);"); - } - - foreach (var methodSymbol in typeMeta.InjectMethods) - { - var parameters = methodSymbol.Parameters - .Select(param => - { - var paramType = - param.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - var paramName = param.Name; - return (paramType, paramName); - }) - .ToArray(); - - foreach (var (paramType, paramName) in parameters) - { - codeWriter.AppendLine( - $"var __{paramName} = resolver.ResolveOrParameter(typeof({paramType}), \"{paramName}\", parameters);"); - } - - var arguments = parameters.Select(x => $"({x.paramType})__{x.paramName}"); - codeWriter.AppendLine($"__x.{methodSymbol.Name}({string.Join(", ", arguments)});"); - } - return true; - } - } - - static bool TryEmitCreateInstanceMethod( - TypeMeta typeMeta, - CodeWriter codeWriter, - ReferenceSymbols references, - in SourceProductionContext context) - { - if (typeMeta.ExplictInjectConstructors.Count > 1) - { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.MultipleCtorAttributeNotSupported, - typeMeta.Syntax.Identifier.GetLocation(), - typeMeta.TypeName)); - return false; - } - - var constructorSymbol = typeMeta.ExplictInjectConstructors.Count == 1 - ? typeMeta.ExplictInjectConstructors.First() - : typeMeta.Constructors.OrderByDescending(ctor => ctor.Parameters.Length).FirstOrDefault(); - - if (constructorSymbol != null) - { - if (!constructorSymbol.CanBeCallFromInternal()) - { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.PrivateConstructorNotSupported, - typeMeta.Syntax.Identifier.GetLocation(), - typeMeta.TypeName)); - return false; - } - - if (constructorSymbol.Arity > 0) - { - context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.GenericsNotSupported, - typeMeta.Syntax.Identifier.GetLocation(), - typeMeta.TypeName)); - return false; - } - } - - using (codeWriter.BeginBlockScope("public object CreateInstance(IObjectResolver resolver, IReadOnlyList parameters)")) - { - if (references.UnityEngineComponent != null && - typeMeta.InheritsFrom(references.UnityEngineComponent)) - { - codeWriter.AppendLine($"throw new NotSupportedException(\"UnityEngine.Component:{typeMeta.TypeName} cannot be `new`\");"); - return true; - } - if (constructorSymbol is null) - { - codeWriter.AppendLine($"var __instance = new {typeMeta.TypeName}();"); - codeWriter.AppendLine("Inject(__instance, resolver, parameters);"); - codeWriter.AppendLine("return __instance;"); - return true; - } - var parameters = constructorSymbol.Parameters - .Select(param => - { - var paramType = - param.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - var paramName = param.Name; - return (paramType, paramName); - }) - .ToArray(); - - foreach (var (paramType, paramName) in parameters) - { - codeWriter.AppendLine( - $"var __{paramName} = resolver.ResolveOrParameter(typeof({paramType}), \"{paramName}\", parameters);"); - } - - var arguments = parameters.Select(x => $"({x.paramType})__{x.paramName}"); - codeWriter.AppendLine($"var __instance = new {typeMeta.TypeName}({string.Join(", ", arguments)});"); - codeWriter.AppendLine("Inject(__instance, resolver, parameters);"); - codeWriter.AppendLine("return __instance;"); - } - return true; - } } - -class Comparer : IEqualityComparer<(TypeDeclarationSyntax, INamedTypeSymbol)?> -{ - public static readonly Comparer Instance = new(); - - public bool Equals((TypeDeclarationSyntax, INamedTypeSymbol)? x, (TypeDeclarationSyntax, INamedTypeSymbol)? y) - { - return x!.Value.Item1.Equals(y!.Value.Item1); - } - - public int GetHashCode((TypeDeclarationSyntax, INamedTypeSymbol)? obj) - { - return obj!.Value.Item1.GetHashCode(); - } -} \ No newline at end of file diff --git a/VContainer/UserSettings/Layouts/default-2021.dwlt b/VContainer/UserSettings/Layouts/default-2021.dwlt index 5867e2fe..e8483971 100644 --- a/VContainer/UserSettings/Layouts/default-2021.dwlt +++ b/VContainer/UserSettings/Layouts/default-2021.dwlt @@ -48,7 +48,7 @@ MonoBehaviour: m_MinSize: {x: 300, y: 200} m_MaxSize: {x: 24288, y: 16192} vertical: 0 - controlID: 37 + controlID: 83 --- !u!114 &3 MonoBehaviour: m_ObjectHideFlags: 52 @@ -68,8 +68,8 @@ MonoBehaviour: y: 0 width: 383.5 height: 977 - m_MinSize: {x: 275, y: 50} - m_MaxSize: {x: 4000, y: 4000} + m_MinSize: {x: 276, y: 71} + m_MaxSize: {x: 4001, y: 4021} m_ActualView: {fileID: 13} m_Panes: - {fileID: 13} @@ -94,8 +94,8 @@ MonoBehaviour: y: 0 width: 305.5 height: 579 - m_MinSize: {x: 200, y: 200} - m_MaxSize: {x: 4000, y: 4000} + m_MinSize: {x: 201, y: 221} + m_MaxSize: {x: 4001, y: 4021} m_ActualView: {fileID: 14} m_Panes: - {fileID: 14} @@ -120,8 +120,8 @@ MonoBehaviour: y: 579 width: 1254.5 height: 398 - m_MinSize: {x: 100, y: 100} - m_MaxSize: {x: 4000, y: 4000} + m_MinSize: {x: 101, y: 121} + m_MaxSize: {x: 4001, y: 4021} m_ActualView: {fileID: 17} m_Panes: - {fileID: 12} @@ -223,7 +223,7 @@ MonoBehaviour: m_MinSize: {x: 200, y: 200} m_MaxSize: {x: 16192, y: 16192} vertical: 1 - controlID: 38 + controlID: 84 --- !u!114 &10 MonoBehaviour: m_ObjectHideFlags: 52 @@ -248,7 +248,7 @@ MonoBehaviour: m_MinSize: {x: 200, y: 100} m_MaxSize: {x: 16192, y: 8096} vertical: 0 - controlID: 39 + controlID: 85 --- !u!114 &11 MonoBehaviour: m_ObjectHideFlags: 52 @@ -268,8 +268,8 @@ MonoBehaviour: y: 0 width: 949 height: 579 - m_MinSize: {x: 200, y: 200} - m_MaxSize: {x: 4000, y: 4000} + m_MinSize: {x: 202, y: 221} + m_MaxSize: {x: 4002, y: 4021} m_ActualView: {fileID: 15} m_Panes: - {fileID: 15} @@ -292,7 +292,7 @@ MonoBehaviour: m_MaxSize: {x: 10000, y: 10000} m_TitleContent: m_Text: Project - m_Image: {fileID: -5179483145760003458, guid: 0000000000000000d000000000000000, + m_Image: {fileID: -5467254957812901981, guid: 0000000000000000d000000000000000, type: 0} m_Tooltip: m_Pos: @@ -333,7 +333,7 @@ MonoBehaviour: scrollPos: {x: 0, y: 0} m_SelectedIDs: 84440000 m_LastClickedID: 17540 - m_ExpandedIDs: 00000000724400007844000000ca9a3bffffff7f + m_ExpandedIDs: 0000000086440000 m_RenameOverlay: m_UserAcceptedRename: 0 m_Name: @@ -361,7 +361,7 @@ MonoBehaviour: scrollPos: {x: 0, y: 0} m_SelectedIDs: m_LastClickedID: 0 - m_ExpandedIDs: 0000000072440000 + m_ExpandedIDs: 0000000086440000 m_RenameOverlay: m_UserAcceptedRename: 0 m_Name: @@ -476,7 +476,7 @@ MonoBehaviour: m_MaxSize: {x: 4000, y: 4000} m_TitleContent: m_Text: Hierarchy - m_Image: {fileID: -3734745235275155857, guid: 0000000000000000d000000000000000, + m_Image: {fileID: 7966133145522015247, guid: 0000000000000000d000000000000000, type: 0} m_Tooltip: m_Pos: @@ -495,7 +495,7 @@ MonoBehaviour: scrollPos: {x: 0, y: 0} m_SelectedIDs: m_LastClickedID: 0 - m_ExpandedIDs: 2cfbffff + m_ExpandedIDs: 1efbffff m_RenameOverlay: m_UserAcceptedRename: 0 m_Name: @@ -535,7 +535,7 @@ MonoBehaviour: m_MaxSize: {x: 4000, y: 4000} m_TitleContent: m_Text: Scene - m_Image: {fileID: 8634526014445323508, guid: 0000000000000000d000000000000000, + m_Image: {fileID: 2593428753322112591, guid: 0000000000000000d000000000000000, type: 0} m_Tooltip: m_Pos: @@ -875,7 +875,7 @@ MonoBehaviour: m_MaxSize: {x: 4000, y: 4000} m_TitleContent: m_Text: Game - m_Image: {fileID: 4621777727084837110, guid: 0000000000000000d000000000000000, + m_Image: {fileID: -6423792434712278376, guid: 0000000000000000d000000000000000, type: 0} m_Tooltip: m_Pos: @@ -969,7 +969,7 @@ MonoBehaviour: m_MaxSize: {x: 4000, y: 4000} m_TitleContent: m_Text: Console - m_Image: {fileID: -4950941429401207979, guid: 0000000000000000d000000000000000, + m_Image: {fileID: -4327648978806127646, guid: 0000000000000000d000000000000000, type: 0} m_Tooltip: m_Pos: