-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Changed the source generator to use the hoist pattern
Instead of having the users source generator be the entry point we create the entrypoint. This way we can 100% make sure the assembly resolver loaded before the users types get loaded. The main problem was that if users source generator contained static fields of types contained within external assemblies this would explode because the assembly resolver had not be subscribed yet
- Loading branch information
1 parent
303d9e3
commit 99ce352
Showing
30 changed files
with
894 additions
and
318 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
90 changes: 90 additions & 0 deletions
90
src/SourceGenerator.Foundations.Contracts/IncrementalGenerator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
using System; | ||
using System.Diagnostics; | ||
using SGF.Diagnostics; | ||
using Microsoft.CodeAnalysis; | ||
|
||
namespace SGF | ||
{ | ||
/// <summary> | ||
/// Used as a base class for creating your own source generator. This class provides some helper | ||
/// methods and impoved debugging expereince. The generator that implements this must apply the | ||
/// <see cref="GeneratorAttribute"/> but not inheirt from <see cref="IIncrementalGenerator"/> | ||
/// </summary> | ||
public abstract class IncrementalGenerator : IDisposable | ||
{ | ||
private readonly IGeneratorEnvironment m_developmentPlatform; | ||
|
||
/// <summary> | ||
/// Gets the name of the source generator | ||
/// </summary> | ||
public string Name { get; } | ||
|
||
/// <summary> | ||
/// Gets the log that can allow you to output information to your | ||
/// IDE of choice | ||
/// </summary> | ||
public ILogger Logger { get; } | ||
|
||
|
||
/// <summary> | ||
/// Initializes a new instance of the incremental generator with an optional name | ||
/// </summary> | ||
protected IncrementalGenerator( | ||
string? name, | ||
IGeneratorEnvironment developmentPlatform, | ||
ILogger logger) | ||
{ | ||
m_developmentPlatform = developmentPlatform; | ||
Name = name ?? GetType().Name; | ||
Logger = logger; | ||
} | ||
|
||
/// <summary> | ||
/// Implement to initalize the incremental source generator | ||
/// </ summary > | ||
public abstract void OnInitialize(SgfInitializationContext context); | ||
|
||
/// <summary> | ||
/// Override to add logic for disposing this instance and free resources | ||
/// </summary> | ||
protected virtual void Dipose() | ||
{ } | ||
|
||
/// <summary> | ||
/// Attaches the debugger automtically if you are running from Visual Studio. You have the option | ||
/// to stop or just continue | ||
/// </summary> | ||
protected void AttachDebugger() | ||
{ | ||
Process process = Process.GetCurrentProcess(); | ||
m_developmentPlatform.AttachDebugger(process.Id); | ||
} | ||
|
||
/// <summary> | ||
/// Raised when one of the generator functions throws an unhandle exception. Override this to define your own behaviour | ||
/// to handle the exception. | ||
/// </summary> | ||
/// <param name="exception">The exception that was thrown</param> | ||
protected virtual void OnException(Exception exception) | ||
{ | ||
Logger.Error(exception, $"Unhandled exception was throw while running the generator {Name}"); | ||
} | ||
|
||
/// <summary> | ||
/// Events raised when the exception is being thrown by the app domain | ||
/// </summary> | ||
private void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) | ||
{ | ||
if (e.ExceptionObject is Exception exception) | ||
{ | ||
OnException(exception); | ||
} | ||
} | ||
|
||
/// <inheritdoc cref="IDisposable"/> | ||
void IDisposable.Dispose() | ||
{ | ||
Dipose(); | ||
} | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
src/SourceGenerator.Foundations.Contracts/SgfGeneratorAttribute.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using System; | ||
|
||
namespace SGF | ||
{ | ||
/// <summary> | ||
/// Applied a class the inheirts from <see cref="IncrementalGenerator"/> | ||
/// that will have Source Generator Foundations wrapper generated around it. This adds | ||
/// better error handling and logging to the given generator. | ||
/// </summary> | ||
[AttributeUsage(AttributeTargets.Class)] | ||
public sealed class SgfGeneratorAttribute : Attribute | ||
{ | ||
} | ||
} |
131 changes: 131 additions & 0 deletions
131
src/SourceGenerator.Foundations.Contracts/SgfInitializationContext.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using SGF.Diagnostics; | ||
using System; | ||
using System.Reflection; | ||
|
||
namespace SGF | ||
{ | ||
/// <summary> | ||
/// Middleware wrapper around a <see cref="IncrementalGeneratorInitializationContext"/> to allow for | ||
/// wraping with exception handling and provide a better user expereince | ||
/// </summary> | ||
public readonly struct SgfInitializationContext | ||
{ | ||
private readonly ILogger m_logger; | ||
private readonly IncrementalGeneratorInitializationContext m_context; | ||
|
||
public SyntaxValueProvider SyntaxProvider => m_context.SyntaxProvider; | ||
public IncrementalValueProvider<Compilation> CompilationProvider => m_context.CompilationProvider; | ||
public IncrementalValueProvider<ParseOptions> ParseOptionsProvider => m_context.ParseOptionsProvider; | ||
public IncrementalValuesProvider<AdditionalText> AdditionalTextsProvider => m_context.AdditionalTextsProvider; | ||
public IncrementalValueProvider<AnalyzerConfigOptionsProvider> AnalyzerConfigOptionsProvider => m_context.AnalyzerConfigOptionsProvider; | ||
public IncrementalValuesProvider<MetadataReference> MetadataReferencesProvider => m_context.MetadataReferencesProvider; | ||
|
||
public SgfInitializationContext( | ||
IncrementalGeneratorInitializationContext context, | ||
ILogger logger) | ||
{ | ||
m_logger = logger; | ||
m_context = context; | ||
} | ||
|
||
public void RegisterSourceOutput<TSource>(IncrementalValueProvider<TSource> source, Action<SgfSourceProductionContext, TSource> action) | ||
{ | ||
ILogger logger = m_logger; | ||
|
||
void wrappedAction(SourceProductionContext context, TSource source) | ||
{ | ||
try | ||
{ | ||
action(new(context, logger), source); | ||
} | ||
catch (Exception exception) | ||
{ | ||
LogException(logger, exception, action.Method); | ||
} | ||
} | ||
m_context.RegisterSourceOutput(source, wrappedAction); | ||
} | ||
|
||
public void RegisterSourceOutput<TSource>(IncrementalValuesProvider<TSource> source, Action<SgfSourceProductionContext, TSource> action) | ||
{ | ||
ILogger logger = m_logger; | ||
void wrappedAction(SourceProductionContext context, TSource source) | ||
{ | ||
try | ||
{ | ||
action(new(context, logger), source); | ||
} | ||
catch (Exception exception) | ||
{ | ||
LogException(logger, exception, action.Method); | ||
} | ||
} | ||
m_context.RegisterSourceOutput(source, wrappedAction); | ||
} | ||
|
||
public void RegisterImplementationSourceOutput<TSource>(IncrementalValueProvider<TSource> source, Action<SgfSourceProductionContext, TSource> action) | ||
{ | ||
ILogger logger = m_logger; | ||
void wrappedAction(SourceProductionContext context, TSource source) | ||
{ | ||
try | ||
{ | ||
SgfSourceProductionContext sgfContext = new SgfSourceProductionContext(context, logger); | ||
action(sgfContext, source); | ||
logger.Information($" SourceFiles: {sgfContext.SourceCount}"); | ||
} | ||
catch (Exception exception) | ||
{ | ||
LogException(logger, exception, action.Method); | ||
} | ||
} | ||
m_context.RegisterImplementationSourceOutput(source, wrappedAction); | ||
} | ||
|
||
|
||
|
||
public void RegisterImplementationSourceOutput<TSource>(IncrementalValuesProvider<TSource> source, Action<SgfSourceProductionContext, TSource> action) | ||
{ | ||
ILogger logger = m_logger; | ||
|
||
void wrappedAction(SourceProductionContext context, TSource source) | ||
{ | ||
try | ||
{ | ||
action(new(context, logger), source); | ||
} | ||
catch (Exception exception) | ||
{ | ||
LogException(logger, exception, action.Method); | ||
} | ||
} | ||
m_context.RegisterImplementationSourceOutput(source, wrappedAction); | ||
} | ||
|
||
public void RegisterPostInitializationOutput(Action<IncrementalGeneratorPostInitializationContext> callback) | ||
{ | ||
ILogger logger = m_logger; | ||
void wrappedCallback(IncrementalGeneratorPostInitializationContext context) | ||
{ | ||
try | ||
{ | ||
callback(context); | ||
} | ||
catch (Exception exception) | ||
{ | ||
LogException(logger, exception, callback.Method); | ||
} | ||
} | ||
m_context.RegisterPostInitializationOutput(wrappedCallback); | ||
} | ||
|
||
private static void LogException(ILogger logger, Exception exception, MethodInfo actionInfo) | ||
{ | ||
string methodName = actionInfo.Name; | ||
string className = actionInfo.DeclaringType.FullName; | ||
logger.Error(exception, $"An {exception.GetType().Name} exception was thrown while invoking {className}.{methodName}"); | ||
} | ||
} | ||
} |
Oops, something went wrong.