Skip to content

Commit d79f560

Browse files
authored
Merge pull request #957 from Spartan322/4.3.1-cherry-pick/bugs-gdscript
[4.3] Cherry-picks for the 4.3 (4.3.1) branch - 2nd gdscript bugs batch
2 parents 7865941 + f697591 commit d79f560

29 files changed

+351
-30
lines changed

modules/gdscript/gdscript_editor.cpp

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -862,7 +862,8 @@ static void _get_directory_contents(EditorFileSystemDirectory *p_dir, HashMap<St
862862
}
863863
}
864864

865-
static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_annotation, int p_argument, const String p_quote_style, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {
865+
static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_annotation, int p_argument, const String p_quote_style, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, String &r_arghint) {
866+
r_arghint = _make_arguments_hint(p_annotation->info->info, p_argument, true);
866867
if (p_annotation->name == SNAME("@export_range")) {
867868
if (p_argument == 3 || p_argument == 4 || p_argument == 5) {
868869
// Slider hint.
@@ -2127,7 +2128,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
21272128
// Look in blocks first.
21282129
int last_assign_line = -1;
21292130
const GDScriptParser::ExpressionNode *last_assigned_expression = nullptr;
2130-
GDScriptParser::DataType id_type;
2131+
GDScriptCompletionIdentifier id_type;
21312132
GDScriptParser::SuiteNode *suite = p_context.current_suite;
21322133
bool is_function_parameter = false;
21332134

@@ -2149,7 +2150,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
21492150
if (can_be_local && suite && suite->has_local(p_identifier->name)) {
21502151
const GDScriptParser::SuiteNode::Local &local = suite->get_local(p_identifier->name);
21512152

2152-
id_type = local.get_datatype();
2153+
id_type.type = local.get_datatype();
21532154

21542155
// Check initializer as the first assignment.
21552156
switch (local.type) {
@@ -2187,7 +2188,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
21872188
base.type.is_meta_type = p_context.current_function && p_context.current_function->is_static;
21882189

21892190
if (_guess_identifier_type_from_base(p_context, base, p_identifier->name, base_identifier)) {
2190-
id_type = base_identifier.type;
2191+
id_type = base_identifier;
21912192
}
21922193
}
21932194
}
@@ -2227,7 +2228,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
22272228
c.current_line = type_test->operand->start_line;
22282229
c.current_suite = suite;
22292230
if (type_test->test_datatype.is_hard_type()) {
2230-
id_type = type_test->test_datatype;
2231+
id_type.type = type_test->test_datatype;
22312232
if (last_assign_line < c.current_line) {
22322233
// Override last assignment.
22332234
last_assign_line = c.current_line;
@@ -2245,10 +2246,10 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
22452246
c.current_line = last_assign_line;
22462247
GDScriptCompletionIdentifier assigned_type;
22472248
if (_guess_expression_type(c, last_assigned_expression, assigned_type)) {
2248-
if (id_type.is_set() && assigned_type.type.is_set() && !GDScriptAnalyzer::check_type_compatibility(id_type, assigned_type.type)) {
2249+
if (id_type.type.is_set() && assigned_type.type.is_set() && !GDScriptAnalyzer::check_type_compatibility(id_type.type, assigned_type.type)) {
22492250
// The assigned type is incompatible. The annotated type takes priority.
2251+
r_type = id_type;
22502252
r_type.assigned_expression = last_assigned_expression;
2251-
r_type.type = id_type;
22522253
} else {
22532254
r_type = assigned_type;
22542255
}
@@ -2266,8 +2267,8 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
22662267
GDScriptParser::FunctionNode *parent_function = base_type.class_type->get_member(p_context.current_function->identifier->name).function;
22672268
if (parent_function->parameters_indices.has(p_identifier->name)) {
22682269
const GDScriptParser::ParameterNode *parameter = parent_function->parameters[parent_function->parameters_indices[p_identifier->name]];
2269-
if ((!id_type.is_set() || id_type.is_variant()) && parameter->get_datatype().is_hard_type()) {
2270-
id_type = parameter->get_datatype();
2270+
if ((!id_type.type.is_set() || id_type.type.is_variant()) && parameter->get_datatype().is_hard_type()) {
2271+
id_type.type = parameter->get_datatype();
22712272
}
22722273
if (parameter->initializer) {
22732274
GDScriptParser::CompletionContext c = p_context;
@@ -2283,7 +2284,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
22832284
base_type = base_type.class_type->base_type;
22842285
break;
22852286
case GDScriptParser::DataType::NATIVE: {
2286-
if (id_type.is_set() && !id_type.is_variant()) {
2287+
if (id_type.type.is_set() && !id_type.type.is_variant()) {
22872288
base_type = GDScriptParser::DataType();
22882289
break;
22892290
}
@@ -2304,8 +2305,8 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
23042305
}
23052306
}
23062307

2307-
if (id_type.is_set() && !id_type.is_variant()) {
2308-
r_type.type = id_type;
2308+
if (id_type.type.is_set() && !id_type.type.is_variant()) {
2309+
r_type = id_type;
23092310
return true;
23102311
}
23112312

@@ -3197,7 +3198,7 @@ ::Error GDScriptLanguage::complete_code(const String &p_code, const String &p_pa
31973198
break;
31983199
}
31993200
const GDScriptParser::AnnotationNode *annotation = static_cast<const GDScriptParser::AnnotationNode *>(completion_context.node);
3200-
_find_annotation_arguments(annotation, completion_context.current_argument, quote_style, options);
3201+
_find_annotation_arguments(annotation, completion_context.current_argument, quote_style, options, r_call_hint);
32013202
r_forced = true;
32023203
} break;
32033204
case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD: {
@@ -3321,11 +3322,36 @@ ::Error GDScriptLanguage::complete_code(const String &p_code, const String &p_pa
33213322
case GDScriptParser::COMPLETION_SUBSCRIPT: {
33223323
const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(completion_context.node);
33233324
GDScriptCompletionIdentifier base;
3324-
if (!_guess_expression_type(completion_context, subscript->base, base)) {
3325-
break;
3326-
}
3325+
const bool res = _guess_expression_type(completion_context, subscript->base, base);
3326+
3327+
// If the type is not known, we assume it is BUILTIN, since indices on arrays is the most common use case.
3328+
if (!subscript->is_attribute && (!res || base.type.kind == GDScriptParser::DataType::BUILTIN || base.type.is_variant())) {
3329+
if (base.value.get_type() == Variant::DICTIONARY) {
3330+
List<PropertyInfo> members;
3331+
base.value.get_property_list(&members);
33273332

3328-
_find_identifiers_in_base(base, false, false, options, 0);
3333+
for (const PropertyInfo &E : members) {
3334+
ScriptLanguage::CodeCompletionOption option(E.name.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, ScriptLanguage::LOCATION_LOCAL);
3335+
options.insert(option.display, option);
3336+
}
3337+
}
3338+
if (!subscript->index || subscript->index->type != GDScriptParser::Node::LITERAL) {
3339+
_find_identifiers(completion_context, false, options, 0);
3340+
}
3341+
} else if (res) {
3342+
if (!subscript->is_attribute) {
3343+
// Quote the options if they are not accessed as attribute.
3344+
3345+
HashMap<String, ScriptLanguage::CodeCompletionOption> opt;
3346+
_find_identifiers_in_base(base, false, false, opt, 0);
3347+
for (const KeyValue<String, CodeCompletionOption> &E : opt) {
3348+
ScriptLanguage::CodeCompletionOption option(E.value.insert_text.quote(quote_style), E.value.kind, E.value.location);
3349+
options.insert(option.display, option);
3350+
}
3351+
} else {
3352+
_find_identifiers_in_base(base, false, false, options, 0);
3353+
}
3354+
}
33293355
} break;
33303356
case GDScriptParser::COMPLETION_TYPE_ATTRIBUTE: {
33313357
if (!completion_context.current_class) {

modules/gdscript/gdscript_parser.cpp

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ void GDScriptParser::override_completion_context(const Node *p_for_node, Complet
251251
if (!for_completion) {
252252
return;
253253
}
254-
if (completion_context.node != p_for_node) {
254+
if (p_for_node == nullptr || completion_context.node != p_for_node) {
255255
return;
256256
}
257257
CompletionContext context;
@@ -262,11 +262,12 @@ void GDScriptParser::override_completion_context(const Node *p_for_node, Complet
262262
context.current_line = tokenizer->get_cursor_line();
263263
context.current_argument = p_argument;
264264
context.node = p_node;
265+
context.parser = this;
265266
completion_context = context;
266267
}
267268

268-
void GDScriptParser::make_completion_context(CompletionType p_type, Node *p_node, int p_argument) {
269-
if (!for_completion) {
269+
void GDScriptParser::make_completion_context(CompletionType p_type, Node *p_node, int p_argument, bool p_force) {
270+
if (!for_completion || (!p_force && completion_context.type != COMPLETION_NONE)) {
270271
return;
271272
}
272273
if (previous.cursor_place != GDScriptTokenizerText::CURSOR_MIDDLE && previous.cursor_place != GDScriptTokenizerText::CURSOR_END && current.cursor_place == GDScriptTokenizerText::CURSOR_NONE) {
@@ -284,8 +285,8 @@ void GDScriptParser::make_completion_context(CompletionType p_type, Node *p_node
284285
completion_context = context;
285286
}
286287

287-
void GDScriptParser::make_completion_context(CompletionType p_type, Variant::Type p_builtin_type) {
288-
if (!for_completion) {
288+
void GDScriptParser::make_completion_context(CompletionType p_type, Variant::Type p_builtin_type, bool p_force) {
289+
if (!for_completion || (!p_force && completion_context.type != COMPLETION_NONE)) {
289290
return;
290291
}
291292
if (previous.cursor_place != GDScriptTokenizerText::CURSOR_MIDDLE && previous.cursor_place != GDScriptTokenizerText::CURSOR_END && current.cursor_place == GDScriptTokenizerText::CURSOR_NONE) {
@@ -1640,23 +1641,29 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali
16401641
advance();
16411642
// Arguments.
16421643
push_completion_call(annotation);
1643-
make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, 0);
16441644
int argument_index = 0;
16451645
do {
1646+
make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, argument_index);
1647+
set_last_completion_call_arg(argument_index);
16461648
if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
16471649
// Allow for trailing comma.
16481650
break;
16491651
}
16501652

1651-
make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, argument_index);
1652-
set_last_completion_call_arg(argument_index++);
16531653
ExpressionNode *argument = parse_expression(false);
1654+
16541655
if (argument == nullptr) {
16551656
push_error("Expected expression as the annotation argument.");
16561657
valid = false;
1657-
continue;
1658+
} else {
1659+
annotation->arguments.push_back(argument);
1660+
1661+
if (argument->type == Node::LITERAL) {
1662+
override_completion_context(argument, COMPLETION_ANNOTATION_ARGUMENTS, annotation, argument_index);
1663+
}
16581664
}
1659-
annotation->arguments.push_back(argument);
1665+
1666+
argument_index++;
16601667
} while (match(GDScriptTokenizer::Token::COMMA) && !is_at_end());
16611668

16621669
pop_multiline();
@@ -2472,7 +2479,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_pr
24722479
}
24732480

24742481
// Completion can appear whenever an expression is expected.
2475-
make_completion_context(COMPLETION_IDENTIFIER, nullptr);
2482+
make_completion_context(COMPLETION_IDENTIFIER, nullptr, -1, false);
24762483

24772484
GDScriptTokenizer::Token token = current;
24782485
GDScriptTokenizer::Token::Type token_type = token.type;
@@ -2489,8 +2496,17 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_pr
24892496

24902497
advance(); // Only consume the token if there's a valid rule.
24912498

2499+
// After a token was consumed, update the completion context regardless of a previously set context.
2500+
24922501
ExpressionNode *previous_operand = (this->*prefix_rule)(nullptr, p_can_assign);
24932502

2503+
#ifdef TOOLS_ENABLED
2504+
// HACK: We can't create a context in parse_identifier since it is used in places were we don't want completion.
2505+
if (previous_operand != nullptr && previous_operand->type == GDScriptParser::Node::IDENTIFIER && prefix_rule == static_cast<ParseFunction>(&GDScriptParser::parse_identifier)) {
2506+
make_completion_context(COMPLETION_IDENTIFIER, previous_operand);
2507+
}
2508+
#endif
2509+
24942510
while (p_precedence <= get_rule(current.type)->precedence) {
24952511
if (previous_operand == nullptr || (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL) || lambda_ended) {
24962512
return previous_operand;
@@ -2925,6 +2941,11 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode
29252941
}
29262942
assignment->assignee = p_previous_operand;
29272943
assignment->assigned_value = parse_expression(false);
2944+
#ifdef TOOLS_ENABLED
2945+
if (assignment->assigned_value != nullptr && assignment->assigned_value->type == GDScriptParser::Node::IDENTIFIER) {
2946+
override_completion_context(assignment->assigned_value, COMPLETION_ASSIGN, assignment);
2947+
}
2948+
#endif
29282949
if (assignment->assigned_value == nullptr) {
29292950
push_error(R"(Expected an expression after "=".)");
29302951
}
@@ -3124,6 +3145,12 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_subscript(ExpressionNode *
31243145
subscript->base = p_previous_operand;
31253146
subscript->index = parse_expression(false);
31263147

3148+
#ifdef TOOLS_ENABLED
3149+
if (subscript->index != nullptr && subscript->index->type == Node::LITERAL) {
3150+
override_completion_context(subscript->index, COMPLETION_SUBSCRIPT, subscript);
3151+
}
3152+
#endif
3153+
31273154
if (subscript->index == nullptr) {
31283155
push_error(R"(Expected expression after "[".)");
31293156
}

modules/gdscript/gdscript_parser.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1457,8 +1457,11 @@ class GDScriptParser {
14571457
}
14581458
void apply_pending_warnings();
14591459
#endif
1460-
void make_completion_context(CompletionType p_type, Node *p_node, int p_argument = -1);
1461-
void make_completion_context(CompletionType p_type, Variant::Type p_builtin_type);
1460+
// Setting p_force to false will prevent the completion context from being update if a context was already set before.
1461+
// This should only be done when we push context before we consumed any tokens for the corresponding structure.
1462+
// See parse_precedence for an example.
1463+
void make_completion_context(CompletionType p_type, Node *p_node, int p_argument = -1, bool p_force = true);
1464+
void make_completion_context(CompletionType p_type, Variant::Type p_builtin_type, bool p_force = true);
14621465
// In some cases it might become necessary to alter the completion context after parsing a subexpression.
14631466
// For example to not override COMPLETE_CALL_ARGUMENTS with COMPLETION_NONE from string literals.
14641467
void override_completion_context(const Node *p_for_node, CompletionType p_type, Node *p_node, int p_argument = -1);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[output]
2+
include=[
3+
{"display": "new(…)"},
4+
{"display": "SIZE_EXPAND"},
5+
{"display": "SIZE_EXPAND_FILL"},
6+
{"display": "SIZE_FILL"},
7+
{"display": "SIZE_SHRINK_BEGIN"},
8+
{"display": "SIZE_SHRINK_CENTER"},
9+
{"display": "SIZE_SHRINK_END"},
10+
]
11+
exclude=[
12+
{"display": "Control.SIZE_EXPAND"},
13+
{"display": "Control.SIZE_EXPAND_FILL"},
14+
{"display": "Control.SIZE_FILL"},
15+
{"display": "Control.SIZE_SHRINK_BEGIN"},
16+
{"display": "Control.SIZE_SHRINK_CENTER"},
17+
{"display": "Control.SIZE_SHRINK_END"},
18+
{"display": "contro_var"}
19+
]
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
extends Control
2+
3+
var contro_var
4+
5+
func _ready():
6+
size_flags_horizontal = Control.➡
7+
pass
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[output]
2+
include=[
3+
{"display": "new(…)"},
4+
{"display": "SIZE_EXPAND"},
5+
{"display": "SIZE_EXPAND_FILL"},
6+
{"display": "SIZE_FILL"},
7+
{"display": "SIZE_SHRINK_BEGIN"},
8+
{"display": "SIZE_SHRINK_CENTER"},
9+
{"display": "SIZE_SHRINK_END"},
10+
]
11+
exclude=[
12+
{"display": "Control.SIZE_EXPAND"},
13+
{"display": "Control.SIZE_EXPAND_FILL"},
14+
{"display": "Control.SIZE_FILL"},
15+
{"display": "Control.SIZE_SHRINK_BEGIN"},
16+
{"display": "Control.SIZE_SHRINK_CENTER"},
17+
{"display": "Control.SIZE_SHRINK_END"},
18+
{"display": "contro_var"}
19+
]
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
extends Control
2+
3+
var contro_var
4+
5+
func _ready():
6+
size_flags_horizontal = Control.SIZE
7+
pass
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[output]
2+
include=[
3+
{"display": "Control.SIZE_EXPAND"},
4+
{"display": "Control.SIZE_EXPAND_FILL"},
5+
{"display": "Control.SIZE_FILL"},
6+
{"display": "Control.SIZE_SHRINK_BEGIN"},
7+
{"display": "Control.SIZE_SHRINK_CENTER"},
8+
{"display": "Control.SIZE_SHRINK_END"},
9+
]
10+
exclude=[
11+
{"display": "contro_var"}
12+
]
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
extends Control
2+
3+
var contro_var
4+
5+
func _ready():
6+
size_flags_horizontal = Con
7+
pass
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[output]
2+
include=[
3+
{"display": "Control.SIZE_EXPAND"},
4+
{"display": "Control.SIZE_EXPAND_FILL"},
5+
{"display": "Control.SIZE_FILL"},
6+
{"display": "Control.SIZE_SHRINK_BEGIN"},
7+
{"display": "Control.SIZE_SHRINK_CENTER"},
8+
{"display": "Control.SIZE_SHRINK_END"},
9+
]
10+
exclude=[
11+
{"display": "contro_var"}
12+
]

0 commit comments

Comments
 (0)