diff --git a/formatting/cmake/Findvera++.cmake b/formatting/cmake/Findvera++.cmake new file mode 100644 index 0000000..c58360e --- /dev/null +++ b/formatting/cmake/Findvera++.cmake @@ -0,0 +1,52 @@ +# - try to find vera++ tool +# +# Cache Variables: +# VERA++_ROOT_DIR +# VERA++_EXECUTABLE +# VERA++_USE_FILE +# +# Non-cache variables you might use in your CMakeLists.txt: +# VERA++_FOUND +# +# Requires these CMake modules: +# FindPackageHandleStandardArgs (known included with CMake >=2.6.2) + +file(TO_CMAKE_PATH "${VERA++_ROOT_DIR}" VERA++_ROOT_DIR) +set(VERA++_ROOT_DIR + "${VERA++_ROOT_DIR}" + CACHE + PATH + "Path to search for vera++") + +if(VERA++_EXECUTABLE AND NOT EXISTS "${VERA++_EXECUTABLE}") + set(VERA++_EXECUTABLE "notfound" CACHE PATH FORCE "") +endif() + +# If we have a custom path, look there first. +if(VERA++_ROOT_DIR) + find_program(VERA++_EXECUTABLE + NAMES + vera++ + PATHS + "${VERA++_ROOT_DIR}" + PATH_SUFFIXES + bin + NO_DEFAULT_PATH) +endif() + +find_program(VERA++_EXECUTABLE NAMES vera++) + +# Find the use file for vera +GET_FILENAME_COMPONENT(VERA++_MODULE_DIR ${CMAKE_CURRENT_LIST_FILE} PATH) +SET(VERA++_USE_FILE "${VERA++_MODULE_DIR}/use_vera++.cmake") + +SET(VERA++_ALL ${VERA++_EXECUTABLE} ${VERA++_USE_FILE}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(vera++ + DEFAULT_MSG + VERA++_ALL + VERA++_EXECUTABLE + VERA++_USE_FILE) + +mark_as_advanced(VERA++_EXECUTABLE) diff --git a/formatting/cmake/use_vera++.cmake b/formatting/cmake/use_vera++.cmake new file mode 100644 index 0000000..106e58d --- /dev/null +++ b/formatting/cmake/use_vera++.cmake @@ -0,0 +1,342 @@ +# +# use_vera++.cmake +# +# The following functions are defined in this document: +# - ADD_VERA_TARGETS +# - ADD_VERA_CHECKSTYLE_TARGET +# + +# ADD_VERA_TARGETS( ... +# [ADD_TO_ALL] +# [NAME ] +# [NAME_ALL ] +# [ROOT ] +# [PROFILE ] +# [RECURSE] +# [EXCLUSION ...] +# [PARAMETER = ...] +# [PARAMETERFILE ...]) +# +# Two custom targets will be created: +# * style_reports is run as part of the build, and is not rerun unless one of +# the file checked is modified (only created if ADD_TO_ALL is provided); +# * style must be explicitely called (make style) and is rerun even if the files +# to check have not been modified. To achieve this behavior, the commands used +# in this target pretend to produce a file without actually producing it. +# Because the output file is not there after the run, the command will be rerun +# again at the next target build. +# The report style is selected based on the build environment, so the style +# problems are properly reported in the IDEs +# +# If ADD_TO_ALL is provided then a target will be added to the default build +# targets so that each time a source file is compiled, it is analyzed with +# vera++. +# +# NAME and NAME_ALL customize the name of the targets (style and style_reports +# by default respectively). +# +# ROOT set the vera++ root directory, containing the rules and profiles (default +# to the current binary directory). +# +# PROFILE selects the vera++ profile to use (default to "default"). +# +# RECURSE selects if the glob expressions should be applied recursively or not. +# +# EXCLUSION list of vera++ exclusion files. Can be used several times. +# +# PARAMETER list of vera++ parameters (name=value). Can be used several times. +# +# PARAMETERFILE list of vera++ parameter files. Can be used several times. +function(add_vera_targets) + # default values + set(target "style") + set(target_all "style_reports") + set(profile "default") + set(root "${CMAKE_CURRENT_BINARY_DIR}") + set(exclusions) + set(parameters) + set(parameterFiles) + set(recurse OFF) + set(addToAll OFF) + set(globs) + # parse the options + math(EXPR lastIdx "${ARGC} - 1") + set(i 0) + while(i LESS ${ARGC}) + set(arg "${ARGV${i}}") + if("${arg}" STREQUAL "ADD_TO_ALL") + set(addToAll ON) + elseif("${arg}" STREQUAL "NAME") + vera_incr(i) + set(target "${ARGV${i}}") + elseif("${arg}" STREQUAL "NAME_ALL") + vera_incr(i) + set(target_all "${ARGV${i}}") + elseif("${arg}" STREQUAL "ROOT") + vera_incr(i) + set(root "${ARGV${i}}") + elseif("${arg}" STREQUAL "PROFILE") + vera_incr(i) + set(profile "${ARGV${i}}") + elseif("${arg}" STREQUAL "EXCLUSION") + vera_incr(i) + list(APPEND exclusions --exclusions "${ARGV${i}}") + elseif("${arg}" STREQUAL "RECURSE") + set(recurse ON) + elseif("${arg}" STREQUAL "PARAMETER") + vera_incr(i) + list(APPEND parameters --parameter "${ARGV${i}}") + elseif("${arg}" STREQUAL "PARAMETERFILE") + vera_incr(i) + list(APPEND parameterFiles --parameters "${ARGV${i}}") + else() + list(APPEND globs ${arg}) + endif() + vera_incr(i) + endwhile() + + if(recurse) + file(GLOB_RECURSE srcs ${globs}) + else() + file(GLOB srcs ${globs}) + endif() + list(SORT srcs) + + if(NOT VERA++_EXECUTABLE AND TARGET vera) + set(vera_program "$") + else() + set(vera_program "${VERA++_EXECUTABLE}") + endif() + + # Two custom targets will be created: + # * style_reports is run as part of the build, and is not rerun unless one of + # the file checked is modified; + # * style must be explicitely called (make style) and is rerun even if the files + # to check have not been modified. To achieve this behavior, the commands used + # in this target pretend to produce a file without actually producing it. + # Because the output file is not there after the run, the command will be rerun + # again at the next target build. + # The report style is selected based on the build environment, so the style + # problems are properly reported in the IDEs + if(MSVC) + set(style vc) + else() + set(style std) + endif() + set(xmlreports) + set(noreports) + set(reportNb 0) + set(reportsrcs) + list(GET srcs 0 first) + get_filename_component(currentDir ${first} PATH) + # add a fake src file in a fake dir to trigger the creation of the last + # custom command + list(APPEND srcs "#12345678900987654321#/0987654321#1234567890") + # Create the directory where the reports will be saved + SET(reportDirectory "${CMAKE_CURRENT_BINARY_DIR}/vera") + FILE(MAKE_DIRECTORY ${reportDirectory}) + foreach(s ${srcs}) + get_filename_component(d ${s} PATH) + if(NOT "${d}" STREQUAL "${currentDir}") + # this is a new dir - lets generate everything needed for the previous dir + string(LENGTH "${CMAKE_SOURCE_DIR}" len) + string(SUBSTRING "${currentDir}" 0 ${len} pre) + if("${pre}" STREQUAL "${CMAKE_SOURCE_DIR}") + string(SUBSTRING "${currentDir}" ${len} -1 currentDir) + string(REGEX REPLACE "^/" "" currentDir "${currentDir}") + endif() + if("${currentDir}" STREQUAL "") + set(currentDir ".") + endif() + set(xmlreport ${reportDirectory}/vera_report_${reportNb}.xml) + if(addToAll) + add_custom_command( + OUTPUT ${xmlreport} + COMMAND ${vera_program} + --root ${root} + --profile ${profile} + --${style}-report=- + --show-rule + --warning + --xml-report=${xmlreport} + ${exclusions} + ${parameters} + ${parameterFiles} + ${reportsrcs} + DEPENDS ${reportsrcs} + COMMENT "Checking style with vera++ in ${currentDir}" + ) + endif() + + set(noreport ${reportDirectory}/vera_noreport_${reportNb}.xml) + add_custom_command( + OUTPUT ${noreport} + COMMAND ${vera_program} + --root ${root} + --profile ${profile} + --${style}-report=- + --show-rule + --warning + # --xml-report=${noreport} + ${exclusions} + ${parameters} + ${parameterFiles} + ${reportsrcs} + DEPENDS ${reportsrcs} + COMMENT "Checking style with vera++ in ${currentDir}" + ) + + list(APPEND xmlreports ${xmlreport}) + list(APPEND noreports ${noreport}) + vera_incr(reportNb) + # clear the list for the next dir + set(reportsrcs) + set(currentDir ${d}) + endif() + list(APPEND reportsrcs ${s}) + endforeach() + # Create the custom targets that will trigger the custom command created + # previously + if(addToAll) + add_custom_target(${target_all} ALL DEPENDS ${xmlreports}) + endif() + add_custom_target(${target} DEPENDS ${noreports}) +endfunction() + + +# ADD_VERA_CHECKSTYLE_TARGET( ... +# [NAME ] +# [ROOT ] +# [PROFILE ] +# [RECURSE] +# [EXCLUSION ...] +# [PARAMETER = ...] +# [PARAMETERFILE ...]) +# +# The checkstyle custom target will be created. This target runs vera++ and +# create checkstyle reports in the ${CMAKE_CURRENT_BINARY_DIR}/checkstyle +# directory. +# +# NAME customize the name of the target (checkstyle by default). +# +# ROOT set the vera++ root directory, containing the rules and profiles (default +# to the current binary directory). +# +# PROFILE selects the vera++ profile to use (default to "default"). +# +# RECURSE selects if the glob expressions should be applied recursively or not. +# +# EXCLUSION list of vera++ exclusion files. Can be used several times. +# +# PARAMETER list of vera++ parameters (name=value). Can be used several times. +# +# PARAMETERFILE list of vera++ parameter files. Can be used several times. +function(add_vera_checkstyle_target) + # default values + set(target "checkstyle") + set(profile "default") + set(root "${CMAKE_CURRENT_BINARY_DIR}") + set(exclusions) + set(parameters) + set(parameterFiles) + set(recurse OFF) + set(globs) + # parse the options + math(EXPR lastIdx "${ARGC} - 1") + set(i 0) + while(i LESS ${ARGC}) + set(arg "${ARGV${i}}") + if("${arg}" STREQUAL "NAME") + vera_incr(i) + set(target "${ARGV${i}}") + elseif("${arg}" STREQUAL "ROOT") + vera_incr(i) + set(root "${ARGV${i}}") + elseif("${arg}" STREQUAL "PROFILE") + vera_incr(i) + set(profile "${ARGV${i}}") + elseif("${arg}" STREQUAL "EXCLUSION") + vera_incr(i) + list(APPEND exclusions --exclusions "${ARGV${i}}") + elseif("${arg}" STREQUAL "RECURSE") + set(recurse ON) + elseif("${arg}" STREQUAL "PARAMETER") + vera_incr(i) + list(APPEND parameters --parameter "${ARGV${i}}") + elseif("${arg}" STREQUAL "PARAMETERFILE") + vera_incr(i) + list(APPEND parameterFiles --parameters "${ARGV${i}}") + else() + list(APPEND globs ${arg}) + endif() + vera_incr(i) + endwhile() + + if(recurse) + file(GLOB_RECURSE srcs ${globs}) + else() + file(GLOB srcs ${globs}) + endif() + list(SORT srcs) + + if(NOT VERA++_EXECUTABLE AND TARGET vera) + set(vera_program "$") + else() + set(vera_program "${VERA++_EXECUTABLE}") + endif() + + set(checkstylereports) + set(reportNb 0) + set(reportsrcs) + list(GET srcs 0 first) + get_filename_component(currentDir ${first} PATH) + # add a fake src file in a fake dir to trigger the creation of the last + # custom command + list(APPEND srcs "#12345678900987654321#/0987654321#1234567890") + # Create the directory where the reports will be saved + SET(checkstyleDirectory "${CMAKE_CURRENT_BINARY_DIR}/checkstyle") + FILE(MAKE_DIRECTORY ${checkstyleDirectory}) + foreach(s ${srcs}) + get_filename_component(d ${s} PATH) + if(NOT "${d}" STREQUAL "${currentDir}") + # this is a new dir - lets generate everything needed for the previous dir + string(LENGTH "${CMAKE_SOURCE_DIR}" len) + string(SUBSTRING "${currentDir}" 0 ${len} pre) + if("${pre}" STREQUAL "${CMAKE_SOURCE_DIR}") + string(SUBSTRING "${currentDir}" ${len} -1 currentDir) + string(REGEX REPLACE "^/" "" currentDir "${currentDir}") + endif() + if("${currentDir}" STREQUAL "") + set(currentDir ".") + endif() + set(checkstylereport ${checkstyleDirectory}/vera_checkstylereport_${reportNb}.xml) + add_custom_command( + OUTPUT ${checkstylereport} + COMMAND ${vera_program} + --root ${root} + --profile ${profile} + --show-rule + --checkstyle-report ${checkstylereport} + ${exclusions} + ${parameters} + ${parameterFiles} + ${reportsrcs} + DEPENDS ${reportsrcs} + COMMENT "Checking style with vera++ in ${currentDir}" + ) + list(APPEND checkstylereports ${checkstylereport}) + vera_incr(reportNb) + # clear the list for the next dir + set(reportsrcs) + set(currentDir ${d}) + endif() + list(APPEND reportsrcs ${s}) + endforeach() + # Create the custom targets that will trigger the custom command created + # previously + add_custom_target(${target} DEPENDS ${checkstylereports}) +endfunction() + +macro(vera_incr var_name) + math(EXPR ${var_name} "${${var_name}} + 1") +endmacro() diff --git a/formatting/vera-root/profiles/default b/formatting/vera-root/profiles/default new file mode 100644 index 0000000..e687df4 --- /dev/null +++ b/formatting/vera-root/profiles/default @@ -0,0 +1,26 @@ +#!/usr/bin/tclsh +# This file defines the set of scripts (rules) that should be executed +# by default (if no specific profile is named when vera++ is launched). + +set rules { + IPSIS_F11 + IPSIS_F13 + + IPSIS_S01 + IPSIS_S02 + IPSIS_S03 + IPSIS_S04_FILENAME + IPSIS_S04_NAMESPACE + IPSIS_S04_CLASS + IPSIS_S04_ENUM + IPSIS_S04_TYPEDEF + IPSIS_S04_METHOD + IPSIS_S04_VARIABLE + IPSIS_S05 + IPSIS_S06 + IPSIS_S09 + + IPSIS_C01 + IPSIS_C09 + IPSIS_C11 +} diff --git a/formatting/vera-root/profiles/sciqlop b/formatting/vera-root/profiles/sciqlop new file mode 100644 index 0000000..91cb0a1 --- /dev/null +++ b/formatting/vera-root/profiles/sciqlop @@ -0,0 +1,26 @@ +#!/usr/bin/tclsh +# This file defines the set of scripts (rules) that should be executed for a +# default IPSIS project. + +set rules { + IPSIS_F11 + IPSIS_F13 + + IPSIS_S01 + IPSIS_S02 + IPSIS_S03 + IPSIS_S04_FILENAME + IPSIS_S04_NAMESPACE + IPSIS_S04_CLASS + IPSIS_S04_ENUM + IPSIS_S04_TYPEDEF + IPSIS_S04_METHOD + IPSIS_S04_VARIABLE + IPSIS_S05 + IPSIS_S06 + IPSIS_S09 + + IPSIS_C01 + IPSIS_C09 + IPSIS_C11 +} diff --git a/formatting/vera-root/scripts/rules/DUMP.tcl b/formatting/vera-root/scripts/rules/DUMP.tcl new file mode 100644 index 0000000..5047074 --- /dev/null +++ b/formatting/vera-root/scripts/rules/DUMP.tcl @@ -0,0 +1,13 @@ +#!/usr/bin/tclsh +foreach f [getSourceFileNames] { + puts "Tokens in file ${f}:" + foreach t [getTokens $f 1 0 -1 -1 {}] { + set value [lindex $t 0] + set line [lindex $t 1] + set column [lindex $t 2] + set name [lindex $t 3] + + puts "${line}/${column}\t${name}\t${value}" + } + puts "" +} diff --git a/formatting/vera-root/scripts/rules/IPSIS_C01.tcl b/formatting/vera-root/scripts/rules/IPSIS_C01.tcl new file mode 100644 index 0000000..67cd389 --- /dev/null +++ b/formatting/vera-root/scripts/rules/IPSIS_C01.tcl @@ -0,0 +1,65 @@ +#!/usr/bin/tclsh +# The destructor of a class should be virtual + +proc createMachine {initState} { + set machine [dict create state $initState bracesCounter 0] + return $machine +} + +foreach fileName [getSourceFileNames] { + set machines [list] + + set prev1 "" + set prev2 "" + foreach token [getTokens $fileName 1 0 -1 -1 {class struct leftbrace rightbrace virtual compl identifier semicolon}] { + set type [lindex $token 3] + set line [lindex $token 1] + + if {$type == "class" || $type == "struct"} { + lappend machines [createMachine "beforeLeftBrace"] + } + + set machinesToKeep [list] + foreach m $machines { + set keepMachine 1 + dict with m { + if {$state == "beforeLeftBrace"} { + if {$type == "leftbrace"} { + set state "root" + } elseif {$type == "semicolon"} { + set keepMachine 0 + } + } elseif {$state == "root"} { + if {$prev2 != "virtual" && $prev1 == "compl" && $type == "identifier"} { + set dtorName [lindex $token 0] + report $fileName $line "The destructor ~${dtorName}() of the class should be virtual" + } + + if {$type == "leftbrace"} { + incr bracesCounter + set state "consumeBraces" + } elseif {$type == "rightbrace"} { + set keepMachine 0 + } + } elseif {$state == "consumeBraces"} { + if {$type == "leftbrace"} { + incr bracesCounter + } elseif {$type == "rightbrace"} { + incr bracesCounter -1 + if {$bracesCounter == 0} { + set state "root" + } + } + } + } + + if {$keepMachine} { + lappend machinesToKeep $m + } + } + set machines $machinesToKeep + + set prev2 $prev1 + set prev1 $type + } +} diff --git a/formatting/vera-root/scripts/rules/IPSIS_C09.tcl b/formatting/vera-root/scripts/rules/IPSIS_C09.tcl new file mode 100644 index 0000000..e706c6b --- /dev/null +++ b/formatting/vera-root/scripts/rules/IPSIS_C09.tcl @@ -0,0 +1,23 @@ +#!/usr/bin/tclsh +# using namespace are not allowed in header files + +foreach fileName [getSourceFileNames] { + set extension [file extension $fileName] + if {[lsearch {.h .hh .hpp .hxx .ipp} $extension] != -1} { + + set state "start" + foreach token [getTokens $fileName 1 0 -1 -1 {using namespace identifier}] { + set type [lindex $token 3] + + if {$state == "using" && $type == "namespace"} { + report $fileName $usingLine "using namespace not allowed in header file" + } + + if {$type == "using"} { + set usingLine [lindex $token 1] + } + + set state $type + } + } +} diff --git a/formatting/vera-root/scripts/rules/IPSIS_C11.tcl b/formatting/vera-root/scripts/rules/IPSIS_C11.tcl new file mode 100644 index 0000000..26ba357 --- /dev/null +++ b/formatting/vera-root/scripts/rules/IPSIS_C11.tcl @@ -0,0 +1,103 @@ +#!/usr/bin/tclsh +# Exceptions should be thrown by value and catched by reference. +# Moreover a rethrow must be done with throw; + +proc createThrowMachine {line initState} { + set machine [dict create name "throw" keywordLine $line state $initState bracesCounter 0 firstKeyword ""] + return $machine +} + +proc createCatchMachine {line initState} { + set machine [dict create name "catch" keywordLine $line state $initState bracesCounter 0 catchedException ""] + return $machine +} + +foreach fileName [getSourceFileNames] { + set machines [list] + + set lastIdentifier "" + set prev1 "" + set prev2 "" + set prev3 "" + foreach token [getTokens $fileName 1 0 -1 -1 {throw catch new leftbrace rightbrace greater and identifier rightparen semicolon}] { + set type [lindex $token 3] + set line [lindex $token 1] + + if {$type == "identifier"} { + set lastIdentifier [lindex $token 0] + } + + if {$type == "throw"} { + lappend machines [createThrowMachine $line "beforeThrow"] + } elseif {$type == "catch"} { + lappend machines [createCatchMachine $line "waitingForLeftBrace"] + } + + set machinesToKeep [list] + foreach m $machines { + set keepMachine 1 + dict with m { + if {$name == "throw"} { + if {$state == "beforeThrow" && $type == "throw"} { + set state "catchFirstKeyword" + } elseif {$state == "catchFirstKeyword"} { + if {$type == "semicolon"} { + # This is a rethrow + set keepMachine 0 + } else { + set firstKeyword $type + set state "waitForEndOfThrow" + } + } elseif {$state == "waitForEndOfThrow"} { + if {$type == "leftbrace"} { + # This is an exception specification + set keepMachine 0 + } elseif {$type == "semicolon"} { + # This is a throw, check that the first keyword isn't + # new or & + if {$firstKeyword == "new" || $firstKeyword == "and"} { + report $fileName $keywordLine "Exceptions should be thrown by value. Not allocated with new or dereferenced with &." + } + set keepMachine 0 + } + } + } elseif {$name == "catch"} { + if {$state == "waitingForLeftBrace" && $type == "leftbrace"} { + set state "insideCatch" + + if {$prev2 == "identifier" && $prev1 == "rightparen"} { + set catchedException $lastIdentifier + + if {$prev3 != "and"} { + report $fileName $keywordLine "Exceptions should be catched by reference (exception catched: $catchedException)" + } + } + } elseif {$state == "insideCatch"} { + if {$type == "leftbrace"} { + incr bracesCounter + } elseif {$type == "rightbrace"} { + if {$bracesCounter > 0} { + incr bracesCounter -1 + } else { + set keepMachine 0 + } + } elseif {$prev2 == "throw" && $prev1 == "identifier" && $type == "semicolon"} { + if {$lastIdentifier == $catchedException} { + report $fileName $line "Exceptions should be rethrown with 'throw;' instead of 'throw ${catchedException};'" + } + } + } + } + } + + if {$keepMachine} { + lappend machinesToKeep $m + } + } + set machines $machinesToKeep + + set prev3 $prev2 + set prev2 $prev1 + set prev1 $type + } +} diff --git a/formatting/vera-root/scripts/rules/IPSIS_F11.tcl b/formatting/vera-root/scripts/rules/IPSIS_F11.tcl new file mode 100644 index 0000000..7c87f07 --- /dev/null +++ b/formatting/vera-root/scripts/rules/IPSIS_F11.tcl @@ -0,0 +1,39 @@ +#!/usr/bin/tclsh +# control structures should have complete curly-braced block of code + +foreach fileName [getSourceFileNames] { + + set state "start" + set prev "" + foreach token [getTokens $fileName 1 0 -1 -1 {for if while do leftparen rightparen leftbrace rightbrace semicolon}] { + set type [lindex $token 3] + + if {$state == "control"} { + if {$type == "leftparen"} { + incr parenCount + } elseif {$type == "rightparen"} { + incr parenCount -1 + if {$parenCount == 0} { + set state "expectedblock" + } + } + } elseif {$state == "expectedblock"} { + if {$type != "leftbrace"} { + set line [lindex $token 1] + report $fileName $line "full block {} expected in the control structure" + } + set state "block" + } + + if {$type == "for" || $type == "if"} { + set parenCount 0 + set state "control" + } elseif {$type == "do"} { + set state "expectedblock" + } elseif {$type == "while" && $prev != "rightbrace"} { + set parenCount 0 + set state "control" + } + set prev $type + } +} diff --git a/formatting/vera-root/scripts/rules/IPSIS_F13.tcl b/formatting/vera-root/scripts/rules/IPSIS_F13.tcl new file mode 100644 index 0000000..0c68946 --- /dev/null +++ b/formatting/vera-root/scripts/rules/IPSIS_F13.tcl @@ -0,0 +1,73 @@ +#!/usr/bin/tclsh +# namespace names should be recalled at the end of the namespace +# namespace mynamespace { +# } // mynamespace + +proc createNamespaceDict {} { + return [dict create state "waitingForIdentifier" identifier "" bracesCounter 0] +} + +foreach fileName [getSourceFileNames] { + + set namespaces [list] + + foreach token [getTokens $fileName 1 0 -1 -1 {namespace identifier leftbrace rightbrace semicolon cppcomment}] { + set type [lindex $token 3] + + set namespacesToKeep [list] + foreach n $namespaces { + set keepNamespace 1 + dict with n { + if {$state == "waitingForIdentifier" && $type == "identifier"} { + set state "waitingForLeftBrace" + set identifier [lindex $token 0] + } elseif {$state == "waitingForLeftBrace"} { + if {$type == "semicolon"} { + # Wasn't a namespace, remove the dict + set keepNamespace 0 + } elseif {$type == "leftbrace"} { + set bracesCounter 0 + set state "waitingForRightBrace" + } + } elseif {$state == "waitingForRightBrace"} { + if {$type == "leftbrace"} { + incr bracesCounter + } elseif {$type == "rightbrace"} { + if {$bracesCounter > 0} { + incr bracesCounter -1 + } else { + set state "waitingForComment" + } + } + } elseif {$state == "waitingForComment"} { + if {$type == "cppcomment"} { + set commentValue [lindex $token 0] + # Check that the comment report the namespace name + if {![regexp "\\m$identifier\\M" $commentValue]} { + set line [lindex $token 1] + set commentValue [string trim $commentValue] + report $fileName $line "The namespace $identifier should have been recalled in the comment here (// $identifier). Comment found: $commentValue" + } + } else { + # There should have been a comment here + set line [lindex $token 1] + report $fileName $line "The namespace $identifier should have been recalled in a comment here (// $identifier)" + } + # Namespace processed, remove it from the list + set keepNamespace 0 + } + } + + if {$keepNamespace} { + lappend namespacesToKeep $n + } + } + set namespaces $namespacesToKeep + + # If the token is a namespace keyword, add a namespace dict for the next + # foreach + if {$type == "namespace"} { + lappend namespaces [createNamespaceDict] + } + } +} diff --git a/formatting/vera-root/scripts/rules/IPSIS_S01.tcl b/formatting/vera-root/scripts/rules/IPSIS_S01.tcl new file mode 100644 index 0000000..3bd8fa3 --- /dev/null +++ b/formatting/vera-root/scripts/rules/IPSIS_S01.tcl @@ -0,0 +1,60 @@ +#!/usr/bin/tclsh +# At most one class per header file + +foreach fileName [getSourceFileNames] { + if {[regexp {(?:\\|/)?([^\\/]+?)\.h$} $fileName matchedExpr expectedClassName]} { + set state "waitingForClass" + set alreadyOneClass 0 + set firstClassName "" + set bracesCounter 0 + + set lastIdentifier "" + foreach token [getTokens $fileName 1 0 -1 -1 {class identifier leftbrace rightbrace colon semicolon}] { + set type [lindex $token 3] + + if {$type == "identifier"} { + set lastIdentifier [lindex $token 0] + } + + if {$state == "waitingForClass" && $type == "class"} { + set state "waitingForBeginingOfClass" + } elseif {$state == "waitingForBeginingOfClass"} { + if {$type == "semicolon"} { + set state "waitingForClass" + } elseif {$type == "leftbrace" || $type == "colon"} { + # Check if this is the first class + if {$alreadyOneClass} { + set line [lindex $token 1] + report $fileName $line "At most one public class can be defined in a header file. (class $lastIdentifier found but class $firstClassName already defined)" + } else { + set alreadyOneClass 1 + set firstClassName $lastIdentifier + + # Check that the class has the same name than the file + if {$lastIdentifier != $expectedClassName} { + set line [lindex $token 1] + report $fileName $line "The public class should have the same name than the header file. (class $lastIdentifier found but class $expectedClassName expected)" + } + } + if {$type == "leftbrace"} { + set state "waitingForEndOfClass" + } else { + set state "waitingForLeftBrace" + } + } + } elseif {$state == "waitingForLeftBrace" && $type == "leftbrace"} { + set state "waitingForEndOfClass" + } elseif {$state == "waitingForEndOfClass"} { + if {$type == "leftbrace"} { + incr bracesCounter + } elseif {$type == "rightbrace"} { + if {$bracesCounter > 0} { + incr bracesCounter -1 + } else { + set state "waitingForClass" + } + } + } + } + } +} diff --git a/formatting/vera-root/scripts/rules/IPSIS_S02.tcl b/formatting/vera-root/scripts/rules/IPSIS_S02.tcl new file mode 100644 index 0000000..b90b3e7 --- /dev/null +++ b/formatting/vera-root/scripts/rules/IPSIS_S02.tcl @@ -0,0 +1,45 @@ +#!/usr/bin/tclsh +# The include guards should have the form PROJECT_FILENAME_H + +set projectName [string toupper [getParameter "project-name" "PROJECTNAMENOTFOUND"]] + +foreach fileName [getSourceFileNames] { + if {[regexp {(?:\\|/)?([^\\/]+?)\.h$} $fileName matchedExpr fileNameWithoutExt]} { + set upperFileName [string toupper $fileNameWithoutExt] + set expectedGuard "${projectName}_${upperFileName}_H" + + set state "waitingForIfndef" + + set firstInstruction 1 + set ifndefLine -1 + set prev "" + foreach token [getTokens $fileName 1 0 -1 -1 {pp_ifndef pp_ifdef pp_if pp_include pp_define pp_undef pp_error pp_pragma pp_endif identifier}] { + set type [lindex $token 3] + set line [lindex $token 1] + + if {$firstInstruction} { + set firstInstruction 0 + if {$type != "pp_ifndef"} { + report $fileName $line "The first preprocessor instruction of a header file should be the include guard of the form PROJECTNAME_FILENAME_H. (expected: $expectedGuard)" + break + } else { + set ifndefLine $line + } + } + + if {$prev == "pp_ifndef"} { + if {$type == "identifier"} { + set identifier [lindex $token 0] + if {$identifier != $expectedGuard} { + report $fileName $ifndefLine "The include guard should have the form PROJECTNAME_FILENAME_H. (Found $identifier, expected $expectedGuard)" + } + } else { + report $fileName $ifndefLine "The first preprocessor instruction of a header file should be the include guard of the form PROJECTNAME_FILENAME_H. (expected: $expectedGuard)" + } + break + } + + set prev $type + } + } +} diff --git a/formatting/vera-root/scripts/rules/IPSIS_S03.tcl b/formatting/vera-root/scripts/rules/IPSIS_S03.tcl new file mode 100644 index 0000000..6b24e84 --- /dev/null +++ b/formatting/vera-root/scripts/rules/IPSIS_S03.tcl @@ -0,0 +1,25 @@ +#!/usr/bin/tclsh +# The macros in header files should begin with the name of the project to avoid +# collisions + +set projectName [string toupper [getParameter "project-name" "PROJECTNAMENOTFOUND"]] + +foreach fileName [getSourceFileNames] { + if {[regexp {\.h$} $fileName]} { + + set prev "" + foreach token [getTokens $fileName 1 0 -1 -1 {pp_define identifier}] { + set type [lindex $token 3] + + if {$prev == "pp_define" && $type == "identifier"} { + set identifier [lindex $token 0] + if {![regexp "^${projectName}.*$" $identifier]} { + set line [lindex $token 1] + report $fileName $line "The macros in header files should begin with the name of the project to avoid collisions. (project name: $projectName, macro found: $identifier)" + } + } + + set prev $type + } + } +} diff --git a/formatting/vera-root/scripts/rules/IPSIS_S04_CLASS.tcl b/formatting/vera-root/scripts/rules/IPSIS_S04_CLASS.tcl new file mode 100644 index 0000000..83ed5aa --- /dev/null +++ b/formatting/vera-root/scripts/rules/IPSIS_S04_CLASS.tcl @@ -0,0 +1,124 @@ +#!/usr/bin/tclsh +# Naming conventions for classes and structs + +set classRegex [getParameter "class-regex" {^[A-Z][A-Za-z1-9]*$}] +set structRegex [getParameter "struct-regex" {^[A-Z][A-Za-z1-9]*$}] + +proc createMachine {machineName initState} { + set machine [dict create name $machineName state $initState identifier "" bracesCounter 0 bracketCounter 0 angleBracketCounter 0] + + return $machine +} + +proc isClassMacro {value} { + set classMacroRegexp [getParameter "classmacro-regex" {}] + set classMacros { + "Q_OBJECT" + } + set isClassMacroByRegexp 0 + if {[string length $classMacroRegexp] != 0} { + set isClassMacroByRegexp [regexp $classMacroRegexp $value] + } + return [expr ([lsearch $classMacros $value] != -1) || $isClassMacroByRegexp] +} + +proc isCppType {type value} { + set cppTypes { + "bool" + "char" + "int" + "float" + "double" + "void" + "wchart" + "identifier" + } + + set valueIsClassMacro 0 + if {$type == "identifier"} { + set valueIsClassMacro [isClassMacro $value] + } + + return [expr ([lsearch $cppTypes $type] != -1) && !$valueIsClassMacro] +} + +set tokenFilter { + class + struct + leftbrace + rightbrace + semicolon + colon + identifier + comma + assign + rightparen + leftbracket +} + +foreach fileName [getSourceFileNames] { + + set machines [list] + + set lastIdentifier "" + set lastIdentifier2 "" + set prev1 "" + set prev2 "" + foreach token [getTokens $fileName 1 0 -1 -1 $tokenFilter] { + set type [lindex $token 3] + set line [lindex $token 1] + + # Retrieve identifier value + if {$type == "identifier"} { + set lastIdentifier2 $lastIdentifier + set lastIdentifier [lindex $token 0] + } + + # If the type is a class or a struct, start a class/struct state machine + if {$type == "class"} { + lappend machines [createMachine "class" "waitingForIdentifier"] + } + if {$type == "struct"} { + lappend machines [createMachine "struct" "waitingForIdentifier"] + } + + set machinesToKeep [list] + foreach m $machines { + set keepMachine 1 + dict with m { + # class/struct + if {$name == "class" || $name == "struct"} { + # We retrieve the name of the class when we find a colon or + # a leftbrace. We wait for these tokens to avoid the export + # macros + if {$state == "waitingForIdentifier" && ($type == "colon" || $type == "leftbrace")} { + set state "waitingForLeftBrace" + set identifier $lastIdentifier + } + + if {$state == "waitingForLeftBrace" && $type == "leftbrace"} { + if {$name == "class" && ![regexp $classRegex $identifier]} { + report $fileName $line "The class names should match the following regex: $classRegex (found: $identifier)" + } + if {$name == "struct" && ![regexp $structRegex $identifier]} { + report $fileName $line "The struct names should match the following regex: $structRegex (found: $identifier)" + } + } + + if {[lsearch {semicolon leftbrace comma assign rightparen leftbracket} $type] != -1} { + # End of the state machine + set keepMachine 0 + } + } + } + + if {$keepMachine} { + lappend machinesToKeep $m + } + } + set machines $machinesToKeep + + set prev2 $prev1 + set prev1 $type + } +} diff --git a/formatting/vera-root/scripts/rules/IPSIS_S04_ENUM.tcl b/formatting/vera-root/scripts/rules/IPSIS_S04_ENUM.tcl new file mode 100644 index 0000000..24fcdbb --- /dev/null +++ b/formatting/vera-root/scripts/rules/IPSIS_S04_ENUM.tcl @@ -0,0 +1,41 @@ +#!/usr/bin/tclsh +# Naming conventions for enums + +set enumRegex [getParameter "enum-regex" {^[A-Z][A-Za-z1-9]*$}] + +set tokenFilter { + class + struct + enum + typedef + identifier + leftbrace + rightbrace + semicolon +} + +foreach fileName [getSourceFileNames] { + + set lastIdentifier "" + set prev1 "" + set prev2 "" + foreach token [getTokens $fileName 1 0 -1 -1 $tokenFilter] { + set type [lindex $token 3] + set line [lindex $token 1] + + # Retrieve identifier value + if {$type == "identifier"} { + set lastIdentifier [lindex $token 0] + } + + # Check enum + if {$prev2 == "enum" && $prev1 == "identifier" && $type == "leftbrace"} { + if {![regexp $enumRegex $lastIdentifier]} { + report $fileName $line "The enum names should match the following regex: $enumRegex (found: $lastIdentifier)" + } + } + + set prev2 $prev1 + set prev1 $type + } +} diff --git a/formatting/vera-root/scripts/rules/IPSIS_S04_FILENAME.tcl b/formatting/vera-root/scripts/rules/IPSIS_S04_FILENAME.tcl new file mode 100644 index 0000000..12ecd77 --- /dev/null +++ b/formatting/vera-root/scripts/rules/IPSIS_S04_FILENAME.tcl @@ -0,0 +1,13 @@ +#!/usr/bin/tclsh +# Naming conventions for file names + +set fileNameRegex [getParameter "filename-regex" {^[A-Z]\w*$}] + +foreach fileName [getSourceFileNames] { + # Check file name + if {[regexp {(?:\\|/)?([^\\/]+?)\.(?:h|cpp)$} $fileName matchedExpr fileNameWithoutExt]} { + if {![regexp $fileNameRegex $fileNameWithoutExt]} { + report $fileName 1 "The file names should match the following regex: $fileNameRegex (found: $fileNameWithoutExt)" + } + } +} diff --git a/formatting/vera-root/scripts/rules/IPSIS_S04_METHOD.tcl b/formatting/vera-root/scripts/rules/IPSIS_S04_METHOD.tcl new file mode 100644 index 0000000..94cea25 --- /dev/null +++ b/formatting/vera-root/scripts/rules/IPSIS_S04_METHOD.tcl @@ -0,0 +1,152 @@ +#!/usr/bin/tclsh +# Naming conventions for methods + +set methodRegex [getParameter "method-regex" {^[a-z][A-Za-z1-9]*$}] + +proc createMachine {machineName initState} { + set machine [dict create name $machineName state $initState identifier "" bracesCounter 0 bracketCounter 0 angleBracketCounter 0] + return $machine +} + +proc isClassMacro {value} { + set classMacroRegexp [getParameter "classmacro-regex" {}] + set classMacros { + "Q_OBJECT" + } + set isClassMacroByRegexp 0 + if {[string length $classMacroRegexp] != 0} { + set isClassMacroByRegexp [regexp $classMacroRegexp $value] + } + return [expr ([lsearch $classMacros $value] != -1) || $isClassMacroByRegexp] +} + +proc isCppType {type value} { + set cppTypes { + "bool" + "char" + "int" + "float" + "double" + "void" + "wchart" + "identifier" + } + + set valueIsClassMacro 0 + if {$type == "identifier"} { + set valueIsClassMacro [isClassMacro $value] + } + + return [expr ([lsearch $cppTypes $type] != -1) && !$valueIsClassMacro] +} + +set tokenFilter { + namespace + class + struct + leftbrace + rightbrace + leftparen + rightparen + less + greater + semicolon + identifier + colon_colon + assign + pp_define + bool + char + int + float + double + void + wchart +} + +foreach fileName [getSourceFileNames] { + + set machines [list] + + # Check the functions at the root of the file + lappend machines [createMachine "method" "root"] + + set lastIdentifier "" + set lastIdentifier2 "" + set prev1 "" + set prev2 "" + foreach token [getTokens $fileName 1 0 -1 -1 $tokenFilter] { + set type [lindex $token 3] + set line [lindex $token 1] + + # Retrieve identifier value + if {$type == "identifier"} { + set lastIdentifier2 $lastIdentifier + set lastIdentifier [lindex $token 0] + } + + # Start a method state machine when coming accross a namespace or a + # class/struct + if {$type == "namespace" || $type == "class" || $type == "struct"} { + lappend machines [createMachine "method" "beforeLeftBrace"] + } + + set machinesToKeep [list] + foreach m $machines { + set keepMachine 1 + dict with m { + # Method + if {$name == "method"} { + if {$state == "beforeLeftBrace"} { + if {$type == "leftbrace"} { + set state "root" + } elseif {$type == "semicolon"} { + set keepMachine 0 + } + } elseif {$state == "root"} { + if {[isCppType $prev2 $lastIdentifier2] && $prev1 == "identifier" && $type == "leftparen"} { + if {![regexp $methodRegex $lastIdentifier]} { + report $fileName $line "The method names should match the following regex: $methodRegex (found: $lastIdentifier)" + } + } elseif {$type == "leftbrace"} { + set state "consumeBraces" + incr bracesCounter + } elseif {$type == "less"} { + set state "consumeAngleBracket" + incr angleBracketCounter + } elseif {$type == "rightbrace"} { + # End of the state machine + set keepMachine 0 + } + } elseif {$state == "consumeBraces"} { + if {$type == "leftbrace"} { + incr bracesCounter + } elseif {$type == "rightbrace"} { + incr bracesCounter -1 + if {$bracesCounter == 0} { + set state "root" + } + } + } elseif {$state == "consumeAngleBracket"} { + if {$type == "less"} { + incr angleBracketCounter + } elseif {$type == "greater"} { + incr angleBracketCounter -1 + if {$angleBracketCounter == 0} { + set state "root" + } + } + } + } + } + + if {$keepMachine} { + lappend machinesToKeep $m + } + } + set machines $machinesToKeep + + set prev2 $prev1 + set prev1 $type + } +} diff --git a/formatting/vera-root/scripts/rules/IPSIS_S04_NAMESPACE.tcl b/formatting/vera-root/scripts/rules/IPSIS_S04_NAMESPACE.tcl new file mode 100644 index 0000000..81475b0 --- /dev/null +++ b/formatting/vera-root/scripts/rules/IPSIS_S04_NAMESPACE.tcl @@ -0,0 +1,49 @@ +#!/usr/bin/tclsh +# Naming conventions for namespaces + +set namespaceRegex [getParameter "namespace-regex" {^[a-z][a-z1-9]*$}] + +proc createMachine {machineName initState} { + set machine [dict create name $machineName state $initState identifier "" bracesCounter 0 bracketCounter 0 angleBracketCounter 0] + return $machine +} + +set tokenFilter { + using + namespace + identifier + class + struct + enum + typedef + leftbrace + rightbrace +} + +foreach fileName [getSourceFileNames] { + + set machines [list] + + set lastIdentifier "" + set prev1 "" + set prev2 "" + foreach token [getTokens $fileName 1 0 -1 -1 $tokenFilter] { + set type [lindex $token 3] + set line [lindex $token 1] + + # Retrieve identifier value + if {$type == "identifier"} { + set lastIdentifier [lindex $token 0] + } + + # Check namespace + if {$prev2 == "namespace" && $prev1 == "identifier" && $type == "leftbrace"} { + if {![regexp $namespaceRegex $lastIdentifier]} { + report $fileName $line "The namespace names should match the following regex: $namespaceRegex (found: $lastIdentifier)" + } + } + + set prev2 $prev1 + set prev1 $type + } +} diff --git a/formatting/vera-root/scripts/rules/IPSIS_S04_TYPEDEF.tcl b/formatting/vera-root/scripts/rules/IPSIS_S04_TYPEDEF.tcl new file mode 100644 index 0000000..b457d3b --- /dev/null +++ b/formatting/vera-root/scripts/rules/IPSIS_S04_TYPEDEF.tcl @@ -0,0 +1,87 @@ +#!/usr/bin/tclsh +# Naming conventions for typedefs + +set typedefRegex [getParameter "type-regex" {^[A-Z][A-Za-z1-9]*$}] + +proc createMachine {machineName initState} { + set machine [dict create name $machineName state $initState identifier "" bracesCounter 0] + return $machine +} + +set tokenFilter { + typedef + leftbrace + rightbrace + semicolon + identifier +} + +foreach fileName [getSourceFileNames] { + + set machines [list] + + set lastIdentifier "" + set lastIdentifier2 "" + set prev1 "" + set prev2 "" + foreach token [getTokens $fileName 1 0 -1 -1 $tokenFilter] { + set type [lindex $token 3] + set line [lindex $token 1] + + # Retrieve identifier value + if {$type == "identifier"} { + set lastIdentifier2 $lastIdentifier + set lastIdentifier [lindex $token 0] + } + + # If the type is a typedef, start a typedef state machine + if {$type == "typedef"} { + lappend machines [createMachine "typedef" "waitingForIdentifier"] + } + + set machinesToKeep [list] + foreach m $machines { + set keepMachine 1 + dict with m { + # typedef + if {$name == "typedef"} { + if {$state == "waitingForIdentifier"} { + if {$type == "leftbrace"} { + set state "consumeBraces" + incr bracesCounter + } elseif {$type == "semicolon"} { + if {$prev1 == "identifier"} { + # Check identifier + if {![regexp $typedefRegex $lastIdentifier]} { + report $fileName $line "The typedef names should match the following regex: $typedefRegex (found: $lastIdentifier)" + } + } else { + # Typedef without identifier + report $fileName $line "A typedef should have a name" + } + # End of the state machine + set keepMachine 0 + } + } elseif {$state == "consumeBraces"} { + if {$type == "leftbrace"} { + incr bracesCounter + } elseif {$type == "rightbrace"} { + incr bracesCounter -1 + if {$bracesCounter == 0} { + set state "waitingForIdentifier" + } + } + } + } + } + + if {$keepMachine} { + lappend machinesToKeep $m + } + } + set machines $machinesToKeep + + set prev2 $prev1 + set prev1 $type + } +} diff --git a/formatting/vera-root/scripts/rules/IPSIS_S04_VARIABLE.tcl b/formatting/vera-root/scripts/rules/IPSIS_S04_VARIABLE.tcl new file mode 100644 index 0000000..6f23e3f --- /dev/null +++ b/formatting/vera-root/scripts/rules/IPSIS_S04_VARIABLE.tcl @@ -0,0 +1,351 @@ +#!/usr/bin/tclsh +# Naming conventions for variables + +set localVariableRegex [getParameter "local-var-regex" {^[a-z][A-Za-z1-9]*$}] +set classMemberVariableRegex [getParameter "class-member-var-regex" {^m_[A-Z][A-Za-z1-9]*$}] +set structMemberVariableRegex [getParameter "struct-member-var-regex" {^[a-z][A-Za-z1-9]*$}] +set staticMemberVariableRegex [getParameter "static-member-var-regex" {^s_[A-Z][A-Za-z1-9]*$}] +set constVarRegex [getParameter "const-var-regex" {^[A-Z][A-Z1-9_]*$}] + +proc createMachine {machineName initState} { + set machine [dict create name $machineName state $initState previousState "" identifier "" bracesCounter 0 bracketCounter 0 angleBracketCounter 0] + + # Specific keys for the "memberVariable" type + if {$machineName == "memberVariable"} { + dict set machine parenCounter 0 + dict set machine isStatic 0 + dict set machine isConst 0 + dict set machine isVar 1 + dict set machine isTypedef 0 + dict set machine isInClass 0 + dict set machine classOrStruct "" + } + + return $machine +} + +proc isClassMacro {value} { + set classMacroRegexp [getParameter "classmacro-regex" {}] + set classMacros { + "Q_OBJECT" + } + set isClassMacroByRegexp 0 + if {[string length $classMacroRegexp] != 0} { + set isClassMacroByRegexp [regexp $classMacroRegexp $value] + } + return [expr ([lsearch $classMacros $value] != -1) || $isClassMacroByRegexp] +} + +proc isCppType {type value} { + set cppTypes { + "bool" + "char" + "int" + "float" + "double" + "void" + "wchart" + "identifier" + } + + set valueIsClassMacro 0 + if {$type == "identifier"} { + set valueIsClassMacro [isClassMacro $value] + } + + return [expr ([lsearch $cppTypes $type] != -1) && !$valueIsClassMacro] +} + +set tokenFilter { + using + namespace + class + struct + enum + typedef + pp_define + leftbrace + rightbrace + leftparen + rightparen + leftbracket + rightbracket + semicolon + colon_colon + colon + comma + dot + arrow + assign + static + const + identifier + bool + char + int + float + double + void + wchart + return + operator + less + greater +} + +foreach fileName [getSourceFileNames] { + set machines [list] + + # Check the functions at the root of the file + lappend machines [createMachine "method" "root"] + + # Check static const variables at the root of the file + lappend machines [createMachine "memberVariable" "root"] + + set lastIdentifier "" + set lastIdentifier2 "" + set prev1 "" + set prev2 "" + set insideFunctionParameters 0 + set insideFunction 0 + foreach token [getTokens $fileName 1 0 -1 -1 $tokenFilter] { + set type [lindex $token 3] + set line [lindex $token 1] + + # Retrieve identifier value + if {$type == "identifier"} { + set lastIdentifier2 $lastIdentifier + set lastIdentifier [lindex $token 0] + } + + if {$type == "namespace"} { + # Look for the functions in the namespace to check the local variables + lappend machines [createMachine "method" "beforeLeftBrace"] + # Check the static const variables inside this namespace + lappend machines [createMachine "memberVariable" "beforeLeftBrace"] + } elseif {$type == "class" || ($type == "struct" && !$insideFunctionParameters)} { + # Look for the functions in the class/struct to check the local variables + lappend machines [createMachine "method" "beforeLeftBrace"] + # Check the static const variables inside this namespace + set m [createMachine "memberVariable" "beforeLeftBrace"] + dict set m isInClass 1 + dict set m classOrStruct $type + lappend machines $m + } + + set machinesToKeep [list] + foreach m $machines { + set keepMachine 1 + dict with m { + # Method + if {$name == "method"} { + if {$state == "beforeLeftBrace"} { + if {$type == "leftbrace"} { + set state "root" + } elseif {$type == "semicolon"} { + set keepMachine 0 + } + } elseif {$state == "root"} { + if {[isCppType $prev2 $lastIdentifier2] && $prev1 == "identifier" && $type == "leftparen"} { + # Check the local variables inside this function + set insideFunctionParameters 1 + set insideFunction 1 + lappend machinesToKeep [createMachine "localVariable" "beforeLeftBrace"] + + } elseif {$type == "leftbrace"} { + set state "consumeBraces" + incr bracesCounter + } elseif {$type == "rightbrace"} { + # End of the state machine + set keepMachine 0 + } + } elseif {$state == "consumeBraces"} { + if {$type == "leftbrace"} { + incr bracesCounter + } elseif {$type == "rightbrace"} { + incr bracesCounter -1 + if {$bracesCounter == 0} { + set state "root" + } + } + } + } + + # Local variables + if {$name == "localVariable"} { + if {$state == "beforeLeftBrace"} { + if {$type == "less"} { + set previousState $state + set state "consumeAngleBracket" + incr angleBracketCounter + } elseif {[isCppType $prev2 $lastIdentifier2] && $prev1 == "identifier" && ($type == "comma" || $type == "assign" || $type == "rightparen" || $type == "leftbracket")} { + # Check function parameters + if {![regexp $localVariableRegex $lastIdentifier]} { + report $fileName $line "The method parameter names should match the following regex: $localVariableRegex (found: $lastIdentifier)" + } + } elseif {$type == "leftbrace"} { + set insideFunctionParameters 0 + set state "root" + } elseif {$type == "semicolon"} { + # End of the state machine + set insideFunctionParameters 0 + set insideFunction 0 + set keepMachine 0 + } + } elseif {$state == "root"} { + if {$type == "leftbrace"} { + incr bracesCounter + } elseif {$type == "rightbrace"} { + if {$bracesCounter > 0} { + incr bracesCounter -1 + } else { + # End of the state machine + set insideFunctionParameters 0 + set insideFunction 0 + set keepMachine 0 + } + } + + if {[isCppType $prev2 $lastIdentifier2] && $prev1 == "identifier" && ($type == "assign" || $type == "semicolon" || $type == "leftparen" || $type == "leftbracket")} { + # Check local variable + if {![regexp $localVariableRegex $lastIdentifier]} { + report $fileName $line "The local variable names should match the following regex: $localVariableRegex (found: $lastIdentifier)" + } + } + } elseif {$state == "consumeBracket"} { + if {$type == "leftbracket"} { + incr bracketCounter + } elseif {$type == "rightbracket"} { + incr bracketCounter -1 + if {$bracketCounter == 0} { + set state $previousState + } + } + } elseif {$state == "consumeAngleBracket"} { + if {$type == "less"} { + incr angleBracketCounter + } elseif {$type == "greater"} { + incr angleBracketCounter -1 + if {$angleBracketCounter == 0} { + set state $previousState + } + } + } + + if {$state != "consumeBracket" && $type == "leftbracket"} { + set previousState $state + set state "consumeBracket" + incr bracketCounter + } + } + + # Member and const variables + if {$name == "memberVariable"} { + if {$state == "beforeLeftBrace"} { + if {$type == "leftbrace"} { + set state "root" + } elseif {$type == "semicolon"} { + set keepMachine 0 + } + } elseif {$state == "root"} { + # is/isn't var + if {$type == "using" || $type == "class" || $type == "struct"} { + set isVar 0 + } elseif {$type == "typedef"} { + set isTypedef 1 + } + + # static/const + if {$type == "static"} { + set isStatic 1 + } elseif {$type == "const"} { + set isConst 1 + } + + if {$isVar && !$isTypedef && $prev1 == "pp_define" && $type == "identifier"} { + # Check defines + if {![regexp $constVarRegex $lastIdentifier]} { + report $fileName $line "The constant names should match the following regex: $constVarRegex (found: $lastIdentifier)" + } + } elseif {$isVar && !$isTypedef && $prev1 == "identifier" && ($type == "assign" || $type == "semicolon" || $type == "leftbracket")} { + # Check member variables + # Appart from the const, we don't check the member variables outside + # of their classes to avoid false positive + if {((!$isInClass && $isConst) || ($isInClass && $isStatic && $isConst)) && ![regexp $constVarRegex $lastIdentifier]} { + report $fileName $line "The constant names should match the following regex: $constVarRegex (found: $lastIdentifier)" + } elseif {$isInClass && $isStatic && !$isConst && ![regexp $staticMemberVariableRegex $lastIdentifier]} { + report $fileName $line "The static member variable names should match the following regex: $staticMemberVariableRegex (found: $lastIdentifier)" + } elseif {$isInClass && !$isStatic} { + if {$classOrStruct == "class" && ![regexp $classMemberVariableRegex $lastIdentifier]} { + report $fileName $line "The class member variable names should match the following regex: $classMemberVariableRegex (found: $lastIdentifier)" + } elseif {$classOrStruct == "struct" && ![regexp $structMemberVariableRegex $lastIdentifier] && ![regexp $classMemberVariableRegex $lastIdentifier]} { + report $fileName $line "The struct member variable names should match the following regex: $structMemberVariableRegex or the class member regex: $classMemberVariableRegex (found: $lastIdentifier)" + } + } + } elseif {$type == "leftbrace"} { + set state "consumeBraces" + incr bracesCounter + } elseif {$type == "leftparen"} { + set state "consumeParen" + incr parenCounter + } elseif {$type == "rightbrace"} { + # End of the state machine + set keepMachine 0 + } + + if {$type == "leftbracket"} { + incr bracketCounter + set state "consumeBracket" + } + + # Reinit static, const and isVar + if {$type == "assign" || $type == "semicolon" || $type == "leftbrace"} { + set isStatic 0 + set isConst 0 + set isVar 1 + } + + if {$type == "semicolon"} { + set isTypedef 0 + } + } elseif {$state == "consumeBraces"} { + if {$type == "leftbrace"} { + incr bracesCounter + } elseif {$type == "rightbrace"} { + incr bracesCounter -1 + if {$bracesCounter == 0} { + set state "root" + } + } + } elseif {$state == "consumeParen"} { + if {$type == "leftparen"} { + incr parenCounter + } elseif {$type == "rightparen"} { + incr parenCounter -1 + if {$parenCounter == 0} { + set state "root" + } + } + } elseif {$state == "consumeBracket"} { + if {$type == "leftbracket"} { + incr bracketCounter + } elseif {$type == "rightbracket"} { + incr bracketCounter -1 + if {$bracketCounter == 0} { + set state "root" + } + } + } + } + } + + if {$keepMachine} { + lappend machinesToKeep $m + } + } + set machines $machinesToKeep + + set prev2 $prev1 + set prev1 $type + } +} diff --git a/formatting/vera-root/scripts/rules/IPSIS_S05.tcl b/formatting/vera-root/scripts/rules/IPSIS_S05.tcl new file mode 100644 index 0000000..26f5424 --- /dev/null +++ b/formatting/vera-root/scripts/rules/IPSIS_S05.tcl @@ -0,0 +1,329 @@ +#!/usr/bin/tclsh +# Declaration order inside classes + +proc createMachine {visibility initState} { + set machine [dict create visibility $visibility state $initState waitingFor "typedef" previousState "" bracesCounter 0 isStatic 0] + return $machine +} + +proc createVisibilityMachine {} { + set machine [dict create state "beforeLeftBrace" visibilityState "waitingForPublic" bracesCounter 0 visibility ""] + return $machine +} + +set tokenFilter { + public + protected + private + colon + typedef + class + struct + enum + static + const + identifier + operator + assign + semicolon + leftbrace + rightbrace + leftparen + rightparen +} + +set globalOrder "The global order is public members then public slots, protected members then protected slots and private members then private slots." + +foreach fileName [getSourceFileNames] { + set machines [list] + set visibilityMachines [list] + + set visibilityState "waitingForPublic" + + set prev1 "" + set prev2 "" + set lastIdentifier "" + foreach token [getTokens $fileName 1 0 -1 -1 $tokenFilter] { + set type [lindex $token 3] + set line [lindex $token 1] + + if {$type == "identifier"} { + set lastIdentifier [lindex $token 0] + } + + if {$type == "class" || $type == "struct"} { + lappend visibilityMachines [createVisibilityMachine] + } + + if {$type == "colon" && ($prev1 == "public" || $prev1 == "protected" || $prev1 == "private")} { + lappend machines [createMachine $prev1 "beforeVisibility"] + } + + # Machines for the visibility order + set visibilityMachinesToKeep [list] + foreach m $visibilityMachines { + set keepMachine 1 + dict with m { + if {$state == "beforeLeftBrace"} { + if {$type == "semicolon"} { + set keepMachine 0 + } elseif {$type == "leftbrace"} { + set state "root" + } + } elseif {$state == "root"} { + if {$type == "public" || $type == "protected" || $type == "private"} { + set visibility $type + set state "waitingForColon" + } elseif {$type == "leftbrace"} { + incr bracesCounter + set state "consumeBraces" + } elseif {$type == "rightbrace"} { + set keepMachine 0 + } + } elseif {$state == "waitingForColon"} { + if {$type == "colon"} { + if {$visibility == "public" && $prev1 != "identifier"} { + if {$visibilityState == "waitingForPublicSlots" + || $visibilityState == "waitingForProtected" + || $visibilityState == "waitingForProtectedSlots" + || $visibilityState == "waitingForPrivate" + || $visibilityState == "waitingForPrivateSlots" + || $visibilityState == "done"} { + report $fileName $line "There should be at most one public visibility and it should be at the start of the class. $globalOrder" + } else { + set visibilityState "waitingForPublicSlots" + } + } elseif {$visibility == "public" && $prev1 == "identifier" && $lastIdentifier == "slots"} { + if {$visibilityState == "waitingForProtected" + || $visibilityState == "waitingForProtectedSlots" + || $visibilityState == "waitingForPrivate" + || $visibilityState == "waitingForPrivateSlots" + || $visibilityState == "done"} { + report $fileName $line "There should be at most one public slots visibility and it should be between public and protected. $globalOrder" + } else { + set visibilityState "waitingForProtected" + } + } elseif {$visibility == "protected" && $prev1 != "identifier"} { + if {$visibilityState == "waitingForProtectedSlots" + || $visibilityState == "waitingForPrivate" + || $visibilityState == "waitingForPrivateSlots" + || $visibilityState == "done"} { + report $fileName $line "There should be at most one protected visibility and it should be between public slots and protected slots. $globalOrder" + } else { + set visibilityState "waitingForProtectedSlots" + } + } elseif {$visibility == "protected" && $prev1 == "identifier" && $lastIdentifier == "slots"} { + if {$visibilityState == "waitingForPrivate" + || $visibilityState == "waitingForPrivateSlots" + || $visibilityState == "done"} { + report $fileName $line "There should be at most one protected slots visibility and it should be between protected and private. $globalOrder" + } else { + set visibilityState "waitingForPrivate" + } + } elseif {$visibility == "private" && $prev1 != "identifier"} { + if {$visibilityState == "waitingForPrivateSlots" + || $visibilityState == "done"} { + report $fileName $line "There should be at most one private visibility and it should be between protected slots and private slots. $globalOrder" + } else { + set visibilityState "waitingForPrivateSlots" + } + } elseif {$visibility == "private" && $prev1 == "identifier" && $lastIdentifier == "slots"} { + if {$visibilityState == "done"} { + report $fileName $line "There should be at most one private visibility and it should be the last of the class. $globalOrder" + } else { + set visibilityState "done" + } + } + + set state "root" + } + } elseif {$state == "consumeBraces"} { + if {$type == "leftbrace"} { + incr bracesCounter + } elseif {$type == "rightbrace"} { + incr bracesCounter -1 + if {$bracesCounter == 0} { + set state "root" + } + } + } + } + + if {$keepMachine} { + lappend visibilityMachinesToKeep $m + } + } + set visibilityMachines $visibilityMachinesToKeep + + # Machines for the declaration order inside a visibility + set machinesToKeep [list] + foreach m $machines { + set keepMachine 1 + dict with m { + if {$state == "beforeVisibility" && $type == $visibility} { + set state "root" + } elseif {$state == "root"} { + if {$type == "public" || $type == "protected" || $type == "private" || $type == "rightbrace"} { + # End of the state machine when we reach the next + # visibility block + set keepMachine 0 + } else { + if {$type == "static"} { + set isStatic 1 + } + + if {$waitingFor == "typedef"} { + if {$type == "typedef"} { + set state "consumeUntilNextSemiColon" + } else { + set waitingFor "enum" + } + } + + if {$waitingFor == "enum"} { + if {$type == "typedef"} { + set state "consumeUntilNextSemiColon" + report $fileName $line "The typedefs should be before the enums" + } elseif {$type == "enum"} { + set state "consumeUntilNextSemiColon" + } else { + set waitingFor "class/struct" + } + } + + if {$waitingFor == "class/struct"} { + if {$type == "typedef" || $type == "enum"} { + set state "consumeUntilNextSemiColon" + report $fileName $line "The typedefs and enums should be before the classes/structs" + } elseif {$type == "class" || $type == "struct"} { + set state "consumeUntilNextSemiColon" + } else { + set waitingFor "staticFunc" + } + } + + if {$waitingFor == "staticFunc"} { + if {$type == "typedef" || $type == "enum" || $type == "class" || $type == "struct"} { + set state "consumeUntilNextSemiColon" + report $fileName $line "The typedefs, enums, classes and structs should be before the static functions" + } elseif {$prev1 == "identifier"} { + if {$type == "leftparen"} { + if {$isStatic} { + set state "consumeFunction" + } else { + set waitingFor "method" + } + } elseif {$type == "assign" || $type == "semicolon"} { + set waitingFor "staticVar" + } + } + } + + if {$waitingFor == "staticVar"} { + if {$type == "typedef" || $type == "enum" || $type == "class" || $type == "struct"} { + set state "consumeUntilNextSemiColon" + report $fileName $line "The typedefs, enums, classes and structs should be before the static variables" + } elseif {$prev1 == "identifier"} { + if {$type == "leftparen"} { + if {$isStatic} { + set state "consumeFunction" + report $fileName $line "The static functions should be before the static variables" + } else { + set waitingFor "method" + } + } elseif {$type == "assign" || $type == "semicolon"} { + if {!$isStatic} { + set waitingFor "method" + } + if {$type != "semicolon"} { + set state "consumeUntilNextSemiColon" + } else { + set isStatic 0 + } + } + } + } + + if {$waitingFor == "method"} { + if {$type == "typedef" || $type == "enum" || $type == "class" || $type == "struct"} { + set state "consumeUntilNextSemiColon" + report $fileName $line "The typedefs, enums, classes and structs should be before the methods" + } elseif {$prev2 == "identifier" && $prev1 == "operator" && $type == "assign"} { + # override of operator=() + set state "consumeFunction" + } elseif {$prev1 == "identifier"} { + if {$type == "leftparen"} { + if {$isStatic} { + report $fileName $line "The static functions should be before the methods" + } + set state "consumeFunction" + } elseif {$type == "assign" || $type == "semicolon"} { + if {$isStatic} { + report $fileName $line "The static variables should be before the methods" + } else { + set waitingFor "var" + } + } + } + } + + if {$waitingFor == "var"} { + if {$type == "typedef" || $type == "enum" || $type == "class" || $type == "struct"} { + set state "consumeUntilNextSemiColon" + report $fileName $line "The typedefs, enums, classes and structs should be before the variables" + } elseif {$prev1 == "identifier"} { + if {$type == "leftparen"} { + if {$isStatic} { + report $fileName $line "The static functions should be before the variables" + } else { + report $fileName $line "The methods should be before the variables" + } + set state "consumeFunction" + } elseif {$type == "assign" || $type == "semicolon"} { + if {$isStatic} { + report $fileName $line "The static variables should be before the variables" + } + } + } + } + } + } elseif {$state == "consumeUntilNextSemiColon"} { + set isStatic 0 + if {$type == "semicolon"} { + set state "root" + } elseif {$type == "leftbrace"} { + incr bracesCounter + set previousState $state + set state "consumeBraces" + } + } elseif {$state == "consumeFunction"} { + set isStatic 0 + if {$type == "semicolon"} { + set state "root" + } elseif {$type == "leftbrace"} { + incr bracesCounter + set previousState "root" + set state "consumeBraces" + } + } elseif {$state == "consumeBraces"} { + if {$type == "leftbrace"} { + incr bracesCounter + } elseif {$type == "rightbrace"} { + incr bracesCounter -1 + if {$bracesCounter == 0} { + set state $previousState + } + } + } + } + + if {$keepMachine} { + lappend machinesToKeep $m + } + } + set machines $machinesToKeep + + set prev2 $prev1 + set prev1 $type + } +} \ No newline at end of file diff --git a/formatting/vera-root/scripts/rules/IPSIS_S06.tcl b/formatting/vera-root/scripts/rules/IPSIS_S06.tcl new file mode 100644 index 0000000..cb966d5 --- /dev/null +++ b/formatting/vera-root/scripts/rules/IPSIS_S06.tcl @@ -0,0 +1,166 @@ +#!/usr/bin/tclsh +# Forbid public member variables + +set forbidPublicMemberVarInStruct [getParameter "also-forbid-public-member-var-in-struct" 0] + +proc createMachine {type initState} { + set machine [dict create classOrStruct $type state $initState previousState "" bracesCounter 0 parenCounter 0 bracketCounter 0 isStatic 0 isTypedef 0 isRootClass 0] + return $machine +} + +set tokenFilter { + public + protected + private + typedef + enum + class + struct + static + identifier + assign + semicolon + leftbrace + rightbrace + leftparen + rightparen + leftbracket + rightbracket + operator +} + +foreach fileName [getSourceFileNames] { + if {[regexp {\.h$} $fileName]} { + set machines [list] + + set rootClass 1 + set lastIdentifier "" + set prev1 "" + foreach token [getTokens $fileName 1 0 -1 -1 $tokenFilter] { + set type [lindex $token 3] + set line [lindex $token 1] + + if {$type == "identifier"} { + set lastIdentifier [lindex $token 0] + } + + if {$rootClass && ($type == "class" || $type == "struct")} { + set m [createMachine $type "beforeLeftBrace"] + dict set m isRootClass 1 + lappend machines $m + set rootClass 0 + } + + set machinesToKeep [list] + foreach m $machines { + set keepMachine 1 + dict with m { + if {$state == "beforeLeftBrace"} { + if {$type == "leftbrace"} { + if {$classOrStruct == "class"} { + set state "root" + } elseif {$classOrStruct == "struct"} { + set state "public" + } + } elseif {$type == "semicolon"} { + set keepMachine 0 + set rootClass $isRootClass + } + } elseif {$state == "root"} { + if {$type == "public"} { + set state "public" + } elseif {$type == "leftbrace"} { + incr bracesCounter + set previousState $state + set state "consumeBraces" + } elseif {$type == "rightbrace"} { + set keepMachine 0 + set rootClass $isRootClass + } + } elseif {$state == "public"} { + if {$type == "class" || $type == "struct"} { + lappend machinesToKeep [createMachine $type "beforeLeftBrace"] + } + + if {$type == "static"} { + set isStatic 1 + } + if {$type == "typedef"} { + set isTypedef 1 + } + + if {$prev1 == "identifier" && ($type == "assign" || $type == "semicolon" || $type == "leftbracket")} { + if {!$isStatic && !$isTypedef} { + if {($classOrStruct == "class") || ($classOrStruct == "struct" && $forbidPublicMemberVarInStruct)} { + report $fileName $line "Public member variables are forbidden in $classOrStruct (found: $lastIdentifier)" + } + } + } elseif {$type == "protected" || $type == "private"} { + set isStatic 0 + set isTypedef 0 + set state "root" + } elseif {$type == "leftbrace"} { + incr bracesCounter + set previousState $state + set state "consumeBraces" + } elseif {$type == "leftparen"} { + incr parenCounter + set previousState $state + set state "consumeParen" + } elseif {$type == "rightbrace"} { + set keepMachine 0 + set rootClass $isRootClass + } + + if {$type == "leftbracket"} { + incr bracketCounter + set previousState $state + set state "consumeBracket" + } + + if {$type == "semicolon" || $type == "leftbrace"} { + set isStatic 0 + } + if {$type == "semicolon"} { + set isTypedef 0 + } + } elseif {$state == "consumeBraces"} { + if {$type == "leftbrace"} { + incr bracesCounter + } elseif {$type == "rightbrace"} { + incr bracesCounter -1 + if {$bracesCounter == 0} { + set state $previousState + } + } + } elseif {$state == "consumeParen"} { + if {$type == "leftparen"} { + incr parenCounter + } elseif {$type == "rightparen"} { + incr parenCounter -1 + if {$parenCounter == 0} { + set state $previousState + } + } + } elseif {$state == "consumeBracket"} { + if {$type == "leftbracket"} { + incr bracketCounter + } elseif {$type == "rightbracket"} { + incr bracketCounter -1 + if {$bracketCounter == 0} { + set state $previousState + } + } + } + } + + if {$keepMachine} { + lappend machinesToKeep $m + } + } + set machines $machinesToKeep + + set prev1 $type + } + } +} diff --git a/formatting/vera-root/scripts/rules/IPSIS_S09.tcl b/formatting/vera-root/scripts/rules/IPSIS_S09.tcl new file mode 100644 index 0000000..03c846a --- /dev/null +++ b/formatting/vera-root/scripts/rules/IPSIS_S09.tcl @@ -0,0 +1,60 @@ +#!/usr/bin/tclsh +# Prefer C++ includes of the C std headers (cstdio instead of stdio.h for instance) + +set cStdHeaders { + assert.h + complex.h + ctype.h + errno.h + fenv.h + float.h + inttypes.h + iso646.h + limits.h + locale.h + math.h + setjmp.h + signal.h + stdalign.h + stdarg.h + stdbool.h + stddef.h + stdint.h + stdio.h + stdlib.h + string.h + tgmath.h + time.h + uchar.h + wchar.h + wctype.h +} + +set cStdHeadersWithDifferentCppEquivalent [dict create \ + stdatomic.h atomic \ + threads.h thread \ +] + +set cStdHeadersWithNoCppEquivalent { + stdnoreturn.h +} + +foreach fileName [getSourceFileNames] { + foreach token [getTokens $fileName 1 0 -1 -1 {pp_hheader}] { + set line [lindex $token 1] + set value [lindex $token 0] + + if {[regexp {^[^<]*?<(.*)>[^>]*?$} $value matched headerName]} { + if {[lsearch $cStdHeaders $headerName] != -1} { + set headerWithoutExt [string range $headerName 0 [expr [string length $headerName] - 3]] + set cppHeader "c$headerWithoutExt" + report $fileName $line "$headerName is a C include, use the equivalent C++ include <$cppHeader>" + } elseif {[dict exists $cStdHeadersWithDifferentCppEquivalent $headerName]} { + set cppHeader [dict get $cStdHeadersWithDifferentCppEquivalent $headerName] + report $fileName $line "$headerName is a C include, use the equivalent C++ include <$cppHeader>" + } elseif {[lsearch $cStdHeadersWithNoCppEquivalent $headerName] != -1} { + report $fileName $line "$headerName is a C include without C++ equivalent. Use the C++ constructions instead of relying on C headers." + } + } + } +}