Skip to content

Commit

Permalink
Update the generator framework
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonkuo41 committed Sep 11, 2020
1 parent f4604fe commit 3c0434c
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 47 deletions.
1 change: 1 addition & 0 deletions src/Decuplr.Sourceberg/Generation/GeneratorStartup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace Decuplr.Sourceberg.Generation {
public abstract class GeneratorStartup {
public abstract bool ShouldCapture(SyntaxNode syntax);
public abstract void ConfigureAnalyzer(AnalyzerSetupContext setup);
public abstract void ConfigureServices(IServiceCollection services);
}
Expand Down
49 changes: 38 additions & 11 deletions src/Decuplr.Sourceberg/Generation/SourceGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,32 +1,58 @@
using System.Collections.Immutable;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Text;
using System.Threading;
using Decuplr.Sourceberg.Services;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Text;

namespace Decuplr.Sourceberg.Generation {
internal struct InternalGeneratingContext {
public ITypeSymbolProvider SymbolProvider { get; set; }
public ISourceAddition SourceAddition { get; }
public IContextCollectionProvider ContextCollection { get; }
public ISourceAddition SourceAddition { get; set; }
public IContextCollectionProvider ContextCollectionSource { get; set; }
}

public struct GeneratingContext {
public struct GenerationContext {
public GenerationContext(SyntaxNode node, SemanticModel semanticModel, ImmutableArray<AdditionalText> additionalFiles, AnalyzerConfigOptionsProvider analyzerConfigOptions, CancellationToken cancellationToken) {
Node = node;
SemanticModel = semanticModel;
AdditionalFiles = additionalFiles;
AnalyzerConfigOptions = analyzerConfigOptions;
CancellationToken = cancellationToken;
}

public SyntaxNode Node { get; }

public SemanticModel SemanticModel { get; }

public ImmutableArray<AdditionalText> AdditionalFiles { get; }

public AnalyzerConfigOptionsProvider AnalyzerConfigOptions { get; }

public CancellationToken CancellationToken { get; }
}

public abstract class SourceGeneratorPath {
internal struct GeneratedSourceText {
public string HintName { get; }
public SourceText SourceText { get; }
public GeneratedSourceText(string hintName, SourceText sourceText) {
HintName = hintName;
SourceText = sourceText;
}
}

public abstract class SourceGenerator {

private readonly List<GeneratedSourceText> _generatedSource = new List<GeneratedSourceText>();

internal InternalGeneratingContext Context { get; set; }

internal IReadOnlyList<GeneratedSourceText> GeneratedSource => _generatedSource;

protected ITypeSymbolCollection SourceSymbols => Context.SymbolProvider.Source;

protected ITypeSymbolCollection CurrentSymbols => Context.SymbolProvider.Current;
Expand All @@ -35,16 +61,17 @@ public abstract class SourceGeneratorPath {

protected Compilation CurrentCompilation => CurrentSymbols.DeclaringCompilation;

protected IContextCollection GetContextCollection(ISymbol symbol) => Context.ContextCollection.GetContextCollection(symbol);
protected IContextCollection GetContextCollection(ISymbol symbol) => Context.ContextCollectionSource.GetContextCollection(symbol);

protected IContextCollection GetContextCollection(SyntaxNode node) => Context.ContextCollection.GetContextCollection(node);
protected IContextCollection GetContextCollection(SyntaxNode node) => Context.ContextCollectionSource.GetContextCollection(node);

protected void AddSource(string hintName, string sourceFile) => AddSource(hintName, SourceText.From(sourceFile, Encoding.UTF8));

protected void AddSource(string hintName, SourceText sourceText) => Context.SourceAddition.AddSource(hintName, sourceText);

public abstract void ShouldGenerate(SyntaxNode node);
protected void AddSource(string hintName, SourceText sourceText) {
Context.SourceAddition.AddSource(hintName, sourceText);
_generatedSource.Add(new GeneratedSourceText(hintName, sourceText));
}

public abstract void GenerateContext(GeneratingContext context);
public abstract void GenerateContext(GenerationContext context);
}
}
32 changes: 29 additions & 3 deletions src/Decuplr.Sourceberg/Internal/AnalyzerGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Decuplr.Sourceberg.Internal {

internal struct AnalyzerGroupResult {
public IEnumerable<Diagnostic> GeneratedDiagnostics { get; set; }
public IContextCollectionProvider
}

internal class AnalyzerGroup<TKind, TContext> where TKind : notnull {

private readonly List<AnalyzerGroupNode<TKind, TContext>> _analyzers = new List<AnalyzerGroupNode<TKind, TContext>>();
Expand Down Expand Up @@ -56,21 +62,26 @@ public IServiceCollection RegisterServices(IServiceCollection services) {
return services;
}

public IEnumerable<Diagnostic> InvokeScope(IServiceProvider service, TKind kind, Compilation? compilation, Func<TKind, bool, TContext> context, CancellationToken ct) {
private IEnumerable<Diagnostic> InvokeScopeCore(IServiceProvider service,
TKind kind,
Compilation? compilation,
Func<TKind, bool, TContext> context,
IContextCollectionProvider? externalProvider,
CancellationToken ct) {
using var scope = service.CreateScope();
var scopeService = scope.ServiceProvider;
var accessor = scopeService.GetRequiredService<SourceContextAccessor>();
{
accessor.OnOperationCanceled = ct;
accessor.SourceCompilation = compilation;
}
var contextProvider = scopeService.GetRequiredService<IContextCollectionProvider>();
var contextProvider = externalProvider ?? scopeService.GetRequiredService<IContextCollectionProvider>();

var kindList = ExpandKindForInvocation(kind);

// reverse iterator
Action<CancellationToken> prevNextAction = ct => { };
for(var i = Analyzers.Count - 1; i >=0; --i) {
for (var i = Analyzers.Count - 1; i >= 0; --i) {
var analyzerInfo = Analyzers[i];
var analyzer = (SourceAnalyzerBase)scopeService.GetRequiredService(analyzerInfo.AnalyzerType);
var currentIndex = i;
Expand All @@ -87,6 +98,21 @@ void NextAction(CancellationToken providedCt) {

return scopeService.GetServices<SourceAnalyzerBase>().SelectMany(x => x.ReportingDiagnostics);
}

public IEnumerable<Diagnostic> InvokeScope(IServiceProvider service,
TKind kind,
Compilation? compilation,
Func<TKind, bool, TContext> context,
IContextCollectionProvider externalProvider,
CancellationToken ct)
=> InvokeScopeCore(service, kind, compilation, context, externalProvider, ct);

public IEnumerable<Diagnostic> InvokeScope(IServiceProvider service,
TKind kind,
Compilation? compilation,
Func<TKind, bool, TContext> context,
CancellationToken ct)
=> InvokeScopeCore(service, kind, compilation, context, null, ct);
}

}
36 changes: 6 additions & 30 deletions src/Decuplr.Sourceberg/Internal/SourcebergAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,43 +14,19 @@ namespace Decuplr.Sourceberg.Internal {
/// A helper class to setup the analyzer
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public class SourcebergAnalyzer {
public sealed class SourcebergAnalyzer : SourcebergBase {

private readonly IReadOnlyList<AnalyzerGroupInfo<ISymbol, SymbolAnalysisContextSource, SymbolKind>> symbolActionGroups;
private readonly IReadOnlyList<AnalyzerGroupInfo<SyntaxNode, SyntaxNodeAnalysisContextSource, SyntaxKind>> syntaxActionGroups;
private readonly ServiceCollection _services = new ServiceCollection();

public SourcebergAnalyzer(GeneratorStartup startup) {
var syntaxActionSetup = new SyntaxAnalyzerSetup();
var symbolActionSetup = new SymbolAnalyzerGroupSetup();
startup.ConfigureAnalyzer(new AnalyzerSetupContext(syntaxActionSetup, symbolActionSetup));
startup.ConfigureServices(_services);
syntaxActionGroups = syntaxActionSetup.AnalyzerGroups;
symbolActionGroups = symbolActionSetup.AnalyzerGroups;

foreach(var (actionGroup, _) in syntaxActionGroups) {
actionGroup.RegisterServices(_services);
}
foreach(var (actionGroup, _) in symbolActionGroups) {
actionGroup.RegisterServices(_services);
}

// Add default services
_services.AddScopedGroup<SourceContextAccessor, IAnalysisLifetime>();
_services.AddScopedGroup<TypeSymbolProvider, ITypeSymbolProvider, ISourceAddition>();

_services.AddScopedGroup<AttributeLayoutProvider, IAttributeLayoutProvider>();
_services.AddScopedGroup<ContextCollectionProvider, IContextCollectionProvider>();
_services.AddScoped<TypeSymbolLocatorCache>();
public SourcebergAnalyzer(GeneratorStartup startup)
: base (startup) {
}

public void Build(AnalysisContext context) {
var serviceProvider = _services.BuildServiceProvider();
var serviceProvider = CreateServiceProvider();

context.EnableConcurrentExecution();
// Currently we don't support popping analysis on generated code
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
foreach (var (symbolAction, requestKinds) in symbolActionGroups) {
foreach (var (symbolAction, requestKinds) in SymbolActionGroups) {
context.RegisterSymbolAction(symbolContext => {
var diagnostics = symbolAction.InvokeScope(serviceProvider,
symbolContext.Symbol,
Expand All @@ -61,7 +37,7 @@ public void Build(AnalysisContext context) {
symbolContext.ReportDiagnostic(diagnostic);
}, requestKinds);
}
foreach (var (syntaxAction, requestKinds) in syntaxActionGroups) {
foreach (var (syntaxAction, requestKinds) in SyntaxActionGroups) {
context.RegisterSyntaxNodeAction(syntaxContext => {
var compilation = syntaxContext.Compilation;
var ct = syntaxContext.CancellationToken;
Expand Down
Loading

0 comments on commit 3c0434c

Please sign in to comment.