Skip to content

Commit

Permalink
Add support for Repeat pattern method argument when used with no argu…
Browse files Browse the repository at this point in the history
…ments and add replacePattern support on method
  • Loading branch information
xaviersolau committed Feb 3, 2024
1 parent 5e8e6d7 commit 1992316
Show file tree
Hide file tree
Showing 30 changed files with 688 additions and 112 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/build-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ jobs:
format: cobertura
flag-name: test-${{ matrix.test_path }}
parallel: true
debug: true

end-coverage-pr:
needs: coverage-pr
Expand All @@ -66,4 +65,3 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel-finished: true
debug: true
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,12 @@ public void Generate(string projectFile)
// Setup a locator that will tell the location where the generated classes must be written.
var locator = new RelativeLocator(projectFolder, project.RootNameSpace, suffix: "Impl");

// Setup a selector resolver.
var selectorResolver = new DefaultSelectorResolver();

// Create the automated generator.
var generator = new AutomatedGenerator(
new FileWriter(".generated.cs"),
locator,
resolver,
typeof(EntityPattern),
selectorResolver,
new GeneratorLogger<EntityGeneratorExample>(this.logger));

// Generate the files.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,12 @@ public void Generate(string projectFile)
// Setup a locator that will tell the location where the generated classes must be written.
var locator = new RelativeLocator(projectFolder, project.RootNameSpace, suffix: "Impl");

// Setup a selector resolver.
var selectorResolver = new DefaultSelectorResolver();

// Create the automated generator.
var generator = new AutomatedGenerator(
new FileWriter(".generated.cs"),
locator,
resolver,
typeof(ModelPattern),
selectorResolver,
new GeneratorLogger<ModelGeneratorExample>(this.logger));

// Generate the files.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace SoloX.GeneratorTools.Core.CSharp.Generator.Attributes
/// Attribute used to tell that the pattern element must be repeated.
/// For example use it if you want to repeat a piece of code.
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue)]
public sealed class RepeatAttribute : Attribute
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// ----------------------------------------------------------------------
// <copyright file="AInstanceResolver.cs" company="Xavier Solau">
// Copyright © 2021 Xavier Solau.
// Licensed under the MIT license.
// See LICENSE file in the project root for full license information.
// </copyright>
// ----------------------------------------------------------------------

using Microsoft.CodeAnalysis;
using SoloX.GeneratorTools.Core.CSharp.Model.Use;
using System;
using System.Collections.Generic;
using System.Linq;

namespace SoloX.GeneratorTools.Core.CSharp.Generator.Impl
{
/// <summary>
/// Base type resolver to find a Type from a IDeclarationUse
/// </summary>
public abstract class AInstanceResolver
{
private readonly Dictionary<string, Type> typesByFullName;
private readonly Dictionary<string, Type> typesByName;

/// <summary>
/// Setup instance with the given types to be resolved.
/// </summary>
/// <param name="types">Types to be resolved in selectors.</param>
protected AInstanceResolver(IEnumerable<Type> defaultTypes, params Type[] types)
{
var allTypes = types.Concat(defaultTypes).ToArray();

this.typesByFullName = allTypes.ToDictionary(t => t.FullName);
this.typesByName = allTypes.ToDictionary(t => t.Name);
}

/// <summary>
/// Create a Type instance from the given declarationUse
/// </summary>
/// <typeparam name="TInstance"></typeparam>
/// <param name="declarationUse"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
protected TInstance? CreateInstance<TInstance>(IDeclarationUse<SyntaxNode> declarationUse)

Check warning on line 44 in src/libs/SoloX.GeneratorTools.Core.CSharp/Generator/Impl/AInstanceResolver.cs

View workflow job for this annotation

GitHub Actions / build-pr

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{
if (declarationUse == null)
{
throw new ArgumentNullException(nameof(declarationUse));
}

var instanceType = ResolveAsType(declarationUse);

if (instanceType != null)
{
return (TInstance)Activator.CreateInstance(instanceType);
}

return default;
}

private Type ResolveAsType(IDeclarationUse<SyntaxNode> declarationUse)
{
if (declarationUse is IGenericDeclarationUse genericDeclarationUse)
{
var fullName = declarationUse.Declaration.FullName;
var genericCount = genericDeclarationUse.GenericParameters.Count;

Type type;
if (genericCount > 0)
{
fullName = fullName + '`' + genericCount;

type = GetTypeFromName(fullName);

var genericParameters = new List<Type>();

foreach (var genericParameter in genericDeclarationUse.GenericParameters)
{
var genericParameterType = ResolveAsType(genericParameter);

genericParameters.Add(genericParameterType);
}

type = type.MakeGenericType(genericParameters.ToArray());
}
else
{
type = GetTypeFromName(fullName);
}

return type;
}

return GetTypeFromName(declarationUse.Declaration.Name);
}

/// <summary>
/// Get the type matching the given name.
/// </summary>
/// <param name="name">Name of the type to resolve.</param>
/// <returns>The resolver Type object or null.</returns>
protected Type GetTypeFromName(string name)
{
if (this.typesByFullName.TryGetValue(name, out var type))
{
return type;
}
else if (this.typesByName.TryGetValue(name, out type))
{
return type;
}

throw new NotSupportedException($"Unable to resolve type from name: {name}");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Microsoft.CodeAnalysis;
using SoloX.GeneratorTools.Core.CSharp.Generator.Attributes;
using SoloX.GeneratorTools.Core.CSharp.Generator.Impl.Walker;
using SoloX.GeneratorTools.Core.CSharp.Generator.ReplacePattern;
using SoloX.GeneratorTools.Core.CSharp.Generator.Selectors;
using SoloX.GeneratorTools.Core.CSharp.Model;
using SoloX.GeneratorTools.Core.CSharp.Model.Resolver;
Expand All @@ -31,6 +32,7 @@ public class AutomatedGenerator : IAutomatedGenerator
private readonly IDeclarationResolver resolver;
private readonly Type patternType;
private readonly ISelectorResolver selectorResolver;
private readonly IReplacePatternResolver replacePatternResolver;
private readonly IGeneratorLogger logger;
private readonly PatternAttribute patternAttribute;
private readonly IDeclaration<SyntaxNode> pattern;
Expand All @@ -43,9 +45,10 @@ public class AutomatedGenerator : IAutomatedGenerator
/// <param name="locator">Code generation locator.</param>
/// <param name="resolver">The resolver to resolve workspace symbols.</param>
/// <param name="patternType">The pattern type to use.</param>
/// <param name="logger">Logger instance.</param>
/// <param name="selectorResolver">Selector resolver or null to use the default one.</param>
public AutomatedGenerator(IWriter writer, ILocator locator, IDeclarationResolver resolver, Type patternType, ISelectorResolver selectorResolver, IGeneratorLogger logger)
/// <param name="replacePatternResolver"></param>
/// <param name="logger">Logger instance.</param>
public AutomatedGenerator(IWriter writer, ILocator locator, IDeclarationResolver resolver, Type patternType, IGeneratorLogger logger, ISelectorResolver? selectorResolver = null, IReplacePatternResolver? replacePatternResolver = null)
{
if (writer == null)
{
Expand All @@ -71,7 +74,8 @@ public AutomatedGenerator(IWriter writer, ILocator locator, IDeclarationResolver
this.resolver = resolver;
this.locator = locator;
this.patternType = patternType;
this.selectorResolver = selectorResolver;
this.selectorResolver = selectorResolver ?? new DefaultSelectorResolver();
this.replacePatternResolver = replacePatternResolver ?? new DefaultReplacePatternResolver();
this.logger = logger;
this.patternAttribute = FindAttribute<PatternAttribute>(this.patternType);

Expand Down Expand Up @@ -116,7 +120,8 @@ public IEnumerable<IGeneratedItem> Generate(IEnumerable<ICSharpFile> files)
this.resolver,
replacePatternHandlerFactories,
this.ignoreUsingList,
this.selectorResolver);
this.selectorResolver,
this.replacePatternResolver);

var implName = strategy.ApplyPatternReplace(this.pattern.Name);

Expand Down Expand Up @@ -151,7 +156,8 @@ public IEnumerable<IGeneratedItem> Generate(IEnumerable<ICSharpFile> files)
this.resolver,
replacePatternHandlerFactories,
this.ignoreUsingList,
this.selectorResolver);
this.selectorResolver,
this.replacePatternResolver);

var implName = strategy.ComputeTargetName();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// ----------------------------------------------------------------------
// <copyright file="DefaultReplacePatternResolver.cs" company="Xavier Solau">
// Copyright © 2021 Xavier Solau.
// Licensed under the MIT license.
// See LICENSE file in the project root for full license information.
// </copyright>
// ----------------------------------------------------------------------

using Microsoft.CodeAnalysis;
using SoloX.GeneratorTools.Core.CSharp.Model.Use;
using System.Collections.Generic;
using System;
using SoloX.GeneratorTools.Core.CSharp.Generator.ReplacePattern;

namespace SoloX.GeneratorTools.Core.CSharp.Generator.Impl
{
/// <summary>
/// Default resolver for replace pattern handler factory.
/// </summary>
public class DefaultReplacePatternResolver : AInstanceResolver, IReplacePatternResolver
{
private static readonly IEnumerable<Type> DefaultTypes = new[]
{
typeof(TaskValueTypeReplaceHandler),
};

/// <summary>
/// Setup instance with the given types to be resolved.
/// </summary>
/// <param name="types">Types to be resolved in selectors.</param>
public DefaultReplacePatternResolver(params Type[] types)
: base(DefaultTypes, types)
{
}

/// <inheritdoc/>
public IReplacePatternHandlerFactory GetHandlerFactory(IDeclarationUse<SyntaxNode> replacePatternHandlerTypeUse)
{
return CreateInstance<IReplacePatternHandlerFactory>(replacePatternHandlerTypeUse);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@
using SoloX.GeneratorTools.Core.CSharp.Model.Use;
using System;
using System.Collections.Generic;
using System.Linq;

namespace SoloX.GeneratorTools.Core.CSharp.Generator.Impl
{
/// <summary>
/// Default selector type resolver.
/// </summary>
public class DefaultSelectorResolver : ISelectorResolver
public class DefaultSelectorResolver : AInstanceResolver, ISelectorResolver
{
private static readonly IEnumerable<Type> DefaultTypes = new[]
{
Expand All @@ -29,92 +28,19 @@ public class DefaultSelectorResolver : ISelectorResolver
typeof(AllSelector),
};

private readonly Dictionary<string, Type> typesByFullName;
private readonly Dictionary<string, Type> typesByName;

/// <summary>
/// Setup instance with the given types to be resolved.
/// </summary>
/// <param name="types">Types to be resolved in selectors.</param>
public DefaultSelectorResolver(params Type[] types)
: base(DefaultTypes, types)
{
var allTypes = types.Concat(DefaultTypes).ToArray();

this.typesByFullName = allTypes.ToDictionary(t => t.FullName);
this.typesByName = allTypes.ToDictionary(t => t.Name);
}

/// <inheritdoc/>
public ISelector GetSelector(IDeclarationUse<SyntaxNode> selectorTypeUse)
{
if (selectorTypeUse == null)
{
throw new ArgumentNullException(nameof(selectorTypeUse));
}

var selectorType = ResolveAsType(selectorTypeUse);

if (selectorType != null)
{
return (ISelector)Activator.CreateInstance(selectorType);
}

return null;
}

private Type ResolveAsType(IDeclarationUse<SyntaxNode> selectorTypeUse)
{
if (selectorTypeUse is IGenericDeclarationUse genericDeclarationUse)
{
var fullName = selectorTypeUse.Declaration.FullName;
var genericCount = genericDeclarationUse.GenericParameters.Count;

Type type;
if (genericCount > 0)
{
fullName = fullName + '`' + genericCount;

type = GetTypeFromName(fullName);

var genericParameters = new List<Type>();

foreach (var genericParameter in genericDeclarationUse.GenericParameters)
{
var genericParameterType = ResolveAsType(genericParameter);

genericParameters.Add(genericParameterType);
}

type = type.MakeGenericType(genericParameters.ToArray());
}
else
{
type = GetTypeFromName(fullName);
}

return type;
}

return GetTypeFromName(selectorTypeUse.Declaration.Name);
}

/// <summary>
/// Get the type matching the given name.
/// </summary>
/// <param name="name">Name of the type to resolve.</param>
/// <returns>The resolver Type object or null.</returns>
protected Type GetTypeFromName(string name)
{
if (this.typesByFullName.TryGetValue(name, out var type))
{
return type;
}
else if (this.typesByName.TryGetValue(name, out type))
{
return type;
}

throw new NotSupportedException($"Unable to resolve type from name: {name}");
return CreateInstance<ISelector>(selectorTypeUse);
}
}
}
Loading

0 comments on commit 1992316

Please sign in to comment.