Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 10 additions & 12 deletions modules/gdscript/gdscript_analyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -702,19 +702,18 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
StringName first = first_id->name;
bool type_found = false;

if (first_id->suite && first_id->suite->has_local(first)) {
const GDScriptParser::SuiteNode::Local &local = first_id->suite->get_local(first);
if (local.type == GDScriptParser::SuiteNode::Local::CONSTANT) {
result = local.get_datatype();
if (const GDScriptParser::SuiteNode::Local *local = first_id->suite ? first_id->suite->get_local(first) : nullptr) {
if (local->type == GDScriptParser::SuiteNode::Local::CONSTANT) {
result = local->get_datatype();
if (!result.is_set()) {
// Don't try to resolve it as the constant can be declared below.
push_error(vformat(R"(Local constant "%s" is not resolved at this point.)", first), first_id);
return bad_type;
}
if (result.is_meta_type) {
type_found = true;
} else if (Ref<Script>(local.constant->initializer->reduced_value).is_valid()) {
Ref<GDScript> gdscript = local.constant->initializer->reduced_value;
} else if (Ref<Script>(local->constant->initializer->reduced_value).is_valid()) {
Ref<GDScript> gdscript = local->constant->initializer->reduced_value;
if (gdscript.is_valid()) {
Ref<GDScriptParserRef> ref = parser->get_depended_parser_for(gdscript->get_script_path());
if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) {
Expand All @@ -723,15 +722,15 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
}
result = ref->get_parser()->head->self_type;
} else {
result = make_script_meta_type(local.constant->initializer->reduced_value);
result = make_script_meta_type(local->constant->initializer->reduced_value);
}
type_found = true;
} else {
push_error(vformat(R"(Local constant "%s" is not a valid type.)", first), first_id);
return bad_type;
}
} else {
push_error(vformat(R"(Local %s "%s" cannot be used as a type.)", local.get_name(), first), first_id);
push_error(vformat(R"(Local %s "%s" cannot be used as a type.)", local->get_name(), first), first_id);
return bad_type;
}
}
Expand Down Expand Up @@ -2150,9 +2149,8 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi

#ifdef DEBUG_ENABLED
if (p_assignable->identifier != nullptr && p_assignable->identifier->suite != nullptr && p_assignable->identifier->suite->parent_block != nullptr) {
if (p_assignable->identifier->suite->parent_block->has_local(p_assignable->identifier->name)) {
const GDScriptParser::SuiteNode::Local &local = p_assignable->identifier->suite->parent_block->get_local(p_assignable->identifier->name);
parser->push_warning(p_assignable->identifier, GDScriptWarning::CONFUSABLE_LOCAL_DECLARATION, local.get_name(), p_assignable->identifier->name);
if (const GDScriptParser::SuiteNode::Local *local = p_assignable->identifier->suite->parent_block->get_local(p_assignable->identifier->name)) {
parser->push_warning(p_assignable->identifier, GDScriptWarning::CONFUSABLE_LOCAL_DECLARATION, local->get_name(), p_assignable->identifier->name);
}
}
#endif // DEBUG_ENABLED
Expand Down Expand Up @@ -4522,7 +4520,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
}

#ifdef DEBUG_ENABLED
if (!found_source && p_identifier->suite != nullptr && p_identifier->suite->has_local(p_identifier->name)) {
if (!found_source && p_identifier->suite != nullptr && p_identifier->suite->get_local(p_identifier->name)) {
parser->push_warning(p_identifier, GDScriptWarning::CONFUSABLE_LOCAL_USAGE, p_identifier->name);
}
#endif // DEBUG_ENABLED
Expand Down
82 changes: 40 additions & 42 deletions modules/gdscript/gdscript_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2420,29 +2420,27 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
break;
}

if (can_be_local && suite && suite->has_local(p_identifier->name)) {
const GDScriptParser::SuiteNode::Local &local = suite->get_local(p_identifier->name);

id_type.type = local.get_datatype();
if (const GDScriptParser::SuiteNode::Local *local = (can_be_local && suite) ? suite->get_local(p_identifier->name) : nullptr) {
id_type.type = local->get_datatype();

// Check initializer as the first assignment.
switch (local.type) {
switch (local->type) {
case GDScriptParser::SuiteNode::Local::VARIABLE:
if (local.variable->initializer) {
last_assign_line = local.variable->initializer->end_line;
last_assigned_expression = local.variable->initializer;
if (local->variable->initializer) {
last_assign_line = local->variable->initializer->end_line;
last_assigned_expression = local->variable->initializer;
}
break;
case GDScriptParser::SuiteNode::Local::CONSTANT:
if (local.constant->initializer) {
last_assign_line = local.constant->initializer->end_line;
last_assigned_expression = local.constant->initializer;
if (local->constant->initializer) {
last_assign_line = local->constant->initializer->end_line;
last_assigned_expression = local->constant->initializer;
}
break;
case GDScriptParser::SuiteNode::Local::PARAMETER:
if (local.parameter->initializer) {
last_assign_line = local.parameter->initializer->end_line;
last_assigned_expression = local.parameter->initializer;
if (local->parameter->initializer) {
last_assign_line = local->parameter->initializer->end_line;
last_assigned_expression = local->parameter->initializer;
}
is_function_parameter = true;
break;
Expand Down Expand Up @@ -3315,16 +3313,20 @@ static bool _get_subscript_type(GDScriptParser::CompletionContext &p_context, co
} break;
case GDScriptParser::IdentifierNode::Source::LOCAL_VARIABLE: {
// TODO: Do basic assignment flow analysis like in `_guess_expression_type`.
const GDScriptParser::SuiteNode::Local local = identifier_node->suite->get_local(identifier_node->name);
switch (local.type) {
const GDScriptParser::SuiteNode::Local *local = identifier_node->suite->get_local(identifier_node->name);
if (!local) {
break;
}

switch (local->type) {
case GDScriptParser::SuiteNode::Local::CONSTANT: {
if (local.constant->initializer && local.constant->initializer->type == GDScriptParser::Node::GET_NODE) {
get_node = static_cast<GDScriptParser::GetNodeNode *>(local.constant->initializer);
if (local->constant->initializer && local->constant->initializer->type == GDScriptParser::Node::GET_NODE) {
get_node = static_cast<GDScriptParser::GetNodeNode *>(local->constant->initializer);
}
} break;
case GDScriptParser::SuiteNode::Local::VARIABLE: {
if (local.variable->initializer && local.variable->initializer->type == GDScriptParser::Node::GET_NODE) {
get_node = static_cast<GDScriptParser::GetNodeNode *>(local.variable->initializer);
if (local->variable->initializer && local->variable->initializer->type == GDScriptParser::Node::GET_NODE) {
get_node = static_cast<GDScriptParser::GetNodeNode *>(local->variable->initializer);
}
} break;
default: {
Expand Down Expand Up @@ -4443,32 +4445,28 @@ ::Error GDScriptEditorLanguage::lookup_code(const String &p_code, const String &
// Lookup local variables.
const GDScriptParser::SuiteNode *suite = context.current_suite;
while (suite) {
if (suite->has_local(p_symbol)) {
const GDScriptParser::SuiteNode::Local &local = suite->get_local(p_symbol);

switch (local.type) {
case GDScriptParser::SuiteNode::Local::UNDEFINED:
return ERR_BUG;
if (const GDScriptParser::SuiteNode::Local *local = suite->get_local(p_symbol)) {
switch (local->type) {
case GDScriptParser::SuiteNode::Local::CONSTANT:
r_result.type = LookupResult::Type::LOCAL_CONSTANT;
r_result.description = local.constant->doc_data.description;
r_result.is_deprecated = local.constant->doc_data.is_deprecated;
r_result.deprecated_message = local.constant->doc_data.deprecated_message;
r_result.is_experimental = local.constant->doc_data.is_experimental;
r_result.experimental_message = local.constant->doc_data.experimental_message;
if (local.constant->initializer != nullptr) {
r_result.value = GDScriptDocGen::docvalue_from_expression(local.constant->initializer);
r_result.description = local->constant->doc_data.description;
r_result.is_deprecated = local->constant->doc_data.is_deprecated;
r_result.deprecated_message = local->constant->doc_data.deprecated_message;
r_result.is_experimental = local->constant->doc_data.is_experimental;
r_result.experimental_message = local->constant->doc_data.experimental_message;
if (local->constant->initializer != nullptr) {
r_result.value = GDScriptDocGen::docvalue_from_expression(local->constant->initializer);
}
break;
case GDScriptParser::SuiteNode::Local::VARIABLE:
r_result.type = LookupResult::Type::LOCAL_VARIABLE;
r_result.description = local.variable->doc_data.description;
r_result.is_deprecated = local.variable->doc_data.is_deprecated;
r_result.deprecated_message = local.variable->doc_data.deprecated_message;
r_result.is_experimental = local.variable->doc_data.is_experimental;
r_result.experimental_message = local.variable->doc_data.experimental_message;
if (local.variable->initializer != nullptr) {
r_result.value = GDScriptDocGen::docvalue_from_expression(local.variable->initializer);
r_result.description = local->variable->doc_data.description;
r_result.is_deprecated = local->variable->doc_data.is_deprecated;
r_result.deprecated_message = local->variable->doc_data.deprecated_message;
r_result.is_experimental = local->variable->doc_data.is_experimental;
r_result.experimental_message = local->variable->doc_data.experimental_message;
if (local->variable->initializer != nullptr) {
r_result.value = GDScriptDocGen::docvalue_from_expression(local->variable->initializer);
}
break;
case GDScriptParser::SuiteNode::Local::PARAMETER:
Expand All @@ -4478,10 +4476,10 @@ ::Error GDScriptEditorLanguage::lookup_code(const String &p_code, const String &
break;
}

GDScriptDocGen::doctype_from_datatype(local.get_datatype(), r_result.doc_type, r_result.enumeration);
GDScriptDocGen::doctype_from_datatype(local->get_datatype(), r_result.doc_type, r_result.enumeration);

r_result.script_path = base_type.script_path;
r_result.location = local.start_line;
r_result.location = local->start_line;
return OK;
}
suite = suite->parent_block;
Expand Down
59 changes: 19 additions & 40 deletions modules/gdscript/gdscript_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1998,19 +1998,17 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context,
switch (statement->type) {
case Node::VARIABLE: {
VariableNode *variable = static_cast<VariableNode *>(statement);
const SuiteNode::Local &local = current_suite->get_local(variable->identifier->name);
if (local.type != SuiteNode::Local::UNDEFINED) {
push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", local.get_name(), variable->identifier->name), variable->identifier);
if (const SuiteNode::Local *local = current_suite->get_local(variable->identifier->name)) {
push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", local->get_name(), variable->identifier->name), variable->identifier);
}
current_suite->add_local(variable, current_function);
break;
}
case Node::CONSTANT: {
ConstantNode *constant = static_cast<ConstantNode *>(statement);
const SuiteNode::Local &local = current_suite->get_local(constant->identifier->name);
if (local.type != SuiteNode::Local::UNDEFINED) {
if (const SuiteNode::Local *local = current_suite->get_local(constant->identifier->name)) {
String name;
if (local.type == SuiteNode::Local::CONSTANT) {
if (local->type == SuiteNode::Local::CONSTANT) {
name = "constant";
} else {
name = "variable";
Expand Down Expand Up @@ -2353,9 +2351,8 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() {

SuiteNode *suite = alloc_node<SuiteNode>();
if (n_for->variable) {
const SuiteNode::Local &local = current_suite->get_local(n_for->variable->name);
if (local.type != SuiteNode::Local::UNDEFINED) {
push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", local.get_name(), n_for->variable->name), n_for->variable);
if (const SuiteNode::Local *local = current_suite->get_local(n_for->variable->name)) {
push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", local->get_name(), n_for->variable->name), n_for->variable);
}
suite->add_local(SuiteNode::Local(n_for->variable, current_function));
}
Expand Down Expand Up @@ -2608,8 +2605,8 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
}
}

if (current_suite->has_local(pattern->bind->name)) {
push_error(vformat(R"(There's already a %s named "%s" in this scope.)", current_suite->get_local(pattern->bind->name).get_name(), pattern->bind->name));
if (const SuiteNode::Local *local = current_suite->get_local(pattern->bind->name)) {
push_error(vformat(R"(There's already a %s named "%s" in this scope.)", local->get_name(), pattern->bind->name));
complete_extents(pattern);
return nullptr;
}
Expand Down Expand Up @@ -2846,33 +2843,29 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode
}
identifier->suite = current_suite;

if (current_suite != nullptr && current_suite->has_local(identifier->name)) {
const SuiteNode::Local &declaration = current_suite->get_local(identifier->name);

identifier->source_function = declaration.source_function;
switch (declaration.type) {
if (const SuiteNode::Local *declaration = current_suite ? current_suite->get_local(identifier->name) : nullptr) {
identifier->source_function = declaration->source_function;
switch (declaration->type) {
case SuiteNode::Local::CONSTANT:
identifier->source = IdentifierNode::LOCAL_CONSTANT;
identifier->constant_source = declaration.constant;
identifier->constant_source = declaration->constant;
break;
case SuiteNode::Local::VARIABLE:
identifier->source = IdentifierNode::LOCAL_VARIABLE;
identifier->variable_source = declaration.variable;
identifier->variable_source = declaration->variable;
break;
case SuiteNode::Local::PARAMETER:
identifier->source = IdentifierNode::FUNCTION_PARAMETER;
identifier->parameter_source = declaration.parameter;
identifier->parameter_source = declaration->parameter;
break;
case SuiteNode::Local::FOR_VARIABLE:
identifier->source = IdentifierNode::LOCAL_ITERATOR;
identifier->bind_source = declaration.bind;
identifier->bind_source = declaration->bind;
break;
case SuiteNode::Local::PATTERN_BIND:
identifier->source = IdentifierNode::LOCAL_BIND;
identifier->bind_source = declaration.bind;
identifier->bind_source = declaration->bind;
break;
case SuiteNode::Local::UNDEFINED:
ERR_FAIL_V_MSG(nullptr, "Undefined local found.");
}
}

Expand Down Expand Up @@ -4362,24 +4355,14 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
return &rules[p_token_type];
}

bool GDScriptParser::SuiteNode::has_local(const StringName &p_name) const {
if (locals_indices.has(p_name)) {
return true;
}
if (parent_block != nullptr) {
return parent_block->has_local(p_name);
}
return false;
}

const GDScriptParser::SuiteNode::Local &GDScriptParser::SuiteNode::get_local(const StringName &p_name) const {
const GDScriptParser::SuiteNode::Local *GDScriptParser::SuiteNode::get_local(const StringName &p_name) const {
if (locals_indices.has(p_name)) {
return locals[locals_indices[p_name]];
return &locals[locals_indices[p_name]];
}
if (parent_block != nullptr) {
return parent_block->get_local(p_name);
}
return empty;
return nullptr;
}

bool GDScriptParser::AnnotationNode::apply(GDScriptParser *p_this, Node *p_target, ClassNode *p_class) {
Expand Down Expand Up @@ -5303,8 +5286,6 @@ GDScriptParser::DataType GDScriptParser::SuiteNode::Local::get_datatype() const
case FOR_VARIABLE:
case PATTERN_BIND:
return bind->type_constraint;
case UNDEFINED:
return DataType();
}
return DataType();
}
Expand All @@ -5321,8 +5302,6 @@ String GDScriptParser::SuiteNode::Local::get_name() const {
return "for loop iterator";
case SuiteNode::Local::PATTERN_BIND:
return "pattern bind";
case SuiteNode::Local::UNDEFINED:
return "<undefined>";
default:
return String();
}
Expand Down
9 changes: 3 additions & 6 deletions modules/gdscript/gdscript_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1132,14 +1132,13 @@ class GDScriptParser {
LocalVector<Node *> statements;
struct Local {
enum Type {
UNDEFINED,
CONSTANT,
VARIABLE,
PARAMETER,
FOR_VARIABLE,
PATTERN_BIND,
};
Type type = UNDEFINED;
Type type;
union {
ConstantNode *constant = nullptr;
VariableNode *variable;
Expand All @@ -1157,7 +1156,7 @@ class GDScriptParser {
DataType get_datatype() const;
String get_name() const;

Local() {}
Local() = delete;
Local(ConstantNode *p_constant, FunctionNode *p_source_function) {
type = CONSTANT;
constant = p_constant;
Expand Down Expand Up @@ -1203,7 +1202,6 @@ class GDScriptParser {
end_column = p_identifier->end_column;
}
};
Local empty;
LocalVector<Local> locals;
HashMap<StringName, uint32_t> locals_indices;

Expand All @@ -1215,8 +1213,7 @@ class GDScriptParser {
bool has_unreachable_code = false; // Just so warnings aren't given more than once per block.
bool is_in_loop = false; // The block is nested in a loop (directly or indirectly).

bool has_local(const StringName &p_name) const;
const Local &get_local(const StringName &p_name) const;
const Local *get_local(const StringName &p_name) const;
template <typename T>
void add_local(T *p_local, FunctionNode *p_source_function) {
locals_indices[p_local->identifier->name] = locals.size();
Expand Down