.NAME Use of C++ macros

.DESCRIPTION
	C++ macros shall only be used for include guards, type 
qualifiers, or storage class specifiers. 

.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 #define 
	if { [::utilities::isTreeOperator $node pp_define] } {
		# Macros with args violate the rule
		set pp_macro_args [::logiscope::treeDown $node 1]
		if { [::utilities::isTreeOperator $pp_macro_args pp_macro_args] } {
			# macro with parameters => not allowed
			::logiscope::addViolation $node
			return 0
		}
		# If macros don't have args, look at their value
		set pp_text [::logiscope::treeDown $node 2]
		set content ""
		::logiscope::forobj l $pp_text treeChild {
			set content $content[::utilities::treeAtomString $l]
		}		
		set content [string trim [suppressCommentsOnly $content]]
		# If the maccro value is empty, or a storage class specifier, or a type qualifier, the rule is not violated
		# In any other case, it is violated
		if {[expr [string length $content] != 0]} {
			if {[expr [lsearch [list auto register static extern const volatile restrict] $content] == -1]} {
				::logiscope::addViolation $node
			}
		}
	}
	return 1
}

# suppress comments only in Java, C and C++ line of code
proc suppressCommentsOnly { str } {
	set res ""
	set state KEEP
	for { set i 0 } { $i < [string length $str] } { incr i } {
		set char [string range $str $i $i]
		set twoChars [string range $str $i [expr $i + 1]]
		switch -- $state {
			KEEP {
				switch -- $char {
					"/" {
						if { "$twoChars" == "//" } {
							set i [string length $str]
						} else {
							if { "$twoChars" == "/*" } {
								set state INCOM
							} else {
								set res "$res$char"
							}
						}
					}
					default {
						set res "$res$char"
					}
				}
			}
			INCOM {
				if { "$char" == "\\" } {
					set res "$res$twoChars"
					incr i
				} else {
					if { "$twoChars" == "*/" } {
						set state KEEP
						incr i
					}
				}
			}
		}
	}
	return $res
}
