.NAME non-volatile variable with one use

.DESCRIPTION
	A project shall not contain non-volatile POD variables having only one use. 

.COMMENT V7.0

.SEVERITY Required

.COMMENT
Please refer to the MISRA C++:2008 document for further details (www.misra-cpp.com)
Remark: if a variable is only initalized and not used in the code,
it is considered as not used (rule MISRA0-1-3) and not as used only one time.

.COMMAND tcl

.REQUIRED cpp_semantic

.CODE

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

proc compute {standardName tree} {
  # Initialize the variables as unused
	if {[utilities::isFirstFile]} {
		initUsedVariables
	}

	# Check all ... nodes
	utilities::filterTree $tree [list [namespace current]::check]
  
	# Look for unused variables
	if {[utilities::isLastFile]} {
		checkUnusedVariables  
	}
}

proc initUsedVariables {} {
	foreach curScope [::Scopes::getAllScopes] {
		foreach curVariable [::Semantic::getAttribute $curScope variables] {
			set default 1
			if {[string equal [::Semantic::getAttribute $curVariable defaultValue] ""]} {
				set default 0
			}
			::Semantic::setAttribute $curVariable usedOne $default
		}
	}
}

proc check { node } {
	# checking all nodes associated to an identifier
	switch -- [::utilities::treeOperatorName $node] {
		identifier -
		dot -
		arrow -
		qualified_id {
			# searching associated semantic object
			set semObj [::Semantic::getSemanticObject $node]
			if { $semObj != -1 } {
				# if variable, then setting it as used
				if { "[::Semantic::getAttribute $semObj kind]" == "variable"} {
					::Semantic::incrAttribute $semObj usedOne
				}
				if { [::utilities::isTreeOperator $node dot] || [::utilities::isTreeOperator $node arrow] } {
					check [::logiscope::treeDown $node 0]
				}
				return 0
			}
		}
	}
	return 1
}

# Parse all variables of all scopes, and add violation for each unused variable (i.e declared but not used)
proc checkUnusedVariables {} {
	foreach curScope [::Scopes::getAllScopes] {
		set check 1
		if { [::Scopes::isScopeKind $curScope FUNCTION] } {
			if { [lsearch [::Semantic::getAttribute $curScope qualifier] extern] >= 0 ||
				 ![::Semantic::exists $curScope withStatements] ||
				 [::Semantic::getAttribute $curScope withStatements] == 0 } {
				 set check 0
			}
			set check 0
		}
		if { $check } {
			foreach curVar [::Semantic::getAttribute $curScope variables] {
				if {[::Semantic::hasSoftlink $curVar]} {
					continue
				}
				if {[isVolatile $curVar] || ![isPOD $curVar]} {
					continue
				}
				if {![::Semantic::exists $curVar usedOne] ||
				    [::Semantic::getAttribute $curVar name] eq ""} {
					continue
				}
				set usage [::Semantic::getAttribute $curVar usedOne]
				set classStatic 0
				if {[::Semantic::exists $curVar qualifier] &&
					[lsearch -exact [::Semantic::getAttribute $curVar qualifier] static] >= 0 &&
					[::Semantic::exists $curVar scope] &&
					[::Scopes::isScopeKind [::Semantic::getAttribute $curVar scope] CLASS]} {
					set classStatic 1
				}
				# if current scope is FILE then look at included files
				if { [::Scopes::isScopeKind $curScope FILE] } {
					set varName [::Semantic::getAttribute $curVar name]
					set includeList [::Semantic::getAttribute $curScope includes]
					foreach sc $includeList {
						set res [::Scopes::getWithName $sc variables $varName -1]
						if { $res != -1 } {
							if { [::Semantic::exists $res usedOne] } {
								incr usage [::Semantic::getAttribute $res usedOne]
							}
							break
						}
					}
				}
				if { $usage == 2 + $classStatic } {
					# Violation $filename $pos $end $numLine $standardName "message" 
					::Semantic::violation $curVar "MISRA_0_1_4" "MISRA_0_1_4 - Every defined variable shall be used."
				}
			}
		}
	}
}

proc isVolatile {varId} {
	expr {[::Semantic::exists $varId qualifier] &&
		  [lsearch -exact [::Semantic::getAttribute $varId qualifier] volatile] >= 0}
}

proc isPOD {varId} {
	set type [::Semantic::getAttribute $varId type]
	while {[::Semantic::getAttribute $varId typeKind] eq "Typedef"} {
		set type [::Semantic::getAttribute $type type]
	}
	switch -exact -- [::Semantic::getAttribute $type typeKind] {
		TypeInstanciated {
			return 0
		}
		TypeClass {
			# fall through
		}
		default {
			return 1
		}
	}
	# $type == TypeClass
	set scope [::Semantic::getAttribute $type scopeClass]
	if {[::Semantic::exists $scope functions]} {
		foreach function [::Semantic::getAttribute $scope functions] {
			if {[::Semantic::exists $function pureVirtual]} {
				return 0
			}
			if {[::Semantic::exists $function qualifier] &&
				[lsearch -exact [::Semantic::getAttribute $function qualifier] virtual] >= 0} {
				return 0
			}
			if {[string index [::Semantic::exists $function name] 0] eq "~"} {
				return 0
			}
		}
	}
	return 1
}
