File: Simplification\Simplifiers\AbstractMemberAccessExpressionSimplifier.cs
Web Access
Project: ..\..\..\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// 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.Diagnostics.CodeAnalysis;
using System.Threading;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
 
namespace Microsoft.CodeAnalysis.Simplification.Simplifiers
{
    internal abstract class AbstractMemberAccessExpressionSimplifier<
        TExpressionSyntax,
        TMemberAccessExpressionSyntax,
        TThisExpressionSyntax>
        where TExpressionSyntax : SyntaxNode
        where TMemberAccessExpressionSyntax : TExpressionSyntax
        where TThisExpressionSyntax : TExpressionSyntax
    {
        protected abstract ISyntaxFacts SyntaxFacts { get; }
        protected abstract ISpeculationAnalyzer GetSpeculationAnalyzer(
            SemanticModel semanticModel, TMemberAccessExpressionSyntax memberAccessExpression, CancellationToken cancellationToken);
 
        protected abstract bool MayCauseParseDifference(TMemberAccessExpressionSyntax memberAccessExpression);
 
        /// <summary>
        /// Checks a member access expression <c>expr.Name</c> and, if it is of the form <c>this.Name</c> or
        /// <c>Me.Name</c> determines if it is safe to replace with just <c>Name</c> alone.
        /// </summary>
        public bool ShouldSimplifyThisMemberAccessExpression(
            TMemberAccessExpressionSyntax? memberAccessExpression,
            SemanticModel semanticModel,
            SimplifierOptions simplifierOptions,
            [NotNullWhen(true)] out TThisExpressionSyntax? thisExpression,
            out ReportDiagnostic severity,
            CancellationToken cancellationToken)
        {
            severity = default;
            thisExpression = null;
 
            if (memberAccessExpression is null)
                return false;
 
            var syntaxFacts = this.SyntaxFacts;
            if (!syntaxFacts.IsSimpleMemberAccessExpression(memberAccessExpression))
                return false;
 
            thisExpression = syntaxFacts.GetExpressionOfMemberAccessExpression(memberAccessExpression) as TThisExpressionSyntax;
            if (!syntaxFacts.IsThisExpression(thisExpression))
                return false;
 
            var symbolInfo = semanticModel.GetSymbolInfo(memberAccessExpression, cancellationToken);
            if (symbolInfo.Symbol == null)
                return false;
 
            if (!simplifierOptions.TryGetQualifyMemberAccessOption(symbolInfo.Symbol.Kind, out var optionValue))
                return false;
 
            // We always simplify a static accesses off of this/me.  Otherwise, we fall back to whatever the user's option is.
            if (!symbolInfo.Symbol.IsStatic && optionValue.Value)
                return false;
 
            var speculationAnalyzer = GetSpeculationAnalyzer(semanticModel, memberAccessExpression, cancellationToken);
            var newSymbolInfo = speculationAnalyzer.SpeculativeSemanticModel.GetSymbolInfo(speculationAnalyzer.ReplacedExpression, cancellationToken);
            if (!symbolInfo.Symbol.Equals(newSymbolInfo.Symbol, SymbolEqualityComparer.IncludeNullability))
                return false;
 
            severity = optionValue.Notification.Severity;
            return !semanticModel.SyntaxTree.OverlapsHiddenPosition(memberAccessExpression.Span, cancellationToken) &&
                   !MayCauseParseDifference(memberAccessExpression);
        }
    }
}