Repository: dotnet/roslyn
File: src/Compilers/CSharp/Portable/Syntax/SyntaxTreeDiagnosticEnumerator.cs (C#)

Code before:
// 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.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.CodeAnalysis.CSharp
{
    /// <summary>
    /// An enumerator for diagnostic lists.
    /// </summary>
    internal static class SyntaxTreeDiagnosticEnumerator
    {
        private const int DefaultStackCapacity = 8;

        public static IEnumerable<Diagnostic> EnumerateDiagnostics(SyntaxTree syntaxTree, GreenNode root, int position)
        {
            if (!root.ContainsDiagnostics)
                yield break;

            using var stack = new NodeIterationStack(DefaultStackCapacity);
            stack.PushNodeOrToken(root);

            while (stack.Any())
            {
                var node = stack.Top.Node;

                if (!stack.Top.ProcessedDiagnostics)
                {
                    foreach (SyntaxDiagnosticInfo sdi in node.GetDiagnostics())
                    {
                        // For tokens, we've already seen leading trivia (as we push leading/trailing trivia explicit as
                        // green nodes to process on the stack), so we have to roll back.  For nodes, we have yet to see the
                        // leading trivia, so those don't need an adjustment.
                        int leadingWidthAlreadyCounted = node.IsToken ? node.GetLeadingTriviaWidth() : 0;

                        // don't produce locations outside of tree span
                        var length = syntaxTree.GetRoot().FullSpan.Length;
                        var spanStart = Math.Min(position - leadingWidthAlreadyCounted + sdi.Offset, length);
                        var spanWidth = Math.Min(spanStart + sdi.Width, length) - spanStart;

                        yield return new CSDiagnostic(sdi, new SourceLocation(syntaxTree, new TextSpan(spanStart, spanWidth)));
                    }

                    stack.MarkProcessedDiagnosticsForStackTop();
                }

                if (tryContinueWithThisNode(node))

Review comment (suggestion):
can have a simple while loop here, instead of having to store state, return true, and then restart where we were.

Code after:
// 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.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.CodeAnalysis.CSharp
{
    /// <summary>
    /// An enumerator for diagnostic lists.
    /// </summary>
    internal static class SyntaxTreeDiagnosticEnumerator
    {
        private const int DefaultStackCapacity = 8;

        public static IEnumerable<Diagnostic> EnumerateDiagnostics(SyntaxTree syntaxTree, GreenNode root, int position)
        {
            Debug.Assert(root.ContainsDiagnostics, "Caller should have checked that the root has diagnostics");

            using var stack = new NodeIterationStack(DefaultStackCapacity);
            stack.PushNodeOrToken(root);

            while (stack.Any())
            {
                var node = stack.Top.Node;

                if (!stack.Top.ProcessedDiagnostics)
                {
                    foreach (SyntaxDiagnosticInfo sdi in node.GetDiagnostics())
                    {
                        // For tokens, we've already seen leading trivia (as we push leading/trailing trivia explicit as
                        // green nodes to process on the stack), so we have to roll back.  For nodes, we have yet to see the
                        // leading trivia, so those don't need an adjustment.
                        int leadingWidthAlreadyCounted = node.IsToken ? node.GetLeadingTriviaWidth() : 0;

                        // don't produce locations outside of tree span
                        var length = syntaxTree.GetRoot().FullSpan.Length;
                        var spanStart = Math.Min(position - leadingWidthAlreadyCounted + sdi.Offset, length);
                        var spanWidth = Math.Min(spanStart + sdi.Width, length) - spanStart;

                        yield return new CSDiagnostic(sdi, new SourceLocation(syntaxTree, new TextSpan(spanStart, spanWidth)));
                    }

                    stack.Top.ProcessedDiagnostics = true;
                }

                processNode(node);
            }