Skip to content

Commit bc0b0f5

Browse files
committed
GDTrait
1 parent ef34c3d commit bc0b0f5

28 files changed

+1301
-77
lines changed

doc/classes/ProjectSettings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,9 @@
673673
<member name="debug/gdscript/warnings/unused_signal" type="int" setter="" getter="" default="1">
674674
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a signal is declared but never explicitly used in the class.
675675
</member>
676+
<member name="debug/gdscript/warnings/unused_static_overriding_trait" type="int" setter="" getter="" default="1">
677+
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an overridden static member from a trait is not declared with the [code]static[/code] keyword.
678+
</member>
676679
<member name="debug/gdscript/warnings/unused_variable" type="int" setter="" getter="" default="1">
677680
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a local variable is unused.
678681
</member>

modules/gdscript/editor/gdscript_docgen.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ void GDScriptDocGen::_doctype_from_gdtype(const GDType &p_gdtype, String &r_type
124124
}
125125
r_type = "Object";
126126
return;
127+
case GDType::TRAIT:
127128
case GDType::CLASS:
128129
if (p_gdtype.is_meta_type) {
129130
r_type = GDScript::get_class_static();
@@ -369,6 +370,10 @@ void GDScriptDocGen::_generate_docs(GDScript *p_script, const GDP::ClassNode *p_
369370
for (const GDP::ClassNode::Member &member : p_class->members) {
370371
switch (member.type) {
371372
case GDP::ClassNode::Member::CLASS: {
373+
if (p_class->type == GDP::Node::TRAIT) {
374+
// Trait do not have scripts for their inner classes.
375+
continue;
376+
}
372377
const GDP::ClassNode *inner_class = member.m_class;
373378
const StringName &class_name = inner_class->identifier->name;
374379

modules/gdscript/editor/gdscript_translation_parser_plugin.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ void GDScriptEditorTranslationParserPlugin::_traverse_class(const GDScriptParser
144144
const GDScriptParser::ClassNode::Member &m = p_class->members[i];
145145
// Other member types can't contain translatable strings.
146146
switch (m.type) {
147+
case GDScriptParser::ClassNode::Member::TRAIT:
147148
case GDScriptParser::ClassNode::Member::CLASS:
148149
_traverse_class(m.m_class);
149150
break;

modules/gdscript/gdscript.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2752,7 +2752,8 @@ Vector<String> GDScriptLanguage::get_reserved_words() const {
27522752
"namespace", // Reserved for potential future use.
27532753
"signal",
27542754
"static",
2755-
"trait", // Reserved for potential future use.
2755+
"trait",
2756+
"uses",
27562757
"var",
27572758
// Other keywords.
27582759
"await",

modules/gdscript/gdscript_analyzer.cpp

Lines changed: 821 additions & 51 deletions
Large diffs are not rendered by default.

modules/gdscript/gdscript_analyzer.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ class GDScriptAnalyzer {
7878
void resolve_class_interface(GDScriptParser::ClassNode *p_class, bool p_recursive);
7979
void resolve_class_body(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source = nullptr);
8080
void resolve_class_body(GDScriptParser::ClassNode *p_class, bool p_recursive);
81+
void resolve_class_uses(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source = nullptr);
82+
void resolve_class_uses(GDScriptParser::ClassNode *p_class, bool p_recursive);
8183
void resolve_function_signature(GDScriptParser::FunctionNode *p_function, const GDScriptParser::Node *p_source = nullptr, bool p_is_lambda = false);
8284
void resolve_function_body(GDScriptParser::FunctionNode *p_function, bool p_is_lambda = false);
8385
void resolve_node(GDScriptParser::Node *p_node, bool p_is_root = true);
@@ -153,9 +155,17 @@ class GDScriptAnalyzer {
153155
void is_shadowing(GDScriptParser::IdentifierNode *p_identifier, const String &p_context, const bool p_in_local_scope);
154156
#endif
155157

158+
// Resolving Traits Helpers.
159+
void override_member_function(GDScriptParser::FunctionNode *p_target_function, const GDScriptParser::FunctionNode *p_source_function, const String &p_trait_name);
160+
void extend_class(GDScriptParser::ClassNode *p_class, const GDScriptParser::ClassNode *p_trait, const GDScriptParser::Node *p_trait_name_node, const String &p_trait_name);
161+
#ifdef TOOLS_ENABLED
162+
void copy_over_member_doc_data(GDScriptParser::MemberDocData &p_target_doc_data, const GDScriptParser::MemberDocData &p_source_doc_data);
163+
#endif
164+
156165
public:
157166
Error resolve_inheritance();
158167
Error resolve_interface();
168+
Error resolve_uses();
159169
Error resolve_body();
160170
Error resolve_dependencies();
161171
Error analyze();

modules/gdscript/gdscript_cache.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,21 @@ Error GDScriptParserRef::raise_status(Status p_new_status) {
9191
result = get_analyzer()->resolve_inheritance();
9292
} break;
9393
case INHERITANCE_SOLVED: {
94+
status = USES_SOLVED;
95+
result = get_analyzer()->resolve_uses();
96+
} break;
97+
case USES_SOLVED: {
9498
status = INTERFACE_SOLVED;
9599
result = get_analyzer()->resolve_interface();
96100
} break;
97101
case INTERFACE_SOLVED: {
98-
status = FULLY_SOLVED;
102+
status = BODY_SOLVED;
99103
result = get_analyzer()->resolve_body();
100104
} break;
105+
case BODY_SOLVED: {
106+
status = FULLY_SOLVED;
107+
result = get_analyzer()->resolve_dependencies();
108+
} break;
101109
case FULLY_SOLVED: {
102110
return result;
103111
}

modules/gdscript/gdscript_cache.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ class GDScriptParserRef : public RefCounted {
4848
EMPTY,
4949
PARSED,
5050
INHERITANCE_SOLVED,
51+
USES_SOLVED,
5152
INTERFACE_SOLVED,
53+
BODY_SOLVED,
5254
FULLY_SOLVED,
5355
};
5456

modules/gdscript/gdscript_compiler.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
138138
result.script_type = result.script_type_ref.ptr();
139139
result.native_type = p_datatype.native_type;
140140
} break;
141+
case GDScriptParser::DataType::TRAIT:
141142
case GDScriptParser::DataType::CLASS: {
142143
if (p_handle_metatype && p_datatype.is_meta_type) {
143144
result.kind = GDScriptDataType::NATIVE;
@@ -149,6 +150,9 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
149150
result.kind = GDScriptDataType::GDSCRIPT;
150151
result.builtin_type = p_datatype.builtin_type;
151152
result.native_type = p_datatype.native_type;
153+
if (p_datatype.kind == GDScriptParser::DataType::TRAIT) {
154+
result.kind = GDScriptDataType::GDTRAIT;
155+
}
152156

153157
bool is_local_class = parser->has_class(p_datatype.class_type);
154158

@@ -357,6 +361,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
357361
}
358362
} break;
359363
case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:
364+
case GDScriptParser::IdentifierNode::MEMBER_TRAIT:
360365
case GDScriptParser::IdentifierNode::MEMBER_CLASS: {
361366
// Try class constants.
362367
GDScript *owner = codegen.script;
@@ -2986,6 +2991,11 @@ Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptP
29862991
}
29872992

29882993
Error GDScriptCompiler::_compile_class(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
2994+
if (p_class->type == GDScriptParser::Node::TRAIT) {
2995+
// No need to compile traits.
2996+
return OK;
2997+
}
2998+
29892999
// Compile member functions, getters, and setters.
29903000
for (int i = 0; i < p_class->members.size(); i++) {
29913001
const GDScriptParser::ClassNode::Member &member = p_class->members[i];

modules/gdscript/gdscript_editor.cpp

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,7 +1048,7 @@ static void _find_global_enums(HashMap<String, ScriptLanguage::CodeCompletionOpt
10481048
}
10491049
}
10501050

1051-
static void _list_available_types(bool p_inherit_only, GDScriptParser::CompletionContext &p_context, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {
1051+
static void _list_available_types(bool p_inherit_only, bool p_include_trait, GDScriptParser::CompletionContext &p_context, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {
10521052
// Built-in Variant Types
10531053
_find_built_in_variants(r_result, true);
10541054

@@ -1084,6 +1084,12 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio
10841084
ScriptLanguage::CodeCompletionOption option(member.m_class->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_LOCAL + location_offset);
10851085
r_result.insert(option.display, option);
10861086
} break;
1087+
case GDScriptParser::ClassNode::Member::TRAIT: {
1088+
if (p_include_trait) {
1089+
ScriptLanguage::CodeCompletionOption option(member.m_class->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_LOCAL + location_offset);
1090+
r_result.insert(option.display, option);
1091+
}
1092+
} break;
10871093
case GDScriptParser::ClassNode::Member::ENUM: {
10881094
if (!p_inherit_only) {
10891095
ScriptLanguage::CodeCompletionOption option(member.m_enum->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, ScriptLanguage::LOCATION_LOCAL + location_offset);
@@ -1181,6 +1187,7 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class,
11811187
option.default_value = member.constant->initializer->reduced_value;
11821188
}
11831189
break;
1190+
case GDScriptParser::ClassNode::Member::TRAIT:
11841191
case GDScriptParser::ClassNode::Member::CLASS:
11851192
if (p_only_functions) {
11861193
continue;
@@ -1261,6 +1268,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
12611268

12621269
while (!base_type.has_no_type()) {
12631270
switch (base_type.kind) {
1271+
case GDScriptParser::DataType::TRAIT:
12641272
case GDScriptParser::DataType::CLASS: {
12651273
_find_identifiers_in_class(base_type.class_type, p_only_functions, p_types_only, base_type.is_meta_type, false, p_add_braces, r_result, p_recursion_depth);
12661274
// This already finds all parent identifiers, so we are done.
@@ -1565,7 +1573,7 @@ static void _find_identifiers(const GDScriptParser::CompletionContext &p_context
15651573
}
15661574

15671575
static const char *_keywords_with_space[] = {
1568-
"and", "not", "or", "in", "as", "class", "class_name", "extends", "is", "func", "signal", "await",
1576+
"and", "not", "or", "in", "as", "class", "class_name", "trait", "extends", "uses", "is", "func", "signal", "await",
15691577
"const", "enum", "static", "var", "if", "elif", "else", "for", "match", "when", "while",
15701578
nullptr
15711579
};
@@ -1892,6 +1900,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
18921900
r_type = _type_from_variant(p_expression->reduced_value, p_context);
18931901
switch (p_expression->get_datatype().kind) {
18941902
case GDScriptParser::DataType::ENUM:
1903+
case GDScriptParser::DataType::TRAIT:
18951904
case GDScriptParser::DataType::CLASS:
18961905
r_type.type = p_expression->get_datatype();
18971906
break;
@@ -2256,7 +2265,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
22562265
}
22572266

22582267
// If the found type was not fully analyzed we analyze it now.
2259-
if (found && r_type.type.kind == GDScriptParser::DataType::CLASS && !r_type.type.class_type->resolved_body) {
2268+
if (found && (r_type.type.kind == GDScriptParser::DataType::CLASS || r_type.type.kind == GDScriptParser::DataType::TRAIT) && !r_type.type.class_type->resolved_body) {
22602269
Error err;
22612270
Ref<GDScriptParserRef> r = GDScriptCache::get_parser(r_type.type.script_path, GDScriptParserRef::FULLY_SOLVED, err);
22622271
}
@@ -2294,6 +2303,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
22942303
case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:
22952304
case GDScriptParser::IdentifierNode::MEMBER_FUNCTION:
22962305
case GDScriptParser::IdentifierNode::MEMBER_SIGNAL:
2306+
case GDScriptParser::IdentifierNode::MEMBER_TRAIT:
22972307
case GDScriptParser::IdentifierNode::MEMBER_CLASS:
22982308
case GDScriptParser::IdentifierNode::INHERITED_VARIABLE:
22992309
case GDScriptParser::IdentifierNode::STATIC_VARIABLE:
@@ -2419,6 +2429,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
24192429
GDScriptParser::DataType base_type = p_context.current_class->base_type;
24202430
while (base_type.is_set()) {
24212431
switch (base_type.kind) {
2432+
case GDScriptParser::DataType::TRAIT:
24222433
case GDScriptParser::DataType::CLASS:
24232434
if (base_type.class_type->has_function(p_context.current_function->identifier->name)) {
24242435
GDScriptParser::FunctionNode *parent_function = base_type.class_type->get_member(p_context.current_function->identifier->name).function;
@@ -2530,6 +2541,7 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
25302541
bool is_static = base_type.is_meta_type;
25312542
while (base_type.is_set()) {
25322543
switch (base_type.kind) {
2544+
case GDScriptParser::DataType::TRAIT:
25332545
case GDScriptParser::DataType::CLASS:
25342546
if (base_type.class_type->has_member(p_identifier)) {
25352547
const GDScriptParser::ClassNode::Member &member = base_type.class_type->get_member(p_identifier);
@@ -2590,6 +2602,12 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
25902602
}
25912603
r_type = _callable_type_from_method_info(member.function->info);
25922604
return true;
2605+
case GDScriptParser::ClassNode::Member::TRAIT:
2606+
r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
2607+
r_type.type.kind = GDScriptParser::DataType::TRAIT;
2608+
r_type.type.class_type = member.m_class;
2609+
r_type.type.is_meta_type = true;
2610+
return true;
25932611
case GDScriptParser::ClassNode::Member::CLASS:
25942612
r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
25952613
r_type.type.kind = GDScriptParser::DataType::CLASS;
@@ -2788,6 +2806,7 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex
27882806

27892807
while (base_type.is_set() && !base_type.is_variant()) {
27902808
switch (base_type.kind) {
2809+
case GDScriptParser::DataType::TRAIT:
27912810
case GDScriptParser::DataType::CLASS:
27922811
if (base_type.class_type->has_function(p_method)) {
27932812
GDScriptParser::FunctionNode *method = base_type.class_type->get_member(p_method).function;
@@ -2941,6 +2960,7 @@ static void _list_call_arguments(GDScriptParser::CompletionContext &p_context, c
29412960

29422961
while (base_type.is_set() && !base_type.is_variant()) {
29432962
switch (base_type.kind) {
2963+
case GDScriptParser::DataType::TRAIT:
29442964
case GDScriptParser::DataType::CLASS: {
29452965
if (base_type.is_meta_type && method == SNAME("new")) {
29462966
const GDScriptParser::ClassNode *current = base_type.class_type;
@@ -3085,6 +3105,7 @@ static void _list_call_arguments(GDScriptParser::CompletionContext &p_context, c
30853105
n++;
30863106
}
30873107
} break;
3108+
case GDScriptParser::DataType::TRAIT:
30883109
case GDScriptParser::DataType::CLASS: {
30893110
GDScriptParser::ClassNode *clss = tweened_object->datatype.class_type;
30903111
native_type = clss->base_type.native_type;
@@ -3510,7 +3531,30 @@ ::Error GDScriptLanguage::complete_code(const String &p_code, const String &p_pa
35103531
}
35113532
} break;
35123533
case GDScriptParser::COMPLETION_INHERIT_TYPE: {
3513-
_list_available_types(true, completion_context, options);
3534+
_list_available_types(true, false, completion_context, options);
3535+
r_forced = true;
3536+
} break;
3537+
case GDScriptParser::COMPLETION_USES_TYPE: {
3538+
const GDScriptParser::ClassNode *current = completion_context.current_class;
3539+
for (const GDScriptParser::ClassNode::Member &member : current->members) {
3540+
switch (member.type) {
3541+
case GDScriptParser::ClassNode::Member::TRAIT:
3542+
case GDScriptParser::ClassNode::Member::CLASS:
3543+
if (member.m_class && member.m_class->identifier) {
3544+
ScriptLanguage::CodeCompletionOption option(member.m_class->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_LOCAL);
3545+
options.insert(option.display, option);
3546+
}
3547+
break;
3548+
default:
3549+
break;
3550+
}
3551+
}
3552+
LocalVector<StringName> global_classes;
3553+
ScriptServer::get_global_class_list(global_classes);
3554+
for (const StringName &E : global_classes) {
3555+
ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE);
3556+
options.insert(option.display, option);
3557+
}
35143558
r_forced = true;
35153559
} break;
35163560
case GDScriptParser::COMPLETION_TYPE_NAME_OR_VOID: {
@@ -3519,11 +3563,11 @@ ::Error GDScriptLanguage::complete_code(const String &p_code, const String &p_pa
35193563
}
35203564
[[fallthrough]];
35213565
case GDScriptParser::COMPLETION_TYPE_NAME: {
3522-
_list_available_types(false, completion_context, options);
3566+
_list_available_types(false, true, completion_context, options);
35233567
r_forced = true;
35243568
} break;
35253569
case GDScriptParser::COMPLETION_PROPERTY_DECLARATION_OR_TYPE: {
3526-
_list_available_types(false, completion_context, options);
3570+
_list_available_types(false, false, completion_context, options);
35273571
ScriptLanguage::CodeCompletionOption get("get", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
35283572
options.insert(get.display, get);
35293573
ScriptLanguage::CodeCompletionOption set("set", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
@@ -3933,6 +3977,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
39333977

39343978
while (true) {
39353979
switch (base_type.kind) {
3980+
case GDScriptParser::DataType::TRAIT:
39363981
case GDScriptParser::DataType::CLASS: {
39373982
ERR_FAIL_NULL_V(base_type.class_type, ERR_BUG);
39383983

@@ -3952,6 +3997,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
39523997
case GDScriptParser::ClassNode::Member::UNDEFINED:
39533998
case GDScriptParser::ClassNode::Member::GROUP:
39543999
return ERR_BUG;
4000+
case GDScriptParser::ClassNode::Member::TRAIT:
39554001
case GDScriptParser::ClassNode::Member::CLASS: {
39564002
String doc_type_name;
39574003
String doc_enum_name;
@@ -4580,7 +4626,7 @@ ::Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symb
45804626
}
45814627
prev = E;
45824628
}
4583-
if (base_type.kind != GDScriptParser::DataType::CLASS) {
4629+
if (base_type.kind != GDScriptParser::DataType::CLASS && base_type.kind != GDScriptParser::DataType::TRAIT) {
45844630
GDScriptCompletionIdentifier base;
45854631
if (!_guess_expression_type(context, prev, base)) {
45864632
break;

0 commit comments

Comments
 (0)