<commit_before>package dotty.tools.dotc
package transform
package init

import core._
import Contexts.Context
import Decorators._
import StdNames._
import Symbols._
import Constants.Constant
import Types._

import ast.tpd._
import config.Printers.init
import reporting.trace

import Effects._, Potentials._, Summary._, Util._

object Summarization {

  /** Summarization of potentials and effects for an expression
   *
   *  Optimization:
   *
   *   1. potentials for expression of primitive value types can be
   *      safely abandoned, as they are always fully initialized.
   */
  def analyze(expr: Tree)(implicit env: Env): Summary =
  trace("summarizing " + expr.show, init, s => Summary.show(s.asInstanceOf[Summary])) {
    val summary: Summary = expr match {
      case Ident(nme.WILDCARD) =>
        // TODO:  disallow `var x: T = _`
        Summary.empty

      case Ident(name) =>
        assert(name.isTermName, "type trees should not reach here")
        analyze(expr.tpe, expr)

      case supert: Super =>
        val SuperType(thisTp, superTp) = supert.tpe.asInstanceOf[SuperType]
        val thisRef = ThisRef(thisTp.widen.classSymbol.asClass)(supert)
        val pot = SuperRef(thisRef, superTp.classSymbol.asClass)(supert)
        Summary.empty + pot

      case Select(qualifier, name) =>
        val (pots, effs) = analyze(qualifier)
        if (env.ignoredMethods.contains(expr.symbol)) (Potentials.empty, effs)
        else {
          val (pots2, effs2) = pots.select(expr.symbol, expr)
          (pots2, effs ++ effs2)
        }

      case _: This =>
        // With self type, the type can be `A & B`.
        def classes(tp: Type): Set[ClassSymbol] = tp.widen match {
          case AndType(tp1, tp2)  =>
            classes(tp1) ++ classes(tp2)

          case tp =>
            Set(tp.classSymbol.asClass)
        }

        val pots: Potentials = classes(expr.tpe).map{ ThisRef(_)(expr) }
        (pots, Effects.empty)

      case Apply(fun, args) =>
        val summary = analyze(fun)
        val ignoredCall = env.ignoredMethods.contains(expr.symbol)

        val res = args.foldLeft(summary) { (sum, arg) =>
          val (pots1, effs1) = analyze(arg)
          if (ignoredCall) sum.withEffs(effs1)
          else sum.withEffs(pots1.promote(arg) ++ effs1)
        }

        if (ignoredCall) (Potentials.empty, res._2)
        else res

      case TypeApply(fun, args) =>
        analyze(fun)

      case Literal(const) =>
        Summary.empty

      case New(tpt) =>
        def typeRefOf(tp: Type): TypeRef = tp.typeConstructor match {
          case tref: TypeRef => tref
          case hklambda: HKTypeLambda => typeRefOf(hklambda.resType)
        }

        val tref = typeRefOf(tpt.tpe)
        val cls = tref.classSymbol.asClass
        // local class may capture, thus we need to track it
        if (tref.prefix == NoPrefix) {
          val enclosingCls = cls.enclosingClass.asClass
          val thisRef = ThisRef(enclosingCls)(expr)
          Summary.empty + Warm(cls, thisRef)(expr)
        }
        else {
          val (pots, effs) = analyze(tref.prefix, expr)
          if (pots.isEmpty) Summary.empty.withEffs(effs)
          else {
            assert(pots.size == 1)
            (Warm(cls, pots.head)(expr).toPots, effs)
          }
        }

      case Typed(expr, tpt) =>
        if (tpt.tpe.hasAnnotation(defn.UncheckedAnnot)) Summary.empty
        else analyze(expr)

      case NamedArg(name, arg) =>
        analyze(arg)

      case Assign(lhs, rhs) =>
        val (pots, effs) = analyze(rhs)
        (Potentials.empty, pots.promote(expr) ++ effs)

      case closureDef(ddef) =>     // must be before `Block`
        val (pots, effs) = analyze(ddef.rhs)
        Summary.empty + Fun(pots, effs)(expr)

      case Block(stats, expr) =>
        val effs = stats.foldLeft(Effects.empty) { (acc, stat) => acc ++ analyze(stat)._2 }
        val (pots2, effs2) = analyze(expr)
        (pots2, effs ++ effs2)

      case If(cond, thenp, elsep) =>
        val (pots0, effs0) = analyze(cond)
        val (pots1, effs1) = analyze(thenp)
        val (pots2, effs2) = analyze(elsep)
        (pots0 ++ pots1 ++ pots2, effs0 ++ effs1 ++ effs2)

      case Annotated(arg, annot) =>
        if (expr.tpe.hasAnnotation(defn.UncheckedAnnot)) Summary.empty
        else analyze(arg)

      case Match(selector, cases) =>
        // possible for switches
        val (pots, effs) = analyze(selector)
        cases.foldLeft((Potentials.empty, pots.promote(selector) ++ effs)) { (acc, cas) =>
          acc union analyze(cas.body)
        }

      // case CaseDef(pat, guard, body) =>
      //   Summary.empty

      case Return(expr, from) =>
        // TODO: return potential to the method
        val (pots, effs) = analyze(expr)
        (Potentials.empty, effs)

      case WhileDo(cond, body) =>
        // for lazy fields, the translation may result im `while (<empty>)`
        val (_, effs1) = if (cond.isEmpty) Summary.empty else analyze(cond)
        val (_, effs2) = analyze(body)
        (Potentials.empty, effs1 ++ effs2)

      case Labeled(_, expr) =>
        val (_, effs1) = analyze(expr)
        (Potentials.empty, effs1)
<commit_msg>Enforce that only initialized values may be returned<commit_after>package dotty.tools.dotc
package transform
package init

import core._
import Contexts.Context
import Decorators._
import StdNames._
import Symbols._
import Constants.Constant
import Types._

import ast.tpd._
import config.Printers.init
import reporting.trace

import Effects._, Potentials._, Summary._, Util._

object Summarization {

  /** Summarization of potentials and effects for an expression
   *
   *  Optimization:
   *
   *   1. potentials for expression of primitive value types can be
   *      safely abandoned, as they are always fully initialized.
   */
  def analyze(expr: Tree)(implicit env: Env): Summary =
  trace("summarizing " + expr.show, init, s => Summary.show(s.asInstanceOf[Summary])) {
    val summary: Summary = expr match {
      case Ident(nme.WILDCARD) =>
        // TODO:  disallow `var x: T = _`
        Summary.empty

      case Ident(name) =>
        assert(name.isTermName, "type trees should not reach here")
        analyze(expr.tpe, expr)

      case supert: Super =>
        val SuperType(thisTp, superTp) = supert.tpe.asInstanceOf[SuperType]
        val thisRef = ThisRef(thisTp.widen.classSymbol.asClass)(supert)
        val pot = SuperRef(thisRef, superTp.classSymbol.asClass)(supert)
        Summary.empty + pot

      case Select(qualifier, name) =>
        val (pots, effs) = analyze(qualifier)
        if (env.ignoredMethods.contains(expr.symbol)) (Potentials.empty, effs)
        else {
          val (pots2, effs2) = pots.select(expr.symbol, expr)
          (pots2, effs ++ effs2)
        }

      case _: This =>
        // With self type, the type can be `A & B`.
        def classes(tp: Type): Set[ClassSymbol] = tp.widen match {
          case AndType(tp1, tp2)  =>
            classes(tp1) ++ classes(tp2)

          case tp =>
            Set(tp.classSymbol.asClass)
        }

        val pots: Potentials = classes(expr.tpe).map{ ThisRef(_)(expr) }
        (pots, Effects.empty)

      case Apply(fun, args) =>
        val summary = analyze(fun)
        val ignoredCall = env.ignoredMethods.contains(expr.symbol)

        val res = args.foldLeft(summary) { (sum, arg) =>
          val (pots1, effs1) = analyze(arg)
          if (ignoredCall) sum.withEffs(effs1)
          else sum.withEffs(pots1.promote(arg) ++ effs1)
        }

        if (ignoredCall) (Potentials.empty, res._2)
        else res

      case TypeApply(fun, args) =>
        analyze(fun)

      case Literal(const) =>
        Summary.empty

      case New(tpt) =>
        def typeRefOf(tp: Type): TypeRef = tp.typeConstructor match {
          case tref: TypeRef => tref
          case hklambda: HKTypeLambda => typeRefOf(hklambda.resType)
        }

        val tref = typeRefOf(tpt.tpe)
        val cls = tref.classSymbol.asClass
        // local class may capture, thus we need to track it
        if (tref.prefix == NoPrefix) {
          val enclosingCls = cls.enclosingClass.asClass
          val thisRef = ThisRef(enclosingCls)(expr)
          Summary.empty + Warm(cls, thisRef)(expr)
        }
        else {
          val (pots, effs) = analyze(tref.prefix, expr)
          if (pots.isEmpty) Summary.empty.withEffs(effs)
          else {
            assert(pots.size == 1)
            (Warm(cls, pots.head)(expr).toPots, effs)
          }
        }

      case Typed(expr, tpt) =>
        if (tpt.tpe.hasAnnotation(defn.UncheckedAnnot)) Summary.empty
        else analyze(expr)

      case NamedArg(name, arg) =>
        analyze(arg)

      case Assign(lhs, rhs) =>
        val (pots, effs) = analyze(rhs)
        (Potentials.empty, pots.promote(expr) ++ effs)

      case closureDef(ddef) =>     // must be before `Block`
        val (pots, effs) = analyze(ddef.rhs)
        Summary.empty + Fun(pots, effs)(expr)

      case Block(stats, expr) =>
        val effs = stats.foldLeft(Effects.empty) { (acc, stat) => acc ++ analyze(stat)._2 }
        val (pots2, effs2) = analyze(expr)
        (pots2, effs ++ effs2)

      case If(cond, thenp, elsep) =>
        val (pots0, effs0) = analyze(cond)
        val (pots1, effs1) = analyze(thenp)
        val (pots2, effs2) = analyze(elsep)
        (pots0 ++ pots1 ++ pots2, effs0 ++ effs1 ++ effs2)

      case Annotated(arg, annot) =>
        if (expr.tpe.hasAnnotation(defn.UncheckedAnnot)) Summary.empty
        else analyze(arg)

      case Match(selector, cases) =>
        // possible for switches
        val (pots, effs) = analyze(selector)
        cases.foldLeft((Potentials.empty, pots.promote(selector) ++ effs)) { (acc, cas) =>
          acc union analyze(cas.body)
        }

      // case CaseDef(pat, guard, body) =>
      //   Summary.empty

      case Return(expr, from) =>
        val (pots, effs) = analyze(expr)
        (Potentials.empty, effs ++ pots.promote(expr))

      case WhileDo(cond, body) =>
        // for lazy fields, the translation may result im `while (<empty>)`
        val (_, effs1) = if (cond.isEmpty) Summary.empty else analyze(cond)
        val (_, effs2) = analyze(body)
        (Potentials.empty, effs1 ++ effs2)

      case Labeled(_, expr) =>
        val (_, effs1) = analyze(expr)
        (Potentials.empty, effs1)

{'fraction_non_alphanumeric': 0.12063492063492064, 'fraction_numerical': 0.007503607503607504, 'mean_word_length': 2.2877925363693863, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 63, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '6719894', 'n_tokens_mistral': 3431, 'n_tokens_neox': 3339, 'n_words': 828}