summaryrefslogtreecommitdiff
path: root/modules/gdscript
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript')
-rw-r--r--modules/gdscript/SCsub2
-rw-r--r--modules/gdscript/doc_classes/GDScript.xml2
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp26
-rw-r--r--modules/gdscript/gdscript.cpp359
-rw-r--r--modules/gdscript/gdscript.h59
-rw-r--r--modules/gdscript/gdscript_compiler.cpp58
-rw-r--r--modules/gdscript/gdscript_editor.cpp134
-rw-r--r--modules/gdscript/gdscript_function.cpp164
-rw-r--r--modules/gdscript/gdscript_function.h21
-rw-r--r--modules/gdscript/gdscript_functions.cpp89
-rw-r--r--modules/gdscript/gdscript_functions.h5
-rw-r--r--modules/gdscript/gdscript_parser.cpp755
-rw-r--r--modules/gdscript/gdscript_parser.h35
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp40
-rw-r--r--modules/gdscript/gdscript_tokenizer.h39
-rw-r--r--modules/gdscript/register_types.cpp6
16 files changed, 1358 insertions, 436 deletions
diff --git a/modules/gdscript/SCsub b/modules/gdscript/SCsub
index 73f09f1659..6904154953 100644
--- a/modules/gdscript/SCsub
+++ b/modules/gdscript/SCsub
@@ -9,5 +9,3 @@ env_gdscript.add_source_files(env.modules_sources, "*.cpp")
if env['tools']:
env_gdscript.add_source_files(env.modules_sources, "./editor/*.cpp")
-
-Export('env')
diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml
index 632970f8c0..4cefdbd7cb 100644
--- a/modules/gdscript/doc_classes/GDScript.xml
+++ b/modules/gdscript/doc_classes/GDScript.xml
@@ -8,7 +8,7 @@
[method new] creates a new instance of the script. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/getting_started/scripting/gdscript/index.html</link>
+ <link>https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/index.html</link>
</tutorials>
<demos>
</demos>
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index f2a1a5b50c..e4aee842ba 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -43,10 +43,6 @@ static bool _is_text_char(CharType c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
}
-static bool _is_whitespace(CharType c) {
- return c == '\t' || c == ' ';
-}
-
static bool _is_char(CharType c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
@@ -76,6 +72,7 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
bool in_word = false;
bool in_function_name = false;
bool in_variable_declaration = false;
+ bool in_function_args = false;
bool in_member_variable = false;
bool in_node_path = false;
bool is_hex_notation = false;
@@ -121,8 +118,8 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
is_hex_notation = false;
}
- // check for dot or underscore or 'x' for hex notation in floating point number
- if ((str[j] == '.' || str[j] == 'x' || str[j] == '_') && !in_word && prev_is_number && !is_number) {
+ // check for dot or underscore or 'x' for hex notation in floating point number or 'e' for scientific notation
+ if ((str[j] == '.' || str[j] == 'x' || str[j] == '_' || str[j] == 'e') && !in_word && prev_is_number && !is_number) {
is_number = true;
is_symbol = false;
is_char = false;
@@ -224,17 +221,24 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
}
if (is_symbol) {
- in_function_name = false;
- in_member_variable = false;
- if (expect_type && str[j] != ' ' && str[j] != '\t' && str[j] != ':') {
+ if (in_function_name) {
+ in_function_args = true;
+ }
+
+ if (in_function_args && str[j] == ')') {
+ in_function_args = false;
+ }
+
+ if (expect_type && prev_is_char) {
expect_type = false;
}
+
if (j > 0 && str[j] == '>' && str[j - 1] == '-') {
expect_type = true;
}
- if (in_variable_declaration || previous_text == "(" || previous_text == ",") {
+ if (in_variable_declaration || in_function_args) {
int k = j;
// Skip space
while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) {
@@ -248,6 +252,8 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
}
in_variable_declaration = false;
+ in_function_name = false;
+ in_member_variable = false;
}
if (!in_node_path && in_region == -1 && str[j] == '$') {
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index cff3be76ae..538249c8e2 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -30,13 +30,13 @@
#include "gdscript.h"
-#include "engine.h"
+#include "core/engine.h"
+#include "core/global_constants.h"
+#include "core/io/file_access_encrypted.h"
+#include "core/os/file_access.h"
+#include "core/os/os.h"
+#include "core/project_settings.h"
#include "gdscript_compiler.h"
-#include "global_constants.h"
-#include "io/file_access_encrypted.h"
-#include "os/file_access.h"
-#include "os/os.h"
-#include "project_settings.h"
///////////////////////////
@@ -181,7 +181,11 @@ Variant GDScript::_new(const Variant **p_args, int p_argcount, Variant::CallErro
bool GDScript::can_instance() const {
- return valid || (!tool && !ScriptServer::is_scripting_enabled());
+#ifdef TOOLS_ENABLED
+ return valid && (tool || ScriptServer::is_scripting_enabled());
+#else
+ return valid;
+#endif
}
Ref<Script> GDScript::get_base_script() const {
@@ -290,11 +294,6 @@ bool GDScript::get_property_default_value(const StringName &p_property, Variant
#ifdef TOOLS_ENABLED
- /*
- for (const Map<StringName,Variant>::Element *I=member_default_values.front();I;I=I->next()) {
- print_line("\t"+String(String(I->key())+":"+String(I->get())));
- }
- */
const Map<StringName, Variant>::Element *E = member_default_values_cache.find(p_property);
if (E) {
r_value = E->get();
@@ -310,27 +309,6 @@ bool GDScript::get_property_default_value(const StringName &p_property, Variant
ScriptInstance *GDScript::instance_create(Object *p_this) {
- if (!tool && !ScriptServer::is_scripting_enabled()) {
-
-#ifdef TOOLS_ENABLED
-
- //instance a fake script for editing the values
- //plist.invert();
-
- /*print_line("CREATING PLACEHOLDER");
- for(List<PropertyInfo>::Element *E=plist.front();E;E=E->next()) {
- print_line(E->get().name);
- }*/
- PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(GDScriptLanguage::get_singleton(), Ref<Script>(this), p_this));
- placeholders.insert(si);
- //_update_placeholder(si);
- _update_exports();
- return si;
-#else
- return NULL;
-#endif
- }
-
GDScript *top = this;
while (top->_base)
top = top->_base;
@@ -349,6 +327,18 @@ ScriptInstance *GDScript::instance_create(Object *p_this) {
Variant::CallError unchecked_error;
return _create_instance(NULL, 0, p_this, Object::cast_to<Reference>(p_this), unchecked_error);
}
+
+PlaceHolderScriptInstance *GDScript::placeholder_instance_create(Object *p_this) {
+#ifdef TOOLS_ENABLED
+ PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(GDScriptLanguage::get_singleton(), Ref<Script>(this), p_this));
+ placeholders.insert(si);
+ _update_exports();
+ return si;
+#else
+ return NULL;
+#endif
+}
+
bool GDScript::instance_has(const Object *p_this) const {
#ifndef NO_THREADS
@@ -405,7 +395,6 @@ bool GDScript::_update_exports() {
bool changed = false;
if (source_changed_cache) {
- //print_line("updating source for "+get_path());
source_changed_cache = false;
changed = true;
@@ -432,34 +421,40 @@ bool GDScript::_update_exports() {
base_cache = Ref<GDScript>();
}
- if (c->extends_used && String(c->extends_file) != "" && String(c->extends_file) != get_path()) {
-
- String path = c->extends_file;
- if (path.is_rel_path()) {
+ if (c->extends_used) {
+ String path = "";
+ if (String(c->extends_file) != "" && String(c->extends_file) != get_path()) {
+ path = c->extends_file;
+ if (path.is_rel_path()) {
- String base = get_path();
- if (base == "" || base.is_rel_path()) {
+ String base = get_path();
+ if (base == "" || base.is_rel_path()) {
- ERR_PRINT(("Could not resolve relative path for parent class: " + path).utf8().get_data());
- } else {
- path = base.get_base_dir().plus_file(path);
+ ERR_PRINT(("Could not resolve relative path for parent class: " + path).utf8().get_data());
+ } else {
+ path = base.get_base_dir().plus_file(path);
+ }
}
- }
+ } else if (c->extends_class.size() != 0) {
+ String base = c->extends_class[0];
- if (path != get_path()) {
+ if (ScriptServer::is_global_class(base))
+ path = ScriptServer::get_global_class_path(base);
+ }
- Ref<GDScript> bf = ResourceLoader::load(path);
+ if (path != "") {
+ if (path != get_path()) {
- if (bf.is_valid()) {
+ Ref<GDScript> bf = ResourceLoader::load(path);
- //print_line("parent is: "+bf->get_path());
- base_cache = bf;
- bf->inheriters_cache.insert(get_instance_id());
+ if (bf.is_valid()) {
- //bf->_update_exports(p_instances,true,false);
+ base_cache = bf;
+ bf->inheriters_cache.insert(get_instance_id());
+ }
+ } else {
+ ERR_PRINT(("Path extending itself in " + path).utf8().get_data());
}
- } else {
- ERR_PRINT(("Path extending itself in " + path).utf8().get_data());
}
}
@@ -471,7 +466,6 @@ bool GDScript::_update_exports() {
continue;
members_cache.push_back(c->variables[i]._export);
- //print_line("found "+c->variables[i]._export.name);
member_default_values_cache[c->variables[i].identifier] = c->variables[i].default_value;
}
@@ -480,9 +474,19 @@ bool GDScript::_update_exports() {
for (int i = 0; i < c->_signals.size(); i++) {
_signals[c->_signals[i].name] = c->_signals[i].arguments;
}
+ } else {
+ for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
+ E->get()->set_build_failed(true);
+ }
+ return false;
}
} else {
- //print_line("unchanged is "+get_path());
+ if (!valid) {
+ for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
+ E->get()->set_build_failed(true);
+ }
+ return false;
+ }
}
if (base_cache.is_valid()) {
@@ -491,17 +495,15 @@ bool GDScript::_update_exports() {
}
}
- if (/*changed &&*/ placeholders.size()) { //hm :(
+ if (placeholders.size()) { //hm :(
- //print_line("updating placeholders for "+get_path());
-
- //update placeholders if any
+ // update placeholders if any
Map<StringName, Variant> values;
List<PropertyInfo> propnames;
_update_exports_values(values, propnames);
for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
-
+ E->get()->set_build_failed(false);
E->get()->update(propnames, values);
}
}
@@ -521,7 +523,6 @@ void GDScript::update_exports() {
Set<ObjectID> copy = inheriters_cache; //might get modified
- //print_line("update exports for "+get_path()+" ic: "+itos(copy.size()));
for (Set<ObjectID>::Element *E = copy.front(); E; E = E->next()) {
Object *id = ObjectDB::get_instance(E->get());
GDScript *s = Object::cast_to<GDScript>(id);
@@ -596,6 +597,15 @@ Error GDScript::reload(bool p_keep_state) {
return err;
}
}
+#if DEBUG_ENABLED
+ for (const List<GDScriptWarning>::Element *E = parser.get_warnings().front(); E; E = E->next()) {
+ const GDScriptWarning &warning = E->get();
+ if (ScriptDebugger::get_singleton()) {
+ Vector<ScriptLanguage::StackInfo> si;
+ ScriptDebugger::get_singleton()->send_error("", get_path(), warning.line, warning.get_name(), warning.get_message(), ERR_HANDLER_WARNING, si);
+ }
+ }
+#endif
valid = true;
@@ -725,22 +735,36 @@ Error GDScript::load_byte_code(const String &p_path) {
FileAccess *fa = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_V(!fa, ERR_CANT_OPEN);
+
FileAccessEncrypted *fae = memnew(FileAccessEncrypted);
ERR_FAIL_COND_V(!fae, ERR_CANT_OPEN);
+
Vector<uint8_t> key;
key.resize(32);
for (int i = 0; i < key.size(); i++) {
key.write[i] = script_encryption_key[i];
}
+
Error err = fae->open_and_parse(fa, key, FileAccessEncrypted::MODE_READ);
- ERR_FAIL_COND_V(err, err);
+
+ if (err) {
+ fa->close();
+ memdelete(fa);
+ memdelete(fae);
+
+ ERR_FAIL_COND_V(err, err);
+ }
+
bytecode.resize(fae->get_len());
fae->get_buffer(bytecode.ptrw(), bytecode.size());
+ fae->close();
memdelete(fae);
+
} else {
bytecode = FileAccess::get_file_as_array(p_path);
}
+
ERR_FAIL_COND_V(bytecode.size() == 0, ERR_PARSE_ERROR);
path = p_path;
@@ -808,7 +832,6 @@ Error GDScript::load_source_code(const String &p_path) {
#ifdef TOOLS_ENABLED
source_changed_cache = true;
#endif
- //print_line("LSC :"+get_path());
path = p_path;
return OK;
}
@@ -844,7 +867,6 @@ bool GDScript::has_script_signal(const StringName &p_signal) const {
else if (base_cache.is_valid()) {
return base_cache->has_script_signal(p_signal);
}
-
#endif
return false;
}
@@ -1490,7 +1512,6 @@ int GDScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_
p_info_arr[current].self_time = elem->self()->profile.last_frame_self_time;
p_info_arr[current].total_time = elem->self()->profile.last_frame_total_time;
p_info_arr[current].signature = elem->self()->profile.signature;
- //print_line(String(elem->self()->profile.signature)+": "+itos(elem->self()->profile.last_frame_call_count));
current++;
}
elem = elem->next();
@@ -1529,7 +1550,7 @@ struct GDScriptDepSort {
void GDScriptLanguage::reload_all_scripts() {
#ifdef DEBUG_ENABLED
- print_line("RELOAD ALL SCRIPTS");
+ print_verbose("GDScript: Reloading all scripts");
if (lock) {
lock->lock();
}
@@ -1539,7 +1560,7 @@ void GDScriptLanguage::reload_all_scripts() {
SelfList<GDScript> *elem = script_list.first();
while (elem) {
if (elem->self()->get_path().is_resource_file()) {
- print_line("FOUND: " + elem->self()->get_path());
+ print_verbose("GDScript: Found: " + elem->self()->get_path());
scripts.push_back(Ref<GDScript>(elem->self())); //cast to gdscript to avoid being erased by accident
}
elem = elem->next();
@@ -1555,7 +1576,7 @@ void GDScriptLanguage::reload_all_scripts() {
for (List<Ref<GDScript> >::Element *E = scripts.front(); E; E = E->next()) {
- print_line("RELOADING: " + E->get()->get_path());
+ print_verbose("GDScript: Reloading: " + E->get()->get_path());
E->get()->load_source_code(E->get()->get_path());
E->get()->reload(true);
}
@@ -1686,7 +1707,6 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
void GDScriptLanguage::frame() {
- //print_line("calls: "+itos(calls));
calls = 0;
#ifdef DEBUG_ENABLED
@@ -1771,10 +1791,11 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"remote",
"sync",
"master",
+ "puppet",
"slave",
"remotesync",
"mastersync",
- "slavesync",
+ "puppetsync",
0
};
@@ -1796,7 +1817,7 @@ bool GDScriptLanguage::handles_global_class_type(const String &p_type) const {
return p_type == "GDScript";
}
-String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type) const {
+String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const {
PoolVector<uint8_t> sourcef;
Error err;
@@ -1861,12 +1882,180 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
}
}
}
+ if (r_icon_path) {
+ if (c->icon_path.empty() || c->icon_path.is_abs_path())
+ *r_icon_path = c->icon_path;
+ else if (c->icon_path.is_rel_path())
+ *r_icon_path = p_path.get_base_dir().plus_file(c->icon_path).simplify_path();
+ }
return c->name;
}
return String();
}
+#ifdef DEBUG_ENABLED
+String GDScriptWarning::get_message() const {
+
+#define CHECK_SYMBOLS(m_amount) ERR_FAIL_COND_V(symbols.size() < m_amount, String());
+
+ switch (code) {
+ case UNASSIGNED_VARIABLE_OP_ASSIGN: {
+ CHECK_SYMBOLS(1);
+ return "Using assignment with operation but the variable '" + symbols[0] + "' was not previously assigned a value.";
+ } break;
+ case UNASSIGNED_VARIABLE: {
+ CHECK_SYMBOLS(1);
+ return "The variable '" + symbols[0] + "' was used but never assigned a value.";
+ } break;
+ case UNUSED_VARIABLE: {
+ CHECK_SYMBOLS(1);
+ return "The local variable '" + symbols[0] + "' is declared but never used in the block.";
+ } break;
+ case UNUSED_CLASS_VARIABLE: {
+ CHECK_SYMBOLS(1);
+ return "The class variable '" + symbols[0] + "' is declared but never used in the script.";
+ } break;
+ case UNUSED_ARGUMENT: {
+ CHECK_SYMBOLS(2);
+ return "The argument '" + symbols[1] + "' is never used in the function '" + symbols[0] + "'.";
+ } break;
+ case UNREACHABLE_CODE: {
+ CHECK_SYMBOLS(1);
+ return "Unreachable code (statement after return) in function '" + symbols[0] + "()'.";
+ } break;
+ case STANDALONE_EXPRESSION: {
+ return "Standalone expression (the line has no effect).";
+ } break;
+ case VOID_ASSIGNMENT: {
+ CHECK_SYMBOLS(1);
+ return "Assignment operation, but the function '" + symbols[0] + "()' returns void.";
+ } break;
+ case NARROWING_CONVERSION: {
+ return "Narrowing coversion (float is converted to int and lose precision).";
+ } break;
+ case FUNCTION_MAY_YIELD: {
+ CHECK_SYMBOLS(1);
+ return "Assigned variable is typed but the function '" + symbols[0] + "()' may yield and return a GDScriptFunctionState instead.";
+ } break;
+ case VARIABLE_CONFLICTS_FUNCTION: {
+ CHECK_SYMBOLS(1);
+ return "Variable declaration of '" + symbols[0] + "' conflicts with a function of the same name.";
+ } break;
+ case FUNCTION_CONFLICTS_VARIABLE: {
+ CHECK_SYMBOLS(1);
+ return "Function declaration of '" + symbols[0] + "()' conflicts with a variable of the same name.";
+ } break;
+ case FUNCTION_CONFLICTS_CONSTANT: {
+ CHECK_SYMBOLS(1);
+ return "Function declaration of '" + symbols[0] + "()' conflicts with a constant of the same name.";
+ } break;
+ case INCOMPATIBLE_TERNARY: {
+ return "Values of the ternary conditional are not mutually compatible.";
+ } break;
+ case UNUSED_SIGNAL: {
+ CHECK_SYMBOLS(1);
+ return "The signal '" + symbols[0] + "' is declared but never emitted.";
+ } break;
+ case RETURN_VALUE_DISCARDED: {
+ CHECK_SYMBOLS(1);
+ return "The function '" + symbols[0] + "()' returns a value, but this value is never used.";
+ } break;
+ case PROPERTY_USED_AS_FUNCTION: {
+ CHECK_SYMBOLS(2);
+ return "The method '" + symbols[0] + "()' was not found in base '" + symbols[1] + "' but there's a property with the same name. Did you mean to access it?";
+ } break;
+ case CONSTANT_USED_AS_FUNCTION: {
+ CHECK_SYMBOLS(2);
+ return "The method '" + symbols[0] + "()' was not found in base '" + symbols[1] + "' but there's a constant with the same name. Did you mean to access it?";
+ } break;
+ case FUNCTION_USED_AS_PROPERTY: {
+ CHECK_SYMBOLS(2);
+ return "The property '" + symbols[0] + "' was not found in base '" + symbols[1] + "' but there's a method with the same name. Did you mean to call it?";
+ } break;
+ case INTEGER_DIVISION: {
+ return "Integer division, decimal part will be discarded.";
+ } break;
+ case UNSAFE_PROPERTY_ACCESS: {
+ CHECK_SYMBOLS(2);
+ return "The property '" + symbols[0] + "' is not present on the inferred type '" + symbols[1] + "' (but may be present on a subtype).";
+ } break;
+ case UNSAFE_METHOD_ACCESS: {
+ CHECK_SYMBOLS(2);
+ return "The method '" + symbols[0] + "' is not present on the inferred type '" + symbols[1] + "' (but may be present on a subtype).";
+ } break;
+ case UNSAFE_CAST: {
+ CHECK_SYMBOLS(1);
+ return "The value is cast to '" + symbols[0] + "' but has an unknown type.";
+ } break;
+ case UNSAFE_CALL_ARGUMENT: {
+ CHECK_SYMBOLS(4);
+ return "The argument '" + symbols[0] + "' of the function '" + symbols[1] + "' requires a the subtype '" + symbols[2] + "' but the supertype '" + symbols[3] + "' was provided";
+ } break;
+ case DEPRECATED_KEYWORD: {
+ CHECK_SYMBOLS(2);
+ return "The '" + symbols[0] + "' keyword is deprecated and will be removed in a future release, please replace its uses by '" + symbols[1] + "'.";
+ } break;
+ case WARNING_MAX: break; // Can't happen, but silences warning
+ }
+ ERR_EXPLAIN("Invalid GDScript warning code: " + get_name_from_code(code));
+ ERR_FAIL_V(String());
+
+#undef CHECK_SYMBOLS
+}
+
+String GDScriptWarning::get_name() const {
+ return get_name_from_code(code);
+}
+
+String GDScriptWarning::get_name_from_code(Code p_code) {
+ ERR_FAIL_COND_V(p_code < 0 || p_code >= WARNING_MAX, String());
+
+ static const char *names[] = {
+ "UNASSIGNED_VARIABLE",
+ "UNASSIGNED_VARIABLE_OP_ASSIGN",
+ "UNUSED_VARIABLE",
+ "UNUSED_CLASS_VARIABLE",
+ "UNUSED_ARGUMENT",
+ "UNREACHABLE_CODE",
+ "STANDALONE_EXPRESSION",
+ "VOID_ASSIGNMENT",
+ "NARROWING_CONVERSION",
+ "FUNCTION_MAY_YIELD",
+ "VARIABLE_CONFLICTS_FUNCTION",
+ "FUNCTION_CONFLICTS_VARIABLE",
+ "FUNCTION_CONFLICTS_CONSTANT",
+ "INCOMPATIBLE_TERNARY",
+ "UNUSED_SIGNAL",
+ "RETURN_VALUE_DISCARDED",
+ "PROPERTY_USED_AS_FUNCTION",
+ "CONSTANT_USED_AS_FUNCTION",
+ "FUNCTION_USED_AS_PROPERTY",
+ "INTEGER_DIVISION",
+ "UNSAFE_PROPERTY_ACCESS",
+ "UNSAFE_METHOD_ACCESS",
+ "UNSAFE_CAST",
+ "UNSAFE_CALL_ARGUMENT",
+ "DEPRECATED_KEYWORD",
+ NULL
+ };
+
+ return names[(int)p_code];
+}
+
+GDScriptWarning::Code GDScriptWarning::get_code_from_name(const String &p_name) {
+ for (int i = 0; i < WARNING_MAX; i++) {
+ if (get_name_from_code((Code)i) == p_name) {
+ return (Code)i;
+ }
+ }
+
+ ERR_EXPLAIN("Invalid GDScript warning name: " + p_name);
+ ERR_FAIL_V(WARNING_MAX);
+}
+
+#endif // DEBUG_ENABLED
+
GDScriptLanguage::GDScriptLanguage() {
calls = 0;
@@ -1891,18 +2080,28 @@ GDScriptLanguage::GDScriptLanguage() {
_debug_call_stack_pos = 0;
int dmcs = GLOBAL_DEF("debug/settings/gdscript/max_call_stack", 1024);
+ ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/gdscript/max_call_stack", PropertyInfo(Variant::INT, "debug/settings/gdscript/max_call_stack", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater")); //minimum is 1024
+
if (ScriptDebugger::get_singleton()) {
//debugging enabled!
_debug_max_call_stack = dmcs;
- if (_debug_max_call_stack < 1024)
- _debug_max_call_stack = 1024;
_call_stack = memnew_arr(CallLevel, _debug_max_call_stack + 1);
} else {
_debug_max_call_stack = 0;
_call_stack = NULL;
}
+
+#ifdef DEBUG_ENABLED
+ GLOBAL_DEF("debug/gdscript/warnings/enable", true);
+ GLOBAL_DEF("debug/gdscript/warnings/treat_warnings_as_errors", false);
+ GLOBAL_DEF("debug/gdscript/completion/autocomplete_setters_and_getters", false);
+ for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
+ String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower();
+ GLOBAL_DEF("debug/gdscript/warnings/" + warning, !warning.begins_with("unsafe_"));
+ }
+#endif // DEBUG_ENABLED
}
GDScriptLanguage::~GDScriptLanguage() {
@@ -1933,23 +2132,14 @@ RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_ori
script->set_script_path(p_original_path); // script needs this.
script->set_path(p_original_path);
Error err = script->load_byte_code(p_path);
-
- if (err != OK) {
-
- ERR_FAIL_COND_V(err != OK, RES());
- }
+ ERR_FAIL_COND_V(err != OK, RES());
} else {
Error err = script->load_source_code(p_path);
-
- if (err != OK) {
-
- ERR_FAIL_COND_V(err != OK, RES());
- }
+ ERR_FAIL_COND_V(err != OK, RES());
script->set_script_path(p_original_path); // script needs this.
script->set_path(p_original_path);
- //script->set_name(p_path.get_file());
script->reload();
}
@@ -1958,6 +2148,7 @@ RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_ori
return scriptres;
}
+
void ResourceFormatLoaderGDScript::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("gd");
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 79ac9ed413..752d660ffb 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -31,10 +31,10 @@
#ifndef GDSCRIPT_H
#define GDSCRIPT_H
+#include "core/io/resource_loader.h"
+#include "core/io/resource_saver.h"
+#include "core/script_language.h"
#include "gdscript_function.h"
-#include "io/resource_loader.h"
-#include "io/resource_saver.h"
-#include "script_language.h"
class GDScriptNativeClass : public Reference {
@@ -141,13 +141,13 @@ protected:
static void _bind_methods();
public:
- bool is_valid() const { return valid; }
+ virtual bool is_valid() const { return valid; }
const Map<StringName, Ref<GDScript> > &get_subclasses() const { return subclasses; }
const Map<StringName, Variant> &get_constants() const { return constants; }
const Set<StringName> &get_members() const { return members; }
const GDScriptDataType &get_member_type(const StringName &p_member) const {
- ERR_FAIL_COND_V(!member_indices.has(p_member), GDScriptDataType());
+ CRASH_COND(!member_indices.has(p_member));
return member_indices[p_member].data_type;
}
const Map<StringName, GDScriptFunction *> &get_member_functions() const { return member_functions; }
@@ -171,6 +171,7 @@ public:
virtual StringName get_instance_base_type() const; // this may not work in all scripts, will return empty if so
virtual ScriptInstance *instance_create(Object *p_this);
+ virtual PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this);
virtual bool instance_has(const Object *p_this) const;
virtual bool has_source_code() const;
@@ -261,6 +262,50 @@ public:
~GDScriptInstance();
};
+#ifdef DEBUG_ENABLED
+struct GDScriptWarning {
+ enum Code {
+ UNASSIGNED_VARIABLE, // Variable used but never assigned
+ UNASSIGNED_VARIABLE_OP_ASSIGN, // Variable never assigned but used in an assignment operation (+=, *=, etc)
+ UNUSED_VARIABLE, // Local variable is declared but never used
+ UNUSED_CLASS_VARIABLE, // Class variable is declared but never used in the file
+ UNUSED_ARGUMENT, // Function argument is never used
+ UNREACHABLE_CODE, // Code after a return statement
+ STANDALONE_EXPRESSION, // Expression not assigned to a variable
+ VOID_ASSIGNMENT, // Function returns void but it's assigned to a variable
+ NARROWING_CONVERSION, // Float value into an integer slot, precision is lost
+ FUNCTION_MAY_YIELD, // Typed assign of function call that yields (it may return a function state)
+ VARIABLE_CONFLICTS_FUNCTION, // Variable has the same name of a function
+ FUNCTION_CONFLICTS_VARIABLE, // Function has the same name of a variable
+ FUNCTION_CONFLICTS_CONSTANT, // Function has the same name of a constant
+ INCOMPATIBLE_TERNARY, // Possible values of a ternary if are not mutually compatible
+ UNUSED_SIGNAL, // Signal is defined but never emitted
+ RETURN_VALUE_DISCARDED, // Function call returns something but the value isn't used
+ PROPERTY_USED_AS_FUNCTION, // Function not found, but there's a property with the same name
+ CONSTANT_USED_AS_FUNCTION, // Function not found, but there's a constant with the same name
+ FUNCTION_USED_AS_PROPERTY, // Property not found, but there's a function with the same name
+ INTEGER_DIVISION, // Integer divide by integer, decimal part is discarded
+ UNSAFE_PROPERTY_ACCESS, // Property not found in the detected type (but can be in subtypes)
+ UNSAFE_METHOD_ACCESS, // Function not found in the detected type (but can be in subtypes)
+ UNSAFE_CAST, // Cast used in an unknown type
+ UNSAFE_CALL_ARGUMENT, // Function call argument is of a supertype of the require argument
+ DEPRECATED_KEYWORD, // The keyword is deprecated and should be replaced
+ WARNING_MAX,
+ } code;
+ Vector<String> symbols;
+ int line;
+
+ String get_name() const;
+ String get_message() const;
+ static String get_name_from_code(Code p_code);
+ static Code get_code_from_name(const String &p_name);
+
+ GDScriptWarning() :
+ code(WARNING_MAX),
+ line(-1) {}
+};
+#endif // DEBUG_ENABLED
+
class GDScriptLanguage : public ScriptLanguage {
static GDScriptLanguage *singleton;
@@ -397,7 +442,7 @@ public:
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
virtual bool is_using_templates();
virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script);
- virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL, Set<int> *r_safe_lines = NULL) const;
+ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL, List<ScriptLanguage::Warning> *r_warnings = NULL, Set<int> *r_safe_lines = NULL) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;
@@ -448,7 +493,7 @@ public:
/* GLOBAL CLASSES */
virtual bool handles_global_class_type(const String &p_type) const;
- virtual String get_global_class_name(const String &p_path, String *r_base_type = NULL) const;
+ virtual String get_global_class_name(const String &p_path, String *r_base_type = NULL, String *r_icon_path = NULL) const;
GDScriptLanguage();
~GDScriptLanguage();
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index fe393957db..caa7fbfeca 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -132,7 +132,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
result.kind = GDScriptDataType::SCRIPT;
result.script_type = p_datatype.script_type;
result.native_type = result.script_type->get_instance_base_type();
- }
+ } break;
case GDScriptParser::DataType::GDSCRIPT: {
result.kind = GDScriptDataType::GDSCRIPT;
result.script_type = p_datatype.script_type;
@@ -388,7 +388,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
int ret = _parse_expression(codegen, an->elements[i], slevel);
if (ret < 0)
return ret;
- if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
+ if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
slevel++;
codegen.alloc_stack(slevel);
}
@@ -419,7 +419,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
int ret = _parse_expression(codegen, dn->elements[i].key, slevel);
if (ret < 0)
return ret;
- if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
+ if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
slevel++;
codegen.alloc_stack(slevel);
}
@@ -429,7 +429,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
ret = _parse_expression(codegen, dn->elements[i].value, slevel);
if (ret < 0)
return ret;
- if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
+ if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
slevel++;
codegen.alloc_stack(slevel);
}
@@ -518,7 +518,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
}
}
- codegen.opcodes.push_back(src_addr); // source adddress
+ codegen.opcodes.push_back(src_addr); // source address
int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
codegen.alloc_stack(p_stack_level);
@@ -545,7 +545,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
int ret = _parse_expression(codegen, on->arguments[i], slevel);
if (ret < 0)
return ret;
- if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
+ if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
slevel++;
codegen.alloc_stack(slevel);
}
@@ -578,7 +578,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
int ret = _parse_expression(codegen, on->arguments[i], slevel);
if (ret < 0)
return ret;
- if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
+ if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
slevel++;
codegen.alloc_stack(slevel);
}
@@ -606,7 +606,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
if (ret < 0)
return ret;
- if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
+ if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
slevel++;
codegen.alloc_stack(slevel);
}
@@ -655,7 +655,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
ret = _parse_expression(codegen, on->arguments[i], slevel);
if (ret < 0)
return ret;
- if (ret & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
+ if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
slevel++;
codegen.alloc_stack(slevel);
}
@@ -681,7 +681,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
int ret = _parse_expression(codegen, on->arguments[i], slevel);
if (ret < 0)
return ret;
- if (ret & (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS)) {
+ if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
slevel++;
codegen.alloc_stack(slevel);
}
@@ -1009,8 +1009,6 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
return prev_pos;
int retval = prev_pos;
- //print_line("retval: "+itos(retval));
-
if (retval & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
slevel++;
codegen.alloc_stack(slevel);
@@ -1255,6 +1253,25 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
} break;
+ case GDScriptParser::OperatorNode::OP_IS_BUILTIN: {
+ ERR_FAIL_COND_V(on->arguments.size() != 2, false);
+ ERR_FAIL_COND_V(on->arguments[1]->type != GDScriptParser::Node::TYPE_TYPE, false);
+
+ int slevel = p_stack_level;
+
+ int src_address_a = _parse_expression(codegen, on->arguments[0], slevel);
+ if (src_address_a < 0)
+ return -1;
+
+ if (src_address_a & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS)
+ slevel++; //uses stack for return, increase stack
+
+ const GDScriptParser::TypeNode *tn = static_cast<const GDScriptParser::TypeNode *>(on->arguments[1]);
+
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_IS_BUILTIN); // perform operator
+ codegen.opcodes.push_back(src_address_a); // argument 1
+ codegen.opcodes.push_back((int)tn->vtype); // argument 2 (unary only takes one parameter)
+ } break;
default: {
ERR_EXPLAIN("Bug in bytecode compiler, unexpected operator #" + itos(on->op) + " in parse tree while parsing expression.");
@@ -1324,6 +1341,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo
int ret = _parse_expression(codegen, op, p_stack_level);
if (ret < 0) {
+ memdelete(id);
+ memdelete(op);
return ERR_PARSE_ERROR;
}
@@ -1343,6 +1362,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo
// compile the condition
int ret = _parse_expression(codegen, branch.compiled_pattern, p_stack_level);
if (ret < 0) {
+ memdelete(id);
+ memdelete(op);
return ERR_PARSE_ERROR;
}
@@ -1355,6 +1376,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo
Error err = _parse_block(codegen, branch.body, p_stack_level, p_break_addr, continue_addr);
if (err) {
+ memdelete(id);
+ memdelete(op);
return ERR_PARSE_ERROR;
}
@@ -1366,6 +1389,9 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo
codegen.opcodes.write[break_addr + 1] = codegen.opcodes.size();
+ memdelete(id);
+ memdelete(op);
+
} break;
case GDScriptParser::ControlFlowNode::CF_IF: {
@@ -1994,8 +2020,11 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner
p_script->_signals[name] = p_class->_signals[i].arguments;
}
- if (!p_class->owner) {
+ if (p_class->owner) {
parsed_classes.insert(p_class->name);
+ if (parsing_classes.has(p_class->name)) {
+ parsing_classes.erase(p_class->name);
+ }
}
//parse sub-classes
@@ -2011,7 +2040,6 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner
Error err = _parse_class_level(subclass.ptr(), p_script, p_class->subclasses[i], p_keep_state);
if (err)
return err;
- parsing_classes.erase(name);
}
#ifdef TOOLS_ENABLED
@@ -2071,8 +2099,6 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
//validate instances if keeping state
if (p_keep_state) {
-
- print_line("RELOAD KEEP " + p_script->path);
for (Set<Object *>::Element *E = p_script->instances.front(); E;) {
Set<Object *>::Element *N = E->next();
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 2a42524ba7..068e8d2d92 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -31,40 +31,31 @@
#include "gdscript.h"
#include "core/engine.h"
-#include "editor/editor_settings.h"
+#include "core/global_constants.h"
+#include "core/os/file_access.h"
#include "gdscript_compiler.h"
-#include "global_constants.h"
-#include "os/file_access.h"
#ifdef TOOLS_ENABLED
-#include "core/reference.h"
#include "editor/editor_file_system.h"
#include "editor/editor_settings.h"
-#include "engine.h"
#endif
void GDScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
p_delimiters->push_back("#");
- p_delimiters->push_back("\"\"\" \"\"\"");
}
void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const {
p_delimiters->push_back("\" \"");
p_delimiters->push_back("' '");
+ p_delimiters->push_back("\"\"\" \"\"\"");
}
Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const {
-#ifdef TOOLS_ENABLED
- bool th = EDITOR_DEF("text_editor/completion/add_type_hints", false);
-#else
- bool th = false;
-#endif
-
String _template = "extends %BASE%\n"
"\n"
"# Declare member variables here. Examples:\n"
- "# var a %INT_TYPE%= 2\n"
- "# var b %STRING_TYPE%= \"text\"\n"
+ "# var a%INT_TYPE% = 2\n"
+ "# var b%STRING_TYPE% = \"text\"\n"
"\n"
"# Called when the node enters the scene tree for the first time.\n"
"func _ready()%VOID_RETURN%:\n"
@@ -76,9 +67,9 @@ Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const Str
#ifdef TOOLS_ENABLED
if (EDITOR_DEF("text_editor/completion/add_type_hints", false)) {
- _template = _template.replace("%INT_TYPE%", ": int ");
- _template = _template.replace("%STRING_TYPE%", ": String ");
- _template = _template.replace("%FLOAT_TYPE%", " : float");
+ _template = _template.replace("%INT_TYPE%", ": int");
+ _template = _template.replace("%STRING_TYPE%", ": String");
+ _template = _template.replace("%FLOAT_TYPE%", ": float");
_template = _template.replace("%VOID_RETURN%", " -> void");
} else {
_template = _template.replace("%INT_TYPE%", "");
@@ -116,11 +107,24 @@ void GDScriptLanguage::make_template(const String &p_class_name, const String &p
p_script->set_source_code(src);
}
-bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, Set<int> *r_safe_lines) const {
+bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const {
GDScriptParser parser;
Error err = parser.parse(p_script, p_path.get_base_dir(), true, p_path, false, r_safe_lines);
+#ifdef DEBUG_ENABLED
+ if (r_warnings) {
+ for (const List<GDScriptWarning>::Element *E = parser.get_warnings().front(); E; E = E->next()) {
+ const GDScriptWarning &warn = E->get();
+ ScriptLanguage::Warning w;
+ w.line = warn.line;
+ w.code = (int)warn.code;
+ w.string_code = GDScriptWarning::get_name_from_code(warn.code);
+ w.message = warn.get_message();
+ r_warnings->push_back(w);
+ }
+ }
+#endif
if (err) {
r_line_error = parser.get_error_line();
r_col_error = parser.get_error_column();
@@ -179,7 +183,6 @@ int GDScriptLanguage::find_function(const String &p_function, const String &p_co
if (tokenizer.get_token() == GDScriptTokenizer::TK_NEWLINE) {
indent = tokenizer.get_token_line_indent();
}
- //print_line("TOKEN: "+String(GDScriptTokenizer::get_token_name(tokenizer.get_token())));
if (indent == 0 && tokenizer.get_token() == GDScriptTokenizer::TK_PR_FUNCTION && tokenizer.get_token(1) == GDScriptTokenizer::TK_IDENTIFIER) {
String identifier = tokenizer.get_token_identifier(1);
@@ -188,7 +191,6 @@ int GDScriptLanguage::find_function(const String &p_function, const String &p_co
}
}
tokenizer.advance();
- //print_line("NEXT: "+String(GDScriptTokenizer::get_token_name(tokenizer.get_token())));
}
return -1;
}
@@ -455,7 +457,7 @@ String GDScriptLanguage::make_function(const String &p_class, const String &p_na
if (th) {
String type = p_args[i].get_slice(":", 1);
if (!type.empty() && type != "var") {
- s += " : " + type;
+ s += ": " + type;
}
}
}
@@ -1102,6 +1104,7 @@ static bool _guess_expression_type(const GDScriptCompletionContext &p_context, c
} break;
}
} break;
+ default: {}
}
// It may have found a null, but that's never useful
@@ -1178,6 +1181,7 @@ static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, c
c.line = op->line;
c.block = blk;
if (_guess_expression_type(p_context, op->arguments[1], r_type)) {
+ r_type.type.is_meta_type = false;
return true;
}
}
@@ -1210,7 +1214,7 @@ static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, c
int def_from = p_context.function->arguments.size() - p_context.function->default_values.size();
if (i >= def_from) {
- int def_idx = def_from - i;
+ int def_idx = i - def_from;
if (p_context.function->default_values[def_idx]->type == GDScriptParser::Node::TYPE_OPERATOR) {
const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(p_context.function->default_values[def_idx]);
if (op->arguments.size() < 2) {
@@ -1304,37 +1308,38 @@ static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, c
return false;
}
- // Check ClassDB
- if (ClassDB::class_exists(p_identifier)) {
- r_type.type.has_type = true;
- r_type.type.kind = GDScriptParser::DataType::NATIVE;
- r_type.type.native_type = p_identifier;
- if (Engine::get_singleton()->has_singleton(p_identifier)) {
- r_type.type.is_meta_type = false;
- r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier);
- } else {
- r_type.type.is_meta_type = true;
- int idx = GDScriptLanguage::get_singleton()->get_global_map()[p_identifier];
- r_type.value = GDScriptLanguage::get_singleton()->get_global_array()[idx];
+ for (int i = 0; i < 2; i++) {
+ StringName target_id;
+ switch (i) {
+ case 0:
+ // Check ClassDB
+ target_id = p_identifier;
+ break;
+ case 1:
+ // ClassDB again for underscore-prefixed classes
+ target_id = String("_") + p_identifier;
+ break;
}
- return true;
- }
- // ClassDB again for underscore-prefixed classes
- StringName under_id = String("_") + p_identifier;
- if (ClassDB::class_exists(under_id)) {
- r_type.type.has_type = true;
- r_type.type.kind = GDScriptParser::DataType::NATIVE;
- r_type.type.native_type = p_identifier;
- if (Engine::get_singleton()->has_singleton(p_identifier)) {
- r_type.type.is_meta_type = false;
- r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier);
- } else {
- r_type.type.is_meta_type = true;
- int idx = GDScriptLanguage::get_singleton()->get_global_map()[p_identifier];
- r_type.value = GDScriptLanguage::get_singleton()->get_global_array()[idx];
+ if (ClassDB::class_exists(target_id)) {
+ r_type.type.has_type = true;
+ r_type.type.kind = GDScriptParser::DataType::NATIVE;
+ r_type.type.native_type = target_id;
+ if (Engine::get_singleton()->has_singleton(target_id)) {
+ r_type.type.is_meta_type = false;
+ r_type.value = Engine::get_singleton()->get_singleton_object(target_id);
+ } else {
+ r_type.type.is_meta_type = true;
+ const Map<StringName, int>::Element *target_elem = GDScriptLanguage::get_singleton()->get_global_map().find(target_id);
+ // Check because classes like EditorNode are in ClassDB by now, but unknown to GDScript
+ if (!target_elem) {
+ return false;
+ }
+ int idx = target_elem->get();
+ r_type.value = GDScriptLanguage::get_singleton()->get_global_array()[idx];
+ }
+ return true;
}
- return true;
}
// Check autoload singletons
@@ -1365,11 +1370,11 @@ static bool _guess_identifier_type_from_base(const GDScriptCompletionContext &p_
for (int i = 0; i < base_type.class_type->variables.size(); i++) {
GDScriptParser::ClassNode::Member m = base_type.class_type->variables[i];
if (m.identifier == p_identifier) {
- if (m.data_type.has_type) {
- r_type.type = m.data_type;
- return true;
- }
if (m.expression) {
+ if (p_context.line == m.expression->line) {
+ // Variable used in the same expression
+ return false;
+ }
if (_guess_expression_type(p_context, m.expression, r_type)) {
return true;
}
@@ -1378,6 +1383,10 @@ static bool _guess_identifier_type_from_base(const GDScriptCompletionContext &p_
return true;
}
}
+ if (m.data_type.has_type) {
+ r_type.type = m.data_type;
+ return true;
+ }
return false;
}
}
@@ -1991,7 +2000,8 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context
if (!_static) {
List<MethodInfo> methods;
- ClassDB::get_method_list(type, &methods, false, true);
+ bool is_autocompleting_getters = GLOBAL_GET("debug/gdscript/completion/autocomplete_setters_and_getters").booleanize();
+ ClassDB::get_method_list(type, &methods, false, !is_autocompleting_getters);
for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
if (E->get().name.begins_with("_")) {
continue;
@@ -2098,8 +2108,8 @@ static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p
"and", "in", "not", "or", "false", "PI", "TAU", "INF", "NAN", "self", "true", "as", "assert",
"breakpoint", "class", "extends", "is", "func", "preload", "setget", "signal", "tool", "yield",
"const", "enum", "export", "onready", "static", "var", "break", "continue", "if", "elif",
- "else", "for", "pass", "return", "match", "while", "remote", "sync", "master", "slave",
- "remotesync", "mastersync", "slavesync",
+ "else", "for", "pass", "return", "match", "while", "remote", "sync", "master", "puppet", "slave",
+ "remotesync", "mastersync", "puppetsync",
0
};
@@ -2442,7 +2452,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base
} break;
case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT: {
List<StringName> constants;
- Variant::get_numeric_constants_for_type(parser.get_completion_built_in_constant(), &constants);
+ Variant::get_constants_for_type(parser.get_completion_built_in_constant(), &constants);
for (List<StringName>::Element *E = constants.front(); E; E = E->next()) {
options.insert(E->get().operator String());
}
@@ -2585,7 +2595,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base
}
method_hint += arg;
if (use_type_hint && mi.arguments[i].type != Variant::NIL) {
- method_hint += " : ";
+ method_hint += ": ";
if (mi.arguments[i].type == Variant::OBJECT && mi.arguments[i].class_name != StringName()) {
method_hint += mi.arguments[i].class_name.operator String();
} else {
@@ -2917,7 +2927,6 @@ void GDScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_t
break;
}
- //print_line(itos(indent_stack.size())+","+itos(tc)+": "+l);
lines.write[i] = l;
}
@@ -3065,7 +3074,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
case GDScriptParser::DataType::BUILTIN: {
base_type.has_type = false;
- if (Variant::has_numeric_constant(base_type.builtin_type, p_symbol)) {
+ if (Variant::has_constant(base_type.builtin_type, p_symbol)) {
r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
r_result.class_name = Variant::get_type_name(base_type.builtin_type);
r_result.class_member = p_symbol;
@@ -3294,7 +3303,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
}
} else {
/*
- // Because get_integer_constant_enum and get_integer_constant dont work on @GlobalScope
+ // Because get_integer_constant_enum and get_integer_constant don't work on @GlobalScope
// We cannot determine the exact nature of the identifier here
// Otherwise these codes would work
StringName enumName = ClassDB::get_integer_constant_enum("@GlobalScope", p_symbol, true);
@@ -3342,6 +3351,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
return OK;
}
} break;
+ default: {}
}
return ERR_CANT_RESOLVE;
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index bae3f48923..cd6c21a629 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -30,9 +30,9 @@
#include "gdscript_function.h"
+#include "core/os/os.h"
#include "gdscript.h"
#include "gdscript_functions.h"
-#include "os/os.h"
Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, GDScript *p_script, Variant &self, Variant *p_stack, String &r_error) const {
@@ -155,6 +155,7 @@ String GDScriptFunction::_get_call_error(const Variant::CallError &p_err, const
return err_text;
}
+#ifdef DEBUG_ENABLED
static String _get_var_type(const Variant *p_type) {
String basestr;
@@ -164,7 +165,6 @@ static String _get_var_type(const Variant *p_type) {
if (!bobj) {
basestr = "null instance";
} else {
-#ifdef DEBUG_ENABLED
if (ObjectDB::instance_validate(bobj)) {
if (bobj->get_script_instance())
basestr = bobj->get_class() + " (" + bobj->get_script_instance()->get_script()->get_path().get_file() + ")";
@@ -173,10 +173,6 @@ static String _get_var_type(const Variant *p_type) {
} else {
basestr = "previously freed instance";
}
-
-#else
- basestr = "Object";
-#endif
}
} else {
@@ -185,12 +181,14 @@ static String _get_var_type(const Variant *p_type) {
return basestr;
}
+#endif
#if defined(__GNUC__)
#define OPCODES_TABLE \
static const void *switch_table_ops[] = { \
&&OPCODE_OPERATOR, \
&&OPCODE_EXTENDS_TEST, \
+ &&OPCODE_IS_BUILTIN, \
&&OPCODE_SET, \
&&OPCODE_GET, \
&&OPCODE_SET_NAMED, \
@@ -277,7 +275,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#endif
uint32_t alloca_size = 0;
- GDScript *_class;
+ GDScript *script;
int ip = 0;
int line = _initial_line;
@@ -288,7 +286,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
line = p_state->line;
ip = p_state->ip;
alloca_size = p_state->stack.size();
- _class = p_state->_class;
+ script = p_state->script.ptr();
p_instance = p_state->instance;
defarg = p_state->defarg;
self = p_state->self;
@@ -370,9 +368,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
} else {
self = p_instance->owner;
}
- _class = p_instance->script.ptr();
+ script = p_instance->script.ptr();
} else {
- _class = _script;
+ script = _script;
}
}
@@ -397,7 +395,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#define GET_VARIANT_PTR(m_v, m_code_ofs) \
Variant *m_v; \
- m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, _class, self, stack, err_text); \
+ m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, stack, err_text); \
if (unlikely(!m_v)) \
OPCODE_BREAK;
@@ -406,7 +404,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#define CHECK_SPACE(m_space)
#define GET_VARIANT_PTR(m_v, m_code_ofs) \
Variant *m_v; \
- m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, _class, self, stack, err_text);
+ m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, stack, err_text);
#endif
@@ -421,8 +419,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
profile.call_count++;
profile.frame_call_count++;
}
-#endif
bool exit_ok = false;
+#endif
#ifdef DEBUG_ENABLED
OPCODE_WHILE(ip < _code_size) {
@@ -479,56 +477,53 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GET_VARIANT_PTR(dst, 3);
#ifdef DEBUG_ENABLED
- if (a->get_type() != Variant::OBJECT || a->operator Object *() == NULL) {
-
- err_text = "Left operand of 'is' is not an instance of anything.";
- OPCODE_BREAK;
- }
if (b->get_type() != Variant::OBJECT || b->operator Object *() == NULL) {
err_text = "Right operand of 'is' is not a class.";
OPCODE_BREAK;
}
#endif
- Object *obj_A = *a;
- Object *obj_B = *b;
-
- GDScript *scr_B = Object::cast_to<GDScript>(obj_B);
bool extends_ok = false;
+ if (a->get_type() == Variant::OBJECT && a->operator Object *() != NULL) {
+ Object *obj_A = *a;
+ Object *obj_B = *b;
- if (scr_B) {
- //if B is a script, the only valid condition is that A has an instance which inherits from the script
- //in other situation, this shoul return false.
+ GDScript *scr_B = Object::cast_to<GDScript>(obj_B);
- if (obj_A->get_script_instance() && obj_A->get_script_instance()->get_language() == GDScriptLanguage::get_singleton()) {
+ if (scr_B) {
+ //if B is a script, the only valid condition is that A has an instance which inherits from the script
+ //in other situation, this shoul return false.
- GDScript *cmp = static_cast<GDScript *>(obj_A->get_script_instance()->get_script().ptr());
- //bool found=false;
- while (cmp) {
+ if (obj_A->get_script_instance() && obj_A->get_script_instance()->get_language() == GDScriptLanguage::get_singleton()) {
- if (cmp == scr_B) {
- //inherits from script, all ok
- extends_ok = true;
- break;
- }
+ GDScript *cmp = static_cast<GDScript *>(obj_A->get_script_instance()->get_script().ptr());
+ //bool found=false;
+ while (cmp) {
- cmp = cmp->_base;
+ if (cmp == scr_B) {
+ //inherits from script, all ok
+ extends_ok = true;
+ break;
+ }
+
+ cmp = cmp->_base;
+ }
}
- }
- } else {
+ } else {
- GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(obj_B);
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(obj_B);
#ifdef DEBUG_ENABLED
- if (!nc) {
+ if (!nc) {
- err_text = "Right operand of 'is' is not a class (type: '" + obj_B->get_class() + "').";
- OPCODE_BREAK;
- }
+ err_text = "Right operand of 'is' is not a class (type: '" + obj_B->get_class() + "').";
+ OPCODE_BREAK;
+ }
#endif
- extends_ok = ClassDB::is_parent_class(obj_A->get_class_name(), nc->get_name());
+ extends_ok = ClassDB::is_parent_class(obj_A->get_class_name(), nc->get_name());
+ }
}
*dst = extends_ok;
@@ -536,6 +531,21 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
+ OPCODE(OPCODE_IS_BUILTIN) {
+
+ CHECK_SPACE(4);
+
+ GET_VARIANT_PTR(value, 1);
+ Variant::Type var_type = (Variant::Type)_code_ptr[ip + 2];
+ GET_VARIANT_PTR(dst, 3);
+
+ GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
+
+ *dst = value->get_type() == var_type;
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
OPCODE(OPCODE_SET) {
CHECK_SPACE(3);
@@ -666,8 +676,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GET_VARIANT_PTR(src, 2);
bool valid;
+#ifndef DEBUG_ENABLED
+ ClassDB::set_property(p_instance->owner, *index, *src, &valid);
+#else
bool ok = ClassDB::set_property(p_instance->owner, *index, *src, &valid);
-#ifdef DEBUG_ENABLED
if (!ok) {
err_text = "Internal error setting property: " + String(*index);
OPCODE_BREAK;
@@ -687,9 +699,11 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
const StringName *index = &_global_names_ptr[indexname];
GET_VARIANT_PTR(dst, 2);
- bool ok = ClassDB::get_property(p_instance->owner, *index, *dst);
-#ifdef DEBUG_ENABLED
+#ifndef DEBUG_ENABLED
+ ClassDB::get_property(p_instance->owner, *index, *dst);
+#else
+ bool ok = ClassDB::get_property(p_instance->owner, *index, *dst);
if (!ok) {
err_text = "Internal error getting property: " + String(*index);
OPCODE_BREAK;
@@ -736,13 +750,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ASSIGN_TYPED_BUILTIN) {
CHECK_SPACE(4);
- Variant::Type var_type = (Variant::Type)_code_ptr[ip + 1];
GET_VARIANT_PTR(dst, 2);
GET_VARIANT_PTR(src, 3);
+#ifdef DEBUG_ENABLED
+ Variant::Type var_type = (Variant::Type)_code_ptr[ip + 1];
GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
-#ifdef DEBUG_ENABLED
if (src->get_type() != var_type) {
if (Variant::can_convert_strict(src->get_type(), var_type)) {
Variant::CallError ce;
@@ -766,14 +780,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ASSIGN_TYPED_NATIVE) {
CHECK_SPACE(4);
- GET_VARIANT_PTR(type, 1);
GET_VARIANT_PTR(dst, 2);
GET_VARIANT_PTR(src, 3);
#ifdef DEBUG_ENABLED
+ GET_VARIANT_PTR(type, 1);
GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *());
GD_ERR_BREAK(!nc);
- if (!src->get_type() != Variant::OBJECT && !src->get_type() != Variant::NIL) {
+ if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
"' to a variable of type '" + nc->get_name() + "'.";
OPCODE_BREAK;
@@ -795,11 +809,11 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ASSIGN_TYPED_SCRIPT) {
CHECK_SPACE(4);
- GET_VARIANT_PTR(type, 1);
GET_VARIANT_PTR(dst, 2);
GET_VARIANT_PTR(src, 3);
#ifdef DEBUG_ENABLED
+ GET_VARIANT_PTR(type, 1);
Script *base_type = Object::cast_to<Script>(type->operator Object *());
GD_ERR_BREAK(!base_type);
@@ -1239,11 +1253,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
gdfs->state.stack_size = _stack_size;
gdfs->state.self = self;
gdfs->state.alloca_size = alloca_size;
- gdfs->state._class = _class;
+ gdfs->state.script = Ref<GDScript>(_script);
gdfs->state.ip = ip + ipofs;
gdfs->state.line = line;
gdfs->state.instance_id = (p_instance && p_instance->get_owner()) ? p_instance->get_owner()->get_instance_id() : 0;
- gdfs->state.script_id = _class->get_instance_id();
//gdfs->state.result_pos=ip+ipofs-1;
gdfs->state.defarg = defarg;
gdfs->state.instance = p_instance;
@@ -1266,10 +1279,11 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE_BREAK;
}
#endif
+
Object *obj = argobj->operator Object *();
String signal = argname->operator String();
-#ifdef DEBUG_ENABLED
+#ifdef DEBUG_ENABLED
if (!obj) {
err_text = "First argument of yield() is null.";
OPCODE_BREAK;
@@ -1286,17 +1300,19 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE_BREAK;
}
-#endif
Error err = obj->connect(signal, gdfs.ptr(), "_signal_callback", varray(gdfs), Object::CONNECT_ONESHOT);
-#ifdef DEBUG_ENABLED
if (err != OK) {
err_text = "Error connecting to signal: " + signal + " during yield().";
OPCODE_BREAK;
}
+#else
+ obj->connect(signal, gdfs.ptr(), "_signal_callback", varray(gdfs), Object::CONNECT_ONESHOT);
#endif
}
+#ifdef DEBUG_ENABLED
exit_ok = true;
+#endif
OPCODE_BREAK;
}
@@ -1373,7 +1389,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
CHECK_SPACE(2);
GET_VARIANT_PTR(r, 1);
retvalue = *r;
+#ifdef DEBUG_ENABLED
exit_ok = true;
+#endif
OPCODE_BREAK;
}
@@ -1445,9 +1463,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ASSERT) {
CHECK_SPACE(2);
- GET_VARIANT_PTR(test, 1);
#ifdef DEBUG_ENABLED
+ GET_VARIANT_PTR(test, 1);
bool result = test->booleanize();
if (!result) {
@@ -1502,8 +1520,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
DISPATCH_OPCODE;
OPCODE(OPCODE_END) {
-
+#ifdef DEBUG_ENABLED
exit_ok = true;
+#endif
OPCODE_BREAK;
}
@@ -1526,8 +1545,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
String err_file;
if (p_instance)
err_file = p_instance->script->path;
- else if (_class)
- err_file = _class->path;
+ else if (script)
+ err_file = script->path;
if (err_file == "")
err_file = "<built-in>";
String err_func = name;
@@ -1741,17 +1760,14 @@ GDScriptFunction::~GDScriptFunction() {
Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
-#ifdef DEBUG_ENABLED
if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) {
+#ifdef DEBUG_ENABLED
ERR_EXPLAIN("Resumed after yield, but class instance is gone");
ERR_FAIL_V(Variant());
- }
-
- if (state.script_id && !ObjectDB::get_instance(state.script_id)) {
- ERR_EXPLAIN("Resumed after yield, but script is gone");
- ERR_FAIL_V(Variant());
- }
+#else
+ return Variant();
#endif
+ }
Variant arg;
r_error.error = Variant::CallError::CALL_OK;
@@ -1821,9 +1837,6 @@ bool GDScriptFunctionState::is_valid(bool p_extended_check) const {
//class instance gone?
if (state.instance_id && !ObjectDB::get_instance(state.instance_id))
return false;
- //script gone?
- if (state.script_id && !ObjectDB::get_instance(state.script_id))
- return false;
}
return true;
@@ -1832,17 +1845,14 @@ bool GDScriptFunctionState::is_valid(bool p_extended_check) const {
Variant GDScriptFunctionState::resume(const Variant &p_arg) {
ERR_FAIL_COND_V(!function, Variant());
-#ifdef DEBUG_ENABLED
if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) {
+#ifdef DEBUG_ENABLED
ERR_EXPLAIN("Resumed after yield, but class instance is gone");
ERR_FAIL_V(Variant());
- }
-
- if (state.script_id && !ObjectDB::get_instance(state.script_id)) {
- ERR_EXPLAIN("Resumed after yield, but script is gone");
- ERR_FAIL_V(Variant());
- }
+#else
+ return Variant();
#endif
+ }
state.result = p_arg;
Variant::CallError err;
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index 3ce84290fd..5509edf24a 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -31,13 +31,13 @@
#ifndef GDSCRIPT_FUNCTION_H
#define GDSCRIPT_FUNCTION_H
-#include "os/thread.h"
-#include "pair.h"
-#include "reference.h"
-#include "script_language.h"
-#include "self_list.h"
-#include "string_db.h"
-#include "variant.h"
+#include "core/os/thread.h"
+#include "core/pair.h"
+#include "core/reference.h"
+#include "core/script_language.h"
+#include "core/self_list.h"
+#include "core/string_db.h"
+#include "core/variant.h"
class GDScriptInstance;
class GDScript;
@@ -136,6 +136,7 @@ public:
enum Opcode {
OPCODE_OPERATOR,
OPCODE_EXTENDS_TEST,
+ OPCODE_IS_BUILTIN,
OPCODE_SET,
OPCODE_GET,
OPCODE_SET_NAMED,
@@ -271,15 +272,13 @@ private:
public:
struct CallState {
- ObjectID instance_id; //by debug only
- ObjectID script_id;
-
+ ObjectID instance_id;
GDScriptInstance *instance;
Vector<uint8_t> stack;
int stack_size;
Variant self;
uint32_t alloca_size;
- GDScript *_class;
+ Ref<GDScript> script;
int ip;
int line;
int defarg;
diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp
index f2e52d48dd..9ff33594ce 100644
--- a/modules/gdscript/gdscript_functions.cpp
+++ b/modules/gdscript/gdscript_functions.cpp
@@ -30,15 +30,15 @@
#include "gdscript_functions.h"
-#include "class_db.h"
-#include "func_ref.h"
+#include "core/class_db.h"
+#include "core/func_ref.h"
+#include "core/io/json.h"
+#include "core/io/marshalls.h"
+#include "core/math/math_funcs.h"
+#include "core/os/os.h"
+#include "core/reference.h"
+#include "core/variant_parser.h"
#include "gdscript.h"
-#include "io/json.h"
-#include "io/marshalls.h"
-#include "math_funcs.h"
-#include "os/os.h"
-#include "reference.h"
-#include "variant_parser.h"
const char *GDScriptFunctions::get_func_name(Function p_func) {
@@ -106,6 +106,8 @@ const char *GDScriptFunctions::get_func_name(Function p_func) {
"printerr",
"printraw",
"print_debug",
+ "push_error",
+ "push_warning",
"var2str",
"str2var",
"var2bytes",
@@ -642,7 +644,6 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
str += p_args[i]->operator String();
}
- //str+="\n";
print_line(str);
r_ret = Variant();
@@ -657,7 +658,6 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
str += p_args[i]->operator String();
}
- //str+="\n";
print_line(str);
r_ret = Variant();
@@ -672,7 +672,6 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
str += p_args[i]->operator String();
}
- //str+="\n";
print_line(str);
r_ret = Variant();
@@ -686,7 +685,6 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
str += p_args[i]->operator String();
}
- //str+="\n";
print_error(str);
r_ret = Variant();
@@ -698,7 +696,6 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
str += p_args[i]->operator String();
}
- //str+="\n";
OS::get_singleton()->print("%s", str.utf8().get_data());
r_ret = Variant();
@@ -712,14 +709,40 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
ScriptLanguage *script = GDScriptLanguage::get_singleton();
if (script->debug_get_stack_level_count() > 0) {
- str += "\n\t";
- str += "At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)); // + " in function '" + script->debug_get_stack_level_function(0) + "'";
+ str += "\n At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()";
}
- //str+="\n";
print_line(str);
r_ret = Variant();
} break;
+ case PUSH_ERROR: {
+ VALIDATE_ARG_COUNT(1);
+ if (p_args[0]->get_type() != Variant::STRING) {
+ r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::STRING;
+ r_ret = Variant();
+ break;
+ }
+
+ String message = *p_args[0];
+ ERR_PRINTS(message);
+ r_ret = Variant();
+ } break;
+ case PUSH_WARNING: {
+ VALIDATE_ARG_COUNT(1);
+ if (p_args[0]->get_type() != Variant::STRING) {
+ r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::STRING;
+ r_ret = Variant();
+ break;
+ }
+
+ String message = *p_args[0];
+ WARN_PRINTS(message);
+ r_ret = Variant();
+ } break;
case VAR_TO_STR: {
VALIDATE_ARG_COUNT(1);
String vars;
@@ -735,22 +758,14 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
r_ret = Variant();
return;
}
+ r_ret = *p_args[0];
VariantParser::StreamString ss;
ss.s = *p_args[0];
String errs;
int line;
- Error err = VariantParser::parse(&ss, r_ret, errs, line);
-
- if (err != OK) {
- r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = "Parse error at line " + itos(line) + ": " + errs;
- return;
- }
-
+ (void)VariantParser::parse(&ss, r_ret, errs, line);
} break;
case VAR_TO_BYTES: {
VALIDATE_ARG_COUNT(1);
@@ -1464,7 +1479,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
return mi;
} break;
case MATH_ATAN2: {
- MethodInfo mi("atan2", PropertyInfo(Variant::REAL, "x"), PropertyInfo(Variant::REAL, "y"));
+ MethodInfo mi("atan2", PropertyInfo(Variant::REAL, "y"), PropertyInfo(Variant::REAL, "x"));
mi.return_val.type = Variant::REAL;
return mi;
} break;
@@ -1760,11 +1775,25 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
return mi;
} break;
+ case PUSH_ERROR: {
+
+ MethodInfo mi(Variant::NIL, "push_error", PropertyInfo(Variant::STRING, "message"));
+ mi.return_val.type = Variant::NIL;
+ return mi;
+
+ } break;
+ case PUSH_WARNING: {
+
+ MethodInfo mi(Variant::NIL, "push_warning", PropertyInfo(Variant::STRING, "message"));
+ mi.return_val.type = Variant::NIL;
+ return mi;
+
+ } break;
case VAR_TO_STR: {
+
MethodInfo mi("var2str", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::STRING;
return mi;
-
} break;
case STR_TO_VAR: {
@@ -1774,10 +1803,10 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
return mi;
} break;
case VAR_TO_BYTES: {
+
MethodInfo mi("var2bytes", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::POOL_BYTE_ARRAY;
return mi;
-
} break;
case BYTES_TO_VAR: {
@@ -1859,7 +1888,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
} break;
case GET_STACK: {
MethodInfo mi("get_stack");
- mi.return_val.type = Variant::NIL;
+ mi.return_val.type = Variant::ARRAY;
return mi;
} break;
diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_functions.h
index a29f06e839..33d5f27230 100644
--- a/modules/gdscript/gdscript_functions.h
+++ b/modules/gdscript/gdscript_functions.h
@@ -31,7 +31,7 @@
#ifndef GDSCRIPT_FUNCTIONS_H
#define GDSCRIPT_FUNCTIONS_H
-#include "variant.h"
+#include "core/variant.h"
class GDScriptFunctions {
public:
@@ -97,6 +97,8 @@ public:
TEXT_PRINTERR,
TEXT_PRINTRAW,
TEXT_PRINT_DEBUG,
+ PUSH_ERROR,
+ PUSH_WARNING,
VAR_TO_STR,
STR_TO_VAR,
VAR_TO_BYTES,
@@ -117,7 +119,6 @@ public:
LEN,
IS_INSTANCE_VALID,
FUNC_MAX
-
};
static const char *get_func_name(Function p_func);
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 852d465206..6ea0dbcb19 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -32,13 +32,13 @@
#include "core/core_string_names.h"
#include "core/engine.h"
+#include "core/io/resource_loader.h"
+#include "core/os/file_access.h"
+#include "core/print_string.h"
#include "core/project_settings.h"
#include "core/reference.h"
+#include "core/script_language.h"
#include "gdscript.h"
-#include "io/resource_loader.h"
-#include "os/file_access.h"
-#include "print_string.h"
-#include "script_language.h"
template <class T>
T *GDScriptParser::alloc_node() {
@@ -56,6 +56,10 @@ T *GDScriptParser::alloc_node() {
return t;
}
+#ifdef DEBUG_ENABLED
+static String _find_function_name(const GDScriptParser::OperatorNode *p_call);
+#endif // DEBUG_ENABLED
+
bool GDScriptParser::_end_statement() {
if (tokenizer->get_token() == GDScriptTokenizer::TK_SEMICOLON) {
@@ -79,8 +83,11 @@ bool GDScriptParser::_enter_indent_block(BlockNode *p_block) {
}
tokenizer->advance();
- if (tokenizer->get_token() != GDScriptTokenizer::TK_NEWLINE) {
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_EOF) {
+ return false;
+ }
+ if (tokenizer->get_token() != GDScriptTokenizer::TK_NEWLINE) {
// be more python-like
int current = tab_level.back()->get();
tab_level.push_back(current);
@@ -90,10 +97,11 @@ bool GDScriptParser::_enter_indent_block(BlockNode *p_block) {
}
while (true) {
-
if (tokenizer->get_token() != GDScriptTokenizer::TK_NEWLINE) {
return false; //wtf
+ } else if (tokenizer->get_token(1) == GDScriptTokenizer::TK_EOF) {
+ return false;
} else if (tokenizer->get_token(1) != GDScriptTokenizer::TK_NEWLINE) {
int indent = tokenizer->get_token_line_indent();
@@ -607,7 +615,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
_set_error("Built-in type constant or static function expected after '.'");
return NULL;
}
- if (!Variant::has_numeric_constant(bi_type, identifier)) {
+ if (!Variant::has_constant(bi_type, identifier)) {
if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_OPEN &&
Variant::is_method_const(bi_type, identifier) &&
@@ -635,14 +643,26 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
expr = op;
} else {
-
- _set_error("Static constant '" + identifier.operator String() + "' not present in built-in type " + Variant::get_type_name(bi_type) + ".");
- return NULL;
+ // Object is a special case
+ bool valid = false;
+ if (bi_type == Variant::OBJECT) {
+ int object_constant = ClassDB::get_integer_constant("Object", identifier, &valid);
+ if (valid) {
+ ConstantNode *cn = alloc_node<ConstantNode>();
+ cn->value = object_constant;
+ cn->datatype = _type_from_variant(cn->value);
+ expr = cn;
+ }
+ }
+ if (!valid) {
+ _set_error("Static constant '" + identifier.operator String() + "' not present in built-in type " + Variant::get_type_name(bi_type) + ".");
+ return NULL;
+ }
}
} else {
ConstantNode *cn = alloc_node<ConstantNode>();
- cn->value = Variant::get_numeric_constant_value(bi_type, identifier);
+ cn->value = Variant::get_constant_value(bi_type, identifier);
cn->datatype = _type_from_variant(cn->value);
expr = cn;
}
@@ -659,7 +679,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
if (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_TYPE) {
Variant::Type ct = tokenizer->get_token_type();
- if (p_parsing_constant == false) {
+ if (!p_parsing_constant) {
if (ct == Variant::ARRAY) {
if (tokenizer->get_token(2) == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
ArrayNode *arr = alloc_node<ArrayNode>();
@@ -726,16 +746,17 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
}
BlockNode *b = current_block;
- while (b) {
+ while (!bfn && b) {
if (b->variables.has(identifier)) {
IdentifierNode *id = alloc_node<IdentifierNode>();
- LocalVarNode *lv = b->variables[identifier];
id->name = identifier;
id->declared_block = b;
id->line = id_line;
expr = id;
bfn = true;
+#ifdef DEBUG_ENABLED
+ LocalVarNode *lv = b->variables[identifier];
switch (tokenizer->get_token()) {
case GDScriptTokenizer::TK_OP_ASSIGN_ADD:
case GDScriptTokenizer::TK_OP_ASSIGN_BIT_AND:
@@ -747,15 +768,23 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_LEFT:
case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_RIGHT:
case GDScriptTokenizer::TK_OP_ASSIGN_SUB: {
- if (lv->assignments == 0 && !lv->datatype.has_type) {
- _set_error("Using assignment with operation on a variable that was never assigned.");
- return NULL;
+ if (lv->assignments == 0) {
+ if (!lv->datatype.has_type) {
+ _set_error("Using assignment with operation on a variable that was never assigned.");
+ return NULL;
+ }
+ _add_warning(GDScriptWarning::UNASSIGNED_VARIABLE_OP_ASSIGN, -1, identifier.operator String());
}
} // fallthrough
case GDScriptTokenizer::TK_OP_ASSIGN: {
lv->assignments += 1;
+ lv->usages--; // Assignment is not really usage
+ } break;
+ default: {
+ lv->usages++;
}
}
+#endif // DEBUG_ENABLED
break;
}
b = b->parent_block;
@@ -785,6 +814,32 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
}
if (!bfn) {
+#ifdef DEBUG_ENABLED
+ if (current_function) {
+ int arg_idx = current_function->arguments.find(identifier);
+ if (arg_idx != -1) {
+ switch (tokenizer->get_token()) {
+ case GDScriptTokenizer::TK_OP_ASSIGN_ADD:
+ case GDScriptTokenizer::TK_OP_ASSIGN_BIT_AND:
+ case GDScriptTokenizer::TK_OP_ASSIGN_BIT_OR:
+ case GDScriptTokenizer::TK_OP_ASSIGN_BIT_XOR:
+ case GDScriptTokenizer::TK_OP_ASSIGN_DIV:
+ case GDScriptTokenizer::TK_OP_ASSIGN_MOD:
+ case GDScriptTokenizer::TK_OP_ASSIGN_MUL:
+ case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_LEFT:
+ case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_RIGHT:
+ case GDScriptTokenizer::TK_OP_ASSIGN_SUB:
+ case GDScriptTokenizer::TK_OP_ASSIGN: {
+ // Assignment is not really usage
+ current_function->arguments_usage.write[arg_idx] = current_function->arguments_usage[arg_idx] - 1;
+ } break;
+ default: {
+ current_function->arguments_usage.write[arg_idx] = current_function->arguments_usage[arg_idx] + 1;
+ }
+ }
+ }
+ }
+#endif // DEBUG_ENABLED
IdentifierNode *id = alloc_node<IdentifierNode>();
id->name = identifier;
id->line = id_line;
@@ -823,6 +878,20 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
op->arguments.push_back(subexpr);
expr=op;*/
+ } else if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_IS && tokenizer->get_token(1) == GDScriptTokenizer::TK_BUILT_IN_TYPE) {
+ // 'is' operator with built-in type
+ OperatorNode *op = alloc_node<OperatorNode>();
+ op->op = OperatorNode::OP_IS_BUILTIN;
+ op->arguments.push_back(expr);
+
+ tokenizer->advance();
+
+ TypeNode *tn = alloc_node<TypeNode>();
+ tn->vtype = tokenizer->get_token_type();
+ op->arguments.push_back(tn);
+ tokenizer->advance();
+
+ expr = op;
} else if (tokenizer->get_token() == GDScriptTokenizer::TK_BRACKET_OPEN) {
// array
tokenizer->advance();
@@ -1033,12 +1102,18 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
expr = op;
+ } else if (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_TYPE && expression.size() > 0 && expression[expression.size() - 1].is_op && expression[expression.size() - 1].op == OperatorNode::OP_IS) {
+ Expression e = expression[expression.size() - 1];
+ e.op = OperatorNode::OP_IS_BUILTIN;
+ expression.write[expression.size() - 1] = e;
+
+ TypeNode *tn = alloc_node<TypeNode>();
+ tn->vtype = tokenizer->get_token_type();
+ expr = tn;
+ tokenizer->advance();
} else {
//find list [ or find dictionary {
-
- //print_line("found bug?");
-
_set_error("Error parsing expression, misplaced: " + String(tokenizer->get_token_name(tokenizer->get_token())));
return NULL; //nothing
}
@@ -1294,6 +1369,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
switch (expression[i].op) {
case OperatorNode::OP_IS:
+ case OperatorNode::OP_IS_BUILTIN:
priority = -1;
break; //before anything
@@ -1986,12 +2062,20 @@ GDScriptParser::PatternNode *GDScriptParser::_parse_pattern(bool p_static) {
// bind
case GDScriptTokenizer::TK_PR_VAR: {
tokenizer->advance();
+ if (!tokenizer->is_token_literal()) {
+ _set_error("Expected identifier for binding variable name.");
+ return NULL;
+ }
pattern->pt_type = GDScriptParser::PatternNode::PT_BIND;
pattern->bind = tokenizer->get_token_identifier();
- // Check if binding is already used
- if (current_block->variables.has(pattern->bind)) {
- _set_error("Binding name of '" + pattern->bind.operator String() + "' was already used in the pattern.");
- return NULL;
+ // Check if variable name is already used
+ BlockNode *bl = current_block;
+ while (bl) {
+ if (bl->variables.has(pattern->bind)) {
+ _set_error("Binding name of '" + pattern->bind.operator String() + "' is already declared in this scope.");
+ return NULL;
+ }
+ bl = bl->parent_block;
}
// Create local variable for proper identifier detection later
LocalVarNode *lv = alloc_node<LocalVarNode>();
@@ -2412,7 +2496,7 @@ void GDScriptParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_m
Node *condition = NULL;
- // chech for has, then for pattern
+ // check for has, then for pattern
IdentifierNode *has = alloc_node<IdentifierNode>();
has->name = "has";
@@ -2601,6 +2685,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
pending_newline = -1;
}
+#ifdef DEBUG_ENABLED
switch (token) {
case GDScriptTokenizer::TK_EOF:
case GDScriptTokenizer::TK_ERROR:
@@ -2609,13 +2694,13 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
// will check later
} break;
default: {
- // TODO: Make this a warning
- /*if (p_block->has_return) {
- _set_error("Unreacheable code.");
- return;
- }*/
+ if (p_block->has_return && !current_function->has_unreachable_code) {
+ _add_warning(GDScriptWarning::UNREACHABLE_CODE, -1, current_function->name.operator String());
+ current_function->has_unreachable_code = true;
+ }
} break;
}
+#endif // DEBUG_ENABLED
switch (token) {
case GDScriptTokenizer::TK_EOF:
p_block->end_line = tokenizer->get_token_line();
@@ -2728,6 +2813,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
c->line = var_line;
assigned = c;
}
+ lv->assign = assigned;
//must be added later, to avoid self-referencing.
p_block->variables.insert(n, lv);
@@ -2745,6 +2831,8 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
lv->assign_op = op;
lv->assign = assigned;
+ lv->assign_op = op;
+
if (!_end_statement()) {
_set_error("Expected end of statement (var)");
return;
@@ -3387,6 +3475,32 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
tokenizer->advance(2);
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
+ tokenizer->advance();
+
+ if ((tokenizer->get_token() == GDScriptTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type() == Variant::STRING)) {
+ Variant constant = tokenizer->get_token_constant();
+ String icon_path = constant.operator String();
+
+ String abs_icon_path = icon_path.is_rel_path() ? self_path.get_base_dir().plus_file(icon_path).simplify_path() : icon_path;
+ if (!FileAccess::exists(abs_icon_path)) {
+ _set_error("No class icon found at: " + abs_icon_path);
+ return;
+ }
+
+ p_class->icon_path = icon_path;
+
+ tokenizer->advance();
+ } else {
+ _set_error("Optional parameter after 'class_name' must be a string constant file path to an icon.");
+ return;
+ }
+
+ } else if (tokenizer->get_token() == GDScriptTokenizer::TK_CONSTANT) {
+ _set_error("Class icon must be separated by a comma.");
+ return;
+ }
+
} break;
case GDScriptTokenizer::TK_PR_TOOL: {
@@ -3513,6 +3627,17 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
}
}
+#ifdef DEBUG_ENABLED
+ if (p_class->constant_expressions.has(name)) {
+ _add_warning(GDScriptWarning::FUNCTION_CONFLICTS_CONSTANT, -1, name);
+ }
+ for (int i = 0; i < p_class->variables.size(); i++) {
+ if (p_class->variables[i].identifier == name) {
+ _add_warning(GDScriptWarning::FUNCTION_CONFLICTS_VARIABLE, -1, name);
+ }
+ }
+#endif // DEBUG_ENABLED
+
if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
_set_error("Expected '(' after identifier (syntax: 'func <identifier>([arguments]):' ).");
@@ -3524,6 +3649,9 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
Vector<StringName> arguments;
Vector<DataType> argument_types;
Vector<Node *> default_values;
+#ifdef DEBUG_ENABLED
+ Vector<int> arguments_usage;
+#endif // DEBUG_ENABLED
int fnline = tokenizer->get_token_line();
@@ -3550,6 +3678,9 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
StringName argname = tokenizer->get_token_identifier();
arguments.push_back(argname);
+#ifdef DEBUG_ENABLED
+ arguments_usage.push_back(0);
+#endif // DEBUG_ENABLED
tokenizer->advance();
@@ -3621,6 +3752,19 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
BlockNode *block = alloc_node<BlockNode>();
block->parent_class = p_class;
+ FunctionNode *function = alloc_node<FunctionNode>();
+ function->name = name;
+ function->arguments = arguments;
+ function->argument_types = argument_types;
+ function->default_values = default_values;
+ function->_static = _static;
+ function->line = fnline;
+#ifdef DEBUG_ENABLED
+ function->arguments_usage = arguments_usage;
+#endif // DEBUG_ENABLED
+ function->rpc_mode = rpc_mode;
+ rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+
if (name == "_init") {
if (_static) {
@@ -3651,7 +3795,9 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
parenthesis++;
while (true) {
+ current_function = function;
Node *arg = _parse_and_reduce_expression(p_class, _static);
+ current_function = NULL;
cparent->arguments.push_back(arg);
if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
@@ -3695,17 +3841,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
return;
}
- FunctionNode *function = alloc_node<FunctionNode>();
- function->name = name;
function->return_type = return_type;
- function->arguments = arguments;
- function->argument_types = argument_types;
- function->default_values = default_values;
- function->_static = _static;
- function->line = fnline;
-
- function->rpc_mode = rpc_mode;
- rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
if (_static)
p_class->static_functions.push_back(function);
@@ -3730,6 +3866,8 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
ClassNode::Signal sig;
sig.name = tokenizer->get_token_identifier();
+ sig.emissions = 0;
+ sig.line = tokenizer->get_token_line();
tokenizer->advance();
if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
@@ -3821,14 +3959,15 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "FLAGS") {
- //current_export.hint=PROPERTY_HINT_ALL_FLAGS;
tokenizer->advance();
if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
+ ERR_EXPLAIN("Exporting bit flags hint requires string constants.");
+ WARN_DEPRECATED
break;
}
if (tokenizer->get_token() != GDScriptTokenizer::TK_COMMA) {
- _set_error("Expected ')' or ',' in bit flags hint.");
+ _set_error("Expected ',' in bit flags hint.");
return;
}
@@ -4217,6 +4356,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
current_export.type = Variant::INT;
current_export.hint = is_flags ? PROPERTY_HINT_FLAGS : PROPERTY_HINT_ENUM;
+ current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
Dictionary enum_values = constant;
List<Variant> keys;
@@ -4264,10 +4404,10 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
tokenizer->advance();
}
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_ONREADY && tokenizer->get_token() != GDScriptTokenizer::TK_PR_REMOTE && tokenizer->get_token() != GDScriptTokenizer::TK_PR_MASTER && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SLAVE && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_REMOTESYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_MASTERSYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SLAVESYNC) {
+ if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_ONREADY && tokenizer->get_token() != GDScriptTokenizer::TK_PR_REMOTE && tokenizer->get_token() != GDScriptTokenizer::TK_PR_MASTER && tokenizer->get_token() != GDScriptTokenizer::TK_PR_PUPPET && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_REMOTESYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_MASTERSYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_PUPPETSYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SLAVE) {
current_export = PropertyInfo();
- _set_error("Expected 'var', 'onready', 'remote', 'master', 'slave', 'sync', 'remotesync', 'mastersync', 'slavesync'.");
+ _set_error("Expected 'var', 'onready', 'remote', 'master', 'puppet', 'sync', 'remotesync', 'mastersync', 'puppetsync'.");
return;
}
@@ -4324,7 +4464,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
rpc_mode = MultiplayerAPI::RPC_MODE_MASTER;
continue;
} break;
- case GDScriptTokenizer::TK_PR_SLAVE: {
+ case GDScriptTokenizer::TK_PR_SLAVE:
+#ifdef DEBUG_ENABLED
+ _add_warning(GDScriptWarning::DEPRECATED_KEYWORD, tokenizer->get_token_line(), "slave", "puppet");
+#endif
+ case GDScriptTokenizer::TK_PR_PUPPET: {
//may be fallthrough from export, ignore if so
tokenizer->advance();
@@ -4341,7 +4485,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
}
}
- rpc_mode = MultiplayerAPI::RPC_MODE_SLAVE;
+ rpc_mode = MultiplayerAPI::RPC_MODE_PUPPET;
continue;
} break;
case GDScriptTokenizer::TK_PR_REMOTESYNC:
@@ -4357,7 +4501,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
return;
}
- rpc_mode = MultiplayerAPI::RPC_MODE_SYNC;
+ rpc_mode = MultiplayerAPI::RPC_MODE_REMOTESYNC;
continue;
} break;
case GDScriptTokenizer::TK_PR_MASTERSYNC: {
@@ -4375,7 +4519,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
rpc_mode = MultiplayerAPI::RPC_MODE_MASTERSYNC;
continue;
} break;
- case GDScriptTokenizer::TK_PR_SLAVESYNC: {
+ case GDScriptTokenizer::TK_PR_PUPPETSYNC: {
//may be fallthrough from export, ignore if so
tokenizer->advance();
@@ -4387,7 +4531,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
return;
}
- rpc_mode = MultiplayerAPI::RPC_MODE_SLAVESYNC;
+ rpc_mode = MultiplayerAPI::RPC_MODE_PUPPETSYNC;
continue;
} break;
case GDScriptTokenizer::TK_PR_VAR: {
@@ -4413,22 +4557,36 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
member.expression = NULL;
member._export.name = member.identifier;
member.line = tokenizer->get_token_line();
+ member.usages = 0;
member.rpc_mode = rpc_mode;
if (current_class->constant_expressions.has(member.identifier)) {
- _set_error("A constant named '" + String(member.identifier) + "' alread exists in this class (at line: " +
+ _set_error("A constant named '" + String(member.identifier) + "' already exists in this class (at line: " +
itos(current_class->constant_expressions[member.identifier].expression->line) + ").");
return;
}
for (int i = 0; i < current_class->variables.size(); i++) {
if (current_class->variables[i].identifier == member.identifier) {
- _set_error("Variable '" + String(member.identifier) + "' alread exists in this class (at line: " +
+ _set_error("Variable '" + String(member.identifier) + "' already exists in this class (at line: " +
itos(current_class->variables[i].line) + ").");
return;
}
}
-
+#ifdef DEBUG_ENABLED
+ for (int i = 0; i < current_class->functions.size(); i++) {
+ if (current_class->functions[i]->name == member.identifier) {
+ _add_warning(GDScriptWarning::VARIABLE_CONFLICTS_FUNCTION, member.line, member.identifier);
+ break;
+ }
+ }
+ for (int i = 0; i < current_class->static_functions.size(); i++) {
+ if (current_class->static_functions[i]->name == member.identifier) {
+ _add_warning(GDScriptWarning::VARIABLE_CONFLICTS_FUNCTION, member.line, member.identifier);
+ break;
+ }
+ }
+#endif // DEBUG_ENABLED
tokenizer->advance();
rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
@@ -4617,14 +4775,14 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
int line = tokenizer->get_token_line();
if (current_class->constant_expressions.has(const_id)) {
- _set_error("Constant '" + String(const_id) + "' alread exists in this class (at line: " +
+ _set_error("Constant '" + String(const_id) + "' already exists in this class (at line: " +
itos(current_class->constant_expressions[const_id].expression->line) + ").");
return;
}
for (int i = 0; i < current_class->variables.size(); i++) {
if (current_class->variables[i].identifier == const_id) {
- _set_error("A variable named '" + String(const_id) + "' alread exists in this class (at line: " +
+ _set_error("A variable named '" + String(const_id) + "' already exists in this class (at line: " +
itos(current_class->variables[i].line) + ").");
return;
}
@@ -4678,13 +4836,28 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
case GDScriptTokenizer::TK_PR_ENUM: {
//multiple constant declarations..
- int last_assign = -1; // Incremented by 1 right before the assingment.
+ int last_assign = -1; // Incremented by 1 right before the assignment.
String enum_name;
Dictionary enum_dict;
tokenizer->advance();
if (tokenizer->is_token_literal(0, true)) {
enum_name = tokenizer->get_token_literal();
+
+ if (current_class->constant_expressions.has(enum_name)) {
+ _set_error("A constant named '" + String(enum_name) + "' already exists in this class (at line: " +
+ itos(current_class->constant_expressions[enum_name].expression->line) + ").");
+ return;
+ }
+
+ for (int i = 0; i < current_class->variables.size(); i++) {
+ if (current_class->variables[i].identifier == enum_name) {
+ _set_error("A variable named '" + String(enum_name) + "' already exists in this class (at line: " +
+ itos(current_class->variables[i].line) + ").");
+ return;
+ }
+ }
+
tokenizer->advance();
}
if (tokenizer->get_token() != GDScriptTokenizer::TK_CURLY_BRACKET_OPEN) {
@@ -4711,12 +4884,12 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
return;
} else { // tokenizer->is_token_literal(0, true)
- ClassNode::Constant constant;
-
StringName const_id = tokenizer->get_token_literal();
tokenizer->advance();
+ ConstantNode *enum_value_expr;
+
if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) {
tokenizer->advance();
@@ -4733,23 +4906,20 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
return;
}
- ConstantNode *subexpr_const = static_cast<ConstantNode *>(subexpr);
+ enum_value_expr = static_cast<ConstantNode *>(subexpr);
- if (subexpr_const->value.get_type() != Variant::INT) {
+ if (enum_value_expr->value.get_type() != Variant::INT) {
_set_error("Expected an int value for enum");
return;
}
- last_assign = subexpr_const->value;
-
- constant.expression = subexpr_const;
+ last_assign = enum_value_expr->value;
} else {
last_assign = last_assign + 1;
- ConstantNode *cn = alloc_node<ConstantNode>();
- cn->value = last_assign;
- cn->datatype = _type_from_variant(cn->value);
- constant.expression = cn;
+ enum_value_expr = alloc_node<ConstantNode>();
+ enum_value_expr->value = last_assign;
+ enum_value_expr->datatype = _type_from_variant(enum_value_expr->value);
}
if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
@@ -4757,14 +4927,29 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
}
if (enum_name != "") {
- const ConstantNode *cn = static_cast<const ConstantNode *>(constant.expression);
- enum_dict[const_id] = cn->value;
- }
+ enum_dict[const_id] = enum_value_expr->value;
+ } else {
+ if (current_class->constant_expressions.has(const_id)) {
+ _set_error("A constant named '" + String(const_id) + "' already exists in this class (at line: " +
+ itos(current_class->constant_expressions[const_id].expression->line) + ").");
+ return;
+ }
- constant.type.has_type = true;
- constant.type.kind = DataType::BUILTIN;
- constant.type.builtin_type = Variant::INT;
- p_class->constant_expressions.insert(const_id, constant);
+ for (int i = 0; i < current_class->variables.size(); i++) {
+ if (current_class->variables[i].identifier == const_id) {
+ _set_error("A variable named '" + String(const_id) + "' already exists in this class (at line: " +
+ itos(current_class->variables[i].line) + ").");
+ return;
+ }
+ }
+
+ ClassNode::Constant constant;
+ constant.type.has_type = true;
+ constant.type.kind = DataType::BUILTIN;
+ constant.type.builtin_type = Variant::INT;
+ constant.expression = enum_value_expr;
+ p_class->constant_expressions.insert(const_id, constant);
+ }
}
}
@@ -4913,7 +5098,7 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) {
if (found) continue;
if (p->constant_expressions.has(base)) {
- if (!p->constant_expressions[base].expression->type == Node::TYPE_CONSTANT) {
+ if (p->constant_expressions[base].expression->type != Node::TYPE_CONSTANT) {
_set_error("Could not resolve constant '" + base + "'.", p_class->line);
return;
}
@@ -5053,6 +5238,8 @@ String GDScriptParser::DataType::to_string() const {
}
return class_type->name.operator String();
} break;
+ case UNRESOLVED: {
+ } break;
}
return "Unresolved";
@@ -5559,18 +5746,23 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data
if (p_container.kind == DataType::BUILTIN && p_expression.kind == DataType::BUILTIN) {
bool valid = p_container.builtin_type == p_expression.builtin_type;
if (p_allow_implicit_conversion) {
- valid = valid || (p_container.builtin_type == Variant::INT && p_expression.builtin_type == Variant::REAL);
- valid = valid || (p_container.builtin_type == Variant::REAL && p_expression.builtin_type == Variant::INT);
- valid = valid || (p_container.builtin_type == Variant::STRING && p_expression.builtin_type == Variant::NODE_PATH);
- valid = valid || (p_container.builtin_type == Variant::NODE_PATH && p_expression.builtin_type == Variant::STRING);
- valid = valid || (p_container.builtin_type == Variant::BOOL && p_expression.builtin_type == Variant::REAL);
- valid = valid || (p_container.builtin_type == Variant::BOOL && p_expression.builtin_type == Variant::INT);
- valid = valid || (p_container.builtin_type == Variant::INT && p_expression.builtin_type == Variant::BOOL);
- valid = valid || (p_container.builtin_type == Variant::REAL && p_expression.builtin_type == Variant::BOOL);
+ valid = valid || Variant::can_convert_strict(p_expression.builtin_type, p_container.builtin_type);
}
return valid;
}
+ if (p_container.kind == DataType::BUILTIN && p_container.builtin_type == Variant::OBJECT) {
+ // Object built-in is a special case, it's compatible with any object and with null
+ if (p_expression.kind == DataType::BUILTIN && p_expression.builtin_type == Variant::NIL) {
+ return true;
+ }
+ if (p_expression.kind == DataType::BUILTIN) {
+ return false;
+ }
+ // If it's not a built-in, must be an object
+ return true;
+ }
+
if (p_container.kind == DataType::BUILTIN || (p_expression.kind == DataType::BUILTIN && p_expression.builtin_type != Variant::NIL)) {
// Can't mix built-ins with objects
return false;
@@ -5625,7 +5817,10 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data
expr_native = base->base_type.native_type;
expr_script = base->base_type.script_type;
}
- }
+ } break;
+ case DataType::BUILTIN: // Already handled above
+ case DataType::UNRESOLVED: // Not allowed, see above
+ break;
}
switch (p_container.kind) {
@@ -5668,7 +5863,10 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data
expr_class = expr_class->base_type.class_type;
}
return false;
- }
+ } break;
+ case DataType::BUILTIN: // Already handled above
+ case DataType::UNRESOLVED: // Not allowed, see above
+ break;
}
return false;
@@ -5685,15 +5883,37 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
case Node::TYPE_CONSTANT: {
node_type = _type_from_variant(static_cast<ConstantNode *>(p_node)->value);
} break;
+ case Node::TYPE_TYPE: {
+ TypeNode *tn = static_cast<TypeNode *>(p_node);
+ node_type.has_type = true;
+ node_type.is_meta_type = true;
+ node_type.kind = DataType::BUILTIN;
+ node_type.builtin_type = tn->vtype;
+ } break;
case Node::TYPE_ARRAY: {
node_type.has_type = true;
node_type.kind = DataType::BUILTIN;
node_type.builtin_type = Variant::ARRAY;
+#ifdef DEBUG_ENABLED
+ // Check stuff inside the array
+ ArrayNode *an = static_cast<ArrayNode *>(p_node);
+ for (int i = 0; i < an->elements.size(); i++) {
+ _reduce_node_type(an->elements[i]);
+ }
+#endif // DEBUG_ENABLED
} break;
case Node::TYPE_DICTIONARY: {
node_type.has_type = true;
node_type.kind = DataType::BUILTIN;
node_type.builtin_type = Variant::DICTIONARY;
+#ifdef DEBUG_ENABLED
+ // Check stuff inside the dictionarty
+ DictionaryNode *dn = static_cast<DictionaryNode *>(p_node);
+ for (int i = 0; i < dn->elements.size(); i++) {
+ _reduce_node_type(dn->elements[i].key);
+ _reduce_node_type(dn->elements[i].value);
+ }
+#endif // DEBUG_ENABLED
} break;
case Node::TYPE_SELF: {
node_type.has_type = true;
@@ -5704,6 +5924,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
IdentifierNode *id = static_cast<IdentifierNode *>(p_node);
if (id->declared_block) {
node_type = id->declared_block->variables[id->name]->get_datatype();
+ id->declared_block->variables[id->name]->usages += 1;
} else if (id->name == "#match_value") {
// It's a special id just for the match statetement, ignore
break;
@@ -5738,6 +5959,9 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
}
}
} else {
+#ifdef DEBUG_ENABLED
+ _add_warning(GDScriptWarning::UNSAFE_CAST, cn->line, cn->cast_type.to_string());
+#endif // DEBUG_ENABLED
_mark_line_as_unsafe(cn->line);
}
@@ -5769,7 +5993,8 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
// yield can return anything
node_type.has_type = false;
} break;
- case OperatorNode::OP_IS: {
+ case OperatorNode::OP_IS:
+ case OperatorNode::OP_IS_BUILTIN: {
if (op->arguments.size() != 2) {
_set_error("Parser bug: binary operation without 2 arguments.", op->line);
@@ -5786,8 +6011,11 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
}
type_type.is_meta_type = false; // Test the actual type
if (!_is_type_compatible(type_type, value_type) && !_is_type_compatible(value_type, type_type)) {
- // TODO: Make this a warning?
- _set_error("A value of type '" + value_type.to_string() + "' will never be an instance of '" + type_type.to_string() + "'.", op->line);
+ if (op->op == OperatorNode::OP_IS) {
+ _set_error("A value of type '" + value_type.to_string() + "' will never be an instance of '" + type_type.to_string() + "'.", op->line);
+ } else {
+ _set_error("A value of type '" + value_type.to_string() + "' will never be of type '" + type_type.to_string() + "'.", op->line);
+ }
return DataType();
}
}
@@ -5864,6 +6092,12 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
op->line, op->column);
return DataType();
}
+#ifdef DEBUG_ENABLED
+ if (var_op == Variant::OP_DIVIDE && argument_a_type.has_type && argument_a_type.kind == DataType::BUILTIN && argument_a_type.builtin_type == Variant::INT &&
+ argument_b_type.has_type && argument_b_type.kind == DataType::BUILTIN && argument_b_type.builtin_type == Variant::INT) {
+ _add_warning(GDScriptWarning::INTEGER_DIVISION, op->line);
+ }
+#endif // DEBUG_ENABLED
} break;
// Ternary operators
@@ -5882,10 +6116,11 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
node_type = true_type;
} else if (_is_type_compatible(false_type, true_type)) {
node_type = false_type;
+ } else {
+#ifdef DEBUG_ENABLED
+ _add_warning(GDScriptWarning::INCOMPATIBLE_TERNARY, op->line);
+#endif // DEBUG_ENABLED
}
-
- // TODO: Warn if types aren't compatible
-
} break;
// Assignment should never happen within an expression
case OperatorNode::OP_ASSIGN:
@@ -5948,6 +6183,11 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
node_type = result;
} else {
node_type = _reduce_identifier_type(&base_type, member_id->name, op->line);
+#ifdef DEBUG_ENABLED
+ if (!node_type.has_type) {
+ _add_warning(GDScriptWarning::UNSAFE_PROPERTY_ACCESS, op->line, member_id->name.operator String(), base_type.to_string());
+ }
+#endif // DEBUG_ENABLED
}
} else {
_mark_line_as_unsafe(op->line);
@@ -6020,6 +6260,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
case Variant::COLOR: {
error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::STRING;
} break;
+ default: {}
}
}
if (error) {
@@ -6137,6 +6378,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
}
}
} break;
+ default: {}
}
p_node->set_datatype(_resolve_type(node_type, p_node->line));
@@ -6198,6 +6440,10 @@ bool GDScriptParser::_get_function_signature(DataType &p_base_type, const String
StringName native;
if (p_base_type.kind == DataType::GDSCRIPT) {
base_gdscript = p_base_type.script_type;
+ if (base_gdscript.is_null() || !base_gdscript->is_valid()) {
+ // GDScript wasn't properly compíled, don't bother trying
+ return false;
+ }
} else if (p_base_type.kind == DataType::SCRIPT) {
base_script = p_base_type.script_type;
} else if (p_base_type.kind == DataType::NATIVE) {
@@ -6238,6 +6484,12 @@ bool GDScriptParser::_get_function_signature(DataType &p_base_type, const String
base_script = base_script->get_base_script();
}
+ if (native == StringName()) {
+ // Empty native class, might happen in some Script implementations
+ // Just ignore it
+ return false;
+ }
+
#ifdef DEBUG_METHODS_ENABLED
// Only native remains
@@ -6367,6 +6619,15 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat
if (!_is_type_compatible(arg_type, par_types[i], true)) {
types_match = false;
break;
+ } else {
+#ifdef DEBUG_ENABLED
+ if (arg_type.kind == DataType::BUILTIN && arg_type.builtin_type == Variant::INT && par_types[i].kind == DataType::BUILTIN && par_types[i].builtin_type == Variant::REAL) {
+ _add_warning(GDScriptWarning::NARROWING_CONVERSION, p_call->line, Variant::get_type_name(tn->vtype));
+ }
+ if (par_types[i].may_yield && p_call->arguments[i + 1]->type == Node::TYPE_OPERATOR) {
+ _add_warning(GDScriptWarning::FUNCTION_MAY_YIELD, p_call->line, _find_function_name(static_cast<OperatorNode *>(p_call->arguments[i + 1])));
+ }
+#endif // DEBUG_ENABLED
}
}
@@ -6400,6 +6661,13 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat
return_type = _type_from_property(mi.return_val, false);
+#ifdef DEBUG_ENABLED
+ // Check all arguments beforehand to solve warnings
+ for (int i = 1; i < p_call->arguments.size(); i++) {
+ _reduce_node_type(p_call->arguments[i]);
+ }
+#endif // DEBUG_ENABLED
+
// Check arguments
is_vararg = mi.flags & METHOD_FLAG_VARARG;
@@ -6426,6 +6694,13 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat
ERR_FAIL_V(DataType());
}
+#ifdef DEBUG_ENABLED
+ // Check all arguments beforehand to solve warnings
+ for (int i = arg_id + 1; i < p_call->arguments.size(); i++) {
+ _reduce_node_type(p_call->arguments[i]);
+ }
+#endif // DEBUG_ENABLED
+
IdentifierNode *func_id = static_cast<IdentifierNode *>(p_call->arguments[arg_id]);
callee_name = func_id->name;
arg_count -= 1 + arg_id;
@@ -6466,9 +6741,15 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat
}
}
+ bool rets = false;
return_type.has_type = true;
return_type.kind = DataType::BUILTIN;
- return_type.builtin_type = Variant::get_method_return_type(base_type.builtin_type, callee_name);
+ return_type.builtin_type = Variant::get_method_return_type(base_type.builtin_type, callee_name, &rets);
+ // If the method returns, but it might return any type, (Variant::NIL), pretend we don't know the type.
+ // At least make sure we know that it returns
+ if (rets && return_type.builtin_type == Variant::NIL) {
+ return_type.has_type = false;
+ }
break;
}
@@ -6505,8 +6786,18 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat
_set_error("Method '" + callee_name + "' is not declared in the current class.", p_call->line);
return DataType();
}
+ DataType tmp_type;
+ valid = _get_member_type(original_type, func_id->name, tmp_type);
+ if (valid) {
+ if (tmp_type.is_constant) {
+ _add_warning(GDScriptWarning::CONSTANT_USED_AS_FUNCTION, p_call->line, callee_name, original_type.to_string());
+ } else {
+ _add_warning(GDScriptWarning::PROPERTY_USED_AS_FUNCTION, p_call->line, callee_name, original_type.to_string());
+ }
+ }
+ _add_warning(GDScriptWarning::UNSAFE_METHOD_ACCESS, p_call->line, callee_name, original_type.to_string());
_mark_line_as_unsafe(p_call->line);
-#endif
+#endif // DEBUG_ENABLED
return DataType();
}
@@ -6522,7 +6813,19 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat
_set_error("Non-static function '" + String(callee_name) + "' can only be called from an instance.", p_call->line);
return DataType();
}
-#endif
+
+ // Check signal emission for warnings
+ if (callee_name == "emit_signal" && p_call->op == OperatorNode::OP_CALL && p_call->arguments[0]->type == Node::TYPE_SELF && p_call->arguments.size() >= 3 && p_call->arguments[2]->type == Node::TYPE_CONSTANT) {
+ ConstantNode *sig = static_cast<ConstantNode *>(p_call->arguments[2]);
+ String emitted = sig->value.get_type() == Variant::STRING ? sig->value.operator String() : "";
+ for (int i = 0; i < current_class->_signals.size(); i++) {
+ if (current_class->_signals[i].name == emitted) {
+ current_class->_signals.write[i].emissions += 1;
+ break;
+ }
+ }
+ }
+#endif // DEBUG_ENABLED
} break;
}
@@ -6547,8 +6850,15 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat
continue;
}
+ DataType arg_type = arg_types[i - arg_diff];
+
if (!par_type.has_type) {
_mark_line_as_unsafe(p_call->line);
+#ifdef DEBUG_ENABLED
+ if (par_type.may_yield && p_call->arguments[i]->type == Node::TYPE_OPERATOR) {
+ _add_warning(GDScriptWarning::FUNCTION_MAY_YIELD, p_call->line, _find_function_name(static_cast<OperatorNode *>(p_call->arguments[i])));
+ }
+#endif // DEBUG_ENABLED
} else if (!_is_type_compatible(arg_types[i - arg_diff], par_type, true)) {
// Supertypes are acceptable for dynamic compliance
if (!_is_type_compatible(par_type, arg_types[i - arg_diff])) {
@@ -6560,6 +6870,12 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat
} else {
_mark_line_as_unsafe(p_call->line);
}
+ } else {
+#ifdef DEBUG_ENABLED
+ if (arg_type.kind == DataType::BUILTIN && arg_type.builtin_type == Variant::INT && par_type.kind == DataType::BUILTIN && par_type.builtin_type == Variant::REAL) {
+ _add_warning(GDScriptWarning::NARROWING_CONVERSION, p_call->line, callee_name);
+ }
+#endif // DEBUG_ENABLED
}
}
@@ -6616,6 +6932,10 @@ bool GDScriptParser::_get_member_type(const DataType &p_base_type, const StringN
Ref<GDScript> gds;
if (base_type.kind == DataType::GDSCRIPT) {
gds = base_type.script_type;
+ if (gds.is_null() || !gds->is_valid()) {
+ // GDScript wasn't properly compíled, don't bother trying
+ return false;
+ }
}
Ref<Script> scr;
@@ -6678,6 +6998,12 @@ bool GDScriptParser::_get_member_type(const DataType &p_base_type, const StringN
scr = scr->get_base_script();
}
+ if (native == StringName()) {
+ // Empty native class, might happen in some Script implementations
+ // Just ignore it
+ return false;
+ }
+
// Check ClassDB
if (!ClassDB::class_exists(native)) {
native = "_" + native.operator String();
@@ -6795,6 +7121,14 @@ GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType
DataType member_type;
+ for (int i = 0; i < current_class->variables.size(); i++) {
+ if (current_class->variables[i].identifier == p_identifier) {
+ member_type = current_class->variables[i].data_type;
+ current_class->variables.write[i].usages += 1;
+ return member_type;
+ }
+ }
+
if (_get_member_type(base_type, p_identifier, member_type)) {
return member_type;
}
@@ -6922,6 +7256,19 @@ GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType
_set_error("Identifier '" + p_identifier.operator String() + "' is not declared in the current scope.", p_line);
}
+#ifdef DEBUG_ENABLED
+ {
+ DataType tmp_type;
+ List<DataType> arg_types;
+ int argcount;
+ bool _static;
+ bool vararg;
+ if (_get_function_signature(base_type, p_identifier, tmp_type, arg_types, argcount, _static, vararg)) {
+ _add_warning(GDScriptWarning::FUNCTION_USED_AS_PROPERTY, p_line, p_identifier.operator String(), base_type.to_string());
+ }
+ }
+#endif // DEBUG_ENABLED
+
_mark_line_as_unsafe(p_line);
return DataType();
}
@@ -6946,6 +7293,12 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) {
expr.is_constant = true;
c.type = expr;
c.expression->set_datatype(expr);
+
+ DataType tmp;
+ if (_get_member_type(p_class->base_type, E->key(), tmp)) {
+ _set_error("Member '" + String(E->key()) + "' already exists in parent class.", c.expression->line);
+ return;
+ }
}
// Function declarations
@@ -6988,7 +7341,7 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) {
return;
}
- // Replace assigment with implict conversion
+ // Replace assignment with implict conversion
BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>();
convert->line = v.line;
convert->function = GDScriptFunctions::TYPE_CONVERT;
@@ -7174,6 +7527,11 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) {
}
}
}
+#ifdef DEBUG_ENABLED
+ if (p_function->arguments_usage[i] == 0 && !p_function->arguments[i].operator String().begins_with("_")) {
+ _add_warning(GDScriptWarning::UNUSED_ARGUMENT, p_function->line, p_function->name, p_function->arguments[i].operator String());
+ }
+#endif // DEBUG_ENABLED
}
if (!(p_function->name == "_init")) {
@@ -7244,6 +7602,7 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) {
if (p_function->has_yield) {
// yield() will make the function return a GDScriptFunctionState, so the type is ambiguous
p_function->return_type.has_type = false;
+ p_function->return_type.may_yield = true;
}
}
@@ -7270,6 +7629,20 @@ void GDScriptParser::_check_class_blocks_types(ClassNode *p_class) {
if (error_set) return;
}
+#ifdef DEBUG_ENABLED
+ // Warnings
+ for (int i = 0; i < p_class->variables.size(); i++) {
+ if (p_class->variables[i].usages == 0) {
+ _add_warning(GDScriptWarning::UNUSED_CLASS_VARIABLE, p_class->variables[i].line, p_class->variables[i].identifier);
+ }
+ }
+ for (int i = 0; i < p_class->_signals.size(); i++) {
+ if (p_class->_signals[i].emissions == 0) {
+ _add_warning(GDScriptWarning::UNUSED_SIGNAL, p_class->_signals[i].line, p_class->_signals[i].name);
+ }
+ }
+#endif // DEBUG_ENABLED
+
// Inner classes
for (int i = 0; i < p_class->subclasses.size(); i++) {
current_class = p_class->subclasses[i];
@@ -7279,6 +7652,26 @@ void GDScriptParser::_check_class_blocks_types(ClassNode *p_class) {
}
}
+#ifdef DEBUG_ENABLED
+static String _find_function_name(const GDScriptParser::OperatorNode *p_call) {
+ switch (p_call->arguments[0]->type) {
+ case GDScriptParser::Node::TYPE_TYPE: {
+ return Variant::get_type_name(static_cast<GDScriptParser::TypeNode *>(p_call->arguments[0])->vtype);
+ } break;
+ case GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION: {
+ return GDScriptFunctions::get_func_name(static_cast<GDScriptParser::BuiltInFunctionNode *>(p_call->arguments[0])->function);
+ } break;
+ default: {
+ int id_index = p_call->op == GDScriptParser::OperatorNode::OP_PARENT_CALL ? 0 : 1;
+ if (p_call->arguments.size() > id_index && p_call->arguments[id_index]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
+ return static_cast<GDScriptParser::IdentifierNode *>(p_call->arguments[id_index])->name;
+ }
+ } break;
+ }
+ return String();
+}
+#endif // DEBUG_ENABLED
+
void GDScriptParser::_check_block_types(BlockNode *p_block) {
Node *last_var_assign = NULL;
@@ -7297,8 +7690,23 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
lv->datatype = _resolve_type(lv->datatype, lv->line);
_mark_line_as_safe(lv->line);
+ last_var_assign = lv->assign;
if (lv->assign) {
DataType assign_type = _reduce_node_type(lv->assign);
+#ifdef DEBUG_ENABLED
+ if (assign_type.has_type && assign_type.kind == DataType::BUILTIN && assign_type.builtin_type == Variant::NIL) {
+ if (lv->assign->type == Node::TYPE_OPERATOR) {
+ OperatorNode *call = static_cast<OperatorNode *>(lv->assign);
+ if (call->op == OperatorNode::OP_CALL || call->op == OperatorNode::OP_PARENT_CALL) {
+ _add_warning(GDScriptWarning::VOID_ASSIGNMENT, lv->line, _find_function_name(call));
+ }
+ }
+ }
+ if (lv->datatype.has_type && assign_type.may_yield && lv->assign->type == Node::TYPE_OPERATOR) {
+ _add_warning(GDScriptWarning::FUNCTION_MAY_YIELD, lv->line, _find_function_name(static_cast<OperatorNode *>(lv->assign)));
+ }
+#endif // DEBUG_ENABLED
+
if (!_is_type_compatible(lv->datatype, assign_type)) {
// Try supertype test
if (_is_type_compatible(assign_type, lv->datatype)) {
@@ -7311,7 +7719,7 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
lv->line);
return;
}
- // Replace assigment with implict conversion
+ // Replace assignment with implict conversion
BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>();
convert->line = lv->line;
convert->function = GDScriptFunctions::TYPE_CONVERT;
@@ -7329,6 +7737,11 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
lv->assign = convert_call;
lv->assign_op->arguments.write[1] = convert_call;
+#ifdef DEBUG_ENABLED
+ if (lv->datatype.builtin_type == Variant::INT && assign_type.builtin_type == Variant::REAL) {
+ _add_warning(GDScriptWarning::NARROWING_CONVERSION, lv->line);
+ }
+#endif // DEBUG_ENABLED
}
}
if (lv->datatype.infer_type) {
@@ -7343,15 +7756,6 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
_mark_line_as_unsafe(lv->line);
}
}
- last_var_assign = lv->assign;
-
- // TODO: Make a warning
- /*
- if (lv->assignments == 0) {
- _set_error("Variable '" + String(lv->name) + "' is never assigned.", lv->line);
- return;
- }
- */
} break;
case Node::TYPE_OPERATOR: {
OperatorNode *op = static_cast<OperatorNode *>(statement);
@@ -7417,6 +7821,19 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
} else {
rh_type = _reduce_node_type(op->arguments[1]);
}
+#ifdef DEBUG_ENABLED
+ if (rh_type.has_type && rh_type.kind == DataType::BUILTIN && rh_type.builtin_type == Variant::NIL) {
+ if (op->arguments[1]->type == Node::TYPE_OPERATOR) {
+ OperatorNode *call = static_cast<OperatorNode *>(op->arguments[1]);
+ if (call->op == OperatorNode::OP_CALL || call->op == OperatorNode::OP_PARENT_CALL) {
+ _add_warning(GDScriptWarning::VOID_ASSIGNMENT, op->line, _find_function_name(call));
+ }
+ }
+ }
+ if (lh_type.has_type && rh_type.may_yield && op->arguments[1]->type == Node::TYPE_OPERATOR) {
+ _add_warning(GDScriptWarning::FUNCTION_MAY_YIELD, op->line, _find_function_name(static_cast<OperatorNode *>(op->arguments[1])));
+ }
+#endif // DEBUG_ENABLED
if (!_is_type_compatible(lh_type, rh_type)) {
// Try supertype test
@@ -7430,7 +7847,7 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
op->line);
return;
}
- // Replace assigment with implict conversion
+ // Replace assignment with implict conversion
BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>();
convert->line = op->line;
convert->function = GDScriptFunctions::TYPE_CONVERT;
@@ -7447,6 +7864,11 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
convert_call->arguments.push_back(tgt_type);
op->arguments.write[1] = convert_call;
+#ifdef DEBUG_ENABLED
+ if (lh_type.builtin_type == Variant::INT && rh_type.builtin_type == Variant::REAL) {
+ _add_warning(GDScriptWarning::NARROWING_CONVERSION, op->line);
+ }
+#endif // DEBUG_ENABLED
}
}
if (!rh_type.has_type && (op->op != OperatorNode::OP_ASSIGN || lh_type.has_type || op->arguments[0]->type == Node::TYPE_OPERATOR)) {
@@ -7456,15 +7878,29 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
case OperatorNode::OP_CALL:
case OperatorNode::OP_PARENT_CALL: {
_mark_line_as_safe(op->line);
- _reduce_function_call_type(op);
+ DataType func_type = _reduce_function_call_type(op);
+#ifdef DEBUG_ENABLED
+ if (func_type.has_type && (func_type.kind != DataType::BUILTIN || func_type.builtin_type != Variant::NIL)) {
+ // Figure out function name for warning
+ String func_name = _find_function_name(op);
+ if (func_name.empty()) {
+ func_name = "<undetected name>";
+ }
+ _add_warning(GDScriptWarning::RETURN_VALUE_DISCARDED, op->line, func_name);
+ }
+#endif // DEBUG_ENABLED
if (error_set) return;
} break;
+ case OperatorNode::OP_YIELD: {
+ _mark_line_as_safe(op->line);
+ _reduce_node_type(op);
+ } break;
default: {
_mark_line_as_safe(op->line);
_reduce_node_type(op); // Test for safety anyway
- // TODO: Make this a warning
- /*_set_error("Standalone expression, nothing is done in this line.", statement->line);
- return; */
+#ifdef DEBUG_ENABLED
+ _add_warning(GDScriptWarning::STANDALONE_EXPRESSION, statement->line);
+#endif // DEBUG_ENABLED
}
}
} break;
@@ -7531,9 +7967,9 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
default: {
_mark_line_as_safe(statement->line);
_reduce_node_type(statement); // Test for safety anyway
- // TODO: Make this a warning
- /* _set_error("Standalone expression, nothing is done in this line.", statement->line);
- return; */
+#ifdef DEBUG_ENABLED
+ _add_warning(GDScriptWarning::STANDALONE_EXPRESSION, statement->line);
+#endif // DEBUG_ENABLED
}
}
}
@@ -7545,6 +7981,20 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
current_block = p_block;
if (error_set) return;
}
+
+#ifdef DEBUG_ENABLED
+ // Warnings check
+ for (Map<StringName, LocalVarNode *>::Element *E = p_block->variables.front(); E; E = E->next()) {
+ LocalVarNode *lv = E->get();
+ if (!lv->name.operator String().begins_with("_")) {
+ if (lv->usages == 0) {
+ _add_warning(GDScriptWarning::UNUSED_VARIABLE, lv->line, lv->name);
+ } else if (lv->assignments == 0) {
+ _add_warning(GDScriptWarning::UNASSIGNED_VARIABLE, lv->line, lv->name);
+ }
+ }
+ }
+#endif // DEBUG_ENABLED
}
void GDScriptParser::_set_error(const String &p_error, int p_line, int p_column) {
@@ -7558,6 +8008,56 @@ void GDScriptParser::_set_error(const String &p_error, int p_line, int p_column)
error_set = true;
}
+#ifdef DEBUG_ENABLED
+void GDScriptParser::_add_warning(int p_code, int p_line, const String &p_symbol1, const String &p_symbol2, const String &p_symbol3, const String &p_symbol4) {
+ Vector<String> symbols;
+ if (!p_symbol1.empty()) {
+ symbols.push_back(p_symbol1);
+ }
+ if (!p_symbol2.empty()) {
+ symbols.push_back(p_symbol2);
+ }
+ if (!p_symbol3.empty()) {
+ symbols.push_back(p_symbol3);
+ }
+ if (!p_symbol4.empty()) {
+ symbols.push_back(p_symbol4);
+ }
+ _add_warning(p_code, p_line, symbols);
+}
+
+void GDScriptParser::_add_warning(int p_code, int p_line, const Vector<String> &p_symbols) {
+ if (tokenizer->is_ignoring_warnings() || !GLOBAL_GET("debug/gdscript/warnings/enable").booleanize()) {
+ return;
+ }
+ String warn_name = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)p_code).to_lower();
+ if (tokenizer->get_warning_global_skips().has(warn_name)) {
+ return;
+ }
+ if (!GLOBAL_GET("debug/gdscript/warnings/" + warn_name)) {
+ return;
+ }
+
+ GDScriptWarning warn;
+ warn.code = (GDScriptWarning::Code)p_code;
+ warn.symbols = p_symbols;
+ warn.line = p_line == -1 ? tokenizer->get_token_line() : p_line;
+
+ List<GDScriptWarning>::Element *before = NULL;
+ for (List<GDScriptWarning>::Element *E = warnings.front(); E; E = E->next()) {
+ if (E->get().line > warn.line) {
+ break;
+ }
+ before = E;
+ }
+ if (before) {
+ warnings.insert_after(before, warn);
+ } else {
+ warnings.push_front(warn);
+ }
+}
+#endif // DEBUG_ENABLED
+
String GDScriptParser::get_error() const {
return error;
@@ -7624,6 +8124,37 @@ Error GDScriptParser::_parse(const String &p_base_path) {
return ERR_PARSE_ERROR;
}
+#ifdef DEBUG_ENABLED
+ // Resolve warning ignores
+ Vector<Pair<int, String> > warning_skips = tokenizer->get_warning_skips();
+ bool warning_is_error = GLOBAL_GET("debug/gdscript/warnings/treat_warnings_as_errors").booleanize();
+ for (List<GDScriptWarning>::Element *E = warnings.front(); E;) {
+ GDScriptWarning &w = E->get();
+ int skip_index = -1;
+ for (int i = 0; i < warning_skips.size(); i++) {
+ if (warning_skips[i].first >= w.line) {
+ break;
+ }
+ skip_index = i;
+ }
+ List<GDScriptWarning>::Element *next = E->next();
+ bool erase = false;
+ if (skip_index != -1) {
+ if (warning_skips[skip_index].second == GDScriptWarning::get_name_from_code(w.code).to_lower()) {
+ erase = true;
+ }
+ warning_skips.remove(skip_index);
+ }
+ if (erase) {
+ warnings.erase(E);
+ } else if (warning_is_error) {
+ _set_error(w.get_message() + " (warning treated as error)", w.line);
+ return ERR_PARSE_ERROR;
+ }
+ E = next;
+ }
+#endif // DEBUG_ENABLED
+
return OK;
}
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 48f256b4c6..8121fb7f85 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -31,13 +31,14 @@
#ifndef GDSCRIPT_PARSER_H
#define GDSCRIPT_PARSER_H
+#include "core/map.h"
+#include "core/object.h"
+#include "core/script_language.h"
#include "gdscript_functions.h"
#include "gdscript_tokenizer.h"
-#include "map.h"
-#include "object.h"
-#include "script_language.h"
struct GDScriptDataType;
+struct GDScriptWarning;
class GDScriptParser {
public:
@@ -57,6 +58,7 @@ public:
bool is_constant;
bool is_meta_type; // Whether the value can be used as a type
bool infer_type;
+ bool may_yield; // For function calls
Variant::Type builtin_type;
StringName native_type;
@@ -86,6 +88,8 @@ public:
case CLASS: {
return class_type == other.class_type;
} break;
+ case UNRESOLVED: {
+ } break;
}
return false;
}
@@ -95,6 +99,7 @@ public:
is_constant(false),
is_meta_type(false),
infer_type(false),
+ may_yield(false),
builtin_type(Variant::NIL),
class_type(NULL) {}
};
@@ -146,6 +151,7 @@ public:
StringName extends_file;
Vector<StringName> extends_class;
DataType base_type;
+ String icon_path;
struct Member {
PropertyInfo _export;
@@ -160,6 +166,7 @@ public:
Node *expression;
OperatorNode *initial_assignment;
MultiplayerAPI::RPCMode rpc_mode;
+ int usages;
};
struct Constant {
Node *expression;
@@ -169,6 +176,8 @@ public:
struct Signal {
StringName name;
Vector<StringName> arguments;
+ int emissions;
+ int line;
};
Vector<ClassNode *> subclasses;
@@ -197,12 +206,16 @@ public:
bool _static;
MultiplayerAPI::RPCMode rpc_mode;
bool has_yield;
+ bool has_unreachable_code;
StringName name;
DataType return_type;
Vector<StringName> arguments;
Vector<DataType> argument_types;
Vector<Node *> default_values;
BlockNode *body;
+#ifdef DEBUG_ENABLED
+ Vector<int> arguments_usage;
+#endif // DEBUG_ENABLED
virtual DataType get_datatype() const { return return_type; }
virtual void set_datatype(const DataType &p_datatype) { return_type = p_datatype; }
@@ -212,6 +225,7 @@ public:
_static = false;
rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
has_yield = false;
+ has_unreachable_code = false;
}
};
@@ -267,6 +281,7 @@ public:
Node *assign;
OperatorNode *assign_op;
int assignments;
+ int usages;
DataType datatype;
virtual DataType get_datatype() const { return datatype; }
virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
@@ -275,6 +290,7 @@ public:
assign = NULL;
assign_op = NULL;
assignments = 0;
+ usages = 0;
}
};
@@ -331,6 +347,7 @@ public:
OP_PARENT_CALL,
OP_YIELD,
OP_IS,
+ OP_IS_BUILTIN,
//indexing operator
OP_INDEX,
OP_INDEX_NAMED,
@@ -518,6 +535,10 @@ private:
Set<int> *safe_lines;
#endif // DEBUG_ENABLED
+#ifdef DEBUG_ENABLED
+ List<GDScriptWarning> warnings;
+#endif // DEBUG_ENABLED
+
int pending_newline;
List<int> tab_level;
@@ -534,7 +555,6 @@ private:
CompletionType completion_type;
StringName completion_cursor;
- bool completion_static;
Variant::Type completion_built_in_constant;
Node *completion_node;
ClassNode *completion_class;
@@ -550,6 +570,10 @@ private:
MultiplayerAPI::RPCMode rpc_mode;
void _set_error(const String &p_error, int p_line = -1, int p_column = -1);
+#ifdef DEBUG_ENABLED
+ void _add_warning(int p_code, int p_line = -1, const String &p_symbol1 = String(), const String &p_symbol2 = String(), const String &p_symbol3 = String(), const String &p_symbol4 = String());
+ void _add_warning(int p_code, int p_line, const Vector<String> &p_symbols);
+#endif // DEBUG_ENABLED
bool _recover_from_completion();
bool _parse_arguments(Node *p_parent, Vector<Node *> &p_args, bool p_static, bool p_can_codecomplete = false);
@@ -605,6 +629,9 @@ public:
String get_error() const;
int get_error_line() const;
int get_error_column() const;
+#ifdef DEBUG_ENABLED
+ const List<GDScriptWarning> &get_warnings() const { return warnings; }
+#endif // DEBUG_ENABLED
Error parse(const String &p_code, const String &p_base_path = "", bool p_just_validate = false, const String &p_self_path = "", bool p_for_completion = false, Set<int> *r_safe_lines = NULL);
Error parse_bytecode(const Vector<uint8_t> &p_bytecode, const String &p_base_path = "", const String &p_self_path = "");
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 7ae7c72ed3..c37142b3c1 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -30,10 +30,10 @@
#include "gdscript_tokenizer.h"
+#include "core/io/marshalls.h"
+#include "core/map.h"
+#include "core/print_string.h"
#include "gdscript_functions.h"
-#include "io/marshalls.h"
-#include "map.h"
-#include "print_string.h"
const char *GDScriptTokenizer::token_names[TK_MAX] = {
"Empty",
@@ -112,10 +112,11 @@ const char *GDScriptTokenizer::token_names[TK_MAX] = {
"rpc",
"sync",
"master",
+ "puppet",
"slave",
"remotesync",
"mastersync",
- "slavesync",
+ "puppetsync",
"'['",
"']'",
"'{'",
@@ -127,8 +128,8 @@ const char *GDScriptTokenizer::token_names[TK_MAX] = {
"'.'",
"'?'",
"':'",
- "'->'",
"'$'",
+ "'->'",
"'\\n'",
"PI",
"TAU",
@@ -210,10 +211,11 @@ static const _kws _keyword_list[] = {
{ GDScriptTokenizer::TK_PR_REMOTE, "remote" },
{ GDScriptTokenizer::TK_PR_MASTER, "master" },
{ GDScriptTokenizer::TK_PR_SLAVE, "slave" },
+ { GDScriptTokenizer::TK_PR_PUPPET, "puppet" },
{ GDScriptTokenizer::TK_PR_SYNC, "sync" },
{ GDScriptTokenizer::TK_PR_REMOTESYNC, "remotesync" },
{ GDScriptTokenizer::TK_PR_MASTERSYNC, "mastersync" },
- { GDScriptTokenizer::TK_PR_SLAVESYNC, "slavesync" },
+ { GDScriptTokenizer::TK_PR_PUPPETSYNC, "puppetsync" },
{ GDScriptTokenizer::TK_PR_CONST, "const" },
{ GDScriptTokenizer::TK_PR_ENUM, "enum" },
//controlflow
@@ -258,11 +260,11 @@ bool GDScriptTokenizer::is_token_literal(int p_offset, bool variable_safe) const
case TK_PR_SIGNAL:
case TK_PR_REMOTE:
case TK_PR_MASTER:
- case TK_PR_SLAVE:
+ case TK_PR_PUPPET:
case TK_PR_SYNC:
case TK_PR_REMOTESYNC:
case TK_PR_MASTERSYNC:
- case TK_PR_SLAVESYNC:
+ case TK_PR_PUPPETSYNC:
return true;
// Literal for non-variables only:
@@ -526,8 +528,13 @@ void GDScriptTokenizerText::_advance() {
return;
}
case '#': { // line comment skip
-
+#ifdef DEBUG_ENABLED
+ String comment;
+#endif // DEBUG_ENABLED
while (GETCHAR(0) != '\n') {
+#ifdef DEBUG_ENABLED
+ comment += GETCHAR(0);
+#endif // DEBUG_ENABLED
code_pos++;
if (GETCHAR(0) == 0) { //end of file
//_make_error("Unterminated Comment");
@@ -535,6 +542,17 @@ void GDScriptTokenizerText::_advance() {
return;
}
}
+#ifdef DEBUG_ENABLED
+ if (comment.begins_with("#warning-ignore:")) {
+ String code = comment.get_slice(":", 1);
+ warning_skips.push_back(Pair<int, String>(line, code.strip_edges().to_lower()));
+ } else if (comment.begins_with("#warning-ignore-all:")) {
+ String code = comment.get_slice(":", 1);
+ warning_global_skips.insert(code.strip_edges().to_lower());
+ } else if (comment.strip_edges() == "#warnings-disable") {
+ ignore_warnings = true;
+ }
+#endif // DEBUG_ENABLED
INCPOS(1);
column = 1;
line++;
@@ -921,7 +939,6 @@ void GDScriptTokenizerText::_advance() {
_make_constant(val);
} else if (period_found || exponent_found) {
double val = str.to_double();
- //print_line("*%*%*%*% to convert: "+str+" result: "+rtos(val));
_make_constant(val);
} else {
int64_t val = str.to_int64();
@@ -1045,6 +1062,9 @@ void GDScriptTokenizerText::set_code(const String &p_code) {
column = 1; //the same holds for columns
tk_rb_pos = 0;
error_flag = false;
+#ifdef DEBUG_ENABLED
+ ignore_warnings = false;
+#endif // DEBUG_ENABLED
last_error = "";
for (int i = 0; i < MAX_LOOKAHEAD + 1; i++)
_advance();
diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h
index 5bd303224c..cc894fb101 100644
--- a/modules/gdscript/gdscript_tokenizer.h
+++ b/modules/gdscript/gdscript_tokenizer.h
@@ -31,11 +31,12 @@
#ifndef GDSCRIPT_TOKENIZER_H
#define GDSCRIPT_TOKENIZER_H
+#include "core/pair.h"
+#include "core/string_db.h"
+#include "core/ustring.h"
+#include "core/variant.h"
+#include "core/vmap.h"
#include "gdscript_functions.h"
-#include "string_db.h"
-#include "ustring.h"
-#include "variant.h"
-#include "vmap.h"
class GDScriptTokenizer {
public:
@@ -118,9 +119,10 @@ public:
TK_PR_SYNC,
TK_PR_MASTER,
TK_PR_SLAVE,
+ TK_PR_PUPPET,
TK_PR_REMOTESYNC,
TK_PR_MASTERSYNC,
- TK_PR_SLAVESYNC,
+ TK_PR_PUPPETSYNC,
TK_BRACKET_OPEN,
TK_BRACKET_CLOSE,
TK_CURLY_BRACKET_OPEN,
@@ -171,6 +173,11 @@ public:
virtual int get_token_line_indent(int p_offset = 0) const = 0;
virtual String get_token_error(int p_offset = 0) const = 0;
virtual void advance(int p_amount = 1) = 0;
+#ifdef DEBUG_ENABLED
+ virtual const Vector<Pair<int, String> > &get_warning_skips() const = 0;
+ virtual const Set<String> &get_warning_global_skips() const = 0;
+ virtual const bool is_ignoring_warnings() const = 0;
+#endif // DEBUG_ENABLED
virtual ~GDScriptTokenizer(){};
};
@@ -190,6 +197,7 @@ class GDScriptTokenizerText : public GDScriptTokenizer {
union {
Variant::Type vtype; //for type types
GDScriptFunctions::Function func; //function for built in functions
+ int warning_code; //for warning skip
};
int line, col;
TokenData() {
@@ -217,6 +225,11 @@ class GDScriptTokenizerText : public GDScriptTokenizer {
int tk_rb_pos;
String last_error;
bool error_flag;
+#ifdef DEBUG_ENABLED
+ Vector<Pair<int, String> > warning_skips;
+ Set<String> warning_global_skips;
+ bool ignore_warnings;
+#endif // DEBUG_ENABLED
void _advance();
@@ -232,6 +245,11 @@ public:
virtual const Variant &get_token_constant(int p_offset = 0) const;
virtual String get_token_error(int p_offset = 0) const;
virtual void advance(int p_amount = 1);
+#ifdef DEBUG_ENABLED
+ virtual const Vector<Pair<int, String> > &get_warning_skips() const { return warning_skips; }
+ virtual const Set<String> &get_warning_global_skips() const { return warning_global_skips; }
+ virtual const bool is_ignoring_warnings() const { return ignore_warnings; }
+#endif // DEBUG_ENABLED
};
class GDScriptTokenizerBuffer : public GDScriptTokenizer {
@@ -265,6 +283,17 @@ public:
virtual const Variant &get_token_constant(int p_offset = 0) const;
virtual String get_token_error(int p_offset = 0) const;
virtual void advance(int p_amount = 1);
+#ifdef DEBUG_ENABLED
+ virtual const Vector<Pair<int, String> > &get_warning_skips() const {
+ static Vector<Pair<int, String> > v;
+ return v;
+ }
+ virtual const Set<String> &get_warning_global_skips() const {
+ static Set<String> s;
+ return s;
+ }
+ virtual const bool is_ignoring_warnings() const { return true; }
+#endif // DEBUG_ENABLED
GDScriptTokenizerBuffer();
};
diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp
index 422223370b..26dcbdcf89 100644
--- a/modules/gdscript/register_types.cpp
+++ b/modules/gdscript/register_types.cpp
@@ -30,12 +30,12 @@
#include "register_types.h"
+#include "core/io/file_access_encrypted.h"
+#include "core/io/resource_loader.h"
+#include "core/os/file_access.h"
#include "editor/gdscript_highlighter.h"
#include "gdscript.h"
#include "gdscript_tokenizer.h"
-#include "io/file_access_encrypted.h"
-#include "io/resource_loader.h"
-#include "os/file_access.h"
GDScriptLanguage *script_language_gd = NULL;
ResourceFormatLoaderGDScript *resource_loader_gd = NULL;