<commit_before>/*
 * Scala.js (https://www.scala-js.org/)
 *
 * Copyright EPFL.
 *
 * Licensed under Apache License 2.0
 * (https://www.apache.org/licenses/LICENSE-2.0).
 *
 * See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 */

package org.scalajs.linker.checker

// In the IR checker, we allow early returns for improved readability
// scalastyle:off return

import scala.language.implicitConversions

import scala.annotation.switch

import scala.collection.mutable

import org.scalajs.ir._
import org.scalajs.ir.Names._
import org.scalajs.ir.Trees._
import org.scalajs.ir.Types._

import org.scalajs.logging._

import org.scalajs.linker.standard._
import org.scalajs.linker.analyzer.{Analyzer, Infos}

/** Checker for the validity of the IR. */
private final class IRChecker(unit: LinkingUnit, logger: Logger) {

  import IRChecker._

  private var errorCount: Int = 0

  /* Per-method state (setup with withPerMethodState).
   * This state is reset per-Closure as well.
   */
  private var declaredLocalVarNamesPerMethod: mutable.Set[LocalName] = _
  private var declaredLabelNamesPerMethod: mutable.Set[LabelName] = _

  private def withPerMethodState[A](body: => A): A = {
    val savedDeclaredLocalVarNamesPerMethod = declaredLocalVarNamesPerMethod
    val savedDeclaredLabelNamesPerMethod = declaredLabelNamesPerMethod
    try {
      declaredLocalVarNamesPerMethod = mutable.Set.empty
      declaredLabelNamesPerMethod = mutable.Set.empty
      body
    } finally {
      declaredLocalVarNamesPerMethod = savedDeclaredLocalVarNamesPerMethod
      declaredLabelNamesPerMethod = savedDeclaredLabelNamesPerMethod
    }
  }

  private val classes: mutable.Map[ClassName, CheckedClass] = {
    val tups = for (classDef <- unit.classDefs) yield {
      implicit val ctx = ErrorContext(classDef)
      val c = new CheckedClass(classDef)
      c.name -> c
    }
    mutable.Map(tups: _*)
  }

  def check(): Int = {
    for (classDef <- unit.classDefs) {
      implicit val ctx = ErrorContext(classDef)

      checkJSClassCaptures(classDef)
      checkJSNativeLoadSpec(classDef)
      checkStaticMembers(classDef)
      checkDuplicateMembers(classDef)

      classDef.kind match {
        case ClassKind.AbstractJSType | ClassKind.NativeJSClass |
            ClassKind.NativeJSModuleClass =>
          if (classDef.fields.nonEmpty ||
              classDef.methods.exists(!_.value.flags.namespace.isStatic) ||
              classDef.exportedMembers.nonEmpty ||
              classDef.topLevelExports.nonEmpty) {
            val kind =
              if (classDef.kind == ClassKind.AbstractJSType) "Abstract"
              else "Native"
            reportError(
                i"$kind JS type ${classDef.name} cannot have instance members")
          }
        case _ =>
          checkScalaClassDef(classDef)
      }
    }
    errorCount
  }

  private def checkJSClassCaptures(classDef: LinkedClass): Unit = {
    implicit val ctx = ErrorContext(classDef)

    for (classCaptures <- classDef.jsClassCaptures) {
      if (classDef.kind != ClassKind.JSClass) {
        reportError(
            i"Class ${classDef.name} which is not a non-native JS class " +
            "cannot have class captures")
      }

      classCaptures.foldLeft(Set.empty[LocalName]) {
        case (alreadyDeclared, p @ ParamDef(ident, _, tpe, mutable, rest)) =>
          implicit val ctx = ErrorContext(p)
          val name = ident.name
          if (alreadyDeclared(name))
            reportError(i"Duplicate JS class capture '$name'")
          if (tpe == NoType)
            reportError(i"The JS class capture $name cannot have type NoType")
          if (mutable)
            reportError(i"The JS class capture $name cannot be mutable")
          if (rest)
            reportError(i"The JS class capture $name cannot be a rest param")
          alreadyDeclared + name
      }

      def isStaticInit(methodDef: Versioned[MethodDef]): Boolean = {
        val m = methodDef.value
        m.flags.namespace == MemberNamespace.PublicStatic &&
        m.methodName.isStaticInitializer
      }

      for (staticInit <- classDef.methods.find(isStaticInit)) {
        implicit val ctx = ErrorContext(staticInit.value)
        reportError(
            i"The non-top-level JS class ${classDef.name} cannot have a " +
            "static initializer")
      }
    }
  }

  private def checkJSNativeLoadSpec(classDef: LinkedClass): Unit = {
    implicit val ctx = ErrorContext(classDef)

    classDef.kind match {
      case ClassKind.NativeJSClass | ClassKind.NativeJSModuleClass =>
        if (classDef.jsNativeLoadSpec.isEmpty) {
          reportError(
              i"Native JS type ${classDef.name} must have a jsNativeLoadSpec")
        }
      case _ =>
        if (classDef.jsNativeLoadSpec.isDefined) {
          reportError(
              i"Non-native JS type ${classDef.name} must not have a " +<commit_msg>IRCheck ClassDef.jsSuperClass<commit_after>/*
 * Scala.js (https://www.scala-js.org/)
 *
 * Copyright EPFL.
 *
 * Licensed under Apache License 2.0
 * (https://www.apache.org/licenses/LICENSE-2.0).
 *
 * See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 */

package org.scalajs.linker.checker

// In the IR checker, we allow early returns for improved readability
// scalastyle:off return

import scala.language.implicitConversions

import scala.annotation.switch

import scala.collection.mutable

import org.scalajs.ir._
import org.scalajs.ir.Names._
import org.scalajs.ir.Trees._
import org.scalajs.ir.Types._

import org.scalajs.logging._

import org.scalajs.linker.standard._
import org.scalajs.linker.analyzer.{Analyzer, Infos}

/** Checker for the validity of the IR. */
private final class IRChecker(unit: LinkingUnit, logger: Logger) {

  import IRChecker._

  private var errorCount: Int = 0

  /* Per-method state (setup with withPerMethodState).
   * This state is reset per-Closure as well.
   */
  private var declaredLocalVarNamesPerMethod: mutable.Set[LocalName] = _
  private var declaredLabelNamesPerMethod: mutable.Set[LabelName] = _

  private def withPerMethodState[A](body: => A): A = {
    val savedDeclaredLocalVarNamesPerMethod = declaredLocalVarNamesPerMethod
    val savedDeclaredLabelNamesPerMethod = declaredLabelNamesPerMethod
    try {
      declaredLocalVarNamesPerMethod = mutable.Set.empty
      declaredLabelNamesPerMethod = mutable.Set.empty
      body
    } finally {
      declaredLocalVarNamesPerMethod = savedDeclaredLocalVarNamesPerMethod
      declaredLabelNamesPerMethod = savedDeclaredLabelNamesPerMethod
    }
  }

  private val classes: mutable.Map[ClassName, CheckedClass] = {
    val tups = for (classDef <- unit.classDefs) yield {
      implicit val ctx = ErrorContext(classDef)
      val c = new CheckedClass(classDef)
      c.name -> c
    }
    mutable.Map(tups: _*)
  }

  def check(): Int = {
    for (classDef <- unit.classDefs) {
      implicit val ctx = ErrorContext(classDef)

      checkJSClassCaptures(classDef)
      checkJSSuperClass(classDef)
      checkJSNativeLoadSpec(classDef)
      checkStaticMembers(classDef)
      checkDuplicateMembers(classDef)

      classDef.kind match {
        case ClassKind.AbstractJSType | ClassKind.NativeJSClass |
            ClassKind.NativeJSModuleClass =>
          if (classDef.fields.nonEmpty ||
              classDef.methods.exists(!_.value.flags.namespace.isStatic) ||
              classDef.exportedMembers.nonEmpty ||
              classDef.topLevelExports.nonEmpty) {
            val kind =
              if (classDef.kind == ClassKind.AbstractJSType) "Abstract"
              else "Native"
            reportError(
                i"$kind JS type ${classDef.name} cannot have instance members")
          }
        case _ =>
          checkScalaClassDef(classDef)
      }
    }
    errorCount
  }

  private def checkJSClassCaptures(classDef: LinkedClass): Unit = {
    implicit val ctx = ErrorContext(classDef)

    for (classCaptures <- classDef.jsClassCaptures) {
      if (classDef.kind != ClassKind.JSClass) {
        reportError(
            i"Class ${classDef.name} which is not a non-native JS class " +
            "cannot have class captures")
      }

      classCaptures.foldLeft(Set.empty[LocalName]) {
        case (alreadyDeclared, p @ ParamDef(ident, _, tpe, mutable, rest)) =>
          implicit val ctx = ErrorContext(p)
          val name = ident.name
          if (alreadyDeclared(name))
            reportError(i"Duplicate JS class capture '$name'")
          if (tpe == NoType)
            reportError(i"The JS class capture $name cannot have type NoType")
          if (mutable)
            reportError(i"The JS class capture $name cannot be mutable")
          if (rest)
            reportError(i"The JS class capture $name cannot be a rest param")
          alreadyDeclared + name
      }

      def isStaticInit(methodDef: Versioned[MethodDef]): Boolean = {
        val m = methodDef.value
        m.flags.namespace == MemberNamespace.PublicStatic &&
        m.methodName.isStaticInitializer
      }

      for (staticInit <- classDef.methods.find(isStaticInit)) {
        implicit val ctx = ErrorContext(staticInit.value)
        reportError(
            i"The non-top-level JS class ${classDef.name} cannot have a " +
            "static initializer")
      }
    }
  }

  private def checkJSSuperClass(classDef: LinkedClass): Unit = {
    implicit val ctx = ErrorContext(classDef)

    if (classDef.kind.isJSClass) {
      classDef.jsSuperClass.fold {
        // .get is OK: the Analyzer checks that a super class is present.
        val superClass = lookupClass(classDef.superClass.get.name)
        if (superClass.jsClassCaptures.isDefined)
          reportError(i"super class ${superClass.name} may not have jsClassCaptures")
      } { tree =>
        val env = Env.fromSignature(NoType, classDef.jsClassCaptures, Nil, AnyType)
        typecheckExpect(tree, env, AnyType)
      }
    } else {
      if (classDef.jsSuperClass.isDefined)
        reportError("Only non-native JS types may have a jsSuperClass")
    }
  }

  private def checkJSNativeLoadSpec(classDef: LinkedClass): Unit = {
    implicit val ctx = ErrorContext(classDef)

    classDef.kind match {
      case ClassKind.NativeJSClass | ClassKind.NativeJSModuleClass =>
        if (classDef.jsNativeLoadSpec.isEmpty) {
          reportError(
              i"Native JS type ${classDef.name} must have a jsNativeLoadSpec")
        }
      case _ =>
        if (classDef.jsNativeLoadSpec.isDefined) {
          reportError(
              i"Non-native JS type ${classDef.name} must not have a " +
{'fraction_non_alphanumeric': 0.09071375186846038, 'fraction_numerical': 0.0009342301943198804, 'mean_word_length': 2.7195969423210564, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 18, 'https://': 4, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6890721', 'n_tokens_mistral': 3118, 'n_tokens_neox': 3084, 'n_words': 757}