Add static type checks in the parser

- Resolve types for all identifiers.
- Error when identifier is not found.
- Match return type and error when not returning a value when it should.
- Check unreachable code (code after sure return).
- Match argument count and types for function calls.
- Determine if return type of function call matches the assignment.
- Do static type check with match statement when possible.
- Use type hints to determine export type.
- Check compatibility between type hint and explicit export type.
This commit is contained in:
George Marques 2018-05-29 23:16:54 -03:00
parent f7793fc5c9
commit 743053734f
No known key found for this signature in database
GPG key ID: 046BD46A3201E43D
9 changed files with 2984 additions and 286 deletions

View file

@ -193,14 +193,6 @@ static String _parser_expr(const GDScriptParser::Node *p_expr) {
case GDScriptParser::OperatorNode::OP_BIT_INVERT: { case GDScriptParser::OperatorNode::OP_BIT_INVERT: {
txt = "~" + _parser_expr(c_node->arguments[0]); txt = "~" + _parser_expr(c_node->arguments[0]);
} break; } break;
case GDScriptParser::OperatorNode::OP_PREINC: {
} break;
case GDScriptParser::OperatorNode::OP_PREDEC: {
} break;
case GDScriptParser::OperatorNode::OP_INC: {
} break;
case GDScriptParser::OperatorNode::OP_DEC: {
} break;
case GDScriptParser::OperatorNode::OP_IN: { case GDScriptParser::OperatorNode::OP_IN: {
txt = _parser_expr(c_node->arguments[0]) + " in " + _parser_expr(c_node->arguments[1]); txt = _parser_expr(c_node->arguments[0]) + " in " + _parser_expr(c_node->arguments[1]);
} break; } break;
@ -455,10 +447,9 @@ static void _parser_show_class(const GDScriptParser::ClassNode *p_class, int p_i
print_line("\n"); print_line("\n");
} }
for (int i = 0; i < p_class->constant_expressions.size(); i++) { for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = p_class->constant_expressions.front(); E; E = E->next()) {
const GDScriptParser::ClassNode::Constant &constant = E->get();
const GDScriptParser::ClassNode::Constant &constant = p_class->constant_expressions[i]; _print_indent(p_indent, "const " + String(E->key()) + "=" + _parser_expr(constant.expression));
_print_indent(p_indent, "const " + String(constant.identifier) + "=" + _parser_expr(constant.expression));
} }
for (int i = 0; i < p_class->variables.size(); i++) { for (int i = 0; i < p_class->variables.size(); i++) {

View file

@ -64,6 +64,7 @@ class GDScript : public Script {
StringName setter; StringName setter;
StringName getter; StringName getter;
MultiplayerAPI::RPCMode rpc_mode; MultiplayerAPI::RPCMode rpc_mode;
GDScriptDataType data_type;
}; };
friend class GDScriptInstance; friend class GDScriptInstance;
@ -145,6 +146,10 @@ public:
const Map<StringName, Ref<GDScript> > &get_subclasses() const { return subclasses; } const Map<StringName, Ref<GDScript> > &get_subclasses() const { return subclasses; }
const Map<StringName, Variant> &get_constants() const { return constants; } const Map<StringName, Variant> &get_constants() const { return constants; }
const Set<StringName> &get_members() const { return members; } 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());
return member_indices[p_member].data_type;
}
const Map<StringName, GDScriptFunction *> &get_member_functions() const { return member_functions; } const Map<StringName, GDScriptFunction *> &get_member_functions() const { return member_functions; }
const Ref<GDScriptNativeClass> &get_native() const { return native; } const Ref<GDScriptNativeClass> &get_native() const { return native; }

View file

@ -777,14 +777,6 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
case GDScriptParser::OperatorNode::OP_BIT_INVERT: { case GDScriptParser::OperatorNode::OP_BIT_INVERT: {
if (!_create_unary_operator(codegen, on, Variant::OP_BIT_NEGATE, p_stack_level)) return -1; if (!_create_unary_operator(codegen, on, Variant::OP_BIT_NEGATE, p_stack_level)) return -1;
} break; } break;
case GDScriptParser::OperatorNode::OP_PREINC: {
} break; //?
case GDScriptParser::OperatorNode::OP_PREDEC: {
} break;
case GDScriptParser::OperatorNode::OP_INC: {
} break;
case GDScriptParser::OperatorNode::OP_DEC: {
} break;
//binary operators (in precedence order) //binary operators (in precedence order)
case GDScriptParser::OperatorNode::OP_IN: { case GDScriptParser::OperatorNode::OP_IN: {
if (!_create_binary_operator(codegen, on, Variant::OP_IN, p_stack_level)) return -1; if (!_create_binary_operator(codegen, on, Variant::OP_IN, p_stack_level)) return -1;
@ -1520,6 +1512,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
if (p_func) { if (p_func) {
gdfunc->_static = p_func->_static; gdfunc->_static = p_func->_static;
gdfunc->rpc_mode = p_func->rpc_mode; gdfunc->rpc_mode = p_func->rpc_mode;
gdfunc->argument_types.resize(p_func->argument_types.size());
} }
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
@ -1770,24 +1763,24 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner
#endif #endif
} }
for (int i = 0; i < p_class->constant_expressions.size(); i++) { for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = p_class->constant_expressions.front(); E; E = E->next()) {
StringName name = p_class->constant_expressions[i].identifier; StringName name = E->key();
ERR_CONTINUE(p_class->constant_expressions[i].expression->type != GDScriptParser::Node::TYPE_CONSTANT); ERR_CONTINUE(E->get().expression->type != GDScriptParser::Node::TYPE_CONSTANT);
if (_is_class_member_property(p_script, name)) { if (_is_class_member_property(p_script, name)) {
_set_error("Member '" + name + "' already exists as a class property.", p_class); _set_error("Member '" + name + "' already exists as a class property.", p_class);
return ERR_ALREADY_EXISTS; return ERR_ALREADY_EXISTS;
} }
GDScriptParser::ConstantNode *constant = static_cast<GDScriptParser::ConstantNode *>(p_class->constant_expressions[i].expression); GDScriptParser::ConstantNode *constant = static_cast<GDScriptParser::ConstantNode *>(E->get().expression);
p_script->constants.insert(name, constant->value); p_script->constants.insert(name, constant->value);
//p_script->constants[constant->value].make_const(); //p_script->constants[constant->value].make_const();
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
p_script->member_lines[name] = p_class->constant_expressions[i].expression->line; p_script->member_lines[name] = E->get().expression->line;
#endif #endif
} }

View file

@ -1236,15 +1236,11 @@ static bool _guess_identifier_type(GDScriptCompletionContext &context, int p_lin
} }
//guess type in constant //guess type in constant
if (context._class->constant_expressions.has(p_identifier)) {
for (int i = 0; i < context._class->constant_expressions.size(); i++) { ERR_FAIL_COND_V(context._class->constant_expressions[p_identifier].expression->type != GDScriptParser::Node::TYPE_CONSTANT, false);
r_type = _get_type_from_variant(static_cast<const GDScriptParser::ConstantNode *>(context._class->constant_expressions[p_identifier].expression)->value);
if (context._class->constant_expressions[i].identifier == p_identifier) { return true;
ERR_FAIL_COND_V(context._class->constant_expressions[i].expression->type != GDScriptParser::Node::TYPE_CONSTANT, false);
r_type = _get_type_from_variant(static_cast<const GDScriptParser::ConstantNode *>(context._class->constant_expressions[i].expression)->value);
return true;
}
} }
if (!(context.function && context.function->_static)) { if (!(context.function && context.function->_static)) {
@ -1372,8 +1368,8 @@ static void _find_identifiers_in_class(GDScriptCompletionContext &context, bool
} }
if (!p_only_functions) { if (!p_only_functions) {
for (int i = 0; i < context._class->constant_expressions.size(); i++) { for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = context._class->constant_expressions.front(); E; E = E->next()) {
result.insert(context._class->constant_expressions[i].identifier); result.insert(E->key());
} }
for (int i = 0; i < context._class->subclasses.size(); i++) { for (int i = 0; i < context._class->subclasses.size(); i++) {
@ -2320,9 +2316,8 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base
options.insert(String(cl->variables[i].identifier)); options.insert(String(cl->variables[i].identifier));
} }
for (int i = 0; i < cl->constant_expressions.size(); i++) { for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = cl->constant_expressions.front(); E; E = E->next()) {
options.insert(String(E->key()));
options.insert(String(cl->constant_expressions[i].identifier));
} }
} }
@ -2816,13 +2811,10 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
//guess in class constants //guess in class constants
for (int i = 0; i < context._class->constant_expressions.size(); i++) { if (context._class->constant_expressions.has(p_symbol)) {
r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
if (context._class->constant_expressions[i].identifier == p_symbol) { r_result.location = context._class->constant_expressions[p_symbol].expression->line;
r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION; return OK;
r_result.location = context._class->constant_expressions[i].expression->line;
return OK;
}
} }
//guess in class variables //guess in class variables

View file

@ -1370,6 +1370,15 @@ int GDScriptFunction::get_default_argument_addr(int p_idx) const {
return default_arguments[p_idx]; return default_arguments[p_idx];
} }
GDScriptDataType GDScriptFunction::get_return_type() const {
return return_type;
}
GDScriptDataType GDScriptFunction::get_argument_type(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, argument_types.size(), GDScriptDataType());
return argument_types[p_idx];
}
StringName GDScriptFunction::get_name() const { StringName GDScriptFunction::get_name() const {
return name; return name;

View file

@ -42,6 +42,22 @@
class GDScriptInstance; class GDScriptInstance;
class GDScript; class GDScript;
struct GDScriptDataType {
bool has_type;
enum {
BUILTIN,
NATIVE,
SCRIPT,
GDSCRIPT
} kind;
Variant::Type builtin_type;
StringName native_type;
Ref<Script> script_type;
GDScriptDataType() :
has_type(false) {}
};
class GDScriptFunction { class GDScriptFunction {
public: public:
enum Opcode { enum Opcode {
@ -139,6 +155,8 @@ private:
#endif #endif
Vector<int> default_arguments; Vector<int> default_arguments;
Vector<int> code; Vector<int> code;
Vector<GDScriptDataType> argument_types;
GDScriptDataType return_type;
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
Vector<StringName> arg_names; Vector<StringName> arg_names;
@ -199,6 +217,8 @@ public:
int get_max_stack_size() const; int get_max_stack_size() const;
int get_default_argument_count() const; int get_default_argument_count() const;
int get_default_argument_addr(int p_idx) const; int get_default_argument_addr(int p_idx) const;
GDScriptDataType get_return_type() const;
GDScriptDataType get_argument_type(int p_idx) const;
GDScript *get_script() const { return _script; } GDScript *get_script() const { return _script; }
StringName get_source() const { return source; } StringName get_source() const { return source; }

View file

@ -1663,7 +1663,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
MethodInfo mi("weakref", PropertyInfo(Variant::OBJECT, "obj")); MethodInfo mi("weakref", PropertyInfo(Variant::OBJECT, "obj"));
mi.return_val.type = Variant::OBJECT; mi.return_val.type = Variant::OBJECT;
mi.return_val.name = "WeakRef"; mi.return_val.class_name = "WeakRef";
return mi; return mi;
@ -1672,19 +1672,20 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
MethodInfo mi("funcref", PropertyInfo(Variant::OBJECT, "instance"), PropertyInfo(Variant::STRING, "funcname")); MethodInfo mi("funcref", PropertyInfo(Variant::OBJECT, "instance"), PropertyInfo(Variant::STRING, "funcname"));
mi.return_val.type = Variant::OBJECT; mi.return_val.type = Variant::OBJECT;
mi.return_val.name = "FuncRef"; mi.return_val.class_name = "FuncRef";
return mi; return mi;
} break; } break;
case TYPE_CONVERT: { case TYPE_CONVERT: {
MethodInfo mi("convert", PropertyInfo(Variant::NIL, "what"), PropertyInfo(Variant::INT, "type")); MethodInfo mi("convert", PropertyInfo(Variant::NIL, "what", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::INT, "type"));
mi.return_val.type = Variant::OBJECT; mi.return_val.type = Variant::NIL;
mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
return mi; return mi;
} break; } break;
case TYPE_OF: { case TYPE_OF: {
MethodInfo mi("typeof", PropertyInfo(Variant::NIL, "what")); MethodInfo mi("typeof", PropertyInfo(Variant::NIL, "what", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::INT; mi.return_val.type = Variant::INT;
return mi; return mi;
@ -1760,7 +1761,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
} break; } break;
case VAR_TO_STR: { case VAR_TO_STR: {
MethodInfo mi("var2str", PropertyInfo(Variant::NIL, "var")); MethodInfo mi("var2str", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::STRING; mi.return_val.type = Variant::STRING;
return mi; return mi;
@ -1773,7 +1774,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
return mi; return mi;
} break; } break;
case VAR_TO_BYTES: { case VAR_TO_BYTES: {
MethodInfo mi("var2bytes", PropertyInfo(Variant::NIL, "var")); 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; mi.return_val.type = Variant::POOL_BYTE_ARRAY;
return mi; return mi;
@ -1796,7 +1797,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
MethodInfo mi("load", PropertyInfo(Variant::STRING, "path")); MethodInfo mi("load", PropertyInfo(Variant::STRING, "path"));
mi.return_val.type = Variant::OBJECT; mi.return_val.type = Variant::OBJECT;
mi.return_val.name = "Resource"; mi.return_val.class_name = "Resource";
return mi; return mi;
} break; } break;
case INST2DICT: { case INST2DICT: {
@ -1826,13 +1827,13 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
} break; } break;
case TO_JSON: { case TO_JSON: {
MethodInfo mi("to_json", PropertyInfo(Variant::NIL, "var")); MethodInfo mi("to_json", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::STRING; mi.return_val.type = Variant::STRING;
return mi; return mi;
} break; } break;
case HASH: { case HASH: {
MethodInfo mi("hash", PropertyInfo(Variant::NIL, "var")); MethodInfo mi("hash", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::INT; mi.return_val.type = Variant::INT;
return mi; return mi;
} break; } break;
@ -1868,7 +1869,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
return mi; return mi;
} break; } break;
case LEN: { case LEN: {
MethodInfo mi("len", PropertyInfo(Variant::NIL, "var")); MethodInfo mi("len", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::INT; mi.return_val.type = Variant::INT;
return mi; return mi;
} break; } break;

File diff suppressed because it is too large Load diff

View file

@ -37,6 +37,8 @@
#include "object.h" #include "object.h"
#include "script_language.h" #include "script_language.h"
struct GDScriptDataType;
class GDScriptParser { class GDScriptParser {
public: public:
struct ClassNode; struct ClassNode;
@ -47,29 +49,93 @@ public:
NATIVE, NATIVE,
SCRIPT, SCRIPT,
GDSCRIPT, GDSCRIPT,
CLASS CLASS,
UNRESOLVED
} kind; } kind;
bool has_type; bool has_type;
bool is_constant;
bool is_meta_type; // Whether the value can be used as a type
Variant::Type builtin_type; Variant::Type builtin_type;
StringName native_type; StringName native_type;
Ref<Script> script_type; Ref<Script> script_type;
ClassNode *class_type; ClassNode *class_type;
DataType *meta_type; String to_string() const {
if (!has_type) return "var";
switch (kind) {
case BUILTIN: {
if (builtin_type == Variant::NIL) return "null";
return Variant::get_type_name(builtin_type);
} break;
case NATIVE: {
if (is_meta_type) {
return "GDScriptNativeClass";
}
return native_type.operator String();
} break;
case SCRIPT:
case GDSCRIPT: {
if (is_meta_type) {
return script_type->get_class_name().operator String();
}
String name = script_type->get_name();
if (name != String()) {
return name;
}
name = script_type->get_path().get_file();
if (name != String()) {
return name;
}
return native_type.operator String();
} break;
case CLASS: {
ERR_FAIL_COND_V(!class_type, String());
if (is_meta_type) {
return "GDScript";
}
if (class_type->name == StringName()) {
return "self";
}
return class_type->name.operator String();
} break;
}
return "Unresolved";
}
bool operator==(const DataType &other) const {
if (!has_type || !other.has_type) {
return true; // Can be considered equal for parsing purpose
}
if (kind != other.kind) {
return false;
}
switch (kind) {
case BUILTIN: {
return builtin_type == other.builtin_type;
} break;
case NATIVE: {
return native_type == other.native_type;
} break;
case GDSCRIPT:
case SCRIPT: {
return script_type == other.script_type;
} break;
case CLASS: {
return class_type == other.class_type;
} break;
}
return false;
}
DataType() : DataType() :
has_type(false), has_type(false),
meta_type(NULL), is_constant(false),
is_meta_type(false),
builtin_type(Variant::NIL), builtin_type(Variant::NIL),
class_type(NULL) {} class_type(NULL) {}
~DataType() {
if (meta_type) {
memdelete(meta_type);
}
}
}; };
struct Node { struct Node {
@ -108,6 +174,8 @@ public:
struct FunctionNode; struct FunctionNode;
struct BlockNode; struct BlockNode;
struct ConstantNode; struct ConstantNode;
struct LocalVarNode;
struct OperatorNode;
struct ClassNode : public Node { struct ClassNode : public Node {
@ -129,10 +197,10 @@ public:
StringName getter; StringName getter;
int line; int line;
Node *expression; Node *expression;
OperatorNode *initial_assignment;
MultiplayerAPI::RPCMode rpc_mode; MultiplayerAPI::RPCMode rpc_mode;
}; };
struct Constant { struct Constant {
StringName identifier;
Node *expression; Node *expression;
DataType type; DataType type;
}; };
@ -144,7 +212,7 @@ public:
Vector<ClassNode *> subclasses; Vector<ClassNode *> subclasses;
Vector<Member> variables; Vector<Member> variables;
Vector<Constant> constant_expressions; Map<StringName, Constant> constant_expressions;
Vector<FunctionNode *> functions; Vector<FunctionNode *> functions;
Vector<FunctionNode *> static_functions; Vector<FunctionNode *> static_functions;
Vector<Signal> _signals; Vector<Signal> _signals;
@ -167,6 +235,7 @@ public:
bool _static; bool _static;
MultiplayerAPI::RPCMode rpc_mode; MultiplayerAPI::RPCMode rpc_mode;
bool has_yield;
StringName name; StringName name;
DataType return_type; DataType return_type;
Vector<StringName> arguments; Vector<StringName> arguments;
@ -181,6 +250,7 @@ public:
type = TYPE_FUNCTION; type = TYPE_FUNCTION;
_static = false; _static = false;
rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
has_yield = false;
} }
}; };
@ -188,11 +258,9 @@ public:
ClassNode *parent_class; ClassNode *parent_class;
BlockNode *parent_block; BlockNode *parent_block;
Map<StringName, int> locals;
List<Node *> statements; List<Node *> statements;
Vector<StringName> variables; Map<StringName, LocalVarNode *> variables;
Vector<DataType> variable_types; bool has_return;
Vector<int> variable_lines;
Node *if_condition; //tiny hack to improve code completion on if () blocks Node *if_condition; //tiny hack to improve code completion on if () blocks
@ -205,15 +273,13 @@ public:
end_line = -1; end_line = -1;
parent_block = NULL; parent_block = NULL;
parent_class = NULL; parent_class = NULL;
has_return = false;
} }
}; };
struct TypeNode : public Node { struct TypeNode : public Node {
Variant::Type vtype; Variant::Type vtype;
DataType datatype;
virtual DataType get_datatype() const { return datatype; }
virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
TypeNode() { type = TYPE_TYPE; } TypeNode() { type = TYPE_TYPE; }
}; };
struct BuiltInFunctionNode : public Node { struct BuiltInFunctionNode : public Node {
@ -224,22 +290,30 @@ public:
struct IdentifierNode : public Node { struct IdentifierNode : public Node {
StringName name; StringName name;
BlockNode *declared_block; // Simplify lookup by checking if it is declared locally
DataType datatype; DataType datatype;
virtual DataType get_datatype() const { return datatype; } virtual DataType get_datatype() const { return datatype; }
virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; } virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
IdentifierNode() { type = TYPE_IDENTIFIER; } IdentifierNode() {
type = TYPE_IDENTIFIER;
declared_block = NULL;
}
}; };
struct LocalVarNode : public Node { struct LocalVarNode : public Node {
StringName name; StringName name;
Node *assign; Node *assign;
OperatorNode *assign_op;
int assignments;
DataType datatype; DataType datatype;
virtual DataType get_datatype() const { return datatype; } virtual DataType get_datatype() const { return datatype; }
virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; } virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
LocalVarNode() { LocalVarNode() {
type = TYPE_LOCAL_VAR; type = TYPE_LOCAL_VAR;
assign = NULL; assign = NULL;
assign_op = NULL;
assignments = 0;
} }
}; };
@ -304,10 +378,6 @@ public:
OP_POS, OP_POS,
OP_NOT, OP_NOT,
OP_BIT_INVERT, OP_BIT_INVERT,
OP_PREINC,
OP_PREDEC,
OP_INC,
OP_DEC,
//binary operators (in precedence order) //binary operators (in precedence order)
OP_IN, OP_IN,
OP_EQUAL, OP_EQUAL,
@ -421,8 +491,9 @@ public:
struct CastNode : public Node { struct CastNode : public Node {
Node *source_node; Node *source_node;
DataType cast_type; DataType cast_type;
virtual DataType get_datatype() const { return cast_type; } DataType return_type;
virtual void set_datatype(const DataType &p_datatype) { cast_type = p_datatype; } virtual DataType get_datatype() const { return return_type; }
virtual void set_datatype(const DataType &p_datatype) { return_type = p_datatype; }
CastNode() { type = TYPE_CAST; } CastNode() { type = TYPE_CAST; }
}; };
@ -462,6 +533,8 @@ public:
COMPLETION_VIRTUAL_FUNC, COMPLETION_VIRTUAL_FUNC,
COMPLETION_YIELD, COMPLETION_YIELD,
COMPLETION_ASSIGN, COMPLETION_ASSIGN,
COMPLETION_TYPE_HINT,
COMPLETION_TYPE_HINT_INDEX,
}; };
private: private:
@ -479,6 +552,7 @@ private:
String error; String error;
int error_line; int error_line;
int error_column; int error_column;
bool check_types;
int pending_newline; int pending_newline;
@ -490,7 +564,6 @@ private:
ClassNode *current_class; ClassNode *current_class;
FunctionNode *current_function; FunctionNode *current_function;
BlockNode *current_block; BlockNode *current_block;
Map<StringName, ClassNode *> class_map;
bool _get_completable_identifier(CompletionType p_type, StringName &identifier); bool _get_completable_identifier(CompletionType p_type, StringName &identifier);
void _make_completable_call(int p_arg); void _make_completable_call(int p_arg);
@ -524,7 +597,7 @@ private:
PatternNode *_parse_pattern(bool p_static); PatternNode *_parse_pattern(bool p_static);
void _parse_pattern_block(BlockNode *p_block, Vector<PatternBranchNode *> &p_branches, bool p_static); void _parse_pattern_block(BlockNode *p_block, Vector<PatternBranchNode *> &p_branches, bool p_static);
void _transform_match_statment(BlockNode *p_block, MatchNode *p_match_statement); void _transform_match_statment(MatchNode *p_match_statement);
void _generate_pattern(PatternNode *p_pattern, Node *p_node_to_match, Node *&p_resulting_node, Map<StringName, Node *> &p_bindings); void _generate_pattern(PatternNode *p_pattern, Node *p_node_to_match, Node *&p_resulting_node, Map<StringName, Node *> &p_bindings);
void _parse_block(BlockNode *p_block, bool p_static); void _parse_block(BlockNode *p_block, bool p_static);
@ -534,6 +607,23 @@ private:
void _determine_inheritance(ClassNode *p_class); void _determine_inheritance(ClassNode *p_class);
bool _parse_type(DataType &r_type, bool p_can_be_void = false); bool _parse_type(DataType &r_type, bool p_can_be_void = false);
DataType _resolve_type(const DataType &p_source, int p_line);
DataType _type_from_variant(const Variant &p_value) const;
DataType _type_from_property(const PropertyInfo &p_property, bool p_nil_is_variant = true) const;
DataType _type_from_gdtype(const GDScriptDataType &p_gdtype) const;
DataType _get_operation_type(const Variant::Operator p_op, const DataType &p_a, const DataType &p_b, bool &r_valid) const;
Variant::Operator _get_variant_operation(const OperatorNode::Operator &p_op) const;
bool _get_function_signature(DataType &p_base_type, const StringName &p_function, DataType &r_return_type, List<DataType> &r_arg_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) const;
bool _get_member_type(const DataType &p_base_type, const StringName &p_member, DataType &r_member_type) const;
bool _is_type_compatible(const DataType &p_container, const DataType &p_expression, bool p_allow_implicit_conversion = false) const;
DataType _reduce_node_type(Node *p_node);
DataType _reduce_function_call_type(const OperatorNode *p_call);
DataType _reduce_identifier_type(const DataType *p_base_type, const StringName &p_identifier, int p_line);
void _check_class_level_types(ClassNode *p_class);
void _check_class_blocks_types(ClassNode *p_class);
void _check_function_types(FunctionNode *p_function);
void _check_block_types(BlockNode *p_block);
Error _parse(const String &p_base_path); Error _parse(const String &p_base_path);