diff --git a/addons/main/script_macros_common.hpp b/addons/main/script_macros_common.hpp index 2f3980a250..7d930c66a8 100644 --- a/addons/main/script_macros_common.hpp +++ b/addons/main/script_macros_common.hpp @@ -865,15 +865,10 @@ Macro: ISNILS() #define COMPILE_SCRIPT(var1) compileScript ['PATHTO_SYS(PREFIX,COMPONENT_F,var1)'] -#define VERSIONING_SYS(var1) class CfgSettings \ -{ \ - class CBA \ - { \ - class Versioning \ - { \ - class var1 \ - { \ - }; \ +#define VERSIONING_SYS(var1) class CfgSettings { \ + class CBA { \ + class Versioning { \ + class var1 {}; \ }; \ }; \ }; @@ -1040,12 +1035,9 @@ Macro: PATHTO_FNC() #define QQEFUNC(var1,var2) QUOTE(QEFUNC(var1,var2)) #ifndef PRELOAD_ADDONS - #define PRELOAD_ADDONS class CfgAddons \ -{ \ - class PreloadAddons \ - { \ - class ADDON \ - { \ + #define PRELOAD_ADDONS class CfgAddons { \ + class PreloadAddons { \ + class ADDON { \ list[]={ QUOTE(ADDON) }; \ }; \ }; \ diff --git a/addons/modules/CfgVehicles.hpp b/addons/modules/CfgVehicles.hpp index e19c435f86..77706b4bf8 100644 --- a/addons/modules/CfgVehicles.hpp +++ b/addons/modules/CfgVehicles.hpp @@ -19,7 +19,7 @@ class CfgVehicles { is3DEN = 0; class Arguments: ArgumentsBaseUnits { - class attackLocType{ + class attackLocType { displayName = CSTRING(AttackLocType); description = CSTRING(AttackLocType_Desc); typeName = "STRING"; diff --git a/addons/ui/flexiMenu/data/menu_popup.hpp b/addons/ui/flexiMenu/data/menu_popup.hpp index 45c6d68ca9..b01b3ef522 100644 --- a/addons/ui/flexiMenu/data/menu_popup.hpp +++ b/addons/ui/flexiMenu/data/menu_popup.hpp @@ -12,7 +12,7 @@ #define _captionColorFG "138 / 256", "146 / 256", "105 / 256" //BIS greenish text #define _captionHgt 1 // 0.75 -class CBA_flexiMenu_rscPopup { // : _flexiMenu_rscRose +class CBA_flexiMenu_rscPopup { // _flexiMenu_rscRose idd = -1; // _flexiMenu_IDD; movingEnable = 0; onLoad = QUOTE(with uiNamespace do {GVAR(display) = _this select 0};); diff --git a/optionals/cache_disable/script_component.hpp b/optionals/cache_disable/script_component.hpp index fd1c00fbd4..aee93da9b1 100644 --- a/optionals/cache_disable/script_component.hpp +++ b/optionals/cache_disable/script_component.hpp @@ -3,11 +3,11 @@ #ifdef DEBUG_ENABLED_CACHE_DISABLE - #define DEBUG_MODE_FULL + #define DEBUG_MODE_FULL #endif #ifdef DEBUG_SETTINGS_CACHE_DISABLE - #define DEBUG_SETTINGS DEBUG_SETTINGS_CACHE_DISABLE + #define DEBUG_SETTINGS DEBUG_SETTINGS_CACHE_DISABLE #endif #undef REQUIRED_VERSION diff --git a/optionals/diagnostic_enable_logging/script_component.hpp b/optionals/diagnostic_enable_logging/script_component.hpp index b1007c1761..cd2c342b8f 100644 --- a/optionals/diagnostic_enable_logging/script_component.hpp +++ b/optionals/diagnostic_enable_logging/script_component.hpp @@ -3,11 +3,11 @@ #ifdef DEBUG_ENABLED_DIAGNOSTIC_LOGGING - #define DEBUG_MODE_FULL + #define DEBUG_MODE_FULL #endif #ifdef DEBUG_SETTINGS_DIAGNOSTIC_LOGGING - #define DEBUG_SETTINGS DEBUG_SETTINGS_DIAGNOSTIC_LOGGING + #define DEBUG_SETTINGS DEBUG_SETTINGS_DIAGNOSTIC_LOGGING #endif #include "\x\cba\addons\main\script_macros.hpp" diff --git a/optionals/disable_missing_mod_check/script_component.hpp b/optionals/disable_missing_mod_check/script_component.hpp index 8c55df94a1..801a00b24a 100644 --- a/optionals/disable_missing_mod_check/script_component.hpp +++ b/optionals/disable_missing_mod_check/script_component.hpp @@ -3,11 +3,11 @@ #ifdef DEBUG_ENABLED_DISABLE_MISSING_MOD_CHECK - #define DEBUG_MODE_FULL + #define DEBUG_MODE_FULL #endif #ifdef DEBUG_SETTINGS_DISABLE_MISSING_MOD_CHECK - #define DEBUG_SETTINGS DEBUG_SETTINGS_DISABLE_MISSING_MOD_CHECK + #define DEBUG_SETTINGS DEBUG_SETTINGS_DISABLE_MISSING_MOD_CHECK #endif #include "\x\cba\addons\main\script_macros.hpp" diff --git a/tools/config_style_checker.py b/tools/config_style_checker.py index 2adb690f88..afa78a2d6b 100755 --- a/tools/config_style_checker.py +++ b/tools/config_style_checker.py @@ -1,187 +1,183 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -"""Config style validator""" - -import argparse import fnmatch import os +import re +import ntpath import sys +import argparse -if sys.version_info.major == 2: - import codecs - open = codecs.open # pylint: disable=invalid-name,redefined-builtin - -def checkConfigStyle(filepath): # pylint: disable=too-many-branches,too-many-statements - """Do a sanity check on a .cpp or .hpp config file - - In code that is not inside comments or string literals, +def check_config_style(filepath): + bad_count_file = 0 + def pushClosing(t): + closingStack.append(closing.expr) + closing << Literal( closingFor[t[0]] ) - * check for tabs - * check for mismatched pairs of parentheses, brackets and braces (), [], {} + def popClosing(): + closing << closingStack.pop() - Returns the number of possible syntax issues found - """ - badCountFile = 0 + reIsClass = re.compile(r'^\s*class(.*)') + reIsClassInherit = re.compile(r'^\s*class(.*):') + reIsClassBody = re.compile(r'^\s*class(.*){') + reBadColon = re.compile(r'\s*class (.*) :') + reSpaceAfterColon = re.compile(r'\s*class (.*): ') + reSpaceBeforeCurly = re.compile(r'\s*class (.*) {') + reClassSingleLine = re.compile(r'\s*class (.*)[{;]') with open(filepath, 'r', encoding='utf-8', errors='ignore') as file: content = file.read() # Store all brackets we find in this file, so we can validate everything on the end - bracketsList = [] + brackets_list = [] # To check if we are in a comment block isInCommentBlock = False checkIfInComment = False # Used in case we are in a line comment (//) ignoreTillEndOfLine = False - # Used in case we are in a comment block (/* */). - # This is true if we detect a * inside a comment block. + # Used in case we are in a comment block (/* */). This is true if we detect a * inside a comment block. # If the next character is a /, it means we end our comment block. checkIfNextIsClosingBlock = False # We ignore everything inside a string isInString = False # Used to store the starting type of a string, so we can match that to the end of a string - inStringType = '' + inStringType = ''; lastIsCurlyBrace = False + checkForSemiColumn = False # Extra information so we know what line we find errors at lineNumber = 1 indexOfCharacter = 0 # Parse all characters in the content of this file to search for potential errors - for c in content: # pylint: disable=invalid-name,too-many-nested-blocks - if lastIsCurlyBrace: + for c in content: + if (lastIsCurlyBrace): lastIsCurlyBrace = False if c == '\n': # Keeping track of our line numbers - # so we can print accurate line number information when - # we detect a possible error - lineNumber += 1 - if isInString: - # while we are in a string, we can ignore everything else - # except the end of the string - if c == inStringType: + lineNumber += 1 # so we can print accurate line number information when we detect a possible error + if (isInString): # while we are in a string, we can ignore everything else, except the end of the string + if (c == inStringType): isInString = False - # if we are not in a comment block, we will check if we are at - # the start of one or count the () {} and [] - elif not isInCommentBlock: + # if we are not in a comment block, we will check if we are at the start of one or count the () {} and [] + elif (isInCommentBlock == False): - # This means we have encountered a /, so we are now checking - # if this is an inline comment or a comment block - if checkIfInComment: + # This means we have encountered a /, so we are now checking if this is an inline comment or a comment block + if (checkIfInComment): checkIfInComment = False - # if the next character after / is a *, we are - # at the start of a comment block - if c == '*': + if c == '*': # if the next character after / is a *, we are at the start of a comment block isInCommentBlock = True - elif c == '/': - # Otherwise, will check if we are in a line comment - # and a line comment is a / followed by another / (//) - # We won't care about anything that comes after it - ignoreTillEndOfLine = True - - if not isInCommentBlock: - if ignoreTillEndOfLine: - # we are in a line comment, just continue going - # through the characters until we find an end of line - if c == '\n': + elif (c == '/'): # Otherwise, will check if we are in an line comment + ignoreTillEndOfLine = True # and an line comment is a / followed by another / (//) We won't care about anything that comes after it + + if (isInCommentBlock == False): + if (ignoreTillEndOfLine): # we are in a line comment, just continue going through the characters until we find an end of line + if (c == '\n'): ignoreTillEndOfLine = False else: # validate brackets - if c in ('"', "'"): + if (c == '"' or c == "'"): isInString = True inStringType = c - elif c == '/': + elif (c == '/'): checkIfInComment = True - elif c == '(': - bracketsList.append('(') - elif c == ')': - if (len(bracketsList) > 0 and bracketsList[-1] in ['{', '[']): - print("ERROR: Possible missing round bracket ')' detected" - " at {0} Line number: {1}".format(filepath, lineNumber)) - badCountFile += 1 - bracketsList.append(')') - elif c == '[': - bracketsList.append('[') - elif c == ']': - if (len(bracketsList) > 0 and bracketsList[-1] in ['{', '(']): - print("ERROR: Possible missing square bracket ']' detected" - " at {0} Line number: {1}".format(filepath, lineNumber)) - badCountFile += 1 - bracketsList.append(']') - elif c == '{': - bracketsList.append('{') - elif c == '}': + elif (c == '('): + brackets_list.append('(') + elif (c == ')'): + if (len(brackets_list) > 0 and brackets_list[-1] in ['{', '[']): + print("ERROR: Possible missing round bracket ')' detected at {0} Line number: {1}".format(filepath,lineNumber)) + bad_count_file += 1 + brackets_list.append(')') + elif (c == '['): + brackets_list.append('[') + elif (c == ']'): + if (len(brackets_list) > 0 and brackets_list[-1] in ['{', '(']): + print("ERROR: Possible missing square bracket ']' detected at {0} Line number: {1}".format(filepath,lineNumber)) + bad_count_file += 1 + brackets_list.append(']') + elif (c == '{'): + brackets_list.append('{') + elif (c == '}'): lastIsCurlyBrace = True - if (len(bracketsList) > 0 and bracketsList[-1] in ['(', '[']): - print("ERROR: Possible missing curly brace '}}' detected" - " at {0} Line number: {1}".format(filepath, lineNumber)) - badCountFile += 1 - bracketsList.append('}') - elif c == '\t': - print("ERROR: Tab detected at {0} Line number: {1}".format(filepath, - lineNumber)) - badCountFile += 1 + if (len(brackets_list) > 0 and brackets_list[-1] in ['(', '[']): + print("ERROR: Possible missing curly brace '}}' detected at {0} Line number: {1}".format(filepath,lineNumber)) + bad_count_file += 1 + brackets_list.append('}') + elif (c== '\t'): + print("ERROR: Tab detected at {0} Line number: {1}".format(filepath,lineNumber)) + bad_count_file += 1 else: # Look for the end of our comment block - if c == '*': - checkIfNextIsClosingBlock = True - elif checkIfNextIsClosingBlock: - if c == '/': + if (c == '*'): + checkIfNextIsClosingBlock = True; + elif (checkIfNextIsClosingBlock): + if (c == '/'): isInCommentBlock = False - elif c != '*': + elif (c != '*'): checkIfNextIsClosingBlock = False indexOfCharacter += 1 - if bracketsList.count('[') != bracketsList.count(']'): - print("ERROR: A possible missing square bracket [ or ] in file {0}" - " [ = {1} ] = {2}".format(filepath, bracketsList.count('['), - bracketsList.count(']'))) - badCountFile += 1 - if bracketsList.count('(') != bracketsList.count(')'): - print("ERROR: A possible missing round bracket ( or ) in file {0}" - " ( = {1} ) = {2}".format(filepath, bracketsList.count('('), - bracketsList.count(')'))) - badCountFile += 1 - if bracketsList.count('{') != bracketsList.count('}'): - print("ERROR: A possible missing curly brace {{ or }} in file {0}" - " {{ = {1} }} = {2}".format(filepath, bracketsList.count('{'), - bracketsList.count('}'))) - badCountFile += 1 - return badCountFile - -def main(): # pylint: disable=missing-function-docstring + if brackets_list.count('[') != brackets_list.count(']'): + print("ERROR: A possible missing square bracket [ or ] in file {0} [ = {1} ] = {2}".format(filepath,brackets_list.count('['),brackets_list.count(']'))) + bad_count_file += 1 + if brackets_list.count('(') != brackets_list.count(')'): + print("ERROR: A possible missing round bracket ( or ) in file {0} ( = {1} ) = {2}".format(filepath,brackets_list.count('('),brackets_list.count(')'))) + bad_count_file += 1 + if brackets_list.count('{') != brackets_list.count('}'): + print("ERROR: A possible missing curly brace {{ or }} in file {0} {{ = {1} }} = {2}".format(filepath,brackets_list.count('{'),brackets_list.count('}'))) + bad_count_file += 1 + + file.seek(0) + for lineNumber, line in enumerate(file.readlines()): + if reIsClass.match(line): + if reBadColon.match(line): + print(f"WARNING: bad class colon {filepath} Line number: {lineNumber+1}") + # bad_count_file += 1 + if reIsClassInherit.match(line): + if not reSpaceAfterColon.match(line): + print(f"WARNING: bad class missing space after colon {filepath} Line number: {lineNumber+1}") + if reIsClassBody.match(line): + if not reSpaceBeforeCurly.match(line): + print(f"WARNING: bad class inherit missing space before curly braces {filepath} Line number: {lineNumber+1}") + if not reClassSingleLine.match(line): + print(f"WARNING: bad class braces placement {filepath} Line number: {lineNumber+1}") + # bad_count_file += 1 + + return bad_count_file + +def main(): + print("Validating Config Style") - sqfList = [] - badCount = 0 + sqf_list = [] + bad_count = 0 parser = argparse.ArgumentParser() - parser.add_argument('-m', '--module', help='only search specified module addon folder', - required=False, default="") + parser.add_argument('-m','--module', help='only search specified module addon folder', required=False, default="") args = parser.parse_args() - # Allow running from root directory as well as from inside the tools directory - rootDir = "../addons" - if os.path.exists("addons"): - rootDir = "addons" + for folder in ['addons', 'optionals']: + # Allow running from root directory as well as from inside the tools directory + rootDir = "../" + folder + if (os.path.exists(folder)): + rootDir = folder - for root, _, filenames in os.walk(rootDir + '/' + args.module): - for filename in fnmatch.filter(filenames, '*.cpp'): - sqfList.append(os.path.join(root, filename)) - for filename in fnmatch.filter(filenames, '*.hpp'): - sqfList.append(os.path.join(root, filename)) + for root, dirnames, filenames in os.walk(rootDir + '/' + args.module): + for filename in fnmatch.filter(filenames, '*.cpp'): + sqf_list.append(os.path.join(root, filename)) + for filename in fnmatch.filter(filenames, '*.hpp'): + sqf_list.append(os.path.join(root, filename)) - for filename in sqfList: - badCount = badCount + checkConfigStyle(filename) + for filename in sqf_list: + bad_count = bad_count + check_config_style(filename) - print("------\nChecked {0} files\nErrors detected: {1}".format(len(sqfList), badCount)) - if badCount == 0: + print("------\nChecked {0} files\nErrors detected: {1}".format(len(sqf_list), bad_count)) + if (bad_count == 0): print("Config validation PASSED") else: print("Config validation FAILED") - return badCount + return bad_count if __name__ == "__main__": sys.exit(main()) diff --git a/tools/sqf_validator.py b/tools/sqf_validator.py index a76b9793c4..facdb1142d 100644 --- a/tools/sqf_validator.py +++ b/tools/sqf_validator.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import fnmatch import os @@ -7,12 +7,8 @@ import sys import argparse -if sys.version_info.major == 2: - import codecs - open = codecs.open - def validKeyWordAfterCode(content, index): - keyWords = ["for", "do", "count", "each", "forEach", "else", "and", "not", "isEqualTo", "in", "call", "spawn", "execVM", "catch", "param", "select", "apply"]; + keyWords = ["for", "do", "count", "each", "forEach", "else", "and", "not", "isEqualTo", "in", "call", "spawn", "execVM", "catch", "param", "select", "apply", "findIf", "remoteExec"]; for word in keyWords: try: subWord = content.index(word, index, index+len(word)) @@ -51,7 +47,7 @@ def popClosing(): inStringType = ''; lastIsCurlyBrace = False - checkForSemiColon = False + checkForSemicolon = False onlyWhitespace = True # Extra information so we know what line we find errors at @@ -62,7 +58,8 @@ def popClosing(): for c in content: if (lastIsCurlyBrace): lastIsCurlyBrace = False - checkForSemiColon = True + # Test generates false positives with binary commands that take CODE as 2nd arg (e.g. findIf) + checkForSemicolon = not re.search('findIf', content, re.IGNORECASE) if c == '\n': # Keeping track of our line numbers onlyWhitespace = True # reset so we can see if # is for a preprocessor command @@ -122,9 +119,9 @@ def popClosing(): if (c not in [' ', '\t', '\n']): onlyWhitespace = False - if (checkForSemiColon): + if (checkForSemicolon): if (c not in [' ', '\t', '\n', '/']): # keep reading until no white space or comments - checkForSemiColon = False + checkForSemicolon = False if (c not in [']', ')', '}', ';', ',', '&', '!', '|', '='] and not validKeyWordAfterCode(content, indexOfCharacter)): # , 'f', 'd', 'c', 'e', 'a', 'n', 'i']): print("ERROR: Possible missing semicolon ';' detected at {0} Line number: {1}".format(filepath,lineNumber)) bad_count_file += 1 @@ -148,6 +145,17 @@ def popClosing(): if brackets_list.count('{') != brackets_list.count('}'): print("ERROR: A possible missing curly brace {{ or }} in file {0} {{ = {1} }} = {2}".format(filepath,brackets_list.count('{'),brackets_list.count('}'))) bad_count_file += 1 + pattern = re.compile('\s*(/\*[\s\S]+?\*/)\s*#include') + if pattern.match(content): + print("ERROR: A found #include after block comment in file {0}".format(filepath)) + bad_count_file += 1 + if ("functions" in filepath): + if (content.startswith("#include \"script_component.hpp\"")): + print(f"ERROR: Using old script_component.hpp in {filepath}") + bad_count_file += 1 + + + return bad_count_file def main(): @@ -161,14 +169,15 @@ def main(): parser.add_argument('-m','--module', help='only search specified module addon folder', required=False, default="") args = parser.parse_args() - # Allow running from root directory as well as from inside the tools directory - rootDir = "../addons" - if (os.path.exists("addons")): - rootDir = "addons" + for folder in ['addons', 'optionals']: + # Allow running from root directory as well as from inside the tools directory + rootDir = "../" + folder + if (os.path.exists(folder)): + rootDir = folder - for root, dirnames, filenames in os.walk(rootDir + '/' + args.module): - for filename in fnmatch.filter(filenames, '*.sqf'): - sqf_list.append(os.path.join(root, filename)) + for root, dirnames, filenames in os.walk(rootDir + '/' + args.module): + for filename in fnmatch.filter(filenames, '*.sqf'): + sqf_list.append(os.path.join(root, filename)) for filename in sqf_list: bad_count = bad_count + check_sqf_syntax(filename) diff --git a/tools/updateIncludes.py b/tools/updateIncludes.py index a3fd82ea28..6ed0b2a469 100644 --- a/tools/updateIncludes.py +++ b/tools/updateIncludes.py @@ -16,6 +16,9 @@ continue file_r_path = os.path.join(include_base_path, relative_path, file) file_v_path = os.path.join(p_drive_path, relative_path, file) + if (file_v_path.endswith(".p3d")): + print(f"ignoring p3d {file_v_path}") + continue if not os.path.isfile(file_v_path): raise Exception("Missing p-drive file {}".format(file_v_path)) shutil.copyfile(file_v_path, file_r_path)