File: Simplification\CSharpSimplificationService.Expander.cs
Web Access
Project: src\src\Workspaces\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Workspaces.csproj (Microsoft.CodeAnalysis.CSharp.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.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Utilities;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.Simplification;
 
using static CSharpSyntaxTokens;
using static SyntaxFactory;
 
internal partial class CSharpSimplificationService
{
    private class Expander : CSharpSyntaxRewriter
    {
        private static readonly SyntaxTrivia s_oneWhitespaceSeparator = SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " ");
 
        private static readonly SymbolDisplayFormat s_typeNameFormatWithGenerics =
            new(
                globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Included,
                genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
                memberOptions:
                    SymbolDisplayMemberOptions.IncludeContainingType,
                localOptions: SymbolDisplayLocalOptions.IncludeType,
                miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | SymbolDisplayMiscellaneousOptions.ExpandNullable,
                typeQualificationStyle:
                    SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces);
 
        private readonly SemanticModel _semanticModel;
        private readonly Func<SyntaxNode, bool> _expandInsideNode;
        private readonly CancellationToken _cancellationToken;
        private readonly SyntaxAnnotation _annotationForReplacedAliasIdentifier;
        private readonly bool _expandParameter;
 
        public Expander(
            SemanticModel semanticModel,
            Func<SyntaxNode, bool> expandInsideNode,
            bool expandParameter,
            CancellationToken cancellationToken,
            SyntaxAnnotation annotationForReplacedAliasIdentifier = null)
        {
            _semanticModel = semanticModel;
            _expandInsideNode = expandInsideNode;
            _expandParameter = expandParameter;
            _cancellationToken = cancellationToken;
            _annotationForReplacedAliasIdentifier = annotationForReplacedAliasIdentifier;
        }
 
        public override SyntaxNode Visit(SyntaxNode node)
        {
            if (_expandInsideNode == null || _expandInsideNode(node))
            {
                return base.Visit(node);
            }
 
            return node;
        }
 
        private bool IsPassedToDelegateCreationExpression(ArgumentSyntax argument, ITypeSymbol type)
        {
            if (type.IsDelegateType() &&
                argument.IsParentKind(SyntaxKind.ArgumentList) &&
                argument.Parent?.Parent is ObjectCreationExpressionSyntax objectCreationExpression)
            {
                var objectCreationType = _semanticModel.GetTypeInfo(objectCreationExpression).Type;
                if (objectCreationType.Equals(type))
                {
                    return true;
                }
            }
 
            return false;
        }
 
        private SpeculationAnalyzer GetSpeculationAnalyzer(ExpressionSyntax expression, ExpressionSyntax newExpression)
            => new(expression, newExpression, _semanticModel, _cancellationToken);
 
        private bool TryCastTo(ITypeSymbol targetType, ExpressionSyntax expression, ExpressionSyntax newExpression, out ExpressionSyntax newExpressionWithCast)
        {
            var speculativeAnalyzer = GetSpeculationAnalyzer(expression, newExpression);
            var speculativeSemanticModel = speculativeAnalyzer.SpeculativeSemanticModel;
            var speculatedExpression = speculativeAnalyzer.ReplacedExpression;
 
            var result = speculatedExpression.CastIfPossible(targetType, speculatedExpression.SpanStart, speculativeSemanticModel, _cancellationToken);
 
            if (result != speculatedExpression)
            {
                newExpressionWithCast = result;
                return true;
            }
 
            newExpressionWithCast = null;
            return false;
        }
 
        private bool TryGetLambdaExpressionBodyWithCast(LambdaExpressionSyntax lambdaExpression, LambdaExpressionSyntax newLambdaExpression, out ExpressionSyntax newLambdaExpressionBodyWithCast)
        {
            if (newLambdaExpression.Body is ExpressionSyntax)
            {
                var body = (ExpressionSyntax)lambdaExpression.Body;
                var newBody = (ExpressionSyntax)newLambdaExpression.Body;
 
                var returnType = (_semanticModel.GetSymbolInfo(lambdaExpression).Symbol as IMethodSymbol)?.ReturnType;
                if (returnType != null)
                {
                    return TryCastTo(returnType, body, newBody, out newLambdaExpressionBodyWithCast);
                }
            }
 
            newLambdaExpressionBodyWithCast = null;
            return false;
        }
 
        public override SyntaxNode VisitReturnStatement(ReturnStatementSyntax node)
        {
            var newNode = base.VisitReturnStatement(node);
 
            if (newNode is ReturnStatementSyntax newReturnStatement)
            {
                if (newReturnStatement.Expression != null)
                {
                    var parentLambda = node.FirstAncestorOrSelf<LambdaExpressionSyntax>();
                    if (parentLambda != null)
                    {
                        var returnType = (_semanticModel.GetSymbolInfo(parentLambda).Symbol as IMethodSymbol)?.ReturnType;
                        if (returnType != null)
                        {
                            if (TryCastTo(returnType, node.Expression, newReturnStatement.Expression, out var newExpressionWithCast))
                            {
                                newNode = newReturnStatement.WithExpression(newExpressionWithCast);
                            }
                        }
                    }
                }
            }
 
            return newNode;
        }
 
        public override SyntaxNode VisitParenthesizedLambdaExpression(ParenthesizedLambdaExpressionSyntax node)
        {
            var newNode = base.VisitParenthesizedLambdaExpression(node);
 
            if (newNode is ParenthesizedLambdaExpressionSyntax parenthesizedLambda)
            {
                // First, try to add a cast to the lambda.
                if (TryGetLambdaExpressionBodyWithCast(node, parenthesizedLambda, out var newLambdaExpressionBodyWithCast))
                {
                    parenthesizedLambda = parenthesizedLambda.WithBody(newLambdaExpressionBodyWithCast);
                }
 
                // Next, try to add a types to the lambda parameters
                if (_expandParameter && parenthesizedLambda.ParameterList != null)
                {
                    var parameterList = parenthesizedLambda.ParameterList;
                    var parameters = parameterList.Parameters.ToArray();
 
                    if (parameters.Length > 0 && parameters.Any(p => p.Type == null))
                    {
                        var parameterSymbols = node.ParameterList.Parameters
                            .Select(p => _semanticModel.GetDeclaredSymbol(p, _cancellationToken))
                            .ToArray();
 
                        if (parameterSymbols.All(p => p.Type?.ContainsAnonymousType() == false))
                        {
                            var newParameters = parameterList.Parameters;
 
                            for (var i = 0; i < parameterSymbols.Length; i++)
                            {
                                var typeSyntax = parameterSymbols[i].Type.GenerateTypeSyntax().WithTrailingTrivia(s_oneWhitespaceSeparator);
                                var newParameter = parameters[i].WithType(typeSyntax).WithAdditionalAnnotations(Simplifier.Annotation);
 
                                var currentParameter = newParameters[i];
                                newParameters = newParameters.Replace(currentParameter, newParameter);
                            }
 
                            var newParameterList = parameterList.WithParameters(newParameters);
                            var newParenthesizedLambda = parenthesizedLambda.WithParameterList(newParameterList);
 
                            return SimplificationHelpers.CopyAnnotations(from: parenthesizedLambda, to: newParenthesizedLambda);
                        }
                    }
                }
 
                return parenthesizedLambda;
            }
 
            return newNode;
        }
 
        public override SyntaxNode VisitSimpleLambdaExpression(SimpleLambdaExpressionSyntax node)
        {
            var newNode = base.VisitSimpleLambdaExpression(node);
 
            if (newNode is SimpleLambdaExpressionSyntax simpleLambda)
            {
                // First, try to add a cast to the lambda.
                if (TryGetLambdaExpressionBodyWithCast(node, simpleLambda, out var newLambdaExpressionBodyWithCast))
                {
                    simpleLambda = simpleLambda.WithBody(newLambdaExpressionBodyWithCast);
                }
 
                // Next, try to add a type to the lambda parameter
                if (_expandParameter)
                {
                    var parameterSymbol = _semanticModel.GetDeclaredSymbol(node.Parameter);
                    if (parameterSymbol?.Type?.ContainsAnonymousType() == false)
                    {
                        var typeSyntax = parameterSymbol.Type.GenerateTypeSyntax().WithTrailingTrivia(s_oneWhitespaceSeparator);
                        var newSimpleLambdaParameter = simpleLambda.Parameter.WithType(typeSyntax).WithoutTrailingTrivia();
 
                        var parenthesizedLambda = ParenthesizedLambdaExpression(
                            simpleLambda.AsyncKeyword,
                            ParameterList([newSimpleLambdaParameter])
                                .WithTrailingTrivia(simpleLambda.Parameter.GetTrailingTrivia())
                                .WithLeadingTrivia(simpleLambda.Parameter.GetLeadingTrivia()),
                            simpleLambda.ArrowToken,
                            simpleLambda.Body).WithAdditionalAnnotations(Simplifier.Annotation);
 
                        return SimplificationHelpers.CopyAnnotations(from: simpleLambda, to: parenthesizedLambda);
                    }
                }
 
                return simpleLambda;
            }
 
            return newNode;
        }
 
        public override SyntaxNode VisitArgument(ArgumentSyntax node)
        {
            _cancellationToken.ThrowIfCancellationRequested();
 
            var newArgument = (ArgumentSyntax)base.VisitArgument(node);
 
            if (node.NameColon == null
                && node.Parent is TupleExpressionSyntax tuple
                && !IsTupleInDeconstruction(tuple)) // The language currently does not allow explicit element names in deconstruction
            {
                var inferredName = node.Expression.TryGetInferredMemberName();
                if (CanMakeNameExplicitInTuple(tuple, inferredName))
                {
                    var identifier = Identifier(inferredName);
                    identifier = TryEscapeIdentifierToken(identifier, node);
 
                    newArgument = newArgument
                        .WithoutLeadingTrivia()
                        .WithNameColon(NameColon(IdentifierName(identifier)))
                        .WithAdditionalAnnotations(Simplifier.Annotation)
                        .WithLeadingTrivia(node.GetLeadingTrivia());
                }
            }
 
            var argumentType = _semanticModel.GetTypeInfo(node.Expression).ConvertedType;
            if (argumentType != null &&
                !IsPassedToDelegateCreationExpression(node, argumentType) &&
                node.Expression.Kind() != SyntaxKind.DeclarationExpression &&
                node.RefOrOutKeyword.Kind() == SyntaxKind.None)
            {
                if (TryCastTo(argumentType, node.Expression, newArgument.Expression, out var newArgumentExpressionWithCast))
                {
                    return newArgument.WithExpression(newArgumentExpressionWithCast);
                }
            }
 
            return newArgument;
        }
 
        private static bool CanMakeNameExplicitInTuple(TupleExpressionSyntax tuple, string name)
        {
            if (name == null || SyntaxFacts.IsReservedTupleElementName(name))
            {
                return false;
            }
 
            var found = false;
            foreach (var argument in tuple.Arguments)
            {
                string elementName;
                if (argument.NameColon != null)
                {
                    elementName = argument.NameColon.Name.Identifier.ValueText;
                }
                else
                {
                    elementName = argument.Expression?.TryGetInferredMemberName();
                }
 
                if (elementName?.Equals(name, StringComparison.Ordinal) == true)
                {
                    if (found)
                    {
                        // No duplicate names allowed
                        return false;
                    }
 
                    found = true;
                }
            }
 
            return true;
        }
 
        public override SyntaxNode VisitAnonymousObjectMemberDeclarator(AnonymousObjectMemberDeclaratorSyntax node)
        {
            var newDeclarator = (AnonymousObjectMemberDeclaratorSyntax)base.VisitAnonymousObjectMemberDeclarator(node);
            if (node.NameEquals == null)
            {
                var inferredName = node.Expression.TryGetInferredMemberName();
                if (inferredName != null)
                {
                    // Creating identifier without elastic trivia to avoid unexpected line break
                    var identifier = Identifier(SyntaxTriviaList.Empty, inferredName, SyntaxTriviaList.Empty);
                    identifier = TryEscapeIdentifierToken(identifier, node);
 
                    newDeclarator = newDeclarator
                        .WithoutLeadingTrivia()
                        .WithNameEquals(NameEquals(IdentifierName(identifier))
                            .WithLeadingTrivia(node.GetLeadingTrivia()))
                        .WithAdditionalAnnotations(Simplifier.Annotation);
                }
            }
 
            return newDeclarator;
        }
 
        public override SyntaxNode VisitBinaryExpression(BinaryExpressionSyntax node)
        {
            _cancellationToken.ThrowIfCancellationRequested();
 
            // Special case: We parenthesize to avoid a situation where inlining an
            // expression can cause code to parse differently. The canonical case is...
            //
            //     var x = 0;
            //     var y = (1 + 2);
            //     var z = new[] { x < x, x > y };
            //
            // Inlining 'y' in the code above will produce code that parses differently
            // (i.e. as a generic method invocation).
            //
            //      var z = new[] { x < x, x > (1 + 2) };
 
            var result = (BinaryExpressionSyntax)base.VisitBinaryExpression(node);
 
            if ((node.Kind() == SyntaxKind.GreaterThanExpression || node.Kind() == SyntaxKind.RightShiftExpression) && !node.IsParentKind(SyntaxKind.ParenthesizedExpression))
            {
                return result.Parenthesize();
            }
 
            return result;
        }
 
        public override SyntaxNode VisitInterpolation(InterpolationSyntax node)
        {
            var result = (InterpolationSyntax)base.VisitInterpolation(node);
 
            if (result.Expression != null && !result.Expression.IsKind(SyntaxKind.ParenthesizedExpression))
            {
                result = result.WithExpression(result.Expression.Parenthesize());
            }
 
            return result;
        }
 
        public override SyntaxNode VisitXmlNameAttribute(XmlNameAttributeSyntax node)
        {
            _cancellationToken.ThrowIfCancellationRequested();
 
            var newNode = (XmlNameAttributeSyntax)base.VisitXmlNameAttribute(node);
 
            return node.CopyAnnotationsTo(newNode);
        }
 
        public override SyntaxNode VisitNameMemberCref(NameMemberCrefSyntax node)
        {
            _cancellationToken.ThrowIfCancellationRequested();
            var rewrittenname = (TypeSyntax)this.Visit(node.Name);
            var parameters = (CrefParameterListSyntax)this.Visit(node.Parameters);
 
            if (rewrittenname.Kind() == SyntaxKind.QualifiedName)
            {
                return node.CopyAnnotationsTo(QualifiedCref(
                    ((QualifiedNameSyntax)rewrittenname).Left
                        .WithAdditionalAnnotations(Simplifier.Annotation),
                    NameMemberCref(((QualifiedNameSyntax)rewrittenname).Right, parameters)
                    .WithLeadingTrivia(SyntaxTriviaList.Empty))
                        .WithLeadingTrivia(node.GetLeadingTrivia())
                        .WithTrailingTrivia(node.GetTrailingTrivia()))
                        .WithAdditionalAnnotations(Simplifier.Annotation);
            }
            else if (rewrittenname.Kind() == SyntaxKind.AliasQualifiedName)
            {
                return node.CopyAnnotationsTo(TypeCref(
                    rewrittenname).WithLeadingTrivia(node.GetLeadingTrivia())
                    .WithTrailingTrivia(node.GetTrailingTrivia()))
                    .WithAdditionalAnnotations(Simplifier.Annotation);
            }
 
            return node.Update(rewrittenname, parameters);
        }
 
        public override SyntaxNode VisitGenericName(GenericNameSyntax node)
        {
            _cancellationToken.ThrowIfCancellationRequested();
 
            var newNode = (SimpleNameSyntax)base.VisitGenericName(node);
 
            return VisitSimpleName(newNode, node);
        }
 
        public override SyntaxNode VisitIdentifierName(IdentifierNameSyntax node)
        {
            _cancellationToken.ThrowIfCancellationRequested();
 
            var newNode = (SimpleNameSyntax)base.VisitIdentifierName(node);
 
            return VisitSimpleName(newNode, node);
        }
 
        private ExpressionSyntax VisitSimpleName(SimpleNameSyntax rewrittenSimpleName, SimpleNameSyntax originalSimpleName)
        {
            _cancellationToken.ThrowIfCancellationRequested();
 
            // if this is "var", then do not process further
            if (originalSimpleName.IsVar)
            {
                return rewrittenSimpleName;
            }
 
            var identifier = rewrittenSimpleName.Identifier;
            ExpressionSyntax newNode = rewrittenSimpleName;
 
            var isInsideCref = originalSimpleName.AncestorsAndSelf(ascendOutOfTrivia: true).Any(n => n is CrefSyntax);
 
            ////
            //// 1. if this identifier is an alias, we'll expand it here and replace the node completely.
            ////
            if (!SyntaxFacts.IsAliasQualifier(originalSimpleName))
            {
                var aliasInfo = _semanticModel.GetAliasInfo(originalSimpleName, _cancellationToken);
                if (aliasInfo != null)
                {
                    var aliasTarget = aliasInfo.Target;
 
                    if (aliasTarget.IsNamespace() && ((INamespaceSymbol)aliasTarget).IsGlobalNamespace)
                    {
                        return rewrittenSimpleName;
                    }
 
                    // if the enclosing expression is a typeof expression that already contains open type we cannot
                    // we need to insert an open type as well.
                    var typeOfExpression = originalSimpleName.GetAncestor<TypeOfExpressionSyntax>();
                    if (typeOfExpression != null && IsTypeOfUnboundGenericType(_semanticModel, typeOfExpression))
                    {
                        aliasTarget = ((INamedTypeSymbol)aliasTarget).ConstructUnboundGenericType();
                    }
 
                    if (aliasTarget is INamedTypeSymbol typeSymbol && typeSymbol.IsTupleType)
                    {
                        return rewrittenSimpleName;
                    }
 
                    // the expanded form replaces the current identifier name.
                    var replacement = FullyQualifyIdentifierName(
                        aliasTarget,
                        newNode,
                        originalSimpleName,
                        replaceNode: true,
                        isInsideCref: isInsideCref,
                        omitLeftHandSide: false)
                            .WithAdditionalAnnotations(Simplifier.Annotation);
 
                    // We replace the simple name completely, so we can't continue and rename the token
                    // with a RenameLocationAnnotation.
                    // There's also no way of removing annotations, so we just add a DoNotRenameAnnotation.
                    switch (replacement.Kind())
                    {
                        case SyntaxKind.AliasQualifiedName:
                            var aliasQualifiedReplacement = (AliasQualifiedNameSyntax)replacement;
                            replacement = replacement.ReplaceNode(
                                    aliasQualifiedReplacement.Name,
                                    aliasQualifiedReplacement.Name.WithIdentifier(
                                        GetNewIdentifier(aliasQualifiedReplacement.Name.Identifier)));
 
                            var firstReplacementToken = replacement.GetFirstToken(true, false, true, true);
                            var firstOriginalToken = originalSimpleName.GetFirstToken(true, false, true, true);
                            if (TryAddLeadingElasticTriviaIfNecessary(firstReplacementToken, firstOriginalToken, out var tokenWithLeadingWhitespace))
                            {
                                replacement = replacement.ReplaceToken(firstOriginalToken, tokenWithLeadingWhitespace);
                            }
 
                            break;
 
                        case SyntaxKind.QualifiedName:
                            var qualifiedReplacement = (QualifiedNameSyntax)replacement;
                            replacement = replacement.ReplaceNode(
                                    qualifiedReplacement.Right,
                                    qualifiedReplacement.Right.WithIdentifier(
                                        GetNewIdentifier(qualifiedReplacement.Right.Identifier)));
                            break;
 
                        case SyntaxKind.IdentifierName:
                            var identifierReplacement = (IdentifierNameSyntax)replacement;
                            replacement = replacement.ReplaceToken(identifier, GetNewIdentifier(identifierReplacement.Identifier));
                            break;
 
                        default:
                            throw ExceptionUtilities.UnexpectedValue(replacement.Kind());
                    }
 
                    replacement = newNode.CopyAnnotationsTo(replacement);
                    replacement = AppendElasticTriviaIfNecessary(replacement, originalSimpleName);
 
                    return replacement;
 
                    SyntaxToken GetNewIdentifier(SyntaxToken _identifier)
                    {
                        var newIdentifier = identifier.CopyAnnotationsTo(_identifier);
 
                        if (_annotationForReplacedAliasIdentifier != null)
                        {
                            newIdentifier = newIdentifier.WithAdditionalAnnotations(_annotationForReplacedAliasIdentifier);
                        }
 
                        var aliasAnnotationInfo = AliasAnnotation.Create(aliasInfo.Name);
                        return newIdentifier.WithAdditionalAnnotations(aliasAnnotationInfo);
                    }
                }
            }
 
            var symbol = _semanticModel.GetSymbolInfo(originalSimpleName.Identifier).Symbol;
            if (symbol == null)
            {
                return newNode;
            }
 
            var typeArgumentSymbols = TypeArgumentSymbolsPresentInName(originalSimpleName);
            var omitLeftSideOfExpression = false;
 
            // Check to see if the type Arguments in the resultant Symbol is recursively defined.
            if (IsTypeArgumentDefinedRecursive(symbol, typeArgumentSymbols, enterContainingSymbol: true))
            {
                if (symbol.ContainingSymbol.Equals(symbol.OriginalDefinition.ContainingSymbol) &&
                    symbol.Kind == SymbolKind.Method &&
                    ((IMethodSymbol)symbol).IsStatic)
                {
                    if (IsTypeArgumentDefinedRecursive(symbol, typeArgumentSymbols, enterContainingSymbol: false))
                    {
                        return newNode;
                    }
                    else
                    {
                        omitLeftSideOfExpression = true;
                    }
                }
                else
                {
                    return newNode;
                }
            }
 
            if (IsInvocationWithDynamicArguments(originalSimpleName, _semanticModel))
            {
                return newNode;
            }
 
            ////
            //// 2. If it's an attribute, make sure the identifier matches the attribute's class name.
            ////
            if (originalSimpleName.GetAncestor<AttributeSyntax>() != null)
            {
                if (symbol.IsConstructor() && symbol.ContainingType?.IsAttribute() == true)
                {
                    symbol = symbol.ContainingType;
                    var name = symbol.Name;
 
                    Debug.Assert(name.StartsWith(originalSimpleName.Identifier.ValueText, StringComparison.Ordinal));
 
                    // if the user already used the Attribute suffix in the attribute, we'll maintain it.
                    if (identifier.ValueText == name && name.EndsWith("Attribute", StringComparison.Ordinal))
                    {
                        identifier = identifier.WithAdditionalAnnotations(SimplificationHelpers.DoNotSimplifyAnnotation);
                    }
 
                    identifier = identifier.CopyAnnotationsTo(VerbatimIdentifier(identifier.LeadingTrivia, name, name, identifier.TrailingTrivia));
                }
            }
 
            ////
            //// 3. Always try to escape keyword identifiers
            ////
            identifier = TryEscapeIdentifierToken(identifier, originalSimpleName).WithAdditionalAnnotations(Simplifier.Annotation);
            if (identifier != rewrittenSimpleName.Identifier)
            {
                switch (newNode.Kind())
                {
                    case SyntaxKind.IdentifierName:
                    case SyntaxKind.GenericName:
                        newNode = ((SimpleNameSyntax)newNode).WithIdentifier(identifier);
                        break;
 
                    default:
                        throw new NotImplementedException();
                }
            }
 
            var parent = originalSimpleName.Parent;
 
            // do not complexify further for location where only simple names are allowed
            if (parent is MemberBindingExpressionSyntax ||
                originalSimpleName.GetAncestor<NameEqualsSyntax>() != null ||
                (parent is MemberAccessExpressionSyntax && parent.Kind() != SyntaxKind.SimpleMemberAccessExpression) ||
                ((parent.Kind() == SyntaxKind.SimpleMemberAccessExpression || parent.Kind() == SyntaxKind.NameMemberCref) && originalSimpleName.IsRightSideOfDot()) ||
                (parent.Kind() == SyntaxKind.QualifiedName && originalSimpleName.IsRightSideOfQualifiedName()) ||
                (parent.Kind() == SyntaxKind.AliasQualifiedName) ||
                (parent.Kind() == SyntaxKind.NameColon))
            {
                return TryAddTypeArgumentToIdentifierName(newNode, symbol);
            }
 
            ////
            //// 4. If this is a standalone identifier or the left side of a qualified name or member access try to fully qualify it
            ////
 
            // we need to treat the constructor as type name, so just get the containing type.
            if (symbol.IsConstructor() && (parent.Kind() == SyntaxKind.ObjectCreationExpression || parent.Kind() == SyntaxKind.NameMemberCref))
            {
                symbol = symbol.ContainingType;
            }
 
            // if it's a namespace or type name, fully qualify it.
            if (symbol.Kind is SymbolKind.NamedType or
                SymbolKind.Namespace)
            {
                var replacement = FullyQualifyIdentifierName(
                    (INamespaceOrTypeSymbol)symbol,
                    newNode,
                    originalSimpleName,
                    replaceNode: false,
                    isInsideCref: isInsideCref,
                    omitLeftHandSide: omitLeftSideOfExpression)
                        .WithAdditionalAnnotations(Simplifier.Annotation);
 
                replacement = AppendElasticTriviaIfNecessary(replacement, originalSimpleName);
 
                return replacement;
            }
 
            // if it's a member access, we're fully qualifying the left side and make it a member access.
            if (symbol.Kind is SymbolKind.Method or
                SymbolKind.Field or
                SymbolKind.Property)
            {
                if (symbol.IsStatic ||
                    originalSimpleName.IsParentKind(SyntaxKind.NameMemberCref) ||
                    _semanticModel.SyntaxTree.IsNameOfContext(originalSimpleName.SpanStart, _semanticModel, _cancellationToken))
                {
                    newNode = FullyQualifyIdentifierName(
                        symbol,
                        newNode,
                        originalSimpleName,
                        replaceNode: false,
                        isInsideCref: isInsideCref,
                        omitLeftHandSide: omitLeftSideOfExpression);
                }
                else
                {
                    if (!IsPropertyNameOfObjectInitializer(originalSimpleName) &&
                        !symbol.IsLocalFunction())
                    {
                        ExpressionSyntax left;
 
                        // Assumption here is, if the enclosing and containing types are different then there is inheritance relationship
                        if (!Equals(_semanticModel.GetEnclosingNamedType(originalSimpleName.SpanStart, _cancellationToken), symbol.ContainingType))
                        {
                            left = BaseExpression();
                        }
                        else
                        {
                            left = ThisExpression();
                        }
 
                        var identifiersLeadingTrivia = newNode.GetLeadingTrivia();
                        newNode = TryAddTypeArgumentToIdentifierName(newNode, symbol);
 
                        newNode = newNode.CopyAnnotationsTo(
                            MemberAccessExpression(
                                SyntaxKind.SimpleMemberAccessExpression,
                                left,
                                (SimpleNameSyntax)newNode.WithLeadingTrivia(null))
                                                         .WithLeadingTrivia(identifiersLeadingTrivia));
                    }
                }
            }
 
            var result = newNode.WithAdditionalAnnotations(Simplifier.Annotation);
            result = AppendElasticTriviaIfNecessary(result, originalSimpleName);
 
            return result;
        }
 
        private ExpressionSyntax TryReplaceAngleBracesWithCurlyBraces(ExpressionSyntax expression, bool isInsideCref)
        {
            if (isInsideCref)
            {
                var leftTokens = expression.DescendantTokens();
                var candidateTokens = new List<SyntaxToken>();
 
                foreach (var candidateToken in leftTokens)
                {
                    if (candidateToken.Kind() is SyntaxKind.LessThanToken or SyntaxKind.GreaterThanToken)
                    {
                        candidateTokens.Add(candidateToken);
                        continue;
                    }
                }
 
                expression = expression.ReplaceTokens(candidateTokens, computeReplacementToken: ReplaceTokenForCref);
            }
 
            return expression;
        }
 
        private static ExpressionSyntax TryAddTypeArgumentToIdentifierName(ExpressionSyntax newNode, ISymbol symbol)
        {
            if (newNode.Kind() == SyntaxKind.IdentifierName && symbol.Kind == SymbolKind.Method)
            {
                if (((IMethodSymbol)symbol).TypeArguments.Length != 0)
                {
                    var typeArguments = ((IMethodSymbol)symbol).TypeArguments;
                    if (!typeArguments.Any(static t => t.ContainsAnonymousType()))
                    {
                        var genericName = GenericName(
                                        ((IdentifierNameSyntax)newNode).Identifier,
                                        TypeArgumentList(
                                            [.. typeArguments.Select(p => ParseTypeName(p.ToDisplayString(s_typeNameFormatWithGenerics)))]))
                                        .WithLeadingTrivia(newNode.GetLeadingTrivia())
                                        .WithTrailingTrivia(newNode.GetTrailingTrivia())
                                        .WithAdditionalAnnotations(Simplifier.Annotation);
 
                        genericName = newNode.CopyAnnotationsTo(genericName);
                        return genericName;
                    }
                }
            }
 
            return newNode;
        }
 
        private IList<ISymbol> TypeArgumentSymbolsPresentInName(SimpleNameSyntax simpleName)
        {
            var typeArgumentSymbols = new List<ISymbol>();
            var typeArgumentListSyntax = simpleName.DescendantNodesAndSelf().Where(n => n is TypeArgumentListSyntax);
            foreach (var typeArgumentList in typeArgumentListSyntax)
            {
                var castedTypeArgument = (TypeArgumentListSyntax)typeArgumentList;
                foreach (var typeArgument in castedTypeArgument.Arguments)
                {
                    var symbol = _semanticModel.GetSymbolInfo(typeArgument).Symbol;
                    if (symbol != null && !typeArgumentSymbols.Contains(symbol))
                    {
                        typeArgumentSymbols.Add(symbol);
                    }
                }
            }
 
            return typeArgumentSymbols;
        }
 
        private static bool IsInvocationWithDynamicArguments(SimpleNameSyntax originalSimpleName, SemanticModel semanticModel)
        {
            var invocationExpression = originalSimpleName.Ancestors().OfType<InvocationExpressionSyntax>().FirstOrDefault();
 
            // Check to see if this is the invocation Expression we wanted to work with
            if (invocationExpression != null && invocationExpression.Expression.GetLastToken() == originalSimpleName.GetLastToken())
            {
                if (invocationExpression.ArgumentList != null)
                {
                    foreach (var argument in invocationExpression.ArgumentList.Arguments)
                    {
                        if (argument != null)
                        {
                            var typeinfo = semanticModel.GetTypeInfo(argument.Expression);
                            if (typeinfo.Type != null && typeinfo.Type.TypeKind == TypeKind.Dynamic)
                            {
                                return true;
                            }
                        }
                    }
                }
            }
 
            return false;
        }
 
        private static bool IsTypeArgumentDefinedRecursive(ISymbol symbol, IList<ISymbol> typeArgumentSymbols, bool enterContainingSymbol)
        {
            if (Equals(symbol, symbol.OriginalDefinition))
            {
                return false;
            }
 
            var typeArgumentsInSymbol = new List<ISymbol>();
            TypeArgumentsInAllContainingSymbol(symbol, typeArgumentsInSymbol, enterContainingSymbol, isRecursive: true);
 
            var typeArgumentsInOriginalDefinition = new List<ISymbol>();
            TypeArgumentsInAllContainingSymbol(symbol.OriginalDefinition, typeArgumentsInOriginalDefinition, enterContainingSymbol, isRecursive: false);
 
            if (typeArgumentsInSymbol.Intersect(typeArgumentsInOriginalDefinition).Any(n => !typeArgumentSymbols.Contains(n)))
            {
                return true;
            }
 
            return false;
        }
 
        private static void TypeArgumentsInAllContainingSymbol(ISymbol symbol, IList<ISymbol> typeArgumentSymbols, bool enterContainingSymbol, bool isRecursive)
        {
            if (symbol == null || symbol.IsNamespace())
            {
                // This is the terminating condition
                return;
            }
 
            if (symbol is INamedTypeSymbol namedTypedSymbol)
            {
                if (namedTypedSymbol.TypeArguments.Length != 0)
                {
                    foreach (var typeArgument in namedTypedSymbol.TypeArguments)
                    {
                        if (!typeArgumentSymbols.Contains(typeArgument))
                        {
                            typeArgumentSymbols.Add(typeArgument);
                            if (isRecursive)
                            {
                                TypeArgumentsInAllContainingSymbol(typeArgument, typeArgumentSymbols, enterContainingSymbol, isRecursive);
                            }
                        }
                    }
                }
            }
 
            if (enterContainingSymbol)
            {
                TypeArgumentsInAllContainingSymbol(symbol.ContainingSymbol, typeArgumentSymbols, enterContainingSymbol, isRecursive);
            }
        }
 
        private static bool IsPropertyNameOfObjectInitializer(SimpleNameSyntax identifierName)
        {
            SyntaxNode currentNode = identifierName;
            SyntaxNode parent = identifierName;
 
            while (parent != null)
            {
                if (parent.Kind() is SyntaxKind.ObjectInitializerExpression or SyntaxKind.WithInitializerExpression)
                {
                    return currentNode.Kind() == SyntaxKind.SimpleAssignmentExpression &&
                        object.Equals(((AssignmentExpressionSyntax)currentNode).Left, identifierName);
                }
                else if (parent is ExpressionSyntax)
                {
                    currentNode = parent;
                    parent = parent.Parent;
 
                    continue;
                }
                else
                {
                    return false;
                }
            }
 
            return false;
        }
 
        private ExpressionSyntax FullyQualifyIdentifierName(
            ISymbol symbol,
            ExpressionSyntax rewrittenNode,
            ExpressionSyntax originalNode,
            bool replaceNode,
            bool isInsideCref,
            bool omitLeftHandSide)
        {
            Debug.Assert(!replaceNode || rewrittenNode.Kind() == SyntaxKind.IdentifierName);
 
            //// TODO: use and expand Generate*Syntax(isymbol) to not depend on symbol display any more.
            //// See GenerateExpressionSyntax();
 
            var result = rewrittenNode;
 
            // only if this symbol has a containing type or namespace there is work for us to do.
            if (replaceNode || symbol.ContainingType != null || symbol.ContainingNamespace != null)
            {
                // we either need to create an AliasQualifiedName if the symbol is directly contained in the global namespace,
                // otherwise it a QualifiedName.
                if (!replaceNode && symbol.ContainingType == null && symbol.ContainingNamespace.IsGlobalNamespace)
                {
                    return rewrittenNode.CopyAnnotationsTo(
                        AliasQualifiedName(
                            IdentifierName(GlobalKeyword),
                            (SimpleNameSyntax)rewrittenNode.WithLeadingTrivia(null))
                                .WithLeadingTrivia(rewrittenNode.GetLeadingTrivia()));
                }
 
                rewrittenNode = TryAddTypeArgumentToIdentifierName(rewrittenNode, symbol);
 
                // Replaces the '<' token with the '{' token since we are inside crefs
                rewrittenNode = TryReplaceAngleBracesWithCurlyBraces(rewrittenNode, isInsideCref);
                result = rewrittenNode;
 
                if (!omitLeftHandSide)
                {
                    var displaySymbol = replaceNode
                        ? symbol
                        : (symbol.ContainingType ?? (ISymbol)symbol.ContainingNamespace);
 
                    var displayString = displaySymbol.ToDisplayString(s_typeNameFormatWithGenerics);
 
                    ExpressionSyntax left = ParseTypeName(displayString);
 
                    // Replaces the '<' token with the '{' token since we are inside crefs
                    left = TryReplaceAngleBracesWithCurlyBraces(left, isInsideCref);
 
                    if (replaceNode)
                    {
                        return left
                            .WithLeadingTrivia(rewrittenNode.GetLeadingTrivia())
                            .WithTrailingTrivia(rewrittenNode.GetTrailingTrivia());
                    }
 
                    // now create syntax for the combination of left and right syntax, or a simple replacement in case of an identifier
                    var parent = originalNode.Parent;
                    var leadingTrivia = rewrittenNode.GetLeadingTrivia();
                    rewrittenNode = rewrittenNode.WithLeadingTrivia(null);
 
                    switch (parent.Kind())
                    {
                        case SyntaxKind.QualifiedName:
                            result = rewrittenNode.CopyAnnotationsTo(
                                QualifiedName(
                                    (NameSyntax)left,
                                    (SimpleNameSyntax)rewrittenNode));
 
                            break;
 
                        case SyntaxKind.SimpleMemberAccessExpression:
                            result = rewrittenNode.CopyAnnotationsTo(
                                MemberAccessExpression(
                                    SyntaxKind.SimpleMemberAccessExpression,
                                    left,
                                    (SimpleNameSyntax)rewrittenNode));
 
                            break;
 
                        default:
                            Debug.Assert(rewrittenNode is SimpleNameSyntax);
 
                            if (SyntaxFacts.IsInNamespaceOrTypeContext(originalNode))
                            {
                                var right = (SimpleNameSyntax)rewrittenNode;
                                result = rewrittenNode.CopyAnnotationsTo(QualifiedName((NameSyntax)left, right.WithAdditionalAnnotations(Simplifier.SpecialTypeAnnotation)));
                            }
                            else if (originalNode.Parent is CrefSyntax)
                            {
                                var right = (SimpleNameSyntax)rewrittenNode;
                                result = rewrittenNode.CopyAnnotationsTo(QualifiedName((NameSyntax)left, right));
                            }
                            else
                            {
                                result = rewrittenNode.CopyAnnotationsTo(
                                    MemberAccessExpression(
                                        SyntaxKind.SimpleMemberAccessExpression,
                                        left,
                                        (SimpleNameSyntax)rewrittenNode));
                            }
 
                            break;
                    }
 
                    result = result.WithLeadingTrivia(leadingTrivia);
                }
            }
 
            return result;
        }
 
        private SyntaxToken ReplaceTokenForCref(SyntaxToken oldToken, SyntaxToken dummySameToken)
        {
            if (oldToken.Kind() == SyntaxKind.LessThanToken)
            {
                return Token(oldToken.LeadingTrivia, SyntaxKind.LessThanToken, "{", "{", oldToken.TrailingTrivia);
            }
 
            if (oldToken.Kind() == SyntaxKind.GreaterThanToken)
            {
                return Token(oldToken.LeadingTrivia, SyntaxKind.GreaterThanToken, "}", "}", oldToken.TrailingTrivia);
            }
 
            Debug.Assert(false, "This method is used only replacing the '<' and '>' to '{' and '}' respectively");
            return default;
        }
 
        private bool IsTypeOfUnboundGenericType(SemanticModel semanticModel, TypeOfExpressionSyntax typeOfExpression)
        {
            if (typeOfExpression != null)
            {
                var type = semanticModel.GetTypeInfo(typeOfExpression.Type, _cancellationToken).Type as INamedTypeSymbol;
 
                // It's possible the immediate type might not be an unbound type, such as typeof(A<>.B). So walk through
                // parent types too
                while (type != null)
                {
                    if (type.IsUnboundGenericType)
                    {
                        return true;
                    }
 
                    type = type.ContainingType;
                }
            }
 
            return false;
        }
 
        public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax originalNode)
        {
            if (_semanticModel.GetSymbolInfo(originalNode).Symbol.IsLocalFunction())
            {
                return originalNode;
            }
 
            var rewrittenNode = (InvocationExpressionSyntax)base.VisitInvocationExpression(originalNode);
            if (originalNode.Expression is MemberAccessExpressionSyntax(SyntaxKind.SimpleMemberAccessExpression) memberAccess)
            {
                var targetSymbol = SimplificationHelpers.GetOriginalSymbolInfo(_semanticModel, memberAccess.Name);
 
                if (targetSymbol != null && targetSymbol.IsReducedExtension() && memberAccess.Expression != null)
                {
                    rewrittenNode = RewriteExtensionMethodInvocation(originalNode, rewrittenNode, ((MemberAccessExpressionSyntax)rewrittenNode.Expression).Expression, (IMethodSymbol)targetSymbol);
                }
            }
 
            return rewrittenNode;
        }
 
        private InvocationExpressionSyntax RewriteExtensionMethodInvocation(
            InvocationExpressionSyntax originalNode,
            InvocationExpressionSyntax rewrittenNode,
            ExpressionSyntax thisExpression,
            IMethodSymbol reducedExtensionMethod)
        {
            var originalMemberAccess = (MemberAccessExpressionSyntax)originalNode.Expression;
 
            // Bail out on extension method invocations in conditional access expression.
            // Note that this is a temporary workaround for https://github.com/dotnet/roslyn/issues/2593.
            // Issue https://github.com/dotnet/roslyn/issues/3260 tracks fixing this workaround.
            if (originalMemberAccess.GetRootConditionalAccessExpression() == null)
            {
                var speculationPosition = originalNode.SpanStart;
                var expression = RewriteExtensionMethodInvocation(speculationPosition, rewrittenNode, thisExpression, reducedExtensionMethod);
 
                // Let's rebind this and verify the original method is being called properly
                var binding = _semanticModel.GetSpeculativeSymbolInfo(originalNode.SpanStart, expression, SpeculativeBindingOption.BindAsExpression);
                if (binding.Symbol != null)
                    return expression;
            }
 
            return rewrittenNode;
        }
 
        private InvocationExpressionSyntax RewriteExtensionMethodInvocation(
            int speculationPosition,
            InvocationExpressionSyntax originalNode,
            ExpressionSyntax thisExpression,
            IMethodSymbol reducedExtensionMethod)
        {
            // It may be the case that this extension method cannot be called in static form.  For example, if the
            // qualified name for the type containing the extension would be ambiguous.  In that case, just return
            // the original call as is.
            var containingTypeString = reducedExtensionMethod.ContainingType.ToDisplayString(s_typeNameFormatWithGenerics);
 
            // We use .ParseExpression here, and not .GenerateTypeSyntax as we want this to be a property
            // MemberAccessExpression, and not a QualifiedNameSyntax.
            var containingTypeSyntax = ParseExpression(containingTypeString);
            var newContainingType = _semanticModel.GetSpeculativeSymbolInfo(speculationPosition, containingTypeSyntax, SpeculativeBindingOption.BindAsExpression).Symbol;
            if (newContainingType == null || !newContainingType.Equals(reducedExtensionMethod.ContainingType))
                return originalNode;
 
            var originalMemberAccess = (MemberAccessExpressionSyntax)originalNode.Expression;
            var newMemberAccess = originalMemberAccess.WithExpression(containingTypeSyntax)
                                                      .WithLeadingTrivia(thisExpression.GetFirstToken().LeadingTrivia);
 
            // Copies the annotation for the member access expression
            newMemberAccess = originalNode.Expression.CopyAnnotationsTo(newMemberAccess).WithAdditionalAnnotations(Simplifier.Annotation);
 
            var thisArgument = Argument(thisExpression).WithLeadingTrivia(SyntaxTriviaList.Empty);
 
            // Copies the annotation for the left hand side of the member access expression to the first argument in the complexified form
            thisArgument = originalMemberAccess.Expression.CopyAnnotationsTo(thisArgument);
 
            var arguments = originalNode.ArgumentList.Arguments.Insert(0, thisArgument);
            var replacementNode = InvocationExpression(
                newMemberAccess,
                originalNode.ArgumentList.WithArguments(arguments));
 
            // This Annotation copy is for the InvocationExpression
            return originalNode.CopyAnnotationsTo(replacementNode)
                               .WithAdditionalAnnotations(Simplifier.Annotation, Formatter.Annotation);
        }
    }
}