File: EditAndContinue\SemanticEditInfo.cs
Web Access
Project: src\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.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;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using Microsoft.CodeAnalysis.Emit;
 
namespace Microsoft.CodeAnalysis.EditAndContinue;
 
internal readonly record struct SyntaxMaps
{
    /// <summary>
    /// The tree the maps operate on (the new tree, since the maps are mapping from new nodes to old nodes/rude edits).
    /// </summary>
    public readonly SyntaxTree NewTree;
 
    public readonly Func<SyntaxNode, SyntaxNode?>? MatchingNodes;
    public readonly Func<SyntaxNode, RuntimeRudeEdit?>? RuntimeRudeEdits;
 
    public SyntaxMaps(
        SyntaxTree newTree,
        Func<SyntaxNode, SyntaxNode?>? matchingNodes = null,
        Func<SyntaxNode, RuntimeRudeEdit?>? runtimeRudeEdits = null)
    {
        // if we have runtime rude edit map we should also have matching node map:
        Debug.Assert(runtimeRudeEdits == null || matchingNodes != null);
 
        NewTree = newTree;
        MatchingNodes = matchingNodes;
        RuntimeRudeEdits = runtimeRudeEdits;
    }
 
    [MemberNotNullWhen(true, nameof(MatchingNodes))]
    public bool HasMap => MatchingNodes != null;
}
 
internal readonly struct SemanticEditInfo
{
    public SemanticEditInfo(
        SemanticEditKind kind,
        SymbolKey symbol,
        SyntaxMaps syntaxMaps,
        SymbolKey? partialType,
        SymbolKey? deletedSymbolContainer)
    {
        Debug.Assert(kind == SemanticEditKind.Delete || deletedSymbolContainer == null);
 
        Kind = kind;
        Symbol = symbol;
        SyntaxMaps = syntaxMaps;
        PartialType = partialType;
        DeletedSymbolContainer = deletedSymbolContainer;
    }
 
    public static SemanticEditInfo CreateInsert(SymbolKey symbol, SymbolKey? partialType)
        => new(SemanticEditKind.Insert, symbol, syntaxMaps: default, partialType, deletedSymbolContainer: null);
 
    public static SemanticEditInfo CreateInsert(ISymbol symbol, CancellationToken cancellationToken)
    {
        Debug.Assert(!symbol.IsPartialDefinition());
        var partialType = symbol.IsPartialImplementation() ? SymbolKey.Create(symbol.ContainingType, cancellationToken) : (SymbolKey?)null;
        return CreateInsert(SymbolKey.Create(symbol, cancellationToken), partialType);
    }
 
    public static SemanticEditInfo CreateUpdate(SymbolKey symbol, SyntaxMaps syntaxMaps, SymbolKey? partialType)
        => new(SemanticEditKind.Update, symbol, syntaxMaps, partialType, deletedSymbolContainer: null);
 
    public static SemanticEditInfo CreateUpdate(ISymbol symbol, SyntaxMaps syntaxMaps, CancellationToken cancellationToken)
    {
        Debug.Assert(!symbol.IsPartialDefinition());
        var partialType = symbol.IsPartialImplementation() ? SymbolKey.Create(symbol.ContainingType, cancellationToken) : (SymbolKey?)null;
        return CreateUpdate(SymbolKey.Create(symbol, cancellationToken), syntaxMaps, partialType);
    }
 
    public static SemanticEditInfo CreateReplace(SymbolKey symbol, SymbolKey? partialType)
        => new(SemanticEditKind.Replace, symbol, syntaxMaps: default, partialType, deletedSymbolContainer: null);
 
    public static SemanticEditInfo CreateDelete(SymbolKey symbol, SymbolKey deletedSymbolContainer, SymbolKey? partialType)
        => new(SemanticEditKind.Delete, symbol, syntaxMaps: default, partialType, deletedSymbolContainer);
 
    public static SemanticEditInfo CreateDelete(ISymbol symbol, SymbolKey containingSymbolKey, CancellationToken cancellationToken)
    {
        Debug.Assert(!symbol.IsPartialDefinition());
        var partialType = symbol.IsPartialImplementation() ? containingSymbolKey : (SymbolKey?)null;
        return CreateDelete(SymbolKey.Create(symbol, cancellationToken), containingSymbolKey, partialType);
    }
 
    /// <summary>
    /// <see cref="SemanticEditKind.Insert"/> or <see cref="SemanticEditKind.Update"/> or <see cref="SemanticEditKind.Delete"/>.
    /// </summary>
    public SemanticEditKind Kind { get; }
 
    /// <summary>
    /// If <see cref="Kind"/> is <see cref="SemanticEditKind.Insert"/> represents the inserted symbol in the new compilation.
    /// If <see cref="Kind"/> is <see cref="SemanticEditKind.Update"/> represents the updated symbol in both compilations.
    /// If <see cref="Kind"/> is <see cref="SemanticEditKind.Delete"/> represents the deleted symbol in the old compilation.
    /// 
    /// We use <see cref="SymbolKey"/> to represent the symbol rather then <see cref="ISymbol"/>,
    /// since different semantic edits might have been calculated against different solution snapshot and thus symbols are not directly comparable.
    /// When the edits are processed we map the <see cref="SymbolKey"/> to the current compilation.
    /// </summary>
    public SymbolKey Symbol { get; }
 
    /// <summary>
    /// If <see cref="Kind"/> is <see cref="SemanticEditKind.Delete"/> represents the containing symbol in the new compilation.
    /// 
    /// We use <see cref="SymbolKey"/> to represent the symbol rather then <see cref="ISymbol"/>,
    /// since different semantic edits might have been calculated against different solution snapshot and thus symbols are not directly comparable.
    /// When the edits are processed we map the <see cref="SymbolKey"/> to the current compilation.
    /// </summary>
    public SymbolKey? DeletedSymbolContainer { get; }
 
    /// <summary>
    /// Syntax maps for nodes in the tree for this edit, which will be merged with other maps from other trees for this type.
    /// </summary>
    public SyntaxMaps SyntaxMaps { get; }
 
    /// <summary>
    /// Specified if the edit needs to be merged with other edits of the same <see cref="PartialType"/>.
    /// 
    /// If specified, the <see cref="SyntaxMaps"/> is either null or incomplete: it only provides mapping of the changed members of a single partial type declaration.
    /// </summary>
    public SymbolKey? PartialType { get; }
}