private static void BuildScopesInFunctionParameters(
            LexicalScope containingScope,
            FunctionDeclarationSyntax function,
            ExpressionLexicalScopesBuilder binder)
        {
            if (function.GenericParameters != null)
            {
                foreach (var parameter in function.GenericParameters)
                {
                    binder.VisitExpression(parameter.TypeExpression, containingScope);
                }
            }

            foreach (var parameter in function.Parameters)
            {
                switch (parameter)
                {
                case NamedParameterSyntax namedParameter:
                    binder.VisitExpression(namedParameter.TypeExpression, containingScope);
                    break;

                case SelfParameterSyntax _:
                case FieldParameterSyntax _:
                    // Nothing to bind
                    break;

                default:
                    throw NonExhaustiveMatchException.For(parameter);
                }
            }
        }
        private void BuildScopesInDeclaration(
            LexicalScope containingScope,
            DeclarationSyntax declaration)
        {
            var binder          = new ExpressionLexicalScopesBuilder();
            var diagnosticCount = diagnostics.Count;

            switch (declaration)
            {
            case NamespaceDeclarationSyntax ns:
            {
                if (ns.InGlobalNamespace)
                {
                    containingScope = globalScope;
                }

                containingScope = BuildNamespaceScopes(containingScope, ns.Name);
                containingScope = BuildUsingDirectivesScope(containingScope, ns.UsingDirectives);
                foreach (var nestedDeclaration in ns.Declarations)
                {
                    BuildScopesInDeclaration(containingScope, nestedDeclaration);
                }
            }
            break;

            case NamedFunctionDeclarationSyntax function:
                BuildScopesInFunctionParameters(containingScope, function, binder);
                binder.VisitExpression(function.ReturnTypeExpression, containingScope);
                BuildScopesInFunctionBody(containingScope, function, binder);
                break;

            case OperatorDeclarationSyntax operatorDeclaration:
                BuildScopesInFunctionParameters(containingScope, operatorDeclaration, binder);
                binder.VisitExpression(operatorDeclaration.ReturnTypeExpression, containingScope);
                BuildScopesInFunctionBody(containingScope, operatorDeclaration, binder);
                break;

            case ConstructorDeclarationSyntax constructor:
                BuildScopesInFunctionParameters(containingScope, constructor, binder);
                BuildScopesInFunctionBody(containingScope, constructor, binder);
                break;

            case InitializerDeclarationSyntax initializer:
                BuildScopesInFunctionParameters(containingScope, initializer, binder);
                BuildScopesInFunctionBody(containingScope, initializer, binder);
                break;

            case TypeDeclarationSyntax typeDeclaration:
                // TODO name scope for type declaration
                foreach (var nestedDeclaration in typeDeclaration.Members)
                {
                    BuildScopesInDeclaration(containingScope, nestedDeclaration);
                }
                break;

            case FieldDeclarationSyntax fieldDeclaration:
                binder.VisitExpression(fieldDeclaration.TypeExpression, containingScope);
                binder.VisitExpression(fieldDeclaration.Initializer, containingScope);
                break;

            default:
                throw NonExhaustiveMatchException.For(declaration);
            }
            if (diagnosticCount != diagnostics.Count)
            {
                declaration.Poison();
            }
        }