<commit_before>/*                     __                                               *\
**     ________ ___   / /  ___      __ ____  Scala.js tools             **
**    / __/ __// _ | / /  / _ | __ / // __/  (c) 2013-2014, LAMP/EPFL   **
**  __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \    http://scala-js.org/       **
** /____/\___/_/ |_/____/_/ | |__/ /____/                               **
**                          |/____/                                     **
\*                                                                      */


package org.scalajs.core.tools.optimizer

import scala.language.implicitConversions

import scala.annotation.switch

import scala.collection.mutable

import org.scalajs.core.ir._
import Definitions._
import Trees._
import Types._

import org.scalajs.core.tools.logging._

/** Checker for the validity of the IR. */
class IRChecker(analyzer: Analyzer, allClassDefs: Seq[ClassDef], logger: Logger) {
  import IRChecker._

  private var _errorCount: Int = 0
  def errorCount: Int = _errorCount

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

  def check(): Boolean = {
    for {
      classDef <- allClassDefs
      if analyzer.classInfos(classDef.name.name).isNeededAtAll
    } {
      implicit val ctx = ErrorContext(classDef)
      checkStaticMembers(classDef)
      classDef.kind match {
        case ClassKind.RawJSType | ClassKind.Interface =>
          if (classDef.defs exists {
            case m: MethodDef if m.static => false
            case _                        => true
          }) {
            reportError(s"${classDef.name} of kind ${classDef.kind} cannot "+
                "have instance members")
          }
        case _ =>
          checkScalaClassDef(classDef)
      }
    }
    errorCount == 0
  }

  def checkStaticMembers(classDef: ClassDef): Unit = {
    for (member <- classDef.defs) {
      implicit val ctx = ErrorContext(member)
      member match {
        case m: MethodDef if m.static =>
          if (!m.name.isInstanceOf[Ident])
            reportError(s"Static method ${m.name} cannot be exported")
          else
            checkMethodDef(m, classDef)

        // Ignore non-static members
        case _ =>
      }
    }
  }

  def checkScalaClassDef(classDef: ClassDef): Unit = {
    assert(classDef.kind != ClassKind.RawJSType &&
        classDef.kind != ClassKind.Interface)

    if (!analyzer.classInfos(classDef.name.name).isAnySubclassInstantiated)
      return

    val isNormalClass =
      classDef.kind == ClassKind.Class || classDef.kind == ClassKind.ModuleClass

    for (member <- classDef.defs) {
      implicit val ctx = ErrorContext(member)
      member match {
        // Ignore static members
        case m: MethodDef if m.static =>

        // Scala declarations
        case v @ VarDef(_, _, _, _) if isNormalClass =>
          checkFieldDef(v, classDef)
        case m: MethodDef if m.name.isInstanceOf[Ident] =>
          checkMethodDef(m, classDef)

        // Exports
        case m: MethodDef if m.name.isInstanceOf[StringLiteral] && isNormalClass =>
          checkExportedMethodDef(m, classDef)
        case member @ PropertyDef(_: StringLiteral, _, _, _) if isNormalClass =>
          checkExportedPropertyDef(member, classDef)
        case member @ ConstructorExportDef(_, _, _) if isNormalClass =>
          checkConstructorExportDef(member, classDef)
        case member @ ModuleExportDef(_) if isNormalClass =>
          checkModuleExportDef(member, classDef)

        // Anything else is illegal
        case _ =>
          reportError(s"Illegal class member of type ${member.getClass.getName}")
      }
    }
  }

  def checkFieldDef(fieldDef: VarDef, classDef: ClassDef): Unit = {
    val VarDef(name, tpe, mutable, rhs) = fieldDef
    implicit val ctx = ErrorContext(fieldDef)

    if (tpe == NoType)
      reportError(s"VarDef cannot have type NoType")
    else
      typecheckExpect(rhs, Env.empty, tpe)
  }

  def checkMethodDef(methodDef: MethodDef, classDef: ClassDef): Unit = {
    val MethodDef(static, Ident(name, _), params, resultType, body) = methodDef
    implicit val ctx = ErrorContext(methodDef)

    val classInfo = analyzer.classInfos(classDef.name.name)
    val methodInfo =
      if (static) classInfo.staticMethodInfos(name)
      else classInfo.methodInfos(name)
    if (!methodInfo.isReachable)
      return

    for (ParamDef(name, tpe, _) <- params)
      if (tpe == NoType)
        reportError(s"Parameter $name has type NoType")

    val isConstructor = isConstructorName(name)
    val resultTypeForSig =
      if (isConstructor) NoType
      else resultType

    val advertizedSig = (params.map(_.ptpe), resultTypeForSig)
    val sigFromName = inferMethodType(name, static)
    if (advertizedSig != sigFromName) {
      reportError(
          s"The signature of ${classDef.name.name}.$name, which is "+
          s"$advertizedSig, does not match its name (should be $sigFromName).")
    }

    val thisType =
      if (static) NoType
      else ClassType(classDef.name.name)
    val bodyEnv = Env.fromSignature(thisType, params, resultType, isConstructor)
    if (resultType == NoType)
      typecheckStat(body, bodyEnv)
    else
      typecheckExpect(body, bodyEnv, resultType)
  }

  def checkExportedMethodDef(methodDef: MethodDef, classDef: ClassDef): Unit = {
    val MethodDef(_, _, params, resultType, body) = methodDef
    implicit val ctx = ErrorContext(methodDef)

    if (!classDef.kind.isClass) {
      reportError(s"Exported method def can only appear in a class")
      return
    }

    for (ParamDef(name, tpe, _) <- params) {
      if (tpe == NoType)
        reportError(s"Parameter $name has type NoType")
      else if (tpe != AnyType)
        reportError(s"Parameter $name of exported method def has type $tpe, "+
            "but must be Any")
    }<commit_msg>Fix #1397: Better IR-checks for exported methods<commit_after>/*                     __                                               *\
**     ________ ___   / /  ___      __ ____  Scala.js tools             **
**    / __/ __// _ | / /  / _ | __ / // __/  (c) 2013-2014, LAMP/EPFL   **
**  __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \    http://scala-js.org/       **
** /____/\___/_/ |_/____/_/ | |__/ /____/                               **
**                          |/____/                                     **
\*                                                                      */


package org.scalajs.core.tools.optimizer

import scala.language.implicitConversions

import scala.annotation.switch

import scala.collection.mutable

import org.scalajs.core.ir._
import Definitions._
import Trees._
import Types._

import org.scalajs.core.tools.logging._

/** Checker for the validity of the IR. */
class IRChecker(analyzer: Analyzer, allClassDefs: Seq[ClassDef], logger: Logger) {
  import IRChecker._

  private var _errorCount: Int = 0
  def errorCount: Int = _errorCount

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

  def check(): Boolean = {
    for {
      classDef <- allClassDefs
      if analyzer.classInfos(classDef.name.name).isNeededAtAll
    } {
      implicit val ctx = ErrorContext(classDef)
      checkStaticMembers(classDef)
      classDef.kind match {
        case ClassKind.RawJSType | ClassKind.Interface =>
          if (classDef.defs exists {
            case m: MethodDef if m.static => false
            case _                        => true
          }) {
            reportError(s"${classDef.name} of kind ${classDef.kind} cannot "+
                "have instance members")
          }
        case _ =>
          checkScalaClassDef(classDef)
      }
    }
    errorCount == 0
  }

  def checkStaticMembers(classDef: ClassDef): Unit = {
    for (member <- classDef.defs) {
      implicit val ctx = ErrorContext(member)
      member match {
        case m: MethodDef if m.static =>
          if (!m.name.isInstanceOf[Ident])
            reportError(s"Static method ${m.name} cannot be exported")
          else
            checkMethodDef(m, classDef)

        // Ignore non-static members
        case _ =>
      }
    }
  }

  def checkScalaClassDef(classDef: ClassDef): Unit = {
    assert(classDef.kind != ClassKind.RawJSType &&
        classDef.kind != ClassKind.Interface)

    if (!analyzer.classInfos(classDef.name.name).isAnySubclassInstantiated)
      return

    val isNormalClass =
      classDef.kind == ClassKind.Class || classDef.kind == ClassKind.ModuleClass

    for (member <- classDef.defs) {
      implicit val ctx = ErrorContext(member)
      member match {
        // Ignore static members
        case m: MethodDef if m.static =>

        // Scala declarations
        case v @ VarDef(_, _, _, _) if isNormalClass =>
          checkFieldDef(v, classDef)
        case m: MethodDef if m.name.isInstanceOf[Ident] =>
          checkMethodDef(m, classDef)

        // Exports
        case m: MethodDef if m.name.isInstanceOf[StringLiteral] && isNormalClass =>
          checkExportedMethodDef(m, classDef)
        case member @ PropertyDef(_: StringLiteral, _, _, _) if isNormalClass =>
          checkExportedPropertyDef(member, classDef)
        case member @ ConstructorExportDef(_, _, _) if isNormalClass =>
          checkConstructorExportDef(member, classDef)
        case member @ ModuleExportDef(_) if isNormalClass =>
          checkModuleExportDef(member, classDef)

        // Anything else is illegal
        case _ =>
          reportError(s"Illegal class member of type ${member.getClass.getName}")
      }
    }
  }

  def checkFieldDef(fieldDef: VarDef, classDef: ClassDef): Unit = {
    val VarDef(name, tpe, mutable, rhs) = fieldDef
    implicit val ctx = ErrorContext(fieldDef)

    if (tpe == NoType)
      reportError(s"VarDef cannot have type NoType")
    else
      typecheckExpect(rhs, Env.empty, tpe)
  }

  def checkMethodDef(methodDef: MethodDef, classDef: ClassDef): Unit = {
    val MethodDef(static, Ident(name, _), params, resultType, body) = methodDef
    implicit val ctx = ErrorContext(methodDef)

    val classInfo = analyzer.classInfos(classDef.name.name)
    val methodInfo =
      if (static) classInfo.staticMethodInfos(name)
      else classInfo.methodInfos(name)
    if (!methodInfo.isReachable)
      return

    for (ParamDef(name, tpe, _) <- params)
      if (tpe == NoType)
        reportError(s"Parameter $name has type NoType")

    val isConstructor = isConstructorName(name)
    val resultTypeForSig =
      if (isConstructor) NoType
      else resultType

    val advertizedSig = (params.map(_.ptpe), resultTypeForSig)
    val sigFromName = inferMethodType(name, static)
    if (advertizedSig != sigFromName) {
      reportError(
          s"The signature of ${classDef.name.name}.$name, which is "+
          s"$advertizedSig, does not match its name (should be $sigFromName).")
    }

    val thisType =
      if (static) NoType
      else ClassType(classDef.name.name)
    val bodyEnv = Env.fromSignature(thisType, params, resultType, isConstructor)
    if (resultType == NoType)
      typecheckStat(body, bodyEnv)
    else
      typecheckExpect(body, bodyEnv, resultType)
  }

  def checkExportedMethodDef(methodDef: MethodDef, classDef: ClassDef): Unit = {
    val MethodDef(static, StringLiteral(name), params, resultType, body) = methodDef
    implicit val ctx = ErrorContext(methodDef)

    if (!classDef.kind.isClass) {
      reportError(s"Exported method def can only appear in a class")
      return
    }

    if (static)
      reportError("Exported method def cannot be static")

    if (name.contains("__"))
      reportError("Exported method def name cannot contain __")

    for (ParamDef(name, tpe, _) <- params) {
      if (tpe == NoType)
        reportError(s"Parameter $name has type NoType")
      else if (tpe != AnyType)
        reportError(s"Parameter $name of exported method def has type $tpe, "+
            "but must be Any")
    }
{'fraction_non_alphanumeric': 0.10442549736094194, 'fraction_numerical': 0.00194884287454324, 'mean_word_length': 2.2207112970711296, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2279484', 'n_tokens_mistral': 3704, 'n_tokens_neox': 3612, 'n_words': 879}