internal void AnalyzeSemantics()

in src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs [2084:2506]


        internal void AnalyzeSemantics(
            EditScript<SyntaxNode> editScript,
            Dictionary<SyntaxNode, EditKind> editMap,
            SourceText oldText,
            ImmutableArray<ActiveStatementSpan> oldActiveStatements,
            List<KeyValuePair<SyntaxNode, SyntaxNode>> triviaEdits,
            List<UpdatedMemberInfo> updatedMembers,
            SemanticModel oldModel,
            SemanticModel newModel,
            [Out]List<SemanticEdit> semanticEdits,
            [Out]List<RudeEditDiagnostic> diagnostics,
            out Diagnostic firstDeclarationErrorOpt,
            CancellationToken cancellationToken)
        {
            // { new type -> constructor update }
            Dictionary<INamedTypeSymbol, ConstructorEdit> instanceConstructorEdits = null;
            Dictionary<INamedTypeSymbol, ConstructorEdit> staticConstructorEdits = null;

            INamedTypeSymbol layoutAttribute = null;
            var newSymbolsWithEdit = new HashSet<ISymbol>();
            int updatedMemberIndex = 0;
            firstDeclarationErrorOpt = null;
            for (int i = 0; i < editScript.Edits.Length; i++)
            {
                cancellationToken.ThrowIfCancellationRequested();

                var edit = editScript.Edits[i];

                ISymbol oldSymbol, newSymbol;
                Func<SyntaxNode, SyntaxNode> syntaxMapOpt;
                SemanticEditKind editKind;

                switch (edit.Kind)
                {
                    case EditKind.Move:
                        // Move is always a Rude Edit.
                        throw ExceptionUtilities.UnexpectedValue(edit.Kind);

                    case EditKind.Delete:
                        {
                            editKind = SemanticEditKind.Delete;

                            if (HasParentEdit(editMap, edit))
                            {
                                continue;
                            }

                            oldSymbol = GetSymbolForEdit(oldModel, edit.OldNode, edit.Kind, editMap, cancellationToken);
                            if (oldSymbol == null)
                            {
                                continue;
                            }

                            // The only member that is allowed to be deleted is a parameterless constructor. 
                            // For any other member a rude edit is reported earlier during syntax edit classification.
                            // Deleting a parameterless constructor needs special handling.
                            // If the new type has a parameterless ctor of the same accessibility then UPDATE.
                            // Error otherwise.

                            Debug.Assert(AsParameterlessConstructor(oldSymbol) != null);

                            SyntaxNode oldTypeSyntax = TryGetContainingTypeDeclaration(edit.OldNode);
                            Debug.Assert(oldTypeSyntax != null);

                            var newType = TryGetPartnerType(oldTypeSyntax, editScript.Match, newModel, cancellationToken);

                            newSymbol = TryGetParameterlessConstructor(newType, oldSymbol.IsStatic);
                            if (newSymbol == null || newSymbol.DeclaredAccessibility != oldSymbol.DeclaredAccessibility)
                            {
                                diagnostics.Add(new RudeEditDiagnostic(
                                    RudeEditKind.Delete,
                                    GetDeletedNodeDiagnosticSpan(editScript.Match.Matches, edit.OldNode),
                                    edit.OldNode,
                                    new[] { GetTopLevelDisplayName(edit.OldNode, EditKind.Delete) }));

                                continue;
                            }

                            editKind = SemanticEditKind.Update;
                            syntaxMapOpt = null;
                        }

                        break;

                    case EditKind.Reorder:
                        // Currently we don't do any semantic checks for reordering
                        // and we don't need to report them to the compiler either.

                        // Reordering of fields is not allowed since it changes the layout of the type.
                        Debug.Assert(!IsDeclarationWithInitializer(edit.OldNode) && !IsDeclarationWithInitializer(edit.NewNode));
                        continue;

                    case EditKind.Insert:
                        {
                            editKind = SemanticEditKind.Insert;

                            SyntaxNode newTypeSyntax = TryGetContainingTypeDeclaration(edit.NewNode);

                            if (newTypeSyntax != null && HasEdit(editMap, newTypeSyntax, EditKind.Insert))
                            {
                                // inserting into a new type
                                continue;
                            }

                            syntaxMapOpt = null;
                            oldSymbol = null;
                            newSymbol = GetSymbolForEdit(newModel, edit.NewNode, edit.Kind, editMap, cancellationToken);

                            if (newSymbol == null)
                            {
                                // node doesn't represent a symbol
                                continue;
                            }

                            // TODO: scripting
                            // inserting a top-level member/type
                            if (newTypeSyntax == null)
                            {
                                break;
                            }

                            var newType = (INamedTypeSymbol)newModel.GetDeclaredSymbol(newTypeSyntax, cancellationToken);
                            var oldType = TryGetPartnerType(newTypeSyntax, editScript.Match, oldModel, cancellationToken);

                            // There has to be a matching old type syntax since the containing type hasn't been inserted.
                            Debug.Assert(oldType != null);
                            Debug.Assert(newType != null);

                            // Validate that the type declarations are correct. If not we can't reason about their members.
                            // Declaration diagnostics are cached on compilation, so we don't need to cache them here.
                            firstDeclarationErrorOpt = 
                                GetFirstDeclarationError(oldModel, oldType, cancellationToken) ??
                                GetFirstDeclarationError(newModel, newType, cancellationToken);

                            if (firstDeclarationErrorOpt != null)
                            {
                                continue;
                            }

                            // Inserting a parameterless constructor needs special handling:
                            // 1) static ctor
                            //    a) old type has an implicit static ctor
                            //       UPDATE of the implicit static ctor
                            //    b) otherwise
                            //       INSERT of a static parameterless ctor
                            // 
                            // 2) public instance ctor
                            //    a) old type has an implicit instance ctor
                            //       UPDATE of the implicit instance ctor
                            //    b) otherwise
                            //       ERROR: adding a non-private member
                            // 3) non-public instance ctor
                            //    a) old type has an implicit instance ctor
                            //       ERROR: changing visibility of the ctor
                            //    b) otherwise
                            //       INSERT of an instance parameterless ctor

                            IMethodSymbol newCtor = AsParameterlessConstructor(newSymbol);
                            if (newCtor != null)
                            {
                                oldSymbol = TryGetParameterlessConstructor(oldType, newSymbol.IsStatic);

                                if (newCtor.IsStatic)
                                {
                                    if (oldSymbol != null)
                                    {
                                        editKind = SemanticEditKind.Update;
                                    }
                                }
                                else if (oldSymbol != null)
                                {
                                    if (oldSymbol.DeclaredAccessibility != newCtor.DeclaredAccessibility)
                                    {
                                        // changing visibility of a member
                                        diagnostics.Add(new RudeEditDiagnostic(
                                            RudeEditKind.ChangingConstructorVisibility,
                                            GetDiagnosticSpan(edit.NewNode, EditKind.Insert)));
                                    }
                                    else
                                    {
                                        editKind = SemanticEditKind.Update;
                                    }
                                }
                            }

                            if (editKind == SemanticEditKind.Insert)
                            {
                                ReportInsertedMemberSymbolRudeEdits(diagnostics, newSymbol);
                                ReportTypeLayoutUpdateRudeEdits(diagnostics, newSymbol, edit.NewNode, newModel, ref layoutAttribute);
                            }

                            bool isConstructorWithMemberInitializers;
                            if ((isConstructorWithMemberInitializers = IsConstructorWithMemberInitializers(edit.NewNode)) ||
                                IsDeclarationWithInitializer(edit.NewNode))
                            {
                                if (DeferConstructorEdit(
                                    oldType,
                                    newType,
                                    editKind,
                                    edit.NewNode,
                                    newSymbol,
                                    newModel,
                                    isConstructorWithMemberInitializers,
                                    ref syntaxMapOpt,
                                    ref instanceConstructorEdits,
                                    ref staticConstructorEdits,
                                    diagnostics,
                                    cancellationToken))
                                {
                                    if (newSymbol.Kind == SymbolKind.Method)
                                    {
                                        // Don't add a separate semantic edit for a field/property with an initializer.
                                        // All edits of initializers will be aggregated to edits of constructors where these initializers are emitted.
                                        continue;
                                    }
                                    else
                                    {
                                        // A semantic edit to create the field/property is gonna be added.
                                        Debug.Assert(editKind == SemanticEditKind.Insert);
                                    }
                                }
                            }
                        }

                        break;

                    case EditKind.Update:
                        {
                            editKind = SemanticEditKind.Update;

                            newSymbol = GetSymbolForEdit(newModel, edit.NewNode, edit.Kind, editMap, cancellationToken);
                            if (newSymbol == null)
                            {
                                // node doesn't represent a symbol
                                continue;
                            }

                            oldSymbol = GetSymbolForEdit(oldModel, edit.OldNode, edit.Kind, editMap, cancellationToken);
                            Debug.Assert((newSymbol == null) == (oldSymbol == null));

                            var oldContainingType = oldSymbol.ContainingType;
                            var newContainingType = newSymbol.ContainingType;

                            // Validate that the type declarations are correct to avoid issues with invalid partial declarations, etc.
                            // Declaration diagnostics are cached on compilation, so we don't need to cache them here.
                            firstDeclarationErrorOpt =
                                GetFirstDeclarationError(oldModel, oldContainingType, cancellationToken) ??
                                GetFirstDeclarationError(newModel, newContainingType, cancellationToken);

                            if (firstDeclarationErrorOpt != null)
                            {
                                continue;
                            }

                            if (updatedMemberIndex < updatedMembers.Count && updatedMembers[updatedMemberIndex].EditOrdinal == i)
                            {
                                var updatedMember = updatedMembers[updatedMemberIndex];

                                ReportStateMachineRudeEdits(oldModel.Compilation, updatedMember, oldSymbol, diagnostics);

                                ReportLambdaAndClosureRudeEdits(
                                    oldModel,
                                    updatedMember.OldBody,
                                    oldSymbol,
                                    newModel,
                                    updatedMember.NewBody,
                                    newSymbol,
                                    updatedMember.ActiveOrMatchedLambdasOpt,
                                    updatedMember.Map,
                                    diagnostics,
                                    out var newBodyHasLambdas,
                                    cancellationToken);

                                // We need to provide syntax map to the compiler if 
                                // 1) The new member has a active statement
                                //    The values of local variables declared or synthesized in the method have to be preserved.
                                // 2) The new member generates a state machine 
                                //    In case the state machine is suspended we need to preserve variables.
                                // 3) The new member contains lambdas
                                //    We need to map new lambdas in the method to the matching old ones. 
                                //    If the old method has lambdas but the new one doesn't there is nothing to preserve.
                                if (updatedMember.HasActiveStatement || updatedMember.NewHasStateMachineSuspensionPoint || newBodyHasLambdas)
                                {
                                    syntaxMapOpt = CreateSyntaxMap(updatedMember.Map.Reverse);
                                }
                                else
                                {
                                    syntaxMapOpt = null;
                                }

                                updatedMemberIndex++;
                            }
                            else
                            {
                                syntaxMapOpt = null;
                            }

                            // If a constructor changes from including initializers to not including initializers
                            // we don't need to aggregate syntax map from all initializers for the constructor update semantic edit.
                            bool isConstructorWithMemberInitializers;
                            if ((isConstructorWithMemberInitializers = IsConstructorWithMemberInitializers(edit.NewNode)) ||
                                IsDeclarationWithInitializer(edit.OldNode) ||
                                IsDeclarationWithInitializer(edit.NewNode))
                            {
                                if (DeferConstructorEdit(
                                    oldContainingType,
                                    newContainingType,
                                    editKind,
                                    edit.NewNode,
                                    newSymbol,
                                    newModel,
                                    isConstructorWithMemberInitializers,
                                    ref syntaxMapOpt,
                                    ref instanceConstructorEdits,
                                    ref staticConstructorEdits,
                                    diagnostics,
                                    cancellationToken))
                                {
                                    // Don't add a separate semantic edit for a field/property with an initializer.
                                    // All edits of initializers will be aggregated to edits of constructors where these initializers are emitted.
                                    continue;
                                }
                            }
                        }

                        break;

                    default:
                        throw ExceptionUtilities.UnexpectedValue(edit.Kind);
                }

                semanticEdits.Add(new SemanticEdit(editKind, oldSymbol, newSymbol, syntaxMapOpt, preserveLocalVariables: syntaxMapOpt != null));
                newSymbolsWithEdit.Add(newSymbol);
            }

            foreach (var edit in triviaEdits)
            {
                var oldSymbol = GetSymbolForEdit(oldModel, edit.Key, EditKind.Update, editMap, cancellationToken);
                var newSymbol = GetSymbolForEdit(newModel, edit.Value, EditKind.Update, editMap, cancellationToken);
                var oldContainingType = oldSymbol.ContainingType;
                var newContainingType = newSymbol.ContainingType;

                // Validate that the type declarations are correct to avoid issues with invalid partial declarations, etc.
                // Declaration diagnostics are cached on compilation, so we don't need to cache them here.
                firstDeclarationErrorOpt =
                    GetFirstDeclarationError(oldModel, oldContainingType, cancellationToken) ??
                    GetFirstDeclarationError(newModel, newContainingType, cancellationToken);

                if (firstDeclarationErrorOpt != null)
                {
                    continue;
                }

                // We need to provide syntax map to the compiler if the member is active (see member update above):
                bool isActiveMember =
                    TryGetOverlappingActiveStatements(oldText, edit.Key.Span, oldActiveStatements, out var start, out var end) ||
                    IsStateMachineMethod(edit.Key) ||
                    ContainsLambda(edit.Key);

                var syntaxMap = isActiveMember ? CreateSyntaxMapForEquivalentNodes(edit.Key, edit.Value) : null;

                // only trivia changed:
                Debug.Assert(IsConstructorWithMemberInitializers(edit.Key) == IsConstructorWithMemberInitializers(edit.Value));
                Debug.Assert(IsDeclarationWithInitializer(edit.Key) == IsDeclarationWithInitializer(edit.Value));

                bool isConstructorWithMemberInitializers;
                if ((isConstructorWithMemberInitializers = IsConstructorWithMemberInitializers(edit.Value)) ||
                    IsDeclarationWithInitializer(edit.Value))
                {
                    if (DeferConstructorEdit(
                        oldContainingType,
                        newContainingType,
                        SemanticEditKind.Update,
                        edit.Value,
                        newSymbol,
                        newModel,
                        isConstructorWithMemberInitializers,
                        ref syntaxMap,
                        ref instanceConstructorEdits,
                        ref staticConstructorEdits,
                        diagnostics,
                        cancellationToken))
                    {
                        // Don't add a separate semantic edit for a field/property with an initializer.
                        // All edits of initializers will be aggregated to edits of constructors where these initializers are emitted.
                        continue;
                    }
                }

                semanticEdits.Add(new SemanticEdit(SemanticEditKind.Update, oldSymbol, newSymbol, syntaxMap, isActiveMember));
                newSymbolsWithEdit.Add(newSymbol);
            }

            if (instanceConstructorEdits != null)
            {
                AddConstructorEdits(
                    instanceConstructorEdits,
                    editScript.Match,
                    oldText,
                    oldModel,
                    oldActiveStatements,
                    newSymbolsWithEdit,
                    isStatic: false,
                    semanticEdits: semanticEdits,
                    diagnostics: diagnostics,
                    cancellationToken: cancellationToken);
            }

            if (staticConstructorEdits != null)
            {
                AddConstructorEdits(
                    staticConstructorEdits,
                    editScript.Match,
                    oldText,
                    oldModel,
                    oldActiveStatements,
                    newSymbolsWithEdit,
                    isStatic: true,
                    semanticEdits: semanticEdits,
                    diagnostics: diagnostics,
                    cancellationToken: cancellationToken);
            }
        }