.NAME Throw or break in switch clauses

.DESCRIPTION
An unconditional throw or break statement shall 
terminate every non-empty switch-clause. 

.COMMENT V7.0

.SEVERITY Required

.COMMENT
Please refer to the MISRA C++:2008 document for further details (www.misra-cpp.com)

.COMMAND tcl

.CODE

::logiscope::include -once common utilities.tcl

proc compute {standardName tree} {
  variable CurrentParameterList
  # Check all ... nodes
  utilities::filterTree $tree [list [namespace current]::check]
}

proc check { node } {
  # Look for a switch
  set curOperator [::logiscope::treeOperator $node]
  set curOperatorName [::logiscope::opName $curOperator]
  if { [::utilities::isTreeOperator $node switch] } {
    # Parse the switch contents to check the rule
	set switchStmts [::logiscope::treeDown $node 1]
	set switchStmtsCompound [::logiscope::treeDown $switchStmts 0]
	set switchContents [::logiscope::treeDown $switchStmtsCompound 0]	
	set caseStart 0
	set caseFinish 0
	set caseEmpty 1
	set idx 0
	set foundThrow 0
	set foundBreak 0

	::logiscope::forobj curChild $switchContents treeChild {
	  set curName [::utilities::getName $curChild]
	  if {[expr [lsearch "case default" $curName] != -1]} {
		# A case or default is found
	    if {[expr $caseStart == 0]} {
		  # If the previous case was closed (startCase=0), we just reinitialize the flags
		  set caseStart 1
		  set foundThrow 0
		  set foundBreak 0
		} elseif {[expr $caseStart == 1]} {
		  # If the previous case was not closed (caseStart=1), there is a potential violation
		  if {[expr $caseEmpty == 0]} {
		    # A violation can only be detected if the previous case was not empty, creating a "drop through" situation
			set previousChild [::logiscope::treeDown $switchContents [expr $idx -1]]
		    # First check if the previous child (which must be an expr_stmt) does not have a throw statement as last element	
			if { [::utilities::isTreeOperator $previousChild expr_stmt] } {
				if { [::utilities::isTreeOperator [::logiscope::treeDown $previousChild 0] throw] } {
				  # In this case, we found a throw in the last statement of the previous case
				  set foundThrow 1
				  set caseStart 1
				}
			}
			if {[expr $foundThrow == 0]} {
			  # Here, the previous case was not closed, and didn't finish with a throw.
			  # The violation is on the previous child
			  ::logiscope::addViolation $previousChild
			  set caseEmpty 1
			}
			set foundThrow 0
		  }
		}
	  } elseif {[string equal $curName break]} {
		# A break is found
	    if {[expr $caseStart == 1]} {
		  # Finding a break closes the case (caseStart=0)
		  set foundBreak 1
		  set caseStart 0
		  set caseEmpty 1
		  set foundThrow 0
		}
	  } elseif {![string equal $curName empty_stmt]} {
		# Something not being a case, default, break or empty statement is found
	    if {[expr $caseStart == 1]} {
		  # We now know that the case contains statements
		  set caseEmpty 0
		}
		# If a break was found, and a statement follows, the rule is violated 
		if {[expr $foundBreak == 1]} {
		  ::logiscope::addViolation $curChild
		}
	  }
	  incr idx
	}
	
	# We check here the last element of the switch
	if {[expr $caseStart == 1]} {
		# First check if the previous child (which must be an expr_stmt) does not have a throw statement as last element	
		if { [::utilities::isTreeOperator [::logiscope::treeDown $switchContents [expr $idx -1]] expr_stmt] } {
			if { [::utilities::isTreeOperator [::logiscope::treeDown [::logiscope::treeDown $switchContents [expr $idx -1]] 0] throw] } {
			  set foundThrow 1
			}
		}
	  if {[expr $foundThrow != 1]} {
	  set lastChild [::logiscope::treeDown $switchContents [expr $idx -1]]
	  ::logiscope::addViolation $lastChild
	  }
	}
  }
  return 1
}
