private static bool ContainsMismatchedExitThatMustBeHandledAtThisLevel(IHaveNestedContent nestedContentBlock, ExitStatement.ExitableStatementType expectedExitType)
        {
            if (nestedContentBlock == null)
            {
                throw new ArgumentNullException("nestedContentBlock");
            }
            if (!Enum.IsDefined(typeof(ExitStatement.ExitableStatementType), expectedExitType))
            {
                throw new ArgumentOutOfRangeException("expectedExitType");
            }

            foreach (var codeBlock in nestedContentBlock.AllExecutableBlocks)
            {
                // If a ForBlock or DoBlock is reached then there can't be a mismatched exit statement that must be handled here - either the For/DoBlock
                // will contain no exit statement or it will contain a mistmatched exit that it must handle itself or it will contain a non-mismatched
                // exit (all of these scenarios indicate that there is no mismatched exit at this level
                if (codeBlock is ILoopOverNestedContent)
                {
                    continue;
                }

                var exitStatement = codeBlock as ExitStatement;
                if (exitStatement != null)
                {
                    if (exitStatement.StatementType != expectedExitType)
                    {
                        return(true);
                    }
                    continue;
                }

                var doubleNestedContentBlock = codeBlock as IHaveNestedContent;
                if (doubleNestedContentBlock != null)
                {
                    if (ContainsMismatchedExitThatMustBeHandledAtThisLevel(doubleNestedContentBlock, expectedExitType))
                    {
                        return(true);
                    }
                }
            }
            return(false);
        }
        /// <summary>
        /// The IHaveNestedContent property AllExecutableBlocks will return executable blocks that are directly contained within a code block and, in the case
        /// of an if or a while block, any executable blocks in conditions. If a full recursive scan of executable blocks is required then this method may be
        /// used.
        /// </summary>
        public static IEnumerable <ICodeBlock> GetAllNestedBlocks(this IHaveNestedContent nestedContentBlock)
        {
            if (nestedContentBlock == null)
            {
                throw new ArgumentNullException("nestedContentBlock");
            }

            foreach (var codeBlock in nestedContentBlock.AllExecutableBlocks)
            {
                yield return(codeBlock);

                var doubleNestedContentBlock = codeBlock as IHaveNestedContent;
                if (doubleNestedContentBlock != null)
                {
                    foreach (var doubleNestedExecutableBlock in doubleNestedContentBlock.GetAllNestedBlocks())
                    {
                        yield return(doubleNestedExecutableBlock);
                    }
                }
            }
        }
        /// <summary>
        /// Does the content within the specified block (that contains nested content) contain a loop with a mismatched EXIT statement (eg. does it contain
        /// a FOR loop with an EXIT DO that does not have a DO loop between the FOR and the EXIT DO) - if so, then the EXIT statment will require multiple
        /// break statements in the translated C# code (one to exit its mismatched containing loop and then another to exit the loop that it targets).
        /// </summary>
        public static bool ContainsLoopThatContainsMismatchedExitThatMustBeHandledAtThisLevel(this IHaveNestedContent nestedContentBlock)
        {
            if (nestedContentBlock == null)
            {
                throw new ArgumentNullException("nestedContentBlock");
            }

            foreach (var codeBlock in nestedContentBlock.AllExecutableBlocks)
            {
                // If a ForBlock or DoBlock is reached then pass off handling to ContainsMismatchedExitThatMustBeHandledAtThisLevel, specifying an expected
                // exit type consistent with the loop
                var forBlock = codeBlock as ForBlock;
                if (forBlock != null)
                {
                    if (ContainsMismatchedExitThatMustBeHandledAtThisLevel(forBlock, expectedExitType: ExitStatement.ExitableStatementType.For))
                    {
                        return(true);
                    }
                    continue;
                }
                var forEachBlock = codeBlock as ForEachBlock;
                if (forEachBlock != null)
                {
                    if (ContainsMismatchedExitThatMustBeHandledAtThisLevel(forEachBlock, expectedExitType: ExitStatement.ExitableStatementType.For))
                    {
                        return(true);
                    }
                    continue;
                }
                var doBlock = codeBlock as DoBlock;
                if (doBlock != null)
                {
                    if (ContainsMismatchedExitThatMustBeHandledAtThisLevel(doBlock, expectedExitType: ExitStatement.ExitableStatementType.Do))
                    {
                        return(true);
                    }
                    continue;
                }
                if ((codeBlock is ILoopOverNestedContent) && (forBlock == null) && (forEachBlock == null) && (doBlock == null))
                {
                    // Sanity checking - if there's another looping construct then this method won't be dealing with it properly!
                    throw new ArgumentException("Unexpected ILoopOverNestedContent type: " + codeBlock.GetType());
                }

                var doubleNestedContentBlock = codeBlock as IHaveNestedContent;
                if (doubleNestedContentBlock != null)
                {
                    if (ContainsLoopThatContainsMismatchedExitThatMustBeHandledAtThisLevel(doubleNestedContentBlock))
                    {
                        return(true);
                    }
                }
            }
            return(false);
        }
        public static ScopeAccessInformation SetParent(this ScopeAccessInformation scopeInformation, IHaveNestedContent parent)
        {
            if (scopeInformation == null)
            {
                throw new ArgumentNullException("scopeInformation");
            }
            if (parent == null)
            {
                throw new ArgumentNullException("parent");
            }

            if ((parent != scopeInformation.ScopeDefiningParent) && !scopeInformation.ScopeDefiningParent.GetAllNestedBlocks().Contains(parent))
            {
                // The parent must either be the current ScopeDefiningParent or be one of the descendant blocks, otherwise the structure will be invalid
                throw new ArgumentException("The parent must either be the current ScopeDefiningParent or be one of the descendant blocks");
            }
            return(new ScopeAccessInformation(
                       parent,
                       scopeInformation.ScopeDefiningParent,
                       scopeInformation.ParentReturnValueNameIfAny,
                       scopeInformation.ErrorRegistrationTokenIfAny,
                       scopeInformation.DirectedWithReferenceIfAny,
                       scopeInformation.ExternalDependencies,
                       scopeInformation.Classes,
                       scopeInformation.Functions,
                       scopeInformation.Properties,
                       scopeInformation.Constants,
                       scopeInformation.Variables,
                       scopeInformation.StructureExitPoints
                       ));
        }
        public static ScopeAccessInformation Extend(
            this ScopeAccessInformation scopeInformation,
            IHaveNestedContent parent,
            IDefineScope scopeDefiningParent,
            CSharpName parentReturnValueNameIfAny,
            CSharpName errorRegistrationTokenIfAny,
            NonNullImmutableList <ICodeBlock> blocks)
        {
            if (parent == null)
            {
                throw new ArgumentNullException("parent");
            }
            if (scopeDefiningParent == null)
            {
                throw new ArgumentNullException("scopeDefiningParent");
            }
            if (scopeInformation == null)
            {
                throw new ArgumentNullException("scopeInformation");
            }
            if (blocks == null)
            {
                throw new ArgumentNullException("blocks");
            }

            var blocksScopeLocation = scopeDefiningParent.Scope;

            blocks = FlattenAllAccessibleBlockLevelCodeBlocks(blocks);
            var variables = scopeInformation.Variables.AddRange(
                blocks
                .OfType <DimStatement>()    // This covers DIM, REDIM, PRIVATE and PUBLIC (they may all be considered the same for these purposes)
                .SelectMany(d => d.Variables.Select(v => new ScopedNameToken(
                                                        v.Name.Content,
                                                        v.Name.LineIndex,
                                                        blocksScopeLocation
                                                        )))
                );

            if (scopeDefiningParent != null)
            {
                variables = variables.AddRange(
                    scopeDefiningParent.ExplicitScopeAdditions
                    .Select(v => new ScopedNameToken(
                                v.Content,
                                v.LineIndex,
                                blocksScopeLocation
                                )
                            )
                    );
            }
            var constants = scopeInformation.Constants.AddRange(
                blocks
                .OfType <ConstStatement>()
                .SelectMany(c => c.Values.Select(v => new ScopedNameToken(
                                                     v.Name.Content,
                                                     v.Name.LineIndex,
                                                     blocksScopeLocation
                                                     )))
                );

            return(new ScopeAccessInformation(
                       parent,
                       scopeDefiningParent,
                       parentReturnValueNameIfAny,
                       errorRegistrationTokenIfAny,
                       scopeInformation.DirectedWithReferenceIfAny,
                       scopeInformation.ExternalDependencies,
                       scopeInformation.Classes.AddRange(
                           blocks
                           .Where(b => b is ClassBlock)
                           .Cast <ClassBlock>()
                           .Select(c => new ScopedNameToken(c.Name.Content, c.Name.LineIndex, ScopeLocationOptions.OutermostScope)) // These are always OutermostScope
                           ),
                       scopeInformation.Functions.AddRange(
                           blocks
                           .Where(b => (b is FunctionBlock) || (b is SubBlock))
                           .Cast <AbstractFunctionBlock>()
                           .Select(b => new ScopedNameToken(b.Name.Content, b.Name.LineIndex, blocksScopeLocation))
                           ),
                       scopeInformation.Properties.AddRange(
                           blocks
                           .Where(b => b is PropertyBlock)
                           .Cast <PropertyBlock>()
                           .Select(p => new ScopedNameToken(p.Name.Content, p.Name.LineIndex, ScopeLocationOptions.WithinClass)) // These are always WithinClass
                           ),
                       constants,
                       variables,
                       scopeInformation.StructureExitPoints
                       ));
        }
        public ScopeAccessInformation(
            IHaveNestedContent parent,
            IDefineScope scopeDefiningParent,
            CSharpName parentReturnValueNameIfAny,
            CSharpName errorRegistrationTokenIfAny,
            DirectedWithReferenceDetails directedWithReferenceIfAny,
            NonNullImmutableList <NameToken> externalDependencies,
            NonNullImmutableList <ScopedNameToken> classes,
            NonNullImmutableList <ScopedNameToken> functions,
            NonNullImmutableList <ScopedNameToken> properties,
            NonNullImmutableList <ScopedNameToken> constants,
            NonNullImmutableList <ScopedNameToken> variables,
            NonNullImmutableList <ExitableNonScopeDefiningConstructDetails> structureExitPoints)
        {
            if (parent == null)
            {
                throw new ArgumentNullException("parent");
            }
            if (scopeDefiningParent == null)
            {
                throw new ArgumentNullException("scopeDefiningParent");
            }
            if (externalDependencies == null)
            {
                throw new ArgumentNullException("externalDependencies");
            }
            if (classes == null)
            {
                throw new ArgumentNullException("classes");
            }
            if (functions == null)
            {
                throw new ArgumentNullException("functions");
            }
            if (properties == null)
            {
                throw new ArgumentNullException("properties");
            }
            if (constants == null)
            {
                throw new ArgumentNullException("constants");
            }
            if (variables == null)
            {
                throw new ArgumentNullException("variables");
            }
            if (structureExitPoints == null)
            {
                throw new ArgumentNullException("structureExitPoints");
            }

            Parent = parent;
            ScopeDefiningParent         = scopeDefiningParent;
            ParentReturnValueNameIfAny  = parentReturnValueNameIfAny;
            ErrorRegistrationTokenIfAny = errorRegistrationTokenIfAny;
            DirectedWithReferenceIfAny  = directedWithReferenceIfAny;
            ExternalDependencies        = externalDependencies;
            Classes             = classes;
            Functions           = functions;
            Properties          = properties;
            Constants           = constants;
            Variables           = variables;
            StructureExitPoints = structureExitPoints;
        }