Skip to content

Commit 3bfe71d

Browse files
authored
Merge pull request scp-fs2open#7670 from Goober5000/fix/msg_arg_count
send-message-list arg-count forgiveness, plus better error checking
2 parents 09de3f1 + 5fa6674 commit 3bfe71d

3 files changed

Lines changed: 48 additions & 10 deletions

File tree

code/parse/sexp.cpp

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1989,17 +1989,18 @@ int query_sexp_args_count(int node, bool only_valid_args = false)
19891989
return count;
19901990
}
19911991

1992+
enum class ArgCountCheck { CORRECT, INCORRECT_BENIGN, INCORRECT_FATAL };
19921993
/**
19931994
* Needed to fix bug with sexps like send-message list which have arguments that need to be supplied as a block
19941995
*
19951996
* @return whether the number of arguments for the supplied operation is correct
19961997
*/
1997-
static bool check_operator_argument_count(int count, int op_index)
1998+
static ArgCountCheck check_operator_argument_count(int count, int op_index)
19981999
{
19992000
Assertion(op_index >= 0 && op_index < sz2i(Operators.size()), "op_index is out of range!");
20002001

20012002
if (count < Operators[op_index].min || count > Operators[op_index].max)
2002-
return false;
2003+
return ArgCountCheck::INCORRECT_FATAL;
20032004

20042005
int op_const = Operators[op_index].value;
20052006

@@ -2011,9 +2012,9 @@ static bool check_operator_argument_count(int count, int op_index)
20112012

20122013
if (op_const == OP_SEND_MESSAGE_LIST || op_const == OP_SEND_MESSAGE_CHAIN)
20132014
if (count % 4 != 0)
2014-
return false;
2015+
return ArgCountCheck::INCORRECT_BENIGN; // historically, this check didn't work at all, and sexps gracefully recovered at runtime
20152016

2016-
return true;
2017+
return ArgCountCheck::CORRECT;
20172018
}
20182019

20192020
// helper functions for check_container_value_data_type()
@@ -2159,6 +2160,7 @@ int check_sexp_syntax(int node, int desired_return_type, int recursive, int *bad
21592160
int var_index = -1;
21602161
size_t st;
21612162
const sexp_container *p_container = nullptr; // for SEXPs that take container name as arg
2163+
int deferred_error = SEXP_CHECK_NO_ERROR, deferred_bad_node = -1; // for recoverable errors, so that the rest of the tree is still checked
21622164

21632165
Assertion(node >= 0 && node < Num_sexp_nodes, "Node %d must be a valid SEXP node!", node);
21642166
Assertion(Sexp_nodes[node].type != SEXP_NOT_USED, "Node %d must be in use!", node);
@@ -2207,8 +2209,15 @@ int check_sexp_syntax(int node, int desired_return_type, int recursive, int *bad
22072209

22082210
count = query_sexp_args_count(op_node);
22092211

2210-
if (!check_operator_argument_count(sz2i(count), op_index))
2211-
return SEXP_CHECK_BAD_ARG_COUNT; // incorrect number of arguments
2212+
auto arg_count_result = check_operator_argument_count(sz2i(count), op_index);
2213+
if (arg_count_result == ArgCountCheck::INCORRECT_FATAL)
2214+
return SEXP_CHECK_BAD_ARG_COUNT; // incorrect number of arguments
2215+
else if (arg_count_result == ArgCountCheck::INCORRECT_BENIGN)
2216+
{
2217+
// incorrect, but defer it and continue checking
2218+
deferred_error = SEXP_CHECK_BAD_ARG_COUNT_BENIGN;
2219+
deferred_bad_node = op_node;
2220+
}
22122221

22132222
node = Sexp_nodes[op_node].rest;
22142223
while (node != -1) {
@@ -2227,7 +2236,7 @@ int check_sexp_syntax(int node, int desired_return_type, int recursive, int *bad
22272236
// thing. (i.e. in the case of a cond statement, the conditional will fall into this if
22282237
// statement. MORE TO DO HERE!!!!
22292238
if (Sexp_nodes[i].subtype == SEXP_ATOM_LIST)
2230-
return 0;
2239+
break;
22312240

22322241
int op2_index = get_operator_index(i);
22332242
int op2_const = SCP_vector_inbounds(Operators, op2_index) ? Operators[op2_index].value : OP_NOT_AN_OP;
@@ -2242,7 +2251,13 @@ int check_sexp_syntax(int node, int desired_return_type, int recursive, int *bad
22422251
}
22432252

22442253
if ((z = check_sexp_syntax(i, (int)opr, recursive, bad_node)) != 0) {
2245-
return z;
2254+
if (!sexp_recoverable_error(z))
2255+
return z;
2256+
// defer recoverable errors so that the rest of the tree is still checked
2257+
if (deferred_error == SEXP_CHECK_NO_ERROR) {
2258+
deferred_error = z;
2259+
deferred_bad_node = bad_node ? *bad_node : -1;
2260+
}
22462261
}
22472262
}
22482263

@@ -3153,7 +3168,13 @@ int check_sexp_syntax(int node, int desired_return_type, int recursive, int *bad
31533168
// we should check the syntax of the actual goal!!!!
31543169
z = Sexp_nodes[node].first;
31553170
if ((z = check_sexp_syntax(z, OPR_AI_GOAL, recursive, bad_node)) != 0){
3156-
return z;
3171+
if (!sexp_recoverable_error(z))
3172+
return z;
3173+
// defer recoverable errors so that the rest of the tree is still checked
3174+
if (deferred_error == SEXP_CHECK_NO_ERROR) {
3175+
deferred_error = z;
3176+
deferred_bad_node = bad_node ? *bad_node : -1;
3177+
}
31573178
}
31583179

31593180
if (Fred_running) {
@@ -4211,6 +4232,14 @@ int check_sexp_syntax(int node, int desired_return_type, int recursive, int *bad
42114232
argnum++;
42124233
}
42134234

4235+
// now that the rest of the tree has been checked, report any recoverable error that was noted along the way
4236+
if (deferred_error != SEXP_CHECK_NO_ERROR)
4237+
{
4238+
if (bad_node)
4239+
*bad_node = deferred_bad_node;
4240+
return deferred_error;
4241+
}
4242+
42144243
return 0;
42154244
}
42164245

@@ -35353,18 +35382,24 @@ bool sexp_recoverable_error(int num)
3535335382
// but the mission will run without crashing.
3535435383
case SEXP_CHECK_AMBIGUOUS_EVENT_NAME:
3535535384
case SEXP_CHECK_AMBIGUOUS_GOAL_NAME:
35385+
return true;
3535635386

3535735387
// Having an invalid gauge in FSO won't hurt,
3535835388
// as all places which call hud_get_gauge() or hud_get_custom_gauge() check its return value for NULL.
3535935389
case SEXP_CHECK_INVALID_CUSTOM_HUD_GAUGE:
3536035390
case SEXP_CHECK_INVALID_ANY_HUD_GAUGE:
35391+
return true;
3536135392

3536235393
// Trying to set an invalid sound environment has no effect, and all sound enviroments are invalid if EFX is disabled.
3536335394
// Invalid sound environment options are simiarly harmless.
3536435395
case SEXP_CHECK_INVALID_SOUND_ENVIRONMENT:
3536535396
case SEXP_CHECK_INVALID_SOUND_ENVIRONMENT_OPTION:
3536635397
return true;
3536735398

35399+
// Certain argument counts historically weren't checked properly, but the runtime code could still recover
35400+
case SEXP_CHECK_BAD_ARG_COUNT_BENIGN:
35401+
return true;
35402+
3536835403
// most errors will halt mission loading
3536935404
default:
3537035405
return false;
@@ -35387,6 +35422,7 @@ const char *sexp_error_message(int num)
3538735422
return "Argument type mismatch";
3538835423

3538935424
case SEXP_CHECK_BAD_ARG_COUNT:
35425+
case SEXP_CHECK_BAD_ARG_COUNT_BENIGN:
3539035426
return "Argument count is illegal";
3539135427

3539235428
case SEXP_CHECK_UNKNOWN_TYPE:

code/parse/sexp.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1205,7 +1205,8 @@ enum sexp_error_check
12051205
SEXP_CHECK_OP_EXPECTED, // operator expected, but found data instead
12061206
SEXP_CHECK_UNKNOWN_OP, // unrecognized operator
12071207
SEXP_CHECK_TYPE_MISMATCH, // return type or data type mismatch
1208-
SEXP_CHECK_BAD_ARG_COUNT, // argument count in incorrect
1208+
SEXP_CHECK_BAD_ARG_COUNT, // argument count is incorrect
1209+
SEXP_CHECK_BAD_ARG_COUNT_BENIGN, // ditto, but don't prevent the mission from loading
12091210
SEXP_CHECK_UNKNOWN_TYPE, // unrecognized return type of data type
12101211

12111212
SEXP_CHECK_INVALID_NUM = 101, // number is not valid

fred2/fredview.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3598,6 +3598,7 @@ int CFREDView::fred_check_sexp(int sexp, int type, const char *location, ...)
35983598
return 1;
35993599
}
36003600

3601+
z = 0;
36013602
if (Error_checker_checks_potential_issues || Error_checker_checks_potential_issues_once)
36023603
z = check_sexp_potential_issues(sexp, &faulty_node, issue_msg);
36033604
if (z)

0 commit comments

Comments
 (0)