File: IntroduceVariable\CSharpIntroduceVariableService.cs
Web Access
Project: src\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.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Composition;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.IntroduceVariable;
using Microsoft.CodeAnalysis.Shared.Extensions;
 
namespace Microsoft.CodeAnalysis.CSharp.IntroduceVariable;
 
[ExportLanguageService(typeof(IIntroduceVariableService), LanguageNames.CSharp), Shared]
internal sealed partial class CSharpIntroduceVariableService :
    AbstractIntroduceVariableService<CSharpIntroduceVariableService, ExpressionSyntax, TypeSyntax, TypeDeclarationSyntax, QueryExpressionSyntax, NameSyntax>
{
    [ImportingConstructor]
    [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
    public CSharpIntroduceVariableService()
    {
    }
 
    protected override bool IsInNonFirstQueryClause(ExpressionSyntax expression)
    {
        var query = expression.GetAncestor<QueryExpressionSyntax>();
        if (query != null)
        {
            // Can't introduce for the first clause in a query.
            var fromClause = expression.GetAncestor<FromClauseSyntax>();
            if (fromClause == null || query.FromClause != fromClause)
            {
                return true;
            }
        }
 
        return false;
    }
 
    protected override bool IsInFieldInitializer(ExpressionSyntax expression)
    {
        return expression.GetAncestorOrThis<VariableDeclaratorSyntax>()
                         .GetAncestorOrThis<FieldDeclarationSyntax>() != null;
    }
 
    protected override bool IsInParameterInitializer(ExpressionSyntax expression)
        => expression.GetAncestorOrThis<EqualsValueClauseSyntax>().IsParentKind(SyntaxKind.Parameter);
 
    protected override bool IsInConstructorInitializer(ExpressionSyntax expression)
        => expression.GetAncestorOrThis<ConstructorInitializerSyntax>() != null;
 
    protected override bool IsInAutoPropertyInitializer(ExpressionSyntax expression)
        => expression.GetAncestorOrThis<EqualsValueClauseSyntax>().IsParentKind(SyntaxKind.PropertyDeclaration);
 
    protected override bool IsInExpressionBodiedMember(ExpressionSyntax expression)
    {
        // walk up until we find a nearest enclosing block or arrow expression.
        for (SyntaxNode node = expression; node != null; node = node.Parent)
        {
            // If we are in an expression bodied member and if the expression has a block body, then,
            // act as if we're in a block context and not in an expression body context at all.
            if (node.IsKind(SyntaxKind.Block))
            {
                return false;
            }
            else if (node.IsKind(SyntaxKind.ArrowExpressionClause))
            {
                return true;
            }
        }
 
        return false;
    }
 
    protected override bool IsInAttributeArgumentInitializer(ExpressionSyntax expression)
    {
        // Don't call the base here.  We want to let the user extract a constant if they've
        // said "Goo(a = 10)"
        var attributeArgument = expression.GetAncestorOrThis<AttributeArgumentSyntax>();
        if (attributeArgument != null)
        {
            // Can't extract an attribute initializer if it contains an array initializer of any
            // sort.  Also, we can't extract if there's any typeof expression within it.
            if (!expression.DepthFirstTraversal().Any(n => n.RawKind == (int)SyntaxKind.ArrayCreationExpression) &&
                !expression.DepthFirstTraversal().Any(n => n.RawKind == (int)SyntaxKind.TypeOfExpression))
            {
                var attributeDecl = attributeArgument.GetAncestorOrThis<AttributeListSyntax>();
 
                // Also can't extract an attribute initializer if the attribute is a global one.
                if (!attributeDecl.IsParentKind(SyntaxKind.CompilationUnit))
                {
                    return true;
                }
            }
        }
 
        return false;
    }
 
    /// <summary>
    /// Checks for conditions where we should not generate a variable for an expression
    /// </summary>
    protected override bool CanIntroduceVariableFor(ExpressionSyntax expression)
    {
        // (a) If that's the only expression in a statement.
        // Otherwise we'll end up with something like "v;" which is not legal in C#.
        if (expression.WalkUpParentheses().IsParentKind(SyntaxKind.ExpressionStatement))
        {
            return false;
        }
 
        // (b) For Null Literals, as AllOccurrences could introduce semantic errors.
        if (expression.IsKind(SyntaxKind.NullLiteralExpression))
        {
            return false;
        }
 
        // (c) For throw expressions.
        if (expression.IsKind(SyntaxKind.ThrowExpression))
        {
            return false;
        }
 
        return true;
    }
 
    protected override IEnumerable<SyntaxNode> GetContainingExecutableBlocks(ExpressionSyntax expression)
        => expression.GetAncestorsOrThis<BlockSyntax>();
 
    protected override IList<bool> GetInsertionIndices(TypeDeclarationSyntax destination, CancellationToken cancellationToken)
        => destination.GetInsertionIndices(cancellationToken);
 
    protected override bool CanReplace(ExpressionSyntax expression)
        => true;
 
    protected override bool IsExpressionInStaticLocalFunction(ExpressionSyntax expression)
    {
        var localFunction = expression.GetAncestor<LocalFunctionStatementSyntax>();
        return localFunction != null && localFunction.Modifiers.Any(SyntaxKind.StaticKeyword);
    }
 
    protected override TNode RewriteCore<TNode>(
        TNode node,
        SyntaxNode replacementNode,
        ISet<ExpressionSyntax> matches)
    {
        return (TNode)Rewriter.Visit(node, replacementNode, matches);
    }
}