Skip to content

Commit

Permalink
Merge pull request #592 from hadashiA/ku/incremental-source-generator
Browse files Browse the repository at this point in the history
Support Incremental Source Generator
  • Loading branch information
hadashiA authored Dec 18, 2023
2 parents bdf4585 + f731983 commit 144f8bf
Show file tree
Hide file tree
Showing 18 changed files with 1,070 additions and 123 deletions.
21 changes: 16 additions & 5 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,27 @@ jobs:
# set release tag(*.*.*) to env.GIT_TAG
- run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV

# Execute scripts: Export Package
- name: Export unitypackage
run: $UNITY_BIN -quit -batchmode -nographics -logFile /dev/stdout -exportPackage Assets/VContainer/Runtime Assets/VContainer/Editor VContainer.${{ env.GIT_TAG }}.unitypackage -projectPath ./
working-directory: VContainer

# Store artifacts.
- uses: actions/upload-artifact@v2
- uses: actions/setup-dotnet@v3
with:
name: VContainer.${{ env.GIT_TAG }}.unitypackage
path: ./VContainer/VContainer.${{ env.GIT_TAG }}.unitypackage
dotnet-version: |
8.0.x
- name: Build SourceGenerator
run: |
dotnet build -c Release ./VContainer.SourceGenerator
dotnet build -c Release ./VContainer.SourceGenerator.Roslyn3
- uses: actions/upload-artifact@v3
with:
name: VContainer.${{ env.GIT_TAG }}
path: |
./VContainer/VContainer.${{ env.GIT_TAG }}.unitypackage
./VContainer.SourceGenerator/bin/Release/netstandard2.0/VContainer.SourceGenerator.dll
./VContainer.SourceGenerator.Roslyn3/bin/Release/netstandard2.0/VContainer.SourceGenerator.Roslyn3.dll
# create-release:
# needs: [build-unity]
Expand Down
90 changes: 90 additions & 0 deletions VContainer.SourceGenerator.Roslyn3/CodeWriter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using System;
using System.Text;

namespace VContainer.SourceGenerator
{
public class CodeWriter
{
readonly struct IndentScope : IDisposable
{
readonly CodeWriter source;

public IndentScope(CodeWriter source)
{
this.source = source;
source.IncreasaeIndent();
}

public void Dispose()
{
source.DecreaseIndent();
}
}

readonly struct BlockScope : IDisposable
{
readonly CodeWriter source;

public BlockScope(CodeWriter source, string? startLine = null)
{
this.source = source;
source.AppendLine(startLine);
source.BeginBlock();
}

public void Dispose()
{
source.EndBlock();
}
}

readonly StringBuilder buffer = new();
int indentLevel;

public void AppendLine(string value = "")
{
if (string.IsNullOrEmpty(value))
{
buffer.AppendLine();
}
else
{
buffer.AppendLine($"{new string(' ', indentLevel * 4)} {value}");
}
}

public override string ToString() => buffer.ToString();

public IDisposable BeginIndentScope() => new IndentScope(this);
public IDisposable BeginBlockScope(string? startLine = null) => new BlockScope(this, startLine);

public void IncreasaeIndent()
{
indentLevel++;
}

public void DecreaseIndent()
{
if (indentLevel > 0)
indentLevel--;
}

public void BeginBlock()
{
AppendLine("{");
IncreasaeIndent();
}

public void EndBlock()
{
DecreaseIndent();
AppendLine("}");
}

public void Clear()
{
buffer.Clear();
indentLevel = 0;
}
}
}
89 changes: 89 additions & 0 deletions VContainer.SourceGenerator.Roslyn3/DiagnosticDescriptors.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using Microsoft.CodeAnalysis;

namespace VContainer.SourceGenerator
{
static class DiagnosticDescriptors
{
const string Category = "VContainer.SourceGenerator.Roslyn3";

public static readonly DiagnosticDescriptor UnexpectedErrorDescriptor = new(
id: "VCON0001",
title: "Unexpected error during generation",
messageFormat: "Unexpected error occurred during code generation: {0}",
category: "Usage",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor AbstractNotAllow = new(
id: "VCON0002",
title: "Injectable type must not be abstract/interface",
messageFormat: "The injectable type of '{0}' is abstract/interface. It is not allowed",
category: Category,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor MultipleCtorAttributeNotSupported = new(
id: "VCON0003",
title: "[Inject] exists in multiple constructors",
messageFormat: "Multiple [Inject] constructors exists in '{0}'",
category: Category,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor MultipleInjectMethodNotSupported = new(
id: "VCON0004",
title: "[Inject] exists in multiple methods",
messageFormat: "Multiple [Inject] methods exists in '{0}'",
category: Category,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor NestedNotSupported = new(
id: "VCON0005",
title: "Nested type is not support to code generation.",
messageFormat: "The injectable object '{0}' is a nested type. It cannot support code generation ",
category: Category,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor PrivateConstructorNotSupported = new(
id: "VCON0006",
title: "The private constructor is not supported to code generation.",
messageFormat: "The injectable constructor of '{0}' is private. It cannot support source generator.",
category: Category,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor PrivateFieldNotSupported = new(
id: "VCON0007",
title: "The private [Inject] field is not supported to code generation.",
messageFormat: "The [Inject] field '{0}' does not have accessible to set from the same dll. It cannot support to inject by the source generator.",
category: Category,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor PrivatePropertyNotSupported = new(
id: "VCON0008",
title: "The private [Inject] property is not supported to code generation",
messageFormat: "The [Inject] '{0}' does not have accessible to set from the same dll. It cannot support to inject by the source generator.",
category: Category,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor PrivateMethodNotSupported = new(
id: "VCON0009",
title: "The private [Inject] method is not supported to code generation.",
messageFormat: "The [Inject] '{0}' does not have accessible to call from the same dll. It cannot support inject by the source generator.",
category: Category,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor GenericsNotSupported = new(
id: "VCON0010",
title: "The [Inject] constructor or method that require generics argument is not supported to code generation.",
messageFormat: "[Inject] '{0}' needs generic arguments. It cannot inject by the source generator.",
category: Category,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
}
}
27 changes: 27 additions & 0 deletions VContainer.SourceGenerator.Roslyn3/ReferenceSymbols.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Microsoft.CodeAnalysis;

namespace VContainer.SourceGenerator
{
public class ReferenceSymbols
{
public static ReferenceSymbols? Create(Compilation compilation)
{
var injectAttribute = compilation.GetTypeByMetadataName("VContainer.InjectAttribute");
if (injectAttribute is null)
return null;

return new ReferenceSymbols
{
VContainerInjectAttribute = injectAttribute,
VContainerInjectIgnoreAttribute = compilation.GetTypeByMetadataName("VContainer.InjectIgnoreAttribute")!,
AttributeBase = compilation.GetTypeByMetadataName("System.Attribute")!,
UnityEngineComponent = compilation.GetTypeByMetadataName("UnityEngine.Component"),
};
}

public INamedTypeSymbol VContainerInjectAttribute { get; private set; } = default!;
public INamedTypeSymbol VContainerInjectIgnoreAttribute { get; private set; } = default!;
public INamedTypeSymbol AttributeBase { get; private set; } = default!;
public INamedTypeSymbol? UnityEngineComponent { get; private set; }
}
}
112 changes: 112 additions & 0 deletions VContainer.SourceGenerator.Roslyn3/SymbolExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;

namespace VContainer.SourceGenerator
{
public static class SymbolExtensions
{
public static IEnumerable<ISymbol> GetAllMembers(this INamedTypeSymbol symbol, bool withoutOverride = true)
{
// Iterate Parent -> Derived
if (symbol.BaseType != null)
{
foreach (var item in GetAllMembers(symbol.BaseType))
{
// override item already iterated in parent type
if (!withoutOverride || !item.IsOverride)
{
yield return item;
}
}
}

foreach (var item in symbol.GetMembers())
{
if (!withoutOverride || !item.IsOverride)
{
yield return item;
}
}
}

public static bool ContainsAttribute(this ISymbol symbol, INamedTypeSymbol attribtue)
{
return symbol.GetAttributes().Any(x => SymbolEqualityComparer.Default.Equals(x.AttributeClass, attribtue));
}

public static IEnumerable<INamedTypeSymbol> GetAllBaseTypes(this INamedTypeSymbol symbol)
{
var t = symbol.BaseType;
while (t != null)
{
yield return t;
t = t.BaseType;
}
}

public static bool CanBeCallFromInternal(this ISymbol symbol)
{
return symbol.DeclaredAccessibility >= Accessibility.Internal;
}

public static string GetClassDeclarationName(this INamedTypeSymbol symbol)
{
if (symbol.TypeArguments.Length == 0)
{
return symbol.Name;
}

var sb = new StringBuilder();

sb.Append(symbol.Name);
sb.Append('<');

var first = true;
foreach (var typeArg in symbol.TypeArguments)
{
if (!first)
{
sb.Append(", ");
}
else
{
first = false;
}
sb.Append(typeArg.Name);
}

sb.Append('>');

return sb.ToString();
}

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) => DistinctBy(source, keySelector, null);

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer)
{
return DistinctByIterator(source, keySelector, comparer);
}

static IEnumerable<TSource> DistinctByIterator<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer)
{
using IEnumerator<TSource> enumerator = source.GetEnumerator();

if (enumerator.MoveNext())
{
var set = new HashSet<TKey>(comparer);
do
{
TSource element = enumerator.Current;
if (set.Add(keySelector(element)))
{
yield return element;
}
}
while (enumerator.MoveNext());
}
}
}
}
Loading

1 comment on commit 144f8bf

@vercel
Copy link

@vercel vercel bot commented on 144f8bf Dec 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.