def hasNoSideEffects()

in scala/scala-impl/src/org/jetbrains/plugins/scala/util/SideEffectsUtil.scala [61:145]


  def hasNoSideEffects(expr: ScExpression): Boolean = hasNoSideEffectsInner(expr)(allowThrows = false)

  def hasNoSideEffectsItself(expr: ScExpression): Boolean =
    hasNoSideEffectsInner(expr, asArg = false, checkSubExpression = false)(allowThrows = false)

  def mayOnlyThrow(expr: ScExpression): Boolean = hasNoSideEffectsInner(expr)(allowThrows = true)

  private def hasNoSideEffectsInner(expr: ScExpression)(implicit allowThrows: Boolean): Boolean =
    hasNoSideEffectsInner(expr, asArg = false)

  private def hasNoSideEffectsInner(expr: ScExpression,
                                    asArg: Boolean,
                                    checkSubExpression: Boolean = true)(implicit allowThrows: Boolean): Boolean = {

    expr match {
      case lit: ScInterpolatedStringLiteral =>
        import ScInterpolatedStringLiteral._
        lit.kind match {
          case Standard |
               Format |
               Raw => true
          case _ => false
        }
      case _: ScLiteral => true
      case _: ScThisReference => true
      case und: ScUnderscoreSection if und.bindingExpr.isEmpty => true
      case ScParenthesisedExpr(inner) => !checkSubExpression || hasNoSideEffectsInner(inner)
      case typed: ScTypedExpression => (!checkSubExpression && !typed.hasAnnotation) || hasNoSideEffectsInner(typed.expr)
      case ref: ScReferenceExpression =>
        if (hasImplicitConversion(ref)) false
        else {
          ref.qualifier.forall(hasNoSideEffectsInner) && (ref.resolve() match {
            case (_: ScBindingPattern) & ScalaPsiUtil.inNameContext(pd: ScPatternDefinition)
              if pd.hasModifierProperty("lazy") => false
            case bp: ScBindingPattern =>
              val tp = bp.`type`()
              !(asArg && FunctionType.isFunctionType(tp.getOrAny))
            case _: ScObject => true // not correct, but very likely that a lone object-ref has no sideeffect
            case p: ScParameter if p.isCallByNameParameter => false
            case p: ScParameter if !(asArg && FunctionType.isFunctionType(p.insideParamType.getOrAny)) => true
            case _: ScSyntheticFunction => true
            case m: PsiMethod => methodHasNoSideEffects(m, ref.qualifier.flatMap(_.`type`().toOption))
            case _ => false
          })
        }
      case t: ScTuple => !checkSubExpression || t.exprs.forall(hasNoSideEffectsInner)
      case nt: ScNamedTuple => !checkSubExpression || nt.components.forall(_.expr.forall(hasNoSideEffectsInner))
      case inf: ScInfixExpr if inf.isAssignmentOperator => false
      case call@ScSugarCallExpr(baseExpr, operation, args) =>
        val checkOperation = operation match {
          case ref if hasImplicitConversion(ref) => false
          case ref if ref.refName.endsWith("_=") => false
          case ResolvesTo(fun: ScSyntheticFunction) => syntheticMethodHasNoSideEffects(fun)
          case ResolvesTo(m: PsiMethod) => methodHasNoSideEffects(m, baseExpr.`type`().toOption)
          case _ => false
        }
        checkOperation &&
          (!checkSubExpression || hasNoSideEffectsInner(baseExpr)) &&
          argsHaveNoSideEffectInner(call, args, checkSubExpression)
      case call@ScMethodCall(baseExpr, args) =>
        val (checkQual, typeOfQual) = baseExpr match {
          case ScReferenceExpression.withQualifier(qual) => (hasNoSideEffectsInner(qual), qual.`type`().toOption)
          case _ => (true, None)
        }
        val checkBaseExpr = baseExpr match {
          case _ if hasImplicitConversion(baseExpr) => false
          case _: ScUnderscoreSection => false
          case Resolved(rr) if rr.isAssignment => false
          case ResolvesTo(m: PsiMethod) => methodHasNoSideEffects(m, typeOfQual)
          case ResolvesTo(fun: ScSyntheticFunction) => syntheticMethodHasNoSideEffects(fun)
          case ResolvesTo(_: ScTypedDefinition) =>
            val withApplyText = baseExpr.getText + ".apply" + args.map(_.getText).mkString("(", ", ", ")")
            val withApply = ScalaPsiElementFactory.createExpressionWithContextFromText(withApplyText, expr.getContext, expr)
            withApply match {
              case ScMethodCall(ResolvesTo(m: PsiMethod), _) =>
                methodHasNoSideEffects(m, typeOfQual)
              case _ => false
            }
          case _ => hasNoSideEffectsInner(baseExpr)
        }
        checkQual && checkBaseExpr && argsHaveNoSideEffectInner(call, args, checkSubExpression)
      case _: ScNewTemplateDefinition => false
      case _ => false
    }
  }