#!/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 } }