Codechange: Use EnumBitSet for FrameFlags.
[openttd-github.git] / .github / script-missing-mode-enforcement.py
blobdfb4764abbc01c71cb51d6274cf7ff2fc94a5c4b
1 """
2 Script to scan the OpenTTD's script API for functions that miss checks for the
3 function being called from the right mode (deity or company mode).
5 When a function calls either ScriptObject::Command or ScriptObject::GetCompany
6 then the function is considered dangerous. When one of the mode enforcement
7 macros from script_error.hpp, i.e. EnforceDeityMode, EnforceCompanyModeValid or
8 EnforceDeityOrCompanyModeValid, are called in the function, then we consider
9 that the function has mode enforcement.
11 Any dangerous function for which no enforcement is found are emitted as errors.
12 """
14 import glob
15 import re
16 import sys
19 def check_mode_enforcement(path):
20 errors = []
21 with open(path, "r") as reader:
22 mode_enforcement_found = False
23 dangerous_function = False
24 for line in reader:
25 # Line does not start with a tab and have <word>::<word>. That looks like the begin of a function, so reset the state.
26 if re.match(r"^[^\t].*\w::\w", line):
27 mode_enforcement_found = False
28 dangerous_function = False
29 currentFunction = line
30 continue
32 if re.match(
33 r"\t(EnforceDeityMode|EnforceCompanyModeValid|EnforceCompanyModeValid_Void|EnforceDeityOrCompanyModeValid|EnforceDeityOrCompanyModeValid_Void)\(",
34 line,
36 # Mode enforcement macro found
37 mode_enforcement_found = True
38 continue
40 if re.match(r".*(ScriptObject::Command|ScriptObject::GetCompany).*", line):
41 # Dangerous function found
42 dangerous_function = True
43 continue
45 # Line with only a closing bracket. That looks like the end of a function, so check for the dangerous function without mode enforcement
46 if re.match(r"^}$", line) and dangerous_function and not mode_enforcement_found:
47 function_name = currentFunction.rstrip("\n").replace("/* static */ ", "")
48 errors.append(f"{path}: {function_name}")
50 return errors
53 def main():
54 errors = []
55 for path in sorted(glob.glob("src/script/api/*.cpp")):
56 # Skip a number of files that yield only false positives
57 if path.endswith(("script_object.cpp", "script_companymode.cpp", "script_controller.cpp", "script_game.cpp")):
58 continue
60 errors.extend(check_mode_enforcement(path))
62 if errors:
63 print("Mode enforcement was expected in the following files/functions:")
64 print("\n".join(errors))
65 sys.exit(1)
67 print("OK")
70 if __name__ == "__main__":
71 main()