/// <summary>
        /// Gets the descendants of the specified element that match the specified type and predicate.
        /// </summary>
        private static List <TElement> GetDescendants <TElement>(SyntaxElement element, Func <TElement, bool> predicate, List <TElement> list)
            where TElement : SyntaxElement
        {
            for (int i = 0; i < element.ChildCount; i++)
            {
                var child = element.GetChild(i);
                if (child != null)
                {
                    if (child is TElement ce && (predicate == null || predicate(ce)))
                    {
                        if (list == null)
                        {
                            list = new List <TElement>();
                        }

                        list.Add(ce);
                    }

                    if (child is SyntaxNode cn)
                    {
                        list = GetDescendants(child, predicate, list);
                    }
                }
            }

            return(list);
        }
        /// <summary>
        /// Gets the descendants of the specified element that match the specified type and predicate.
        /// </summary>
        private static IReadOnlyList <TElement> GetDescendants <TElement>(
            SyntaxElement element,
            Func <TElement, bool> predicate,
            bool includeSelf)
            where TElement : SyntaxElement
        {
            List <TElement> list = null;

            if (includeSelf && element is TElement telem && (predicate == null || predicate(telem)))
            {
                list = list ?? new List <TElement>();
                list.Add(telem);
            }

            var root       = element;
            var childIndex = 0;

            while (element != null)
            {
                if (childIndex < element.ChildCount && childIndex >= 0)
                {
                    // walk down
                    var child = element.GetChild(childIndex);
                    if (child != null)
                    {
                        element    = child;
                        childIndex = 0;

                        if (element is TElement telem2 && (predicate == null || predicate(telem2)))
                        {
                            list = list ?? new List <TElement>();
                            list.Add(telem2);
                        }
                    }
                    else
                    {
                        childIndex++;
                    }
                }
                else if (element == root)
                {
                    break;
                }
                else
                {
                    // walk up
                    childIndex = element.IndexInParent + 1;
                    element    = element.Parent;
                }
            }

            return(list != null?list.ToReadOnly() : EmptyReadOnlyList <TElement> .Instance);
        }
        protected static void GatherDiagnostics(
            SyntaxElement element,
            List <Diagnostic> diagnostics,
            DiagnosticsInclude include,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            cancellationToken.ThrowIfCancellationRequested();
            bool includeSyntax    = (include & DiagnosticsInclude.Syntactic) != 0;
            bool includeSemantic  = (include & DiagnosticsInclude.Semantic) != 0;
            bool includeExpansion = (include & DiagnosticsInclude.Expansion) != 0;

            if (element.HasSyntaxDiagnostics && includeSyntax)
            {
                // each syntax diagnostic is located at the element that carries it.
                diagnostics.AddRange(element.SyntaxDiagnostics.Select(d => d.HasLocation ? d : SetLocation(d, element)));
            }

            if (includeSemantic && element is SyntaxNode node && node.SemanticDiagnostics.Count > 0)
            {
                diagnostics.AddRange(node.SemanticDiagnostics);
            }

            if (includeSemantic || (includeSyntax && element.ContainsSyntaxDiagnostics))
            {
                for (int i = 0, n = element.ChildCount; i < n; i++)
                {
                    var child = element.GetChild(i);
                    if (child != null)
                    {
                        GatherDiagnostics(child, diagnostics, include, cancellationToken);
                    }
                }
            }

            if (includeExpansion && element is Expression expr && expr.GetExpansion() is SyntaxNode expansion)
            {
                var originalCount = diagnostics.Count;
                GatherDiagnostics(expansion, diagnostics, include, cancellationToken);

                if (diagnostics.Count > originalCount)
                {
                    var name     = expr.ReferencedSymbol?.Name ?? "<unknown>";
                    var location = expr is FunctionCallExpression fc ? fc.Name : expr;
                    var errors   = diagnostics[originalCount].Message;
                    var dx       = DiagnosticFacts.GetErrorInExpansion(name, errors).WithLocation(location);
                    diagnostics.SetCount(originalCount);
                    diagnostics.Add(dx);
                }
            }
        }
        private static TElement GetFirstDescendant <TElement>(SyntaxElement element, Func <TElement, bool> predicate, bool includeSelf)
            where TElement : SyntaxElement
        {
            if (includeSelf && element is TElement telem && (predicate == null || predicate(telem)))
            {
                return(telem);
            }

            var root       = element;
            var childIndex = 0;

            while (element != null)
            {
                if (childIndex < element.ChildCount && childIndex >= 0)
                {
                    // walk down
                    var child = element.GetChild(childIndex);
                    if (child != null)
                    {
                        element    = child;
                        childIndex = 0;

                        if (element is TElement telem2 && (predicate == null || predicate(telem2)))
                        {
                            return(telem2);
                        }
                    }
                    else
                    {
                        childIndex++;
                    }
                }
                else if (element == root)
                {
                    break;
                }
                else
                {
                    // walk up
                    childIndex = element.IndexInParent + 1;
                    element    = element.Parent;
                }
            }

            return(null);
        }