.NAME Test of floating point expressions

.DESCRIPTION
 Floating-point expressions shall not be directly or 
indirectly tested for equality or inequality. 

.COMMENT V7.0

.SEVERITY Required

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

.COMMAND tcl

.REQUIRED cpp_semantic

.CODE

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

proc compute {standardName tree} {
  variable CurrentParameterList
  if { [llength $CurrentParameterList] == 0 } {
	set CurrentParameterList [list float double "long double" float32_t]
  }
  # Check all ... nodes
  utilities::filterTree $tree [list [namespace current]::check]
}

proc isFloat { type } {
  variable CurrentParameterList
  
  if { $type == -1 } {
	return 0
  }
  while { "[::Semantic::getAttribute $type typeKind]" == "Typedef" } {
	set type [::Semantic::getAttribute $type type]
  }
  if { [lsearch $CurrentParameterList [::Semantic::getAttribute $type name]] >= 0 } {
	return 1
  }
  return 0
}

proc checkExpr { node op1 op2 } {
	set check 0
	set firstExpr [::logiscope::treeDown $node 0]
	while { [::utilities::isTreeOperator $firstExpr parenth_expr] } {
		set firstExpr [::logiscope::treeDown $firstExpr 0]
	}
	set secondExpr [::logiscope::treeDown $node 1]
	while { [::utilities::isTreeOperator $secondExpr parenth_expr] } {
		set secondExpr [::logiscope::treeDown $secondExpr 0]
	}
	set firstOperator [::utilities::treeOperatorName $firstExpr]
	set secondOperator [::utilities::treeOperatorName $secondExpr]
	if { ( "$firstOperator" == "$op1" && "$secondOperator" == "$op2" ) ||
	     ( "$firstOperator" == "$op1" && "$secondOperator" == "$op1" ) ||
	     ( "$firstOperator" == "$op2" && "$secondOperator" == "$op1" ) ||
	     ( "$firstOperator" == "$op2" && "$secondOperator" == "$op2" ) } {
		set ident1Node [::logiscope::treeDown $firstExpr 0]
		while { [::utilities::isTreeOperator $ident1Node parenth_expr] } {
			set ident1Node [::logiscope::treeDown $ident1Node 0]
		}
		set ident1 [::utilities::getName $ident1Node]
		set ident1OtherNode [::logiscope::treeDown $secondExpr 0]
		while { [::utilities::isTreeOperator $ident1OtherNode parenth_expr] } {
			set ident1OtherNode [::logiscope::treeDown $ident1OtherNode 0]
		}
		set ident1Other [::utilities::getName $ident1OtherNode]
		set ident2Node [::logiscope::treeDown $firstExpr 1]
		while { [::utilities::isTreeOperator $ident2Node parenth_expr] } {
			set ident2Node [::logiscope::treeDown $ident2Node 0]
		}
		set ident2 [::utilities::getName $ident2Node]
		set ident2OtherNode [::logiscope::treeDown $secondExpr 1]
		while { [::utilities::isTreeOperator $ident2OtherNode parenth_expr] } {
			set ident2OtherNode [::logiscope::treeDown $ident2OtherNode 0]
		}
		set ident2Other [::utilities::getName $ident2OtherNode]
		if { "$firstOperator" == "$op1" && "$secondOperator" == "$op2" } {
			if { "$ident1" == "$ident1Other" && "$ident2" == "$ident2Other" } {
				set check 1
			}
		} elseif { "$firstOperator" == "$op1" && "$secondOperator" == "$op1" } {
			if { "$ident1" == "$ident2Other" && "$ident2" == "$ident1Other" } {
				set check 1
			}
		} elseif { "$firstOperator" == "$op2" && "$secondOperator" == "$op1" } {
			if { "$ident1" == "$ident1Other" && "$ident2" == "$ident2Other" } {
				set check 1
			}
		} else {
			if { "$ident1" == "$ident2Other" && "$ident2" == "$ident1Other" } {
				set check 1
			}
		}
		if { $check } {
			set violation 0
			set type1 [::Semantic::getTypeOf $ident1Node]
			if { [isFloat $type1] } {
				set violation 1
			} else {
				set type2 [::Semantic::getTypeOf $ident2Node]
				if { [isFloat $type2] } {
					set violation 1
				}
			}
			if { $violation } {
				::logiscope::addViolation $node
			}
		}
	}
}

proc check { node } {
	set violation 0
	switch -- [::utilities::treeOperatorName $node] {
		eq -
		neq {
			set leftPart [::logiscope::treeDown $node 0]
			set leftType [::Semantic::getTypeOf $leftPart]
			if { [isFloat $leftType] } {
				set violation 1
			} else {
				set rightPart [::logiscope::treeDown $node 1]
				set rightType [::Semantic::getTypeOf $rightPart]
				if { [isFloat $rightType] } {
					set violation 1
				}
			}
			if { $violation } {
				::logiscope::addViolation $node
			}
		}
		and {
			# four forms are not allowed:
			# x <= y && x >= y
			# x <= y && y <= x
			# x >= y && x <= y
			# x >= y && y >= x
			checkExpr $node le ge
		}
		or {
			# four forms are not allowed:
			# x < y || x > y
			# x < y || y < x
			# x > y || x < y
			# x > y || y > x
			checkExpr $node lt gt
		}
	}
	return 1
}
