// 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.CodeStyle; 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 NotificationOption2 notificationOption, CancellationToken cancellationToken) { notificationOption = NotificationOption2.Silent; 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; notificationOption = optionValue.Notification; return !semanticModel.SyntaxTree.OverlapsHiddenPosition(memberAccessExpression.Span, cancellationToken) && !MayCauseParseDifference(memberAccessExpression); } } |