diff --git a/eng/Versions.props b/eng/Versions.props
index 42d6f811752efa..a86e7cc942f824 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -40,9 +40,9 @@
-
- 3.9.0
- 3.9.0
+
+ 4.0.0-3.final
+ 4.0.0-3.final
diff --git a/src/libraries/Common/tests/SourceGenerators/RoslynTestUtils.cs b/src/libraries/Common/tests/SourceGenerators/RoslynTestUtils.cs
index 4928270b06f170..1c57260b8601de 100644
--- a/src/libraries/Common/tests/SourceGenerators/RoslynTestUtils.cs
+++ b/src/libraries/Common/tests/SourceGenerators/RoslynTestUtils.cs
@@ -141,10 +141,9 @@ public static TextSpan MakeSpan(string text, int spanNum)
/// Runs a Roslyn generator over a set of source files.
///
public static async Task<(ImmutableArray, ImmutableArray)> RunGenerator(
- ISourceGenerator generator,
+ IIncrementalGenerator generator,
IEnumerable? references,
IEnumerable sources,
- AnalyzerConfigOptionsProvider? optionsProvider = null,
bool includeBaseReferences = true,
CancellationToken cancellationToken = default)
{
@@ -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();
diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs
index 4a995039104b9d..b448bc79081e8d 100644
--- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs
+++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs
@@ -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 _reportDiagnostic;
@@ -28,13 +30,41 @@ public Parser(Compilation compilation, Action 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;
+ }
+
///
/// Gets the set of logging classes containing methods to output.
///
public IReadOnlyList GetLogClasses(IEnumerable classes)
{
- const string LoggerMessageAttribute = "Microsoft.Extensions.Logging.LoggerMessageAttribute";
-
INamedTypeSymbol loggerMessageAttribute = _compilation.GetTypeByMetadataName(LoggerMessageAttribute);
if (loggerMessageAttribute == null)
{
@@ -442,11 +472,11 @@ public IReadOnlyList GetLogClasses(IEnumerable
+ bool IsAllowedKind(SyntaxKind kind) =>
kind == SyntaxKind.ClassDeclaration ||
kind == SyntaxKind.StructDeclaration ||
kind == SyntaxKind.RecordDeclaration;
-
+
while (parentLoggerClass != null && IsAllowedKind(parentLoggerClass.Kind()))
{
currentLoggerClass.ParentClass = new LoggerClass
diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.cs
index 92105d515e328c..7aaca68e758a28 100644
--- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.cs
+++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.cs
@@ -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;
@@ -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 classDeclarations = context.SyntaxProvider
+ .CreateSyntaxProvider(static (s, _) => Parser.IsSyntaxTargetForGeneration(s), static (ctx, _) => Parser.GetSemanticTargetForGeneration(ctx))
+ .Where(static m => m is not null);
+
+ IncrementalValueProvider<(Compilation, ImmutableArray)> 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 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 logClasses = p.GetLogClasses(receiver.ClassDeclarations);
+ IEnumerable distinctClasses = classes.Distinct();
+
+ var p = new Parser(compilation, context.ReportDiagnostic, context.CancellationToken);
+ IReadOnlyList 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 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));
}
}
}
diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs
index b2fe61084c52ea..a1ec4253469ae7 100644
--- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs
+++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs
@@ -380,6 +380,7 @@ public class Object {}
public class Void {}
public class String {}
public struct DateTime {}
+ public abstract class Attribute {}
}
namespace System.Collections
{
@@ -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);