/// <summary>
        /// If the parent is scope-defining then both the parent and scopeDefiningParent references will be set to it, this is a convenience method to
        /// save having to specify it explicitly for both (for cases where the parent scope - if any - does not have a return value)
        /// </summary>
        public static ScopeAccessInformation Extend(
            this ScopeAccessInformation scopeInformation,
            IDefineScope parent,
            NonNullImmutableList <ICodeBlock> blocks)
        {
            if (scopeInformation == null)
            {
                throw new ArgumentNullException("scopeInformation");
            }
            if (parent == null)
            {
                throw new ArgumentNullException("parent");
            }
            if (blocks == null)
            {
                throw new ArgumentNullException("blocks");
            }

            return(Extend(scopeInformation, parent, null, null, blocks));
        }
        /// <summary>
        /// If the parent is scope-defining then both the parent and scopeDefiningParent references will be set to it, this is a convenience method to
        /// save having to specify it explicitly for both
        /// </summary>
        public static ScopeAccessInformation Extend(
            this ScopeAccessInformation scopeInformation,
            IDefineScope parent,
            CSharpName parentReturnValueNameIfAny,
            CSharpName errorRegistrationTokenIfAny,
            NonNullImmutableList <ICodeBlock> blocks)
        {
            if (scopeInformation == null)
            {
                throw new ArgumentNullException("scopeInformation");
            }
            if (parent == null)
            {
                throw new ArgumentNullException("parent");
            }
            if (blocks == null)
            {
                throw new ArgumentNullException("blocks");
            }

            return(Extend(scopeInformation, parent, parent, parentReturnValueNameIfAny, errorRegistrationTokenIfAny, blocks));
        }
        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;
        }