File: Diagnostics\BoxingOperationAnalyzer.cs
Web Access
Project: src\src\Compilers\Test\Core\Microsoft.CodeAnalysis.Test.Utilities.csproj (Microsoft.CodeAnalysis.Test.Utilities)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
 
namespace Microsoft.CodeAnalysis.Test.Utilities
{
    /// <summary>Analyzer that looks boxing operations.</summary>
    public class BoxingOperationAnalyzer : DiagnosticAnalyzer
    {
        /// <summary>Diagnostic category "Performance".</summary>
        private const string PerformanceCategory = "Performance";
 
        private static readonly LocalizableString s_localizableTitle = "Boxing";
        private static readonly LocalizableString s_localizableMessage = "Boxing is expensive";
 
        /// <summary>The diagnostic descriptor used when boxing is detected.</summary>
        public static readonly DiagnosticDescriptor BoxingDescriptor = new DiagnosticDescriptor(
            "BoxingRule",
            s_localizableTitle,
            s_localizableMessage,
            PerformanceCategory,
            DiagnosticSeverity.Warning,
            isEnabledByDefault: true);
 
        /// <summary>Gets the set of supported diagnostic descriptors from this analyzer.</summary>
        public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
        {
            get { return ImmutableArray.Create(BoxingDescriptor); }
        }
 
        public sealed override void Initialize(AnalysisContext context)
        {
            context.RegisterOperationAction(
                 (operationContext) =>
                 {
                     IOperation operation = operationContext.Operation;
 
                     if (operation.Kind == OperationKind.Conversion)
                     {
                         IConversionOperation conversion = (IConversionOperation)operation;
                         if (conversion.Type.IsReferenceType &&
                             conversion.Operand.Type != null &&
                             conversion.Operand.Type.IsValueType &&
                             conversion.OperatorMethod == null)
                         {
                             Report(operationContext, conversion.Syntax);
                         }
                     }
 
                     // Calls to instance methods of value types don’t have conversions.
                     if (operation.Kind == OperationKind.Invocation)
                     {
                         IInvocationOperation invocation = (IInvocationOperation)operation;
 
                         if (invocation.Instance != null &&
                             invocation.Instance.Type.IsValueType &&
                             invocation.TargetMethod.ContainingType.IsReferenceType)
                         {
                             Report(operationContext, invocation.Instance.Syntax);
                         }
                     }
                 },
                 OperationKind.Conversion,
                 OperationKind.Invocation);
        }
 
        /// <summary>Reports a diagnostic warning for a boxing operation.</summary>
        /// <param name="context">The context.</param>
        /// <param name="boxingExpression">The expression that produces the boxing.</param>
        internal void Report(OperationAnalysisContext context, SyntaxNode boxingExpression)
        {
            context.ReportDiagnostic(Diagnostic.Create(BoxingDescriptor, boxingExpression.GetLocation()));
        }
    }
}