Fix error on parsing statement-less GDScript files,

add an empty file warning,
add relevant tests.
This commit is contained in:
ThreeRhinosInAnElephantCostume 2021-09-11 20:38:15 +02:00
parent bb0122c933
commit e99730340b
13 changed files with 57 additions and 4 deletions

View file

@ -328,6 +328,9 @@
<member name="debug/gdscript/warnings/deprecated_keyword" type="bool" setter="" getter="" default="true">
If [code]true[/code], enables warnings when deprecated keywords are used.
</member>
<member name="debug/gdscript/warnings/empty_file" type="bool" setter="" getter="" default="true">
If [code]true[/code], enables warnings when an empty file is parsed.
</member>
<member name="debug/gdscript/warnings/enable" type="bool" setter="" getter="" default="true">
If [code]true[/code], enables specific GDScript warnings (see [code]debug/gdscript/warnings/*[/code] settings). If [code]false[/code], disables all GDScript warnings.
</member>

View file

@ -337,12 +337,29 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_
tokenizer.set_cursor_position(cursor_line, cursor_column);
script_path = p_script_path;
current = tokenizer.scan();
// Avoid error as the first token.
while (current.type == GDScriptTokenizer::Token::ERROR) {
push_error(current.literal);
// Avoid error or newline as the first token.
// The latter can mess with the parser when opening files filled exclusively with comments and newlines.
while (current.type == GDScriptTokenizer::Token::ERROR || current.type == GDScriptTokenizer::Token::NEWLINE) {
if (current.type == GDScriptTokenizer::Token::ERROR) {
push_error(current.literal);
}
current = tokenizer.scan();
}
#ifdef DEBUG_ENABLED
// Warn about parsing an empty script file:
if (current.type == GDScriptTokenizer::Token::TK_EOF) {
// Create a dummy Node for the warning, pointing to the very beginning of the file
Node *nd = alloc_node<PassNode>();
nd->start_line = 1;
nd->start_column = 0;
nd->end_line = 1;
nd->leftmost_column = 0;
nd->rightmost_column = 0;
push_warning(nd, GDScriptWarning::EMPTY_FILE);
}
#endif
push_multiline(false); // Keep one for the whole parsing.
parse_program();
pop_multiline();

View file

@ -145,6 +145,9 @@ String GDScriptWarning::get_message() const {
case REDUNDANT_AWAIT: {
return R"("await" keyword not needed in this case, because the expression isn't a coroutine nor a signal.)";
}
case EMPTY_FILE: {
return "Empty script file.";
}
case WARNING_MAX:
break; // Can't happen, but silences warning
}
@ -190,6 +193,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"ASSERT_ALWAYS_TRUE",
"ASSERT_ALWAYS_FALSE",
"REDUNDANT_AWAIT",
"EMPTY_FILE",
};
static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names.");

View file

@ -68,6 +68,7 @@ public:
ASSERT_ALWAYS_TRUE, // Expression for assert argument is always true.
ASSERT_ALWAYS_FALSE, // Expression for assert argument is always false.
REDUNDANT_AWAIT, // await is used but expression is synchronous (not a signal nor a coroutine).
EMPTY_FILE, // A script file is empty.
WARNING_MAX,
};

View file

@ -496,7 +496,10 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
}
return result;
}
// Script files matching this pattern are allowed to not contain a test() function.
if (source_file.match("*.notest.gd")) {
return result;
}
// Test running.
const Map<StringName, GDScriptFunction *>::Element *test_function_element = script->get_member_functions().find(GDScriptTestRunner::test_function_name);
if (test_function_element == nullptr) {

View file

@ -0,0 +1,4 @@
>> WARNING
>> Line: 1
>> EMPTY_FILE
>> Empty script file.

View file

@ -0,0 +1 @@
#a comment

View file

@ -0,0 +1,4 @@
>> WARNING
>> Line: 1
>> EMPTY_FILE
>> Empty script file.

View file

@ -0,0 +1,4 @@
>> WARNING
>> Line: 1
>> EMPTY_FILE
>> Empty script file.

View file

@ -0,0 +1,4 @@
#a comment, followed by a bunch of newlines

View file

@ -0,0 +1,4 @@
>> WARNING
>> Line: 1
>> EMPTY_FILE
>> Empty script file.