File: EditAndContinue\DeclarationBody\PropertyOrIndexerAccessorDeclarationBody.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.
 
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Differencing;
using Microsoft.CodeAnalysis.EditAndContinue;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue;
 
/// <summary>
/// Property or indexer accessor with explicit body:
///   T P { get => [|expr;|] }
///   T P { set => [|expr;|] }
///   T P { init => [|expr;|] }
///   T P { get { ... } }
///   T P { set { ... } }
///   T P { init { ... } }
///   T this[...] { get => [|expr;|] }
///   T this[...] { set => [|expr;|] }
///   T this[...] { get { ... } }
///   T this[...] { set { ... } }
///
/// Property or indexer with explicit body:
///   T P => [|expr;|]
///   T this[...] => [|expr;|]
///   
/// Auto-property accessor:
///   T P { [|get;|] }
///   T P { [|set;|] }
///   T P { [|init;|] }
///   
/// Primary record auto-property: 
///   record R([|T P|])
/// </summary>
internal abstract class PropertyOrIndexerAccessorDeclarationBody : MemberBody
{
    /// <summary>
    /// <see cref="ExpressionSyntax"/> or <see cref="BlockSyntax"/> or <see langword="null"/>.
    /// </summary>
    public abstract SyntaxNode? ExplicitBody { get; }
 
    /// <summary>
    /// Active statement outside of <see cref="ExplicitBody"/>.
    /// </summary>
    public abstract SyntaxNode? HeaderActiveStatement { get; }
 
    public abstract TextSpan HeaderActiveStatementSpan { get; }
 
    public abstract SyntaxNode? MatchRoot { get; }
 
    public SyntaxNode RootNode
        => ExplicitBody ?? HeaderActiveStatement!;
 
    public sealed override SyntaxTree SyntaxTree
        => RootNode.SyntaxTree;
 
    public sealed override TextSpan Envelope
        => ExplicitBody?.Span ?? HeaderActiveStatementSpan;
 
    public sealed override OneOrMany<SyntaxNode> RootNodes
        => new(RootNode);
 
    public sealed override SyntaxNode EncompassingAncestor
        => RootNode;
 
    public sealed override StateMachineInfo GetStateMachineInfo()
        => StateMachineInfo.None;
 
    public sealed override ImmutableArray<ISymbol> GetCapturedVariables(SemanticModel model)
        => (ExplicitBody != null) ? model.AnalyzeDataFlow(ExplicitBody).CapturedInside : [];
 
    public sealed override SyntaxNode FindStatementAndPartner(TextSpan span, MemberBody? partnerDeclarationBody, out SyntaxNode? partnerStatement, out int statementPart)
    {
        if (HeaderActiveStatement != null)
        {
            Debug.Assert(partnerDeclarationBody is null or PropertyOrIndexerAccessorDeclarationBody { HeaderActiveStatement: not null });
 
            statementPart = AbstractEditAndContinueAnalyzer.DefaultStatementPart;
            partnerStatement = ((PropertyOrIndexerAccessorDeclarationBody?)partnerDeclarationBody)?.HeaderActiveStatement;
            return HeaderActiveStatement;
        }
 
        Debug.Assert(ExplicitBody != null);
        Debug.Assert(partnerDeclarationBody is null or PropertyOrIndexerAccessorDeclarationBody { ExplicitBody: not null });
 
        return CSharpEditAndContinueAnalyzer.FindStatementAndPartner(
            span,
            body: ExplicitBody,
            partnerBody: ((PropertyOrIndexerAccessorDeclarationBody?)partnerDeclarationBody)?.ExplicitBody,
            out partnerStatement,
            out statementPart);
    }
 
    public sealed override bool TryMatchActiveStatement(DeclarationBody newBody, SyntaxNode oldStatement, ref int statementPart, [NotNullWhen(true)] out SyntaxNode? newStatement)
    {
        var newPropertyBody = (PropertyOrIndexerAccessorDeclarationBody)newBody;
 
        if (HeaderActiveStatement != null)
        {
            if (oldStatement != HeaderActiveStatement)
            {
                newStatement = null;
                return false;
            }
 
            if (newPropertyBody.HeaderActiveStatement != null)
            {
                newStatement = newPropertyBody.HeaderActiveStatement;
                return true;
            }
 
            Contract.ThrowIfNull(newPropertyBody.ExplicitBody);
            (newStatement, statementPart) = CSharpEditAndContinueAnalyzer.GetFirstBodyActiveStatement(newPropertyBody.ExplicitBody);
            return true;
        }
 
        if (newPropertyBody.HeaderActiveStatement != null)
        {
            newStatement = newPropertyBody.HeaderActiveStatement;
            statementPart = AbstractEditAndContinueAnalyzer.DefaultStatementPart;
            return true;
        }
 
        Contract.ThrowIfNull(ExplicitBody);
        Contract.ThrowIfNull(newPropertyBody.ExplicitBody);
        Contract.ThrowIfNull(MatchRoot);
        Contract.ThrowIfNull(newPropertyBody.MatchRoot);
 
        // Statements in explicit bodies will be mapped using a match.
        // We need to special case active statements in root nodes though because the root kinds differ (block vs arrow expr)
        if (oldStatement == ExplicitBody)
        {
            newStatement = newPropertyBody.ExplicitBody;
            statementPart = AbstractEditAndContinueAnalyzer.DefaultStatementPart;
            return true;
        }
 
        newStatement = null;
        return false;
    }
 
    public sealed override Match<SyntaxNode>? ComputeSingleRootMatch(DeclarationBody newBody, IEnumerable<KeyValuePair<SyntaxNode, SyntaxNode>>? knownMatches)
        => MatchRoot is { } oldRoot && ((PropertyOrIndexerAccessorDeclarationBody)newBody).MatchRoot is { } newRoot
            ? SyntaxComparer.Statement.ComputeMatch(oldRoot, newRoot, knownMatches)
            : null;
}