1212namespace XenoAtom . Logging . Generators ;
1313
1414/// <summary>
15- /// Generates implementations for methods annotated with <c>[LogMethod]</c>.
15+ /// Generates implementations for methods annotated with <c>[LogMethod]</c> and <c>[LogMethodMarkup]</c> .
1616/// </summary>
1717[ Generator ( LanguageNames . CSharp ) ]
1818public sealed class LogMethodGenerator : IIncrementalGenerator
1919{
2020 /// <inheritdoc />
2121 public void Initialize ( IncrementalGeneratorInitializationContext context )
2222 {
23- var generatedMethods = context . SyntaxProvider
23+ var logMethods = context . SyntaxProvider
2424 . ForAttributeWithMetadataName (
2525 LogMethodUtilities . LogMethodAttributeMetadataName ,
2626 static ( node , _ ) => node is MethodDeclarationSyntax { AttributeLists . Count : > 0 } ,
27- static ( ctx , ct ) => TryCreateGenerationResult ( ctx , ct ) )
27+ static ( ctx , ct ) => TryCreateGenerationResult ( ctx , isMarkupMethod : false , ct ) ) ;
28+
29+ var logMarkupMethods = context . SyntaxProvider
30+ . ForAttributeWithMetadataName (
31+ LogMethodUtilities . LogMethodMarkupAttributeMetadataName ,
32+ static ( node , _ ) => node is MethodDeclarationSyntax { AttributeLists . Count : > 0 } ,
33+ static ( ctx , ct ) => TryCreateGenerationResult ( ctx , isMarkupMethod : true , ct ) ) ;
34+
35+ var generatedLogMethods = logMethods
2836 . Where ( static result => result is not null )
2937 . Select ( static ( result , _ ) => result ! . Value )
3038 . WithComparer ( LogMethodGenerationResultComparer . Instance ) ;
3139
32- context . RegisterSourceOutput (
33- generatedMethods ,
34- static ( spc , result ) => Emit ( spc , result ) ) ;
40+ var generatedLogMarkupMethods = logMarkupMethods
41+ . Where ( static result => result is not null )
42+ . Select ( static ( result , _ ) => result ! . Value )
43+ . WithComparer ( LogMethodGenerationResultComparer . Instance ) ;
44+
45+ context . RegisterSourceOutput ( generatedLogMethods , Emit ) ;
46+ context . RegisterSourceOutput ( generatedLogMarkupMethods , Emit ) ;
3547 }
3648
37- private static LogMethodGenerationResult ? TryCreateGenerationResult ( GeneratorAttributeSyntaxContext context , CancellationToken cancellationToken )
49+ private static LogMethodGenerationResult ? TryCreateGenerationResult ( GeneratorAttributeSyntaxContext context , bool isMarkupMethod , CancellationToken cancellationToken )
3850 {
3951 cancellationToken . ThrowIfCancellationRequested ( ) ;
4052 if ( context . TargetSymbol is not IMethodSymbol methodSymbol )
@@ -47,12 +59,12 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
4759 return null ;
4860 }
4961
50- if ( ! LogMethodUtilities . TryGetLogMethodAttribute ( methodSymbol , out var attribute ) || attribute is null )
62+ if ( context . Attributes is not { Length : > 0 } )
5163 {
5264 return null ;
5365 }
5466
55- return GenerateMethod ( context . SemanticModel . Compilation , methodSymbol , attribute ) ;
67+ return GenerateMethod ( context . SemanticModel . Compilation , methodSymbol , context . Attributes [ 0 ] , isMarkupMethod ) ;
5668 }
5769
5870 private static void Emit ( SourceProductionContext context , LogMethodGenerationResult result )
@@ -68,10 +80,21 @@ private static void Emit(SourceProductionContext context, LogMethodGenerationRes
6880 }
6981 }
7082
71- private static LogMethodGenerationResult GenerateMethod ( Compilation compilation , IMethodSymbol methodSymbol , AttributeData attribute )
83+ private static LogMethodGenerationResult GenerateMethod ( Compilation compilation , IMethodSymbol methodSymbol , AttributeData attribute , bool isMarkupMethod )
7284 {
7385 var diagnostics = ImmutableArray . CreateBuilder < Diagnostic > ( ) ;
7486
87+ if ( HasBothLogMethodAttributes ( methodSymbol ) )
88+ {
89+ Report (
90+ diagnostics ,
91+ LogMethodDiagnostics . InvalidMethodSignature ,
92+ methodSymbol ,
93+ methodSymbol . Name ,
94+ "Only one of [LogMethod] or [LogMethodMarkup] can be applied." ) ;
95+ return new LogMethodGenerationResult ( null , null , diagnostics . ToImmutable ( ) ) ;
96+ }
97+
7598 if ( ! TryValidateMethod ( methodSymbol , diagnostics , out var loggerParameter , out var exceptionParameter , out var propertiesParameter ) )
7699 {
77100 return new LogMethodGenerationResult ( null , null , diagnostics . ToImmutable ( ) ) ;
@@ -82,6 +105,17 @@ private static LogMethodGenerationResult GenerateMethod(Compilation compilation,
82105 return new LogMethodGenerationResult ( null , null , diagnostics . ToImmutable ( ) ) ;
83106 }
84107
108+ if ( isMarkupMethod &&
109+ compilation . GetTypeByMetadataName ( LogMethodUtilities . LoggerMarkupExtensionsMetadataName ) is null )
110+ {
111+ Report (
112+ diagnostics ,
113+ LogMethodDiagnostics . MissingMarkupSupport ,
114+ methodSymbol ,
115+ methodSymbol . Name ) ;
116+ return new LogMethodGenerationResult ( null , null , diagnostics . ToImmutable ( ) ) ;
117+ }
118+
85119 var messageTemplate = attribute . ConstructorArguments [ 1 ] . Value as string ;
86120 if ( string . IsNullOrEmpty ( messageTemplate ) )
87121 {
@@ -122,6 +156,7 @@ private static LogMethodGenerationResult GenerateMethod(Compilation compilation,
122156 propertiesParameter ,
123157 logLevelValue ,
124158 logMethodName ! ,
159+ isMarkupMethod ,
125160 GetEventIdData ( attribute , methodSymbol . Name ) ) ;
126161
127162 var hintName = CreateHintName ( methodSymbol ) ;
@@ -351,6 +386,7 @@ private static string GenerateSource(
351386 IParameterSymbol ? propertiesParameter ,
352387 int logLevelValue ,
353388 string logMethodName ,
389+ bool isMarkupMethod ,
354390 ( bool HasEventId , int EventId , string EventName ) eventIdData )
355391 {
356392 var source = new StringBuilder ( ) ;
@@ -439,15 +475,27 @@ private static string GenerateSource(
439475 arguments . Add ( BuildInterpolatedMessage ( compilation , tokens , templateParameters ) ) ;
440476
441477 AppendIndent ( source , indent ) ;
442- source . Append ( "global::XenoAtom.Logging.LoggerExtensions." ) ;
443- source . Append ( logMethodName ) ;
444- source . Append ( '(' ) ;
445- source . Append ( loggerIdentifier ) ;
478+ if ( isMarkupMethod )
479+ {
480+ source . Append ( "global::XenoAtom.Logging.LoggerMarkupExtensions.LogMarkup(" ) ;
481+ source . Append ( loggerIdentifier ) ;
482+ source . Append ( ", global::XenoAtom.Logging.LogLevel." ) ;
483+ source . Append ( LogMethodUtilities . GetLogLevelName ( logLevelValue ) ) ;
484+ }
485+ else
486+ {
487+ source . Append ( "global::XenoAtom.Logging.LoggerExtensions." ) ;
488+ source . Append ( logMethodName ) ;
489+ source . Append ( '(' ) ;
490+ source . Append ( loggerIdentifier ) ;
491+ }
492+
446493 if ( arguments . Count > 0 )
447494 {
448495 source . Append ( ", " ) ;
449496 source . Append ( string . Join ( ", " , arguments ) ) ;
450497 }
498+
451499 source . AppendLine ( ");" ) ;
452500
453501 indent -= 4 ;
@@ -598,6 +646,26 @@ private static bool HasGenericContainingType(INamedTypeSymbol? containingType)
598646 return false ;
599647 }
600648
649+ private static bool HasBothLogMethodAttributes ( IMethodSymbol methodSymbol )
650+ {
651+ var hasLogMethod = false ;
652+ var hasLogMethodMarkup = false ;
653+ foreach ( var candidate in methodSymbol . GetAttributes ( ) )
654+ {
655+ var attributeName = candidate . AttributeClass ? . ToDisplayString ( ) ;
656+ if ( attributeName == LogMethodUtilities . LogMethodAttributeMetadataName )
657+ {
658+ hasLogMethod = true ;
659+ }
660+ else if ( attributeName == LogMethodUtilities . LogMethodMarkupAttributeMetadataName )
661+ {
662+ hasLogMethodMarkup = true ;
663+ }
664+ }
665+
666+ return hasLogMethod && hasLogMethodMarkup ;
667+ }
668+
601669 private static string CreateHintName ( IMethodSymbol methodSymbol )
602670 {
603671 var fullyQualifiedName = methodSymbol . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) ;
0 commit comments