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
}
}