File: Microsoft.CodeQuality.Analyzers\QualityGuidelines\AvoidInfiniteRecursion.cs
Web Access
Project: src\sdk\src\Microsoft.CodeAnalysis.NetAnalyzers\src\Microsoft.CodeAnalysis.NetAnalyzers\Microsoft.CodeAnalysis.NetAnalyzers.csproj (Microsoft.CodeAnalysis.NetAnalyzers)
// 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.Immutable;
using Analyzer.Utilities;
using Analyzer.Utilities.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;

namespace Microsoft.CodeQuality.Analyzers.QualityGuidelines
{
    using static MicrosoftCodeQualityAnalyzersResources;

    /// <summary>
    /// CA2011: <inheritdoc cref="AvoidInfiniteRecursionTitle"/>
    /// </summary>
    [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
    public sealed class AvoidInfiniteRecursion : DiagnosticAnalyzer
    {
        internal const string RuleId = "CA2011";

        internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(RuleId,
            CreateLocalizableResourceString(nameof(AvoidInfiniteRecursionTitle)),
            CreateLocalizableResourceString(nameof(AvoidInfiniteRecursionMessageSure)),
            DiagnosticCategory.Reliability,
            RuleLevel.IdeSuggestion,
            description: null,
            isPortedFxCopRule: false,
            isDataflowRule: false);

        internal static readonly DiagnosticDescriptor MaybeRule = DiagnosticDescriptorHelper.Create(RuleId,
            CreateLocalizableResourceString(nameof(AvoidInfiniteRecursionTitle)),
            CreateLocalizableResourceString(nameof(AvoidInfiniteRecursionMessageMaybe)),
            DiagnosticCategory.Reliability,
            RuleLevel.IdeSuggestion,
            description: null,
            isPortedFxCopRule: false,
            isDataflowRule: false);

        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Rule, MaybeRule);

        public override void Initialize(AnalysisContext context)
        {
            context.EnableConcurrentExecution();
            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);

            context.RegisterOperationBlockStartAction(operationBlockStartContext =>
            {
                if (operationBlockStartContext.OwningSymbol is not IMethodSymbol methodSymbol ||
                    methodSymbol.MethodKind != MethodKind.PropertySet)
                {
                    return;
                }

                operationBlockStartContext.RegisterOperationAction(operationContext =>
                {
                    var assignmentOperation = (IAssignmentOperation)operationContext.Operation;

                    if (assignmentOperation.Target is not IPropertyReferenceOperation operationTarget ||
                        operationTarget.Instance is not IInstanceReferenceOperation targetInstanceReference ||
                        targetInstanceReference.ReferenceKind != InstanceReferenceKind.ContainingTypeInstance ||
                        !operationTarget.Member.Equals(methodSymbol.AssociatedSymbol))
                    {
                        return;
                    }

                    IOperation? ancestor = assignmentOperation;
                    do
                    {
                        ancestor = ancestor.Parent;
                    } while (ancestor != null &&
                        ancestor.Kind != OperationKind.AnonymousFunction &&
                        ancestor.Kind != OperationKind.LocalFunction &&
                        ancestor.Kind != OperationKind.Conditional);

                    operationContext.ReportDiagnostic(
                        assignmentOperation.CreateDiagnostic(ancestor != null ? MaybeRule : Rule, operationTarget.Property.Name));
                }, OperationKind.SimpleAssignment);
            });
        }
    }
}