File: System\Windows\Forms\Analyzers\PropertyAllocatesNewInstanceAnalyzer.cs
Web Access
Project: ..\..\..\src\winforms\src\System.Windows.Forms.Analyzers\src\System.Windows.Forms.Analyzers.csproj (System.Windows.Forms.Analyzers)
// 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.Concurrent;
using System.Collections.Immutable;
using System.Windows.Forms.Analyzers.Diagnostics;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
 
namespace System.Windows.Forms.Analyzers;
 
/// <summary>
///  Reports getter return paths that directly construct a reference instance.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public sealed class PropertyAllocatesNewInstanceAnalyzer : DiagnosticAnalyzer
{
    /// <inheritdoc/>
    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
        => [SharedDiagnosticDescriptors.s_propertyAllocatesNewInstance];
 
    /// <inheritdoc/>
    public override void Initialize(AnalysisContext context)
    {
        context.EnableConcurrentExecution();
        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
        context.RegisterCompilationStartAction(
            startContext =>
            {
                ConcurrentDictionary<IPropertySymbol, byte> reportedProperties = new(SymbolEqualityComparer.Default);
                startContext.RegisterOperationAction(
                    context => AnalyzeCreation(context, reportedProperties),
                    OperationKind.ObjectCreation,
                    OperationKind.ArrayCreation,
                    OperationKind.AnonymousObjectCreation);
            });
    }
 
    private static void AnalyzeCreation(
        OperationAnalysisContext context,
        ConcurrentDictionary<IPropertySymbol, byte> reportedProperties)
    {
        if (context.ContainingSymbol is not IMethodSymbol method
            || method.MethodKind != MethodKind.PropertyGet
            || method.AssociatedSymbol is not IPropertySymbol { IsIndexer: false } property
            || context.Operation.Type is not { IsReferenceType: true }
            || DesignerTypeFacts.IsInNestedFunction(context.Operation))
        {
            return;
        }
 
        IOperation value = context.Operation;
        while (value.Parent is IConversionOperation { OperatorMethod: null } or IParenthesizedOperation)
        {
            value = value.Parent;
        }
 
        if (value.Parent is not IReturnOperation || !reportedProperties.TryAdd(property, 0))
        {
            return;
        }
 
        Location? location = property.Locations.FirstOrDefault(item => item.IsInSource);
        if (location is not null)
        {
            context.ReportDiagnostic(Diagnostic.Create(
                SharedDiagnosticDescriptors.s_propertyAllocatesNewInstance,
                location,
                property.Name));
        }
    }
}