/*
 * Copyright 2010-2015 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jetbrains.kotlin.resolve

import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.types.TypeConstructor
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.typeUtil.boundClosure
import org.jetbrains.kotlin.types.typeUtil.constituentTypes
import org.jetbrains.kotlin.utils.DFS

object FiniteBoundRestrictionChecker {
    @JvmStatic
    fun check(
        declaration: KtClass,
        classDescriptor: ClassDescriptor,
        diagnosticHolder: DiagnosticSink
    ) {
        val typeConstructor = classDescriptor.typeConstructor
        if (typeConstructor.parameters.isEmpty()) return

        // For every projection type argument A in every generic type B<…> in the set of constituent types
        // of every type in the B-closure the set of declared upper bounds of every type parameter T add an
        // edge from T to U, where U is the type parameter of the declaration of B<…> corresponding to the type argument A.
        // It is a compile-time error if the graph G has a cycle.
        val graph = GraphBuilder(typeConstructor).build()

        val problemNodes = graph.nodes.filter { graph.isInCycle(it) }
        if (problemNodes.isEmpty()) return
