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.
19 def check_mode_enforcement(path
):
21 with
open(path
, "r") as reader
:
22 mode_enforcement_found
= False
23 dangerous_function
= False
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
33 r
"\t(EnforceDeityMode|EnforceCompanyModeValid|EnforceCompanyModeValid_Void|EnforceDeityOrCompanyModeValid|EnforceDeityOrCompanyModeValid_Void)\(",
36 # Mode enforcement macro found
37 mode_enforcement_found
= True
40 if re
.match(r
".*(ScriptObject::Command|ScriptObject::GetCompany).*", line
):
41 # Dangerous function found
42 dangerous_function
= True
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}")
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")):
60 errors
.extend(check_mode_enforcement(path
))
63 print("Mode enforcement was expected in the following files/functions:")
64 print("\n".join(errors
))
70 if __name__
== "__main__":