Skip to content

Commit

Permalink
[release/6.0-rc1] Migrate LoggerMessageGenerator to IIncrementalGener…
Browse files Browse the repository at this point in the history
…ator (#58271)

* Migrate LoggerMessageGenerator to IIncrementalGenerator

This reduces the time spent in the background in VS running the source generator, since we only need to respond to methods that have the LoggerMessageAttribute on them.

Contributes to #56702

* PR feedback

* PR feedback

Co-authored-by: Eric Erhardt <eric.erhardt@microsoft.com>
  • Loading branch information
github-actions[bot] and eerhardt authored Aug 28, 2021
1 parent 72f18b5 commit 7bfbc96
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,9 @@ public static TextSpan MakeSpan(string text, int spanNum)
/// Runs a Roslyn generator over a set of source files.
/// </summary>
public static async Task<(ImmutableArray<Diagnostic>, ImmutableArray<GeneratedSourceResult>)> RunGenerator(
ISourceGenerator generator,
IIncrementalGenerator generator,
IEnumerable<Assembly>? references,
IEnumerable<string> sources,
AnalyzerConfigOptionsProvider? optionsProvider = null,
bool includeBaseReferences = true,
CancellationToken cancellationToken = default)
{
Expand All @@ -156,7 +155,9 @@ public static TextSpan MakeSpan(string text, int spanNum)

Compilation? comp = await proj!.GetCompilationAsync(CancellationToken.None).ConfigureAwait(false);

CSharpGeneratorDriver cgd = CSharpGeneratorDriver.Create(new[] { generator }, optionsProvider: optionsProvider);
// workaround https://github.com/dotnet/roslyn/pull/55866. We can remove "LangVersion=Preview" when we get a Roslyn build with that change.
CSharpParseOptions options = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview);
CSharpGeneratorDriver cgd = CSharpGeneratorDriver.Create(new[] { generator.AsSourceGenerator() }, parseOptions: options);
GeneratorDriver gd = cgd.RunGenerators(comp!, cancellationToken);

GeneratorDriverRunResult r = gd.GetRunResult();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public partial class LoggerMessageGenerator
{
internal class Parser
{
private const string LoggerMessageAttribute = "Microsoft.Extensions.Logging.LoggerMessageAttribute";

private readonly CancellationToken _cancellationToken;
private readonly Compilation _compilation;
private readonly Action<Diagnostic> _reportDiagnostic;
Expand All @@ -28,13 +30,41 @@ public Parser(Compilation compilation, Action<Diagnostic> reportDiagnostic, Canc
_reportDiagnostic = reportDiagnostic;
}

internal static bool IsSyntaxTargetForGeneration(SyntaxNode node) =>
node is MethodDeclarationSyntax m && m.AttributeLists.Count > 0;

internal static ClassDeclarationSyntax? GetSemanticTargetForGeneration(GeneratorSyntaxContext context)
{
var methodDeclarationSyntax = (MethodDeclarationSyntax)context.Node;

foreach (AttributeListSyntax attributeListSyntax in methodDeclarationSyntax.AttributeLists)
{
foreach (AttributeSyntax attributeSyntax in attributeListSyntax.Attributes)
{
IMethodSymbol attributeSymbol = context.SemanticModel.GetSymbolInfo(attributeSyntax).Symbol as IMethodSymbol;
if (attributeSymbol == null)
{
continue;
}

INamedTypeSymbol attributeContainingTypeSymbol = attributeSymbol.ContainingType;
string fullName = attributeContainingTypeSymbol.ToDisplayString();

if (fullName == LoggerMessageAttribute)
{
return methodDeclarationSyntax.Parent as ClassDeclarationSyntax;
}
}
}

return null;
}

/// <summary>
/// Gets the set of logging classes containing methods to output.
/// </summary>
public IReadOnlyList<LoggerClass> GetLogClasses(IEnumerable<ClassDeclarationSyntax> classes)
{
const string LoggerMessageAttribute = "Microsoft.Extensions.Logging.LoggerMessageAttribute";

INamedTypeSymbol loggerMessageAttribute = _compilation.GetTypeByMetadataName(LoggerMessageAttribute);
if (loggerMessageAttribute == null)
{
Expand Down Expand Up @@ -442,11 +472,11 @@ public IReadOnlyList<LoggerClass> GetLogClasses(IEnumerable<ClassDeclarationSynt
LoggerClass currentLoggerClass = lc;
var parentLoggerClass = (classDec.Parent as TypeDeclarationSyntax);

bool IsAllowedKind(SyntaxKind kind) =>
bool IsAllowedKind(SyntaxKind kind) =>
kind == SyntaxKind.ClassDeclaration ||
kind == SyntaxKind.StructDeclaration ||
kind == SyntaxKind.RecordDeclaration;

while (parentLoggerClass != null && IsAllowedKind(parentLoggerClass.Kind()))
{
currentLoggerClass.ParentClass = new LoggerClass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Tracing;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using Microsoft.CodeAnalysis;
Expand All @@ -15,50 +18,38 @@
namespace Microsoft.Extensions.Logging.Generators
{
[Generator]
public partial class LoggerMessageGenerator : ISourceGenerator
public partial class LoggerMessageGenerator : IIncrementalGenerator
{
[ExcludeFromCodeCoverage]
public void Initialize(GeneratorInitializationContext context)
public void Initialize(IncrementalGeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(SyntaxReceiver.Create);
IncrementalValuesProvider<ClassDeclarationSyntax> classDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(static (s, _) => Parser.IsSyntaxTargetForGeneration(s), static (ctx, _) => Parser.GetSemanticTargetForGeneration(ctx))
.Where(static m => m is not null);

IncrementalValueProvider<(Compilation, ImmutableArray<ClassDeclarationSyntax>)> compilationAndClasses =
context.CompilationProvider.Combine(classDeclarations.Collect());

context.RegisterSourceOutput(compilationAndClasses, static (spc, source) => Execute(source.Item1, source.Item2, spc));
}

[ExcludeFromCodeCoverage]
public void Execute(GeneratorExecutionContext context)
private static void Execute(Compilation compilation, ImmutableArray<ClassDeclarationSyntax> classes, SourceProductionContext context)
{
if (context.SyntaxReceiver is not SyntaxReceiver receiver || receiver.ClassDeclarations.Count == 0)
if (classes.IsDefaultOrEmpty)
{
// nothing to do yet
return;
}

var p = new Parser(context.Compilation, context.ReportDiagnostic, context.CancellationToken);
IReadOnlyList<LoggerClass> logClasses = p.GetLogClasses(receiver.ClassDeclarations);
IEnumerable<ClassDeclarationSyntax> distinctClasses = classes.Distinct();

var p = new Parser(compilation, context.ReportDiagnostic, context.CancellationToken);
IReadOnlyList<LoggerClass> logClasses = p.GetLogClasses(distinctClasses);
if (logClasses.Count > 0)
{
var e = new Emitter();
string result = e.Emit(logClasses, context.CancellationToken);

context.AddSource("LoggerMessage.g.cs", SourceText.From(result, Encoding.UTF8));
}
}

[ExcludeFromCodeCoverage]
private sealed class SyntaxReceiver : ISyntaxReceiver
{
internal static ISyntaxReceiver Create()
{
return new SyntaxReceiver();
}

public List<ClassDeclarationSyntax> ClassDeclarations { get; } = new ();

public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is ClassDeclarationSyntax classSyntax)
{
ClassDeclarations.Add(classSyntax);
}
context.AddSource("LoggerMessage.g.cs", SourceText.From(result, Encoding.UTF8));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ public class Object {}
public class Void {}
public class String {}
public struct DateTime {}
public abstract class Attribute {}
}
namespace System.Collections
{
Expand All @@ -392,10 +393,12 @@ public interface ILogger {}
}
namespace Microsoft.Extensions.Logging
{
public class LoggerMessageAttribute {}
public class LoggerMessageAttribute : System.Attribute {}
}
partial class C
{
[Microsoft.Extensions.Logging.LoggerMessage]
public static partial void Log(ILogger logger);
}
", false, includeBaseReferences: false, includeLoggingReferences: false);

Expand Down

0 comments on commit 7bfbc96

Please sign in to comment.