You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-16 14:00:40 +00:00
GDScript: Improve call analysis
* Add missing `UNSAFE_CALL_ARGUMENT` warning. * Fix `Object` constructor. * Display an error for non-existent static methods.
This commit is contained in:
@@ -248,7 +248,7 @@ Error GDScriptAnalyzer::check_native_member_name_conflict(const StringName &p_me
|
|||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GDScriptParser::get_builtin_type(p_member_name) != Variant::VARIANT_MAX) {
|
if (GDScriptParser::get_builtin_type(p_member_name) < Variant::VARIANT_MAX) {
|
||||||
push_error(vformat(R"(The member "%s" cannot have the same name as a builtin type.)", p_member_name), p_member_node);
|
push_error(vformat(R"(The member "%s" cannot have the same name as a builtin type.)", p_member_name), p_member_node);
|
||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
}
|
}
|
||||||
@@ -669,11 +669,6 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
|
|||||||
return bad_type;
|
return bad_type;
|
||||||
}
|
}
|
||||||
result.kind = GDScriptParser::DataType::VARIANT;
|
result.kind = GDScriptParser::DataType::VARIANT;
|
||||||
} else if (first == SNAME("Object")) {
|
|
||||||
// Object is treated like a native type, not a built-in.
|
|
||||||
result.kind = GDScriptParser::DataType::NATIVE;
|
|
||||||
result.builtin_type = Variant::OBJECT;
|
|
||||||
result.native_type = SNAME("Object");
|
|
||||||
} else if (GDScriptParser::get_builtin_type(first) < Variant::VARIANT_MAX) {
|
} else if (GDScriptParser::get_builtin_type(first) < Variant::VARIANT_MAX) {
|
||||||
// Built-in types.
|
// Built-in types.
|
||||||
if (p_type->type_chain.size() > 1) {
|
if (p_type->type_chain.size() > 1) {
|
||||||
@@ -1704,7 +1699,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
|
|||||||
}
|
}
|
||||||
parent_signature += ") -> ";
|
parent_signature += ") -> ";
|
||||||
|
|
||||||
const String return_type = parent_return_type.is_hard_type() ? parent_return_type.to_string() : "Variant";
|
const String return_type = parent_return_type.to_string_strict();
|
||||||
if (return_type == "null") {
|
if (return_type == "null") {
|
||||||
parent_signature += "void";
|
parent_signature += "void";
|
||||||
} else {
|
} else {
|
||||||
@@ -2888,19 +2883,20 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
|
|||||||
if (!p_call->is_super && callee_type == GDScriptParser::Node::IDENTIFIER) {
|
if (!p_call->is_super && callee_type == GDScriptParser::Node::IDENTIFIER) {
|
||||||
// Call to name directly.
|
// Call to name directly.
|
||||||
StringName function_name = p_call->function_name;
|
StringName function_name = p_call->function_name;
|
||||||
Variant::Type builtin_type = GDScriptParser::get_builtin_type(function_name);
|
|
||||||
|
|
||||||
|
if (function_name == SNAME("Object")) {
|
||||||
|
push_error(R"*(Invalid constructor "Object()", use "Object.new()" instead.)*", p_call);
|
||||||
|
p_call->set_datatype(call_type);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant::Type builtin_type = GDScriptParser::get_builtin_type(function_name);
|
||||||
if (builtin_type < Variant::VARIANT_MAX) {
|
if (builtin_type < Variant::VARIANT_MAX) {
|
||||||
// Is a builtin constructor.
|
// Is a builtin constructor.
|
||||||
call_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
|
call_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
|
||||||
call_type.kind = GDScriptParser::DataType::BUILTIN;
|
call_type.kind = GDScriptParser::DataType::BUILTIN;
|
||||||
call_type.builtin_type = builtin_type;
|
call_type.builtin_type = builtin_type;
|
||||||
|
|
||||||
if (builtin_type == Variant::OBJECT) {
|
|
||||||
call_type.kind = GDScriptParser::DataType::NATIVE;
|
|
||||||
call_type.native_type = function_name; // "Object".
|
|
||||||
}
|
|
||||||
|
|
||||||
bool safe_to_fold = true;
|
bool safe_to_fold = true;
|
||||||
switch (builtin_type) {
|
switch (builtin_type) {
|
||||||
// Those are stored by reference so not suited for compile-time construction.
|
// Those are stored by reference so not suited for compile-time construction.
|
||||||
@@ -2936,7 +2932,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
|
|||||||
|
|
||||||
switch (err.error) {
|
switch (err.error) {
|
||||||
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
|
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
|
||||||
push_error(vformat(R"(Invalid argument for %s constructor: argument %d should be "%s" but is "%s".)", Variant::get_type_name(builtin_type), err.argument + 1,
|
push_error(vformat(R"*(Invalid argument for "%s()" constructor: argument %d should be "%s" but is "%s".)*", Variant::get_type_name(builtin_type), err.argument + 1,
|
||||||
Variant::get_type_name(Variant::Type(err.expected)), p_call->arguments[err.argument]->get_datatype().to_string()),
|
Variant::get_type_name(Variant::Type(err.expected)), p_call->arguments[err.argument]->get_datatype().to_string()),
|
||||||
p_call->arguments[err.argument]);
|
p_call->arguments[err.argument]);
|
||||||
break;
|
break;
|
||||||
@@ -2952,10 +2948,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
|
|||||||
push_error(vformat(R"(No constructor of "%s" matches the signature "%s".)", Variant::get_type_name(builtin_type), signature), p_call->callee);
|
push_error(vformat(R"(No constructor of "%s" matches the signature "%s".)", Variant::get_type_name(builtin_type), signature), p_call->callee);
|
||||||
} break;
|
} break;
|
||||||
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
|
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
|
||||||
push_error(vformat(R"(Too many arguments for %s constructor. Received %d but expected %d.)", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call);
|
push_error(vformat(R"*(Too many arguments for "%s()" constructor. Received %d but expected %d.)*", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call);
|
||||||
break;
|
break;
|
||||||
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
|
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
|
||||||
push_error(vformat(R"(Too few arguments for %s constructor. Received %d but expected %d.)", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call);
|
push_error(vformat(R"*(Too few arguments for "%s()" constructor. Received %d but expected %d.)*", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call);
|
||||||
break;
|
break;
|
||||||
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
|
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
|
||||||
case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
|
case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
|
||||||
@@ -2966,21 +2962,27 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO: Check constructors without constants.
|
|
||||||
|
|
||||||
// If there's one argument, try to use copy constructor (those aren't explicitly defined).
|
// If there's one argument, try to use copy constructor (those aren't explicitly defined).
|
||||||
if (p_call->arguments.size() == 1) {
|
if (p_call->arguments.size() == 1) {
|
||||||
GDScriptParser::DataType arg_type = p_call->arguments[0]->get_datatype();
|
GDScriptParser::DataType arg_type = p_call->arguments[0]->get_datatype();
|
||||||
if (arg_type.is_variant()) {
|
if (arg_type.is_hard_type() && !arg_type.is_variant()) {
|
||||||
mark_node_unsafe(p_call->arguments[0]);
|
|
||||||
} else {
|
|
||||||
if (arg_type.kind == GDScriptParser::DataType::BUILTIN && arg_type.builtin_type == builtin_type) {
|
if (arg_type.kind == GDScriptParser::DataType::BUILTIN && arg_type.builtin_type == builtin_type) {
|
||||||
// Okay.
|
// Okay.
|
||||||
p_call->set_datatype(call_type);
|
p_call->set_datatype(call_type);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
mark_node_unsafe(p_call);
|
||||||
|
// We don't know what type was expected since constructors support overloads.
|
||||||
|
// TODO: Improve this by checking for matching candidates?
|
||||||
|
parser->push_warning(p_call->arguments[0], GDScriptWarning::UNSAFE_CALL_ARGUMENT, "1", function_name, "<unknown type>", "Variant");
|
||||||
|
#endif
|
||||||
|
p_call->set_datatype(call_type);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<MethodInfo> constructors;
|
List<MethodInfo> constructors;
|
||||||
Variant::get_constructor_list(builtin_type, &constructors);
|
Variant::get_constructor_list(builtin_type, &constructors);
|
||||||
bool match = false;
|
bool match = false;
|
||||||
@@ -2997,14 +2999,14 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
|
|||||||
|
|
||||||
for (int i = 0; i < p_call->arguments.size(); i++) {
|
for (int i = 0; i < p_call->arguments.size(); i++) {
|
||||||
GDScriptParser::DataType par_type = type_from_property(info.arguments[i], true);
|
GDScriptParser::DataType par_type = type_from_property(info.arguments[i], true);
|
||||||
|
GDScriptParser::DataType arg_type = p_call->arguments[i]->get_datatype();
|
||||||
if (!is_type_compatible(par_type, p_call->arguments[i]->get_datatype(), true)) {
|
if (!is_type_compatible(par_type, arg_type, true)) {
|
||||||
types_match = false;
|
types_match = false;
|
||||||
break;
|
break;
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
} else {
|
} else {
|
||||||
if (par_type.builtin_type == Variant::INT && p_call->arguments[i]->get_datatype().builtin_type == Variant::FLOAT && builtin_type != Variant::INT) {
|
if (par_type.builtin_type == Variant::INT && arg_type.builtin_type == Variant::FLOAT && builtin_type != Variant::INT) {
|
||||||
parser->push_warning(p_call, GDScriptWarning::NARROWING_CONVERSION, p_call->function_name);
|
parser->push_warning(p_call, GDScriptWarning::NARROWING_CONVERSION, function_name);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -3012,9 +3014,19 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
|
|||||||
|
|
||||||
if (types_match) {
|
if (types_match) {
|
||||||
for (int i = 0; i < p_call->arguments.size(); i++) {
|
for (int i = 0; i < p_call->arguments.size(); i++) {
|
||||||
|
GDScriptParser::DataType par_type = type_from_property(info.arguments[i], true);
|
||||||
if (p_call->arguments[i]->is_constant) {
|
if (p_call->arguments[i]->is_constant) {
|
||||||
update_const_expression_builtin_type(p_call->arguments[i], type_from_property(info.arguments[i], true), "pass");
|
update_const_expression_builtin_type(p_call->arguments[i], par_type, "pass");
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
if (!(par_type.is_variant() && par_type.is_hard_type())) {
|
||||||
|
GDScriptParser::DataType arg_type = p_call->arguments[i]->get_datatype();
|
||||||
|
if (arg_type.is_variant() || !arg_type.is_hard_type() || !is_type_compatible(arg_type, par_type, true)) {
|
||||||
|
mark_node_unsafe(p_call);
|
||||||
|
parser->push_warning(p_call->arguments[i], GDScriptWarning::UNSAFE_CALL_ARGUMENT, itos(i + 1), function_name, par_type.to_string(), arg_type.to_string_strict());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
match = true;
|
match = true;
|
||||||
call_type = type_from_property(info.return_val);
|
call_type = type_from_property(info.return_val);
|
||||||
@@ -3314,8 +3326,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
|
|||||||
#else
|
#else
|
||||||
push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee);
|
push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee);
|
||||||
#endif // SUGGEST_GODOT4_RENAMES
|
#endif // SUGGEST_GODOT4_RENAMES
|
||||||
} else if (!found && (!p_call->is_super && base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::NATIVE && base_type.is_meta_type)) {
|
} else if (!found && (!p_call->is_super && base_type.is_hard_type() && base_type.is_meta_type)) {
|
||||||
push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.native_type), p_call);
|
push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.to_string()), p_call);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3803,6 +3815,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Not a local, so check members.
|
// Not a local, so check members.
|
||||||
|
|
||||||
if (!found_source) {
|
if (!found_source) {
|
||||||
reduce_identifier_from_base(p_identifier);
|
reduce_identifier_from_base(p_identifier);
|
||||||
if (p_identifier->source != GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->get_datatype().is_set()) {
|
if (p_identifier->source != GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->get_datatype().is_set()) {
|
||||||
@@ -3855,10 +3868,10 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
|
|||||||
StringName name = p_identifier->name;
|
StringName name = p_identifier->name;
|
||||||
p_identifier->source = GDScriptParser::IdentifierNode::UNDEFINED_SOURCE;
|
p_identifier->source = GDScriptParser::IdentifierNode::UNDEFINED_SOURCE;
|
||||||
|
|
||||||
// Check globals. We make an exception for Variant::OBJECT because it's the base class for
|
// Not a local or a member, so check globals.
|
||||||
// non-builtin types so we allow doing e.g. Object.new()
|
|
||||||
Variant::Type builtin_type = GDScriptParser::get_builtin_type(name);
|
Variant::Type builtin_type = GDScriptParser::get_builtin_type(name);
|
||||||
if (builtin_type != Variant::OBJECT && builtin_type < Variant::VARIANT_MAX) {
|
if (builtin_type < Variant::VARIANT_MAX) {
|
||||||
if (can_be_builtin) {
|
if (can_be_builtin) {
|
||||||
p_identifier->set_datatype(make_builtin_meta_type(builtin_type));
|
p_identifier->set_datatype(make_builtin_meta_type(builtin_type));
|
||||||
return;
|
return;
|
||||||
@@ -4986,21 +4999,28 @@ void GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p
|
|||||||
GDScriptParser::DataType arg_type = p_call->arguments[i]->get_datatype();
|
GDScriptParser::DataType arg_type = p_call->arguments[i]->get_datatype();
|
||||||
|
|
||||||
if (arg_type.is_variant() || !arg_type.is_hard_type()) {
|
if (arg_type.is_variant() || !arg_type.is_hard_type()) {
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
// Argument can be anything, so this is unsafe (unless the parameter is a hard variant).
|
// Argument can be anything, so this is unsafe (unless the parameter is a hard variant).
|
||||||
if (!(par_type.is_hard_type() && par_type.is_variant())) {
|
if (!(par_type.is_hard_type() && par_type.is_variant())) {
|
||||||
mark_node_unsafe(p_call->arguments[i]);
|
mark_node_unsafe(p_call->arguments[i]);
|
||||||
|
parser->push_warning(p_call->arguments[i], GDScriptWarning::UNSAFE_CALL_ARGUMENT, itos(i + 1), p_call->function_name, par_type.to_string(), arg_type.to_string_strict());
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
} else if (par_type.is_hard_type() && !is_type_compatible(par_type, arg_type, true)) {
|
} else if (par_type.is_hard_type() && !is_type_compatible(par_type, arg_type, true)) {
|
||||||
// Supertypes are acceptable for dynamic compliance, but it's unsafe.
|
|
||||||
mark_node_unsafe(p_call);
|
|
||||||
if (!is_type_compatible(arg_type, par_type)) {
|
if (!is_type_compatible(arg_type, par_type)) {
|
||||||
push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be "%s" but is "%s".)*",
|
push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be "%s" but is "%s".)*",
|
||||||
p_call->function_name, i + 1, par_type.to_string(), arg_type.to_string()),
|
p_call->function_name, i + 1, par_type.to_string(), arg_type.to_string()),
|
||||||
p_call->arguments[i]);
|
p_call->arguments[i]);
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
} else {
|
||||||
|
// Supertypes are acceptable for dynamic compliance, but it's unsafe.
|
||||||
|
mark_node_unsafe(p_call);
|
||||||
|
parser->push_warning(p_call->arguments[i], GDScriptWarning::UNSAFE_CALL_ARGUMENT, itos(i + 1), p_call->function_name, par_type.to_string(), arg_type.to_string_strict());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
} else if (par_type.kind == GDScriptParser::DataType::BUILTIN && par_type.builtin_type == Variant::INT && arg_type.kind == GDScriptParser::DataType::BUILTIN && arg_type.builtin_type == Variant::FLOAT) {
|
} else if (par_type.kind == GDScriptParser::DataType::BUILTIN && par_type.builtin_type == Variant::INT && arg_type.kind == GDScriptParser::DataType::BUILTIN && arg_type.builtin_type == Variant::FLOAT) {
|
||||||
parser->push_warning(p_call, GDScriptWarning::NARROWING_CONVERSION, p_call->function_name);
|
parser->push_warning(p_call->arguments[i], GDScriptWarning::NARROWING_CONVERSION, p_call->function_name);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5028,7 +5048,7 @@ void GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_identifier
|
|||||||
} else if (ClassDB::class_exists(name)) {
|
} else if (ClassDB::class_exists(name)) {
|
||||||
parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "global class");
|
parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "global class");
|
||||||
return;
|
return;
|
||||||
} else if (GDScriptParser::get_builtin_type(name) != Variant::VARIANT_MAX) {
|
} else if (GDScriptParser::get_builtin_type(name) < Variant::VARIANT_MAX) {
|
||||||
parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "built-in type");
|
parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "built-in type");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -601,11 +601,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
|
|||||||
arguments.push_back(arg);
|
arguments.push_back(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(call->function_name) != Variant::VARIANT_MAX) {
|
if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) {
|
||||||
// Construct a built-in type.
|
gen->write_construct(result, GDScriptParser::get_builtin_type(call->function_name), arguments);
|
||||||
Variant::Type vtype = GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
|
|
||||||
|
|
||||||
gen->write_construct(result, vtype, arguments);
|
|
||||||
} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && Variant::has_utility_function(call->function_name)) {
|
} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && Variant::has_utility_function(call->function_name)) {
|
||||||
// Variant utility function.
|
// Variant utility function.
|
||||||
gen->write_call_utility(result, call->function_name, arguments);
|
gen->write_call_utility(result, call->function_name, arguments);
|
||||||
|
|||||||
@@ -52,11 +52,18 @@
|
|||||||
#include "editor/editor_settings.h"
|
#include "editor/editor_settings.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// This function is used to determine that a type is "built-in" as opposed to native
|
||||||
|
// and custom classes. So `Variant::NIL` and `Variant::OBJECT` are excluded:
|
||||||
|
// `Variant::NIL` - `null` is literal, not a type.
|
||||||
|
// `Variant::OBJECT` - `Object` should be treated as a class, not as a built-in type.
|
||||||
static HashMap<StringName, Variant::Type> builtin_types;
|
static HashMap<StringName, Variant::Type> builtin_types;
|
||||||
Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) {
|
Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) {
|
||||||
if (builtin_types.is_empty()) {
|
if (unlikely(builtin_types.is_empty())) {
|
||||||
for (int i = 1; i < Variant::VARIANT_MAX; i++) {
|
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
|
||||||
builtin_types[Variant::get_type_name((Variant::Type)i)] = (Variant::Type)i;
|
Variant::Type type = (Variant::Type)i;
|
||||||
|
if (type != Variant::NIL && type != Variant::OBJECT) {
|
||||||
|
builtin_types[Variant::get_type_name(type)] = type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ public:
|
|||||||
_FORCE_INLINE_ bool is_hard_type() const { return type_source > INFERRED; }
|
_FORCE_INLINE_ bool is_hard_type() const { return type_source > INFERRED; }
|
||||||
|
|
||||||
String to_string() const;
|
String to_string() const;
|
||||||
|
_FORCE_INLINE_ String to_string_strict() const { return is_hard_type() ? to_string() : "Variant"; }
|
||||||
PropertyInfo to_property_info(const String &p_name) const;
|
PropertyInfo to_property_info(const String &p_name) const;
|
||||||
|
|
||||||
_FORCE_INLINE_ void set_container_element_type(const DataType &p_type) {
|
_FORCE_INLINE_ void set_container_element_type(const DataType &p_type) {
|
||||||
@@ -1530,7 +1531,7 @@ public:
|
|||||||
bool is_tool() const { return _is_tool; }
|
bool is_tool() const { return _is_tool; }
|
||||||
ClassNode *find_class(const String &p_qualified_name) const;
|
ClassNode *find_class(const String &p_qualified_name) const;
|
||||||
bool has_class(const GDScriptParser::ClassNode *p_class) const;
|
bool has_class(const GDScriptParser::ClassNode *p_class) const;
|
||||||
static Variant::Type get_builtin_type(const StringName &p_type);
|
static Variant::Type get_builtin_type(const StringName &p_type); // Excluding `Variant::NIL` and `Variant::OBJECT`.
|
||||||
|
|
||||||
CompletionContext get_completion_context() const { return completion_context; }
|
CompletionContext get_completion_context() const { return completion_context; }
|
||||||
CompletionCall get_completion_call() const { return completion_call; }
|
CompletionCall get_completion_call() const { return completion_call; }
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ String GDScriptWarning::get_message() const {
|
|||||||
return vformat(R"(The value is cast to "%s" but has an unknown type.)", symbols[0]);
|
return vformat(R"(The value is cast to "%s" but has an unknown type.)", symbols[0]);
|
||||||
case UNSAFE_CALL_ARGUMENT:
|
case UNSAFE_CALL_ARGUMENT:
|
||||||
CHECK_SYMBOLS(4);
|
CHECK_SYMBOLS(4);
|
||||||
return vformat(R"*(The argument %s of the function "%s()" requires a the subtype "%s" but the supertype "%s" was provided.)*", symbols[0], symbols[1], symbols[2], symbols[3]);
|
return vformat(R"*(The argument %s of the function "%s()" requires the subtype "%s" but the supertype "%s" was provided.)*", symbols[0], symbols[1], symbols[2], symbols[3]);
|
||||||
case UNSAFE_VOID_RETURN:
|
case UNSAFE_VOID_RETURN:
|
||||||
CHECK_SYMBOLS(2);
|
CHECK_SYMBOLS(2);
|
||||||
return vformat(R"*(The method "%s()" returns "void" but it's trying to return a call to "%s()" that can't be ensured to also be "void".)*", symbols[0], symbols[1]);
|
return vformat(R"*(The method "%s()" returns "void" but it's trying to return a call to "%s()" that can't be ensured to also be "void".)*", symbols[0], symbols[1]);
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# GH-73283
|
||||||
|
|
||||||
|
class MyClass:
|
||||||
|
pass
|
||||||
|
|
||||||
|
func test():
|
||||||
|
MyClass.not_existing_method()
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
GDTEST_ANALYZER_ERROR
|
||||||
|
Static function "not_existing_method()" not found in base "MyClass".
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# GH-73213
|
||||||
|
|
||||||
|
func test():
|
||||||
|
print(Object())
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
GDTEST_ANALYZER_ERROR
|
||||||
|
Invalid constructor "Object()", use "Object.new()" instead.
|
||||||
@@ -23,6 +23,7 @@ func test() -> void:
|
|||||||
typed = variant()
|
typed = variant()
|
||||||
inferred = variant()
|
inferred = variant()
|
||||||
|
|
||||||
|
@warning_ignore("unsafe_call_argument") # TODO: Hard vs Weak vs Unknown.
|
||||||
param_weak(typed)
|
param_weak(typed)
|
||||||
param_typed(typed)
|
param_typed(typed)
|
||||||
param_inferred(typed)
|
param_inferred(typed)
|
||||||
|
|||||||
@@ -6,10 +6,12 @@ var prop = null
|
|||||||
|
|
||||||
func check_arg(arg = null) -> void:
|
func check_arg(arg = null) -> void:
|
||||||
if arg != null:
|
if arg != null:
|
||||||
|
@warning_ignore("unsafe_call_argument")
|
||||||
print(check(arg))
|
print(check(arg))
|
||||||
|
|
||||||
func check_recur() -> void:
|
func check_recur() -> void:
|
||||||
if recur != null:
|
if recur != null:
|
||||||
|
@warning_ignore("unsafe_call_argument")
|
||||||
print(check(recur))
|
print(check(recur))
|
||||||
else:
|
else:
|
||||||
recur = 1
|
recur = 1
|
||||||
@@ -22,11 +24,13 @@ func test() -> void:
|
|||||||
|
|
||||||
if prop == null:
|
if prop == null:
|
||||||
set('prop', 1)
|
set('prop', 1)
|
||||||
|
@warning_ignore("unsafe_call_argument")
|
||||||
print(check(prop))
|
print(check(prop))
|
||||||
set('prop', null)
|
set('prop', null)
|
||||||
|
|
||||||
var loop = null
|
var loop = null
|
||||||
while loop != 2:
|
while loop != 2:
|
||||||
if loop != null:
|
if loop != null:
|
||||||
|
@warning_ignore("unsafe_call_argument")
|
||||||
print(check(loop))
|
print(check(loop))
|
||||||
loop = 1 if loop == null else 2
|
loop = 1 if loop == null else 2
|
||||||
|
|||||||
@@ -14,4 +14,5 @@ func test():
|
|||||||
func do_add_node():
|
func do_add_node():
|
||||||
var node = Node.new()
|
var node = Node.new()
|
||||||
node.name = "Node"
|
node.name = "Node"
|
||||||
|
@warning_ignore("unsafe_call_argument")
|
||||||
add_child(node)
|
add_child(node)
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
func variant_func(x: Variant) -> void:
|
||||||
|
print(x)
|
||||||
|
|
||||||
|
func int_func(x: int) -> void:
|
||||||
|
print(x)
|
||||||
|
|
||||||
|
func float_func(x: float) -> void:
|
||||||
|
print(x)
|
||||||
|
|
||||||
|
# We don't want to execute it because of errors, just analyze.
|
||||||
|
func no_exec_test():
|
||||||
|
var untyped_int = 42
|
||||||
|
var untyped_string = "abc"
|
||||||
|
var variant_int: Variant = 42
|
||||||
|
var variant_string: Variant = "abc"
|
||||||
|
var typed_int: int = 42
|
||||||
|
|
||||||
|
variant_func(untyped_int) # No warning.
|
||||||
|
variant_func(untyped_string) # No warning.
|
||||||
|
variant_func(variant_int) # No warning.
|
||||||
|
variant_func(variant_string) # No warning.
|
||||||
|
variant_func(typed_int) # No warning.
|
||||||
|
|
||||||
|
int_func(untyped_int)
|
||||||
|
int_func(untyped_string)
|
||||||
|
int_func(variant_int)
|
||||||
|
int_func(variant_string)
|
||||||
|
int_func(typed_int) # No warning.
|
||||||
|
|
||||||
|
float_func(untyped_int)
|
||||||
|
float_func(untyped_string)
|
||||||
|
float_func(variant_int)
|
||||||
|
float_func(variant_string)
|
||||||
|
float_func(typed_int) # No warning.
|
||||||
|
|
||||||
|
func test():
|
||||||
|
pass
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
GDTEST_OK
|
||||||
|
>> WARNING
|
||||||
|
>> Line: 24
|
||||||
|
>> UNSAFE_CALL_ARGUMENT
|
||||||
|
>> The argument 1 of the function "int_func()" requires the subtype "int" but the supertype "Variant" was provided.
|
||||||
|
>> WARNING
|
||||||
|
>> Line: 25
|
||||||
|
>> UNSAFE_CALL_ARGUMENT
|
||||||
|
>> The argument 1 of the function "int_func()" requires the subtype "int" but the supertype "Variant" was provided.
|
||||||
|
>> WARNING
|
||||||
|
>> Line: 26
|
||||||
|
>> UNSAFE_CALL_ARGUMENT
|
||||||
|
>> The argument 1 of the function "int_func()" requires the subtype "int" but the supertype "Variant" was provided.
|
||||||
|
>> WARNING
|
||||||
|
>> Line: 27
|
||||||
|
>> UNSAFE_CALL_ARGUMENT
|
||||||
|
>> The argument 1 of the function "int_func()" requires the subtype "int" but the supertype "Variant" was provided.
|
||||||
|
>> WARNING
|
||||||
|
>> Line: 30
|
||||||
|
>> UNSAFE_CALL_ARGUMENT
|
||||||
|
>> The argument 1 of the function "float_func()" requires the subtype "float" but the supertype "Variant" was provided.
|
||||||
|
>> WARNING
|
||||||
|
>> Line: 31
|
||||||
|
>> UNSAFE_CALL_ARGUMENT
|
||||||
|
>> The argument 1 of the function "float_func()" requires the subtype "float" but the supertype "Variant" was provided.
|
||||||
|
>> WARNING
|
||||||
|
>> Line: 32
|
||||||
|
>> UNSAFE_CALL_ARGUMENT
|
||||||
|
>> The argument 1 of the function "float_func()" requires the subtype "float" but the supertype "Variant" was provided.
|
||||||
|
>> WARNING
|
||||||
|
>> Line: 33
|
||||||
|
>> UNSAFE_CALL_ARGUMENT
|
||||||
|
>> The argument 1 of the function "float_func()" requires the subtype "float" but the supertype "Variant" was provided.
|
||||||
@@ -3,27 +3,32 @@ extends Node
|
|||||||
func test():
|
func test():
|
||||||
var child = Node.new()
|
var child = Node.new()
|
||||||
child.name = "Child"
|
child.name = "Child"
|
||||||
|
@warning_ignore("unsafe_call_argument")
|
||||||
add_child(child)
|
add_child(child)
|
||||||
child.owner = self
|
child.owner = self
|
||||||
|
|
||||||
var hey = Node.new()
|
var hey = Node.new()
|
||||||
hey.name = "Hey"
|
hey.name = "Hey"
|
||||||
|
@warning_ignore("unsafe_call_argument")
|
||||||
child.add_child(hey)
|
child.add_child(hey)
|
||||||
hey.owner = self
|
hey.owner = self
|
||||||
hey.unique_name_in_owner = true
|
hey.unique_name_in_owner = true
|
||||||
|
|
||||||
var fake_hey = Node.new()
|
var fake_hey = Node.new()
|
||||||
fake_hey.name = "Hey"
|
fake_hey.name = "Hey"
|
||||||
|
@warning_ignore("unsafe_call_argument")
|
||||||
add_child(fake_hey)
|
add_child(fake_hey)
|
||||||
fake_hey.owner = self
|
fake_hey.owner = self
|
||||||
|
|
||||||
var sub_child = Node.new()
|
var sub_child = Node.new()
|
||||||
sub_child.name = "SubChild"
|
sub_child.name = "SubChild"
|
||||||
|
@warning_ignore("unsafe_call_argument")
|
||||||
hey.add_child(sub_child)
|
hey.add_child(sub_child)
|
||||||
sub_child.owner = self
|
sub_child.owner = self
|
||||||
|
|
||||||
var howdy = Node.new()
|
var howdy = Node.new()
|
||||||
howdy.name = "Howdy"
|
howdy.name = "Howdy"
|
||||||
|
@warning_ignore("unsafe_call_argument")
|
||||||
sub_child.add_child(howdy)
|
sub_child.add_child(howdy)
|
||||||
howdy.owner = self
|
howdy.owner = self
|
||||||
howdy.unique_name_in_owner = true
|
howdy.unique_name_in_owner = true
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ func test():
|
|||||||
# Create the required node structure.
|
# Create the required node structure.
|
||||||
var hello = Node.new()
|
var hello = Node.new()
|
||||||
hello.name = "Hello"
|
hello.name = "Hello"
|
||||||
|
@warning_ignore("unsafe_call_argument")
|
||||||
add_child(hello)
|
add_child(hello)
|
||||||
var world = Node.new()
|
var world = Node.new()
|
||||||
world.name = "World"
|
world.name = "World"
|
||||||
|
@warning_ignore("unsafe_call_argument")
|
||||||
hello.add_child(world)
|
hello.add_child(world)
|
||||||
|
|
||||||
# All the ways of writing node paths below with the `$` operator are valid.
|
# All the ways of writing node paths below with the `$` operator are valid.
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ func test():
|
|||||||
if true: (v as Callable).call()
|
if true: (v as Callable).call()
|
||||||
print()
|
print()
|
||||||
|
|
||||||
|
@warning_ignore("unsafe_call_argument")
|
||||||
other(v)
|
other(v)
|
||||||
print()
|
print()
|
||||||
|
|
||||||
|
|||||||
@@ -2,4 +2,5 @@ func foo(x):
|
|||||||
return x + 1
|
return x + 1
|
||||||
|
|
||||||
func test():
|
func test():
|
||||||
|
@warning_ignore("unsafe_call_argument")
|
||||||
print(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(0)))))))))))))))))))))))))
|
print(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(0)))))))))))))))))))))))))
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ class SayNothing extends Say:
|
|||||||
print("howdy, see above")
|
print("howdy, see above")
|
||||||
|
|
||||||
func say(name):
|
func say(name):
|
||||||
|
@warning_ignore("unsafe_call_argument")
|
||||||
super(name + " super'd")
|
super(name + " super'd")
|
||||||
print(prefix, " say nothing... or not? ", name)
|
print(prefix, " say nothing... or not? ", name)
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ func test():
|
|||||||
|
|
||||||
const d = 1.1
|
const d = 1.1
|
||||||
_process(d)
|
_process(d)
|
||||||
|
@warning_ignore("unsafe_call_argument")
|
||||||
print(is_equal_approx(ㄥ, PI + (d * PI)))
|
print(is_equal_approx(ㄥ, PI + (d * PI)))
|
||||||
|
|
||||||
func _process(Δ: float) -> void:
|
func _process(Δ: float) -> void:
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
# GH-73213
|
||||||
|
|
||||||
|
func test():
|
||||||
|
var object := Object.new() # Not `Object()`.
|
||||||
|
print(object.get_class())
|
||||||
|
object.free()
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
GDTEST_OK
|
||||||
|
Object
|
||||||
@@ -12,6 +12,7 @@ func test():
|
|||||||
print("end")
|
print("end")
|
||||||
|
|
||||||
func test_construct(v, f):
|
func test_construct(v, f):
|
||||||
|
@warning_ignore("unsafe_call_argument")
|
||||||
Vector2(v, v) # Built-in type construct.
|
Vector2(v, v) # Built-in type construct.
|
||||||
assert(not f) # Test unary operator reading from `nil`.
|
assert(not f) # Test unary operator reading from `nil`.
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ func test():
|
|||||||
|
|
||||||
@warning_ignore("unsafe_method_access")
|
@warning_ignore("unsafe_method_access")
|
||||||
var path = get_script().get_path().get_base_dir()
|
var path = get_script().get_path().get_base_dir()
|
||||||
|
@warning_ignore("unsafe_call_argument")
|
||||||
var other = load(path + "/static_variables_load.gd")
|
var other = load(path + "/static_variables_load.gd")
|
||||||
|
|
||||||
prints("load.perm:", other.perm)
|
prints("load.perm:", other.perm)
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ func test():
|
|||||||
print(StringName("hello"))
|
print(StringName("hello"))
|
||||||
print(NodePath("hello/world"))
|
print(NodePath("hello/world"))
|
||||||
var node := Node.new()
|
var node := Node.new()
|
||||||
print(RID(node))
|
@warning_ignore("unsafe_call_argument")
|
||||||
|
print(RID(node)) # TODO: Why is the constructor (or implicit cast) not documented?
|
||||||
print(node.get_name)
|
print(node.get_name)
|
||||||
print(node.property_list_changed)
|
print(node.property_list_changed)
|
||||||
node.free()
|
node.free()
|
||||||
|
|||||||
Reference in New Issue
Block a user