private static bool AreEquivalentRecursive(GreenNode before, GreenNode after, Func <SyntaxKind, bool> ignoreChildNode, bool topLevel)
        {
            if (before == after)
            {
                return(true);
            }

            if (before == null || after == null)
            {
                return(false);
            }

            if (before.RawKind != after.RawKind)
            {
                return(false);
            }

            if (before.IsToken)
            {
                Debug.Assert(after.IsToken);
                return(AreTokensEquivalent(before, after));
            }

            if (topLevel)
            {
                // Once we get down to the body level we don't need to go any further and we can
                // consider these trees equivalent.
                switch ((SyntaxKind)before.RawKind)
                {
                case SyntaxKind.Block:
                case SyntaxKind.ArrowExpressionClause:
                    return(true);
                }

                // If we're only checking top level equivalence, then we don't have to go down into
                // the initializer for a field. However, we can't put that optimization for all
                // fields. For example, fields that are 'const' do need their initializers checked as
                // changing them can affect binding results.
                if ((SyntaxKind)before.RawKind == SyntaxKind.FieldDeclaration)
                {
                    var fieldBefore = (Green.FieldDeclarationSyntax)before;
                    var fieldAfter  = (Green.FieldDeclarationSyntax)after;

                    var isConstBefore = fieldBefore.Modifiers.Any((int)SyntaxKind.ConstKeyword);
                    var isConstAfter  = fieldAfter.Modifiers.Any((int)SyntaxKind.ConstKeyword);

                    if (!isConstBefore && !isConstAfter)
                    {
                        ignoreChildNode = childKind => childKind == SyntaxKind.EqualsValueClause;
                    }
                }

                // NOTE(cyrusn): Do we want to avoid going down into attribute expressions?  I don't
                // think we can avoid it as there are likely places in the compiler that use these
                // expressions.  For example, if the user changes [InternalsVisibleTo("goo")] to
                // [InternalsVisibleTo("bar")] then that must count as a top level change as it
                // affects symbol visibility.  Perhaps we could enumerate the places in the compiler
                // that use the values inside source attributes and we can check if we're in an
                // attribute with that name.  It wouldn't be 100% correct (because of annoying things
                // like using aliases), but would likely be good enough for the incremental cases in
                // the IDE.
            }

            if (ignoreChildNode != null)
            {
                var e1 = before.ChildNodesAndTokens().GetEnumerator();
                var e2 = after.ChildNodesAndTokens().GetEnumerator();
                while (true)
                {
                    GreenNode child1 = null;
                    GreenNode child2 = null;

                    // skip ignored children:
                    while (e1.MoveNext())
                    {
                        var c = e1.Current;
                        if (c != null && (c.IsToken || !ignoreChildNode((SyntaxKind)c.RawKind)))
                        {
                            child1 = c;
                            break;
                        }
                    }

                    while (e2.MoveNext())
                    {
                        var c = e2.Current;
                        if (c != null && (c.IsToken || !ignoreChildNode((SyntaxKind)c.RawKind)))
                        {
                            child2 = c;
                            break;
                        }
                    }

                    if (child1 == null || child2 == null)
                    {
                        // false if some children remained
                        return(child1 == child2);
                    }

                    if (!AreEquivalentRecursive(child1, child2, ignoreChildNode, topLevel))
                    {
                        return(false);
                    }
                }
            }
            else
            {
                // simple comparison - not ignoring children

                int slotCount = before.SlotCount;
                if (slotCount != after.SlotCount)
                {
                    return(false);
                }

                for (int i = 0; i < slotCount; i++)
                {
                    var child1 = before.GetSlot(i);
                    var child2 = after.GetSlot(i);

                    if (!AreEquivalentRecursive(child1, child2, ignoreChildNode, topLevel))
                    {
                        return(false);
                    }
                }

                return(true);
            }
        }
        private static bool AreEquivalentRecursive(GreenNode before, GreenNode after, Func<SyntaxKind, bool> ignoreChildNode, bool topLevel)
        {
            if (before == after)
            {
                return true;
            }

            if (before == null || after == null)
            {
                return false;
            }

            if (before.RawKind != after.RawKind)
            {
                return false;
            }

            if (before.IsToken)
            {
                Debug.Assert(after.IsToken);
                return AreTokensEquivalent(before, after);
            }

            if (topLevel)
            {
                // Once we get down to the body level we don't need to go any further and we can
                // consider these trees equivalent.
                switch ((SyntaxKind)before.RawKind)
                {
                    case SyntaxKind.Block:
                    case SyntaxKind.ArrowExpressionClause:
                        return true;
                }

                // If we're only checking top level equivalence, then we don't have to go down into
                // the initializer for a field. However, we can't put that optimization for all
                // fields. For example, fields that are 'const' do need their initializers checked as
                // changing them can affect binding results.
                if ((SyntaxKind)before.RawKind == SyntaxKind.FieldDeclaration)
                {
                    var fieldBefore = (Green.FieldDeclarationSyntax)before;
                    var fieldAfter = (Green.FieldDeclarationSyntax)after;

                    var isConstBefore = fieldBefore.Modifiers.Any((int)SyntaxKind.ConstKeyword);
                    var isConstAfter = fieldAfter.Modifiers.Any((int)SyntaxKind.ConstKeyword);

                    if (!isConstBefore && !isConstAfter)
                    {
                        ignoreChildNode = childKind => childKind == SyntaxKind.EqualsValueClause;
                    }
                }

                // NOTE(cyrusn): Do we want to avoid going down into attribute expressions?  I don't
                // think we can avoid it as there are likely places in the compiler that use these
                // expressions.  For example, if the user changes [InternalsVisibleTo("foo")] to
                // [InternalsVisibleTo("bar")] then that must count as a top level change as it
                // affects symbol visibility.  Perhaps we could enumerate the places in the compiler
                // that use the values inside source attributes and we can check if we're in an
                // attribute with that name.  It wouldn't be 100% correct (because of annoying things
                // like using aliases), but would likely be good enough for the incremental cases in
                // the IDE.
            }

            if (ignoreChildNode != null)
            {
                var e1 = before.ChildNodesAndTokens().GetEnumerator();
                var e2 = after.ChildNodesAndTokens().GetEnumerator();
                while (true)
                {
                    GreenNode child1 = null;
                    GreenNode child2 = null;

                    // skip ignored children:
                    while (e1.MoveNext())
                    {
                        var c = e1.Current;
                        if (c != null && (c.IsToken || !ignoreChildNode((SyntaxKind)c.RawKind)))
                        {
                            child1 = c;
                            break;
                        }
                    }

                    while (e2.MoveNext())
                    {
                        var c = e2.Current;
                        if (c != null && (c.IsToken || !ignoreChildNode((SyntaxKind)c.RawKind)))
                        {
                            child2 = c;
                            break;
                        }
                    }

                    if (child1 == null || child2 == null)
                    {
                        // false if some children remained
                        return child1 == child2;
                    }

                    if (!AreEquivalentRecursive(child1, child2, ignoreChildNode, topLevel))
                    {
                        return false;
                    }
                }
            }
            else
            {
                // simple comparison - not ignoring children

                int slotCount = before.SlotCount;
                if (slotCount != after.SlotCount)
                {
                    return false;
                }

                for (int i = 0; i < slotCount; i++)
                {
                    var child1 = before.GetSlot(i);
                    var child2 = after.GetSlot(i);

                    if (!AreEquivalentRecursive(child1, child2, ignoreChildNode, topLevel))
                    {
                        return false;
                    }
                }

                return true;
            }
        }
Exemple #3
0
        private static bool AreEquivalentRecursive(GreenNode before, GreenNode after, Func <int, bool> ignoreChildNode, bool topLevel)
        {
            if (before == after)
            {
                return(true);
            }

            if (before == null || after == null)
            {
                return(false);
            }

            if (before.RawKind != after.RawKind)
            {
                return(false);
            }

            if (before.IsToken)
            {
                Debug.Assert(after.IsToken);
                return(AreTokensEquivalent(before, after));
            }

            if (ignoreChildNode != null)
            {
                var e1 = before.ChildNodesAndTokens().GetEnumerator();
                var e2 = after.ChildNodesAndTokens().GetEnumerator();
                while (true)
                {
                    GreenNode child1 = null;
                    GreenNode child2 = null;

                    // skip ignored children:
                    while (e1.MoveNext())
                    {
                        var c = e1.Current;
                        if (c != null && (c.IsToken || !ignoreChildNode(c.RawKind)))
                        {
                            child1 = c;
                            break;
                        }
                    }

                    while (e2.MoveNext())
                    {
                        var c = e2.Current;
                        if (c != null && (c.IsToken || !ignoreChildNode(c.RawKind)))
                        {
                            child2 = c;
                            break;
                        }
                    }

                    if (child1 == null || child2 == null)
                    {
                        // false if some children remained
                        return(child1 == child2);
                    }

                    if (!AreEquivalentRecursive(child1, child2, ignoreChildNode, topLevel))
                    {
                        return(false);
                    }
                }
            }
            else
            {
                // simple comparison - not ignoring children

                int slotCount = before.SlotCount;
                if (slotCount != after.SlotCount)
                {
                    return(false);
                }

                for (int i = 0; i < slotCount; i++)
                {
                    var child1 = before.GetSlot(i);
                    var child2 = after.GetSlot(i);

                    if (!AreEquivalentRecursive(child1, child2, ignoreChildNode, topLevel))
                    {
                        return(false);
                    }
                }

                return(true);
            }
        }