All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.nawforce.apexlink.cst.stmts.ControlPatterns.scala Maven / Gradle / Ivy
/*
* Copyright (c) 2022 FinancialForce.com, inc. All rights reserved.
*/
package com.nawforce.apexlink.cst.stmts
import com.nawforce.apexlink.cst.{BlockVerifyContext, CST, ExprContext}
import com.nawforce.apexlink.names.TypeNames
import com.nawforce.pkgforce.path.PathLocation
trait ControlPath {
val returns: Boolean
val unreachable: Boolean
val location: Option[PathLocation]
}
sealed case class UnknownPath(unreachable: Boolean, location: Option[PathLocation])
extends ControlPath {
override val returns = false
}
sealed case class StatementPath(
returns: Boolean,
unreachable: Boolean,
location: Option[PathLocation],
exitsBlock: Boolean
) extends ControlPath
sealed case class BlockPath(
returns: Boolean,
unreachable: Boolean,
location: Option[PathLocation],
failedPaths: Array[ControlPath],
enters: Boolean
) extends ControlPath
sealed trait ControlPattern {
def addControlPath(context: BlockVerifyContext, cst: CST): ControlPath
protected def nextPathUnreachable(context: BlockVerifyContext): Boolean = {
val paths = context.getPaths
val nonVoid = context.returnType != TypeNames.Void
!context.hasBranchingControl && (paths.lastOption.exists(_.unreachable) || paths.exists {
// some block returns depend on entry to the block
// we cannot be certain in all cases
case s: StatementPath => s.returns || s.exitsBlock
case b: BlockPath => nonVoid && b.enters && b.returns
case _ => false // unknown stmts/blocks
})
}
}
// default
object NoControlPattern extends ControlPattern {
def addControlPath(context: BlockVerifyContext, cst: CST): ControlPath = {
context.addPath(UnknownPath(nextPathUnreachable(context), cst.safeLocation))
}
}
// continue, break / return, throw
class ExitControlPattern(method: Boolean, block: Boolean) extends ControlPattern {
def addControlPath(context: BlockVerifyContext, cst: CST): ControlPath = {
context.addPath(StatementPath(method, nextPathUnreachable(context), cst.safeLocation, block))
}
}
object ExitControlPattern {
def apply(exitsMethod: Boolean, exitsBlock: Boolean): ExitControlPattern = {
new ExitControlPattern(exitsMethod, exitsBlock)
}
}
// default blocks (methods etc.)
class BlockControlPattern extends ControlPattern {
override def addControlPath(context: BlockVerifyContext, cst: CST): ControlPath = {
val paths = context.getPaths
val parent = context.parentFlowContext
val blockUnreachable = parent.exists(p => nextPathUnreachable(p))
val path = if (paths.length == 0 || blockUnreachable) {
BlockPath(returns = false, blockUnreachable, cst.safeLocation, Array(), enters = false)
} else {
val failedPaths = Option
.unless(context.returnType == TypeNames.Void) { collectFailedPaths(context, paths) }
.getOrElse(Array())
BlockPath(
willBlockReturn(paths, failedPaths),
blockUnreachable,
cst.safeLocation,
failedPaths,
willEnterBlock(context)
)
}
parent.foreach(_.addPath(path))
path
}
protected def collectFailedPaths(
context: BlockVerifyContext,
paths: Array[ControlPath]
): Array[ControlPath] = {
val path = paths
.filterNot(_.unreachable)
.last
if (path.returns) {
// block is valid
Array[ControlPath]()
} else {
handlePathFailure(path)
}
}
protected def willEnterBlock(context: BlockVerifyContext): Boolean = false
protected def willBlockReturn(
paths: Array[ControlPath],
failedPaths: Array[ControlPath]
): Boolean =
failedPaths.isEmpty
protected def handlePathFailure(path: ControlPath): Array[ControlPath] = {
// failed inner paths take precedence
path match {
case BlockPath(_, _, _, failed, _) if failed.length > 0 => failed
case _ => Array(path)
}
}
}
object BlockControlPattern {
def apply(): BlockControlPattern = {
new BlockControlPattern()
}
}
// if/else, try/catch
class BranchControlPattern(requiredBranches: Array[Boolean], exclusive: Boolean)
extends BlockControlPattern {
override protected def collectFailedPaths(
context: BlockVerifyContext,
paths: Array[ControlPath]
): Array[ControlPath] = {
if (paths.length != requiredBranches.length) {
// missing branches
Array()
} else {
paths
.zip(requiredBranches)
.collect {
case (path, required) if required && !path.returns => path
}
.flatMap(handlePathFailure)
}
}
override protected def willEnterBlock(context: BlockVerifyContext): Boolean = exclusive
override protected def willBlockReturn(
paths: Array[ControlPath],
failedPaths: Array[ControlPath]
): Boolean = {
// make sure empty failed paths is genuine
super.willBlockReturn(paths, failedPaths) && paths.length == requiredBranches.length
}
}
object BranchControlPattern {
def apply(requiredBranches: Array[Boolean]): BranchControlPattern = {
new BranchControlPattern(requiredBranches, false)
}
def apply(expr: Option[ExprContext], requiredBranches: Int): BranchControlPattern = {
new BranchControlPattern(Array.fill(requiredBranches)(true), true)
}
}