File: src\roslyn\src\Analyzers\CSharp\Analyzers\QualifyMemberAccess\CSharpQualifyMemberAccessDiagnosticAnalyzer.cs
Web Access
Project: src\roslyn\src\Features\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Features.csproj (Microsoft.CodeAnalysis.CSharp.Features)
// 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.

using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Simplification;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.QualifyMemberAccess;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;

namespace Microsoft.CodeAnalysis.CSharp.QualifyMemberAccess;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class CSharpQualifyMemberAccessDiagnosticAnalyzer
    : AbstractQualifyMemberAccessDiagnosticAnalyzer<SyntaxKind, ExpressionSyntax, SimpleNameSyntax>
{
    protected override ISimplification Simplification
        => CSharpSimplification.Instance;

    protected override bool IsAlreadyQualifiedMemberAccess(ExpressionSyntax node)
        => node.IsKind(SyntaxKind.ThisExpression);

    // If the member is already qualified with `base.`,
    // or member is in object initialization context,
    // or member in property or field initialization,
    // or member in constructor initializer, it cannot be qualified.
    protected override bool CanMemberAccessBeQualified(ISymbol containingSymbol, SyntaxNode node)
    {
        if (node.GetAncestorOrThis<AttributeSyntax>() != null)
            return false;

        if (node.GetAncestorOrThis<ConstructorInitializerSyntax>() != null)
            return false;

        if (node.IsKind(SyntaxKind.BaseExpression))
            return false;

        if (IsInPropertyOrFieldInitialization(containingSymbol, node))
            return false;

        if (node.Parent is AssignmentExpressionSyntax { Parent: InitializerExpressionSyntax(SyntaxKind.ObjectInitializerExpression), Left: var left } &&
            left == node)
        {
            return false;
        }

        return true;
    }

    private static bool IsInPropertyOrFieldInitialization(ISymbol containingSymbol, SyntaxNode node)
    {
        return (containingSymbol.Kind is SymbolKind.Field or SymbolKind.Property) &&
            containingSymbol.DeclaringSyntaxReferences
                .Select(declaringSyntaxReferences => declaringSyntaxReferences.GetSyntax())
                .Any(declaringSyntax => IsInPropertyInitialization(declaringSyntax, node) || IsInFieldInitialization(declaringSyntax, node));
    }

    private static bool IsInPropertyInitialization(SyntaxNode declarationSyntax, SyntaxNode node)
        => declarationSyntax.IsKind(SyntaxKind.PropertyDeclaration) && declarationSyntax.Contains(node);

    private static bool IsInFieldInitialization(SyntaxNode declarationSyntax, SyntaxNode node)
        => declarationSyntax.GetAncestorsOrThis(n => n.IsKind(SyntaxKind.FieldDeclaration) && n.Contains(node)).Any();

    protected override Location GetLocation(IOperation operation) => operation.Syntax.GetLocation();
}