File: UsingToStringInLoggersAnalyzer.cs
Web Access
Project: src\src\Analyzers\Microsoft.Analyzers.Extra\Microsoft.Analyzers.Extra.csproj (Microsoft.Analyzers.Extra)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
 
namespace Microsoft.Extensions.ExtraAnalyzers;
 
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class UsingToStringInLoggersAnalyzer : DiagnosticAnalyzer
{
    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(DiagDescriptors.UsingToStringInLoggers);
 
    public override void Initialize(AnalysisContext context)
    {
        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
        context.EnableConcurrentExecution();
 
        context.RegisterCompilationStartAction(compilationStartContext =>
        {
            compilationStartContext.RegisterOperationBlockStartAction(operationBlockContext =>
            {
                if (operationBlockContext.OwningSymbol.Kind != SymbolKind.Method)
                {
                    return;
                }
 
                operationBlockContext.RegisterOperationAction(AnalyzeInvocation, OperationKind.Invocation);
            });
        });
    }
 
    private static void AnalyzeInvocation(OperationAnalysisContext context)
    {
        var invocation = (IInvocationOperation)context.Operation;
        if (IsLoggerMethod(invocation.TargetMethod))
        {
            foreach (var diagnostic in AnalyzeLogger(invocation))
            {
                context.ReportDiagnostic(diagnostic);
            }
        }
    }
 
    [ExcludeFromCodeCoverage]
    private static bool IsLoggerMethod(ISymbol symbol)
    {
        return symbol.GetAttributes().Any(a => a.AttributeClass != null && IsLoggerMessageAttribute(a.AttributeClass));
    }
 
    private static bool IsLoggerMessageAttribute(ISymbol attributeSymbol)
    {
        return attributeSymbol.Name == "LoggerMessageAttribute"
            && attributeSymbol.ContainingNamespace.ToString() == "Microsoft.Extensions.Logging";
    }
 
    private static IEnumerable<Diagnostic> AnalyzeLogger(IInvocationOperation invocation)
    {
        foreach (var arg in invocation.Arguments)
        {
            if (arg.Value is IInvocationOperation argOperation
                && argOperation.TargetMethod.Name == "ToString")
            {
                yield return Diagnostic.Create(DiagDescriptors.UsingToStringInLoggers, arg.Syntax.GetLocation());
            }
        }
    }
}