Skip to content

Commit 677009d

Browse files
Warn user when user-marked deprecated method is used
Note to self about methods for other non-user classes Display warning for deprecated methods on built-in classes (like AnimationPlayer) Protect from doc data being null Push warning for user-defined signal marked as `@deprecated` Recognize user-defined constant marked as `@deprecated` Recognize user-defined enums and values in anonymous enums marked as `@deprecated` Recognize user-defined property marked as `@deprecated` Recognize local variable marked as `@deprecated` Recognize built-in class (such as for constructors or static methods) marked as deprecated Recognize global autoload marked as `@deprecated` Recognize constant in `@GlobalScope` marked as deprecated Recognize global script class(?) marked as deprecated Recognize types from type hints in assignment statements marked as deprecated Recognize signal from native class marked as deprecated Recognize property from native class marked as deprecated Recognize constant from native class marked as deprecated Recognize enum from native class marked as deprecated Recognize individual value in enum marked as deprecated Recognize local constant marked as deprecated Recognize inner class in expression marked as deprecated Recognize individual value in user-defined named enum in inner class marked as deprecated Fix recognition of individual value from native enum with enum name marked as deprecated Remove some duplicate code Update modules/gdscript/gdscript_warning.cpp Co-authored-by: Micky <[email protected]> Provide context for warning, including type and name, when available Get rid of `auto` usages Fix some crashes when trying to access a null identifier Add test for warning Fix variable redeclaration Update test to use classes that don't crash Try removing AnimationPlayer case from unit test Add description in documentation for DEPRECATED_IDENTIFIER Replace DEBUG_ENABLED with TOOLS_ENABLED Add explanation for test case being commented out Update doc/classes/ProjectSettings.xml Co-authored-by: Micky <[email protected]> Update test Apply suggestions from code review Co-authored-by: A Thousand Ships <[email protected]> # Conflicts: # modules/gdscript/gdscript_analyzer.cpp Update modules/gdscript/gdscript_analyzer.cpp Co-authored-by: A Thousand Ships <[email protected]>
1 parent 8d8041b commit 677009d

File tree

6 files changed

+449
-2
lines changed

6 files changed

+449
-2
lines changed

doc/classes/ProjectSettings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,9 @@
527527
<member name="debug/gdscript/warnings/confusable_local_usage" type="int" setter="" getter="" default="1">
528528
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an identifier that will be shadowed below in the block is used.
529529
</member>
530+
<member name="debug/gdscript/warnings/deprecated_identifier" type="int" setter="" getter="" default="1">
531+
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when deprecated identifiers are used. These include any deprecated class, enum, constant, signal, property, and method.
532+
</member>
530533
<member name="debug/gdscript/warnings/deprecated_keyword" type="int" setter="" getter="" default="1">
531534
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when deprecated keywords are used.
532535
[b]Note:[/b] There are currently no deprecated keywords, so this warning is never produced.

modules/gdscript/gdscript_analyzer.cpp

Lines changed: 234 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@
4444
#include "core/templates/hash_map.h"
4545
#include "scene/main/node.h"
4646

47+
#ifdef TOOLS_ENABLED
48+
#include "editor/doc/editor_help.h"
49+
#endif
50+
4751
#if defined(TOOLS_ENABLED) && !defined(DISABLE_DEPRECATED)
4852
#define SUGGEST_GODOT4_RENAMES
4953
#include "editor/project_upgrade/renames_map_3_to_4.h"
@@ -2088,6 +2092,57 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
20882092
type = specified_type;
20892093
}
20902094

2095+
#ifdef TOOLS_ENABLED
2096+
DocTools *dd = EditorHelp::get_doc_data();
2097+
bool is_deprecated = false;
2098+
String new_value_type = "value";
2099+
String value_name = type.native_type;
2100+
switch (type.kind) {
2101+
case GDScriptParser::DataType::Kind::BUILTIN: // No built-in datatypes are deprecated.
2102+
break;
2103+
case GDScriptParser::DataType::Kind::NATIVE:
2104+
is_deprecated = dd && dd->class_list.has(type.native_type) && dd->class_list[type.native_type].is_deprecated;
2105+
new_value_type = "class";
2106+
break;
2107+
case GDScriptParser::DataType::Kind::SCRIPT: {
2108+
StringName class_name = type.script_type->get_doc_class_name();
2109+
is_deprecated = dd && dd->class_list.has(class_name) && dd->class_list[class_name].is_deprecated;
2110+
new_value_type = "class";
2111+
break;
2112+
}
2113+
case GDScriptParser::DataType::Kind::CLASS:
2114+
is_deprecated = type.class_type->doc_data.is_deprecated;
2115+
new_value_type = "class";
2116+
2117+
// TODO: Not recognizing the autoload name as a class type
2118+
// var my_var: MyAutoload # <--- not getting this name
2119+
if (type.class_type && type.class_type->identifier) {
2120+
value_name = type.class_type->identifier->name;
2121+
} else {
2122+
value_name = "";
2123+
}
2124+
break;
2125+
case GDScriptParser::DataType::Kind::ENUM: {
2126+
StringName enum_type = type.enum_type; // Something like MyEnum.
2127+
GDScriptParser::ClassNode *class_type = type.class_type;
2128+
StringName class_name = (class_type && class_type->identifier) ? class_type->identifier->name : "@GlobalScope";
2129+
if (dd && dd->class_list.has(class_name)) {
2130+
DocData::ClassDoc class_doc = dd->class_list[class_name];
2131+
if (class_doc.enums.has(enum_type)) {
2132+
is_deprecated = class_doc.enums[enum_type].is_deprecated;
2133+
}
2134+
}
2135+
new_value_type = "enum";
2136+
break;
2137+
}
2138+
default:
2139+
break;
2140+
}
2141+
if (is_deprecated) {
2142+
parser->push_warning(p_assignable, GDScriptWarning::DEPRECATED_IDENTIFIER, new_value_type, value_name);
2143+
}
2144+
#endif
2145+
20912146
if (p_assignable->initializer != nullptr) {
20922147
reduce_expression(p_assignable->initializer);
20932148

@@ -4040,6 +4095,38 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
40404095
StringName name = p_identifier->name;
40414096

40424097
if (base.kind == GDScriptParser::DataType::ENUM) {
4098+
#ifdef TOOLS_ENABLED
4099+
StringName class_name;
4100+
DocTools *dd = EditorHelp::get_doc_data();
4101+
// TODO: Is this a proper way of detecting it's from a native class?
4102+
// e.g., "AnimationPlayer.AnimationProcessCallback"
4103+
// User-defined enums also seem to have a "native_type" so we can't
4104+
// detect actual native classes solely based on whether or not that
4105+
// string is defined.
4106+
if (base.native_type && !base.class_type) {
4107+
class_name = String(base.native_type).get_slicec('.', 0);
4108+
}
4109+
4110+
else if (base.class_type && base.class_type->identifier && base.class_type->identifier->name) {
4111+
class_name = base.class_type->identifier->name;
4112+
// It's an inner class, so we need to get the outer class's name
4113+
// as well to construct its full name as found in the doc data.
4114+
if (base.class_type->outer != nullptr && base.class_type->outer->identifier != nullptr) {
4115+
class_name = String(base.class_type->outer->identifier->name) + "." + class_name;
4116+
}
4117+
}
4118+
4119+
if (dd && dd->class_list.has(class_name)) {
4120+
for (const DocData::ConstantDoc &doc : dd->class_list[class_name].constants) {
4121+
if (doc.enumeration == base.enum_type && doc.name == name) {
4122+
if (doc.is_deprecated) {
4123+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "enum value", vformat("%s.%s", base.enum_type, doc.name));
4124+
}
4125+
break;
4126+
}
4127+
}
4128+
}
4129+
#endif
40434130
if (base.is_meta_type) {
40444131
if (base.enum_values.has(name)) {
40454132
p_identifier->set_datatype(type_from_metatype(base));
@@ -4185,6 +4272,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
41854272
p_identifier->reduced_value = member.constant->initializer->reduced_value;
41864273
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
41874274
p_identifier->constant_source = member.constant;
4275+
#ifdef TOOLS_ENABLED
4276+
if (member.constant->doc_data.is_deprecated) {
4277+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "constant", name);
4278+
}
4279+
#endif
41884280
return;
41894281
}
41904282

@@ -4193,6 +4285,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
41934285
p_identifier->is_constant = true;
41944286
p_identifier->reduced_value = member.enum_value.value;
41954287
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
4288+
#ifdef TOOLS_ENABLED
4289+
if (member.enum_value.doc_data.is_deprecated) {
4290+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "enum value", name);
4291+
}
4292+
#endif
41964293
return;
41974294
}
41984295

@@ -4201,6 +4298,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
42014298
p_identifier->is_constant = true;
42024299
p_identifier->reduced_value = member.m_enum->dictionary;
42034300
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
4301+
#ifdef TOOLS_ENABLED
4302+
if (member.m_enum->doc_data.is_deprecated) {
4303+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "enum", name);
4304+
}
4305+
#endif
42044306
return;
42054307
}
42064308

@@ -4210,6 +4312,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
42104312
p_identifier->source = member.variable->is_static ? GDScriptParser::IdentifierNode::STATIC_VARIABLE : GDScriptParser::IdentifierNode::MEMBER_VARIABLE;
42114313
p_identifier->variable_source = member.variable;
42124314
member.variable->usages += 1;
4315+
#ifdef TOOLS_ENABLED
4316+
if (member.variable->doc_data.is_deprecated) {
4317+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "property or variable", name);
4318+
}
4319+
#endif
42134320
return;
42144321
}
42154322
} break;
@@ -4220,6 +4327,12 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
42204327
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_SIGNAL;
42214328
p_identifier->signal_source = member.signal;
42224329
member.signal->usages += 1;
4330+
4331+
#ifdef TOOLS_ENABLED
4332+
if (member.signal->doc_data.is_deprecated) {
4333+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "signal", name);
4334+
}
4335+
#endif
42234336
return;
42244337
}
42254338
} break;
@@ -4237,6 +4350,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
42374350
case GDScriptParser::ClassNode::Member::CLASS: {
42384351
reduce_identifier_from_base_set_class(p_identifier, member.get_datatype());
42394352
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CLASS;
4353+
#ifdef TOOLS_ENABLED
4354+
if (script_class->get_member(name).m_class->doc_data.is_deprecated) {
4355+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "class", name);
4356+
}
4357+
#endif
42404358
return;
42414359
}
42424360

@@ -4326,6 +4444,19 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
43264444
p_identifier->set_datatype(type_from_property(getter->get_return_info(), false, !has_setter));
43274445
p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
43284446
}
4447+
#ifdef TOOLS_ENABLED
4448+
DocTools *dd = EditorHelp::get_doc_data();
4449+
if (dd && dd->class_list.has(native)) {
4450+
for (const DocData::PropertyDoc &doc : dd->class_list[native].properties) {
4451+
if (doc.name == name) {
4452+
if (doc.is_deprecated) {
4453+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "property", name);
4454+
}
4455+
break;
4456+
}
4457+
}
4458+
}
4459+
#endif
43294460
return;
43304461
}
43314462
if (ClassDB::get_method_info(native, name, &method_info)) {
@@ -4338,11 +4469,33 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
43384469
// Signal is a type too.
43394470
p_identifier->set_datatype(make_signal_type(method_info));
43404471
p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
4472+
#ifdef TOOLS_ENABLED
4473+
DocTools *dd = EditorHelp::get_doc_data();
4474+
if (dd && dd->class_list.has(native)) {
4475+
for (const DocData::MethodDoc &doc : dd->class_list[native].signals) {
4476+
if (doc.name == name) {
4477+
if (doc.is_deprecated) {
4478+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "signal", name);
4479+
}
4480+
break;
4481+
}
4482+
}
4483+
}
4484+
#endif
43414485
return;
43424486
}
43434487
if (ClassDB::has_enum(native, name)) {
43444488
p_identifier->set_datatype(make_native_enum_type(name, native));
43454489
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
4490+
#ifdef TOOLS_ENABLED
4491+
DocTools *dd = EditorHelp::get_doc_data();
4492+
if (dd && dd->class_list.has(native) && dd->class_list[native].enums.has(name)) {
4493+
DocData::EnumDoc doc = dd->class_list[native].enums[name];
4494+
if (doc.is_deprecated) {
4495+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "enum", name);
4496+
}
4497+
}
4498+
#endif
43464499
return;
43474500
}
43484501
bool valid = false;
@@ -4353,6 +4506,20 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
43534506
p_identifier->reduced_value = int_constant;
43544507
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
43554508

4509+
#ifdef TOOLS_ENABLED
4510+
DocTools *dd = EditorHelp::get_doc_data();
4511+
if (dd && dd->class_list.has(native)) {
4512+
for (const DocData::ConstantDoc &doc : dd->class_list[native].constants) {
4513+
if (doc.name == name) {
4514+
if (doc.is_deprecated) {
4515+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "constant", name);
4516+
}
4517+
break;
4518+
}
4519+
}
4520+
}
4521+
#endif
4522+
43564523
// Check whether this constant, which exists, belongs to an enum
43574524
StringName enum_name = ClassDB::get_integer_constant_enum(native, name);
43584525
if (enum_name != StringName()) {
@@ -4405,6 +4572,12 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
44054572
// TODO: Constant should have a value on the node itself.
44064573
p_identifier->reduced_value = p_identifier->constant_source->initializer->reduced_value;
44074574
found_source = true;
4575+
4576+
#ifdef TOOLS_ENABLED
4577+
if (p_identifier->constant_source->doc_data.is_deprecated) {
4578+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "constant", p_identifier->name);
4579+
}
4580+
#endif
44084581
break;
44094582
case GDScriptParser::IdentifierNode::MEMBER_SIGNAL:
44104583
p_identifier->signal_source->usages++;
@@ -4424,7 +4597,12 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
44244597
if (p_identifier->variable_source && p_identifier->variable_source->assignments == 0 && !(p_identifier->get_datatype().is_hard_type() && p_identifier->get_datatype().kind == GDScriptParser::DataType::BUILTIN)) {
44254598
parser->push_warning(p_identifier, GDScriptWarning::UNASSIGNED_VARIABLE, p_identifier->name);
44264599
}
4427-
#endif // DEBUG_ENABLED
4600+
#endif
4601+
#ifdef TOOLS_ENABLED
4602+
if (p_identifier->variable_source->doc_data.is_deprecated) {
4603+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "property or variable", p_identifier->name);
4604+
}
4605+
#endif
44284606
break;
44294607
case GDScriptParser::IdentifierNode::LOCAL_ITERATOR:
44304608
p_identifier->set_datatype(p_identifier->bind_source->get_datatype());
@@ -4543,13 +4721,29 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
45434721
}
45444722

45454723
if (class_exists(name)) {
4724+
#ifdef TOOLS_ENABLED
4725+
DocTools *dd = EditorHelp::get_doc_data();
4726+
if (dd && dd->class_list.has(name)) {
4727+
if (dd->class_list[name].is_deprecated) {
4728+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "class", name);
4729+
}
4730+
}
4731+
#endif
45464732
p_identifier->source = GDScriptParser::IdentifierNode::NATIVE_CLASS;
45474733
p_identifier->set_datatype(make_native_meta_type(name));
45484734
return;
45494735
}
45504736

45514737
if (ScriptServer::is_global_class(name)) {
45524738
p_identifier->set_datatype(make_global_class_meta_type(name, p_identifier));
4739+
#ifdef TOOLS_ENABLED
4740+
DocTools *dd = EditorHelp::get_doc_data();
4741+
if (dd && dd->class_list.has(name)) {
4742+
if (dd->class_list[name].is_deprecated) {
4743+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "class", name);
4744+
}
4745+
}
4746+
#endif
45534747
return;
45544748
}
45554749

@@ -4590,6 +4784,14 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
45904784
}
45914785
}
45924786
}
4787+
#ifdef TOOLS_ENABLED
4788+
DocTools *dd = EditorHelp::get_doc_data();
4789+
if (dd && dd->class_list.has(name)) {
4790+
if (dd->class_list[name].is_deprecated) {
4791+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "class", name);
4792+
}
4793+
}
4794+
#endif
45934795
result.is_constant = true;
45944796
p_identifier->set_datatype(result);
45954797
return;
@@ -4607,6 +4809,17 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
46074809
}
46084810
p_identifier->is_constant = true;
46094811
p_identifier->reduced_value = value;
4812+
4813+
#ifdef TOOLS_ENABLED
4814+
DocTools *dd = EditorHelp::get_doc_data();
4815+
if (dd && dd->class_list.has("@GlobalScope")) {
4816+
for (const DocData::ConstantDoc &cd : dd->class_list["@GlobalScope"].constants) {
4817+
if (cd.name == name && cd.is_deprecated) {
4818+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "constant", name);
4819+
}
4820+
}
4821+
}
4822+
#endif
46104823
return;
46114824
}
46124825

@@ -5858,6 +6071,12 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
58586071
r_return_type.is_meta_type = false;
58596072
r_return_type.is_coroutine = found_function->is_coroutine;
58606073

6074+
// For user-defined methods.
6075+
#ifdef TOOLS_ENABLED
6076+
if (found_function->doc_data.is_deprecated) {
6077+
parser->push_warning(p_source, GDScriptWarning::DEPRECATED_IDENTIFIER, "function", found_function->identifier->name);
6078+
}
6079+
#endif
58616080
return true;
58626081
}
58636082

@@ -5901,7 +6120,20 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
59016120
if (native_method && r_native_class) {
59026121
*r_native_class = native_method->get_instance_class();
59036122
}
5904-
#endif // DEBUG_ENABLED
6123+
#endif
6124+
#ifdef TOOLS_ENABLED
6125+
DocTools *dd = EditorHelp::get_doc_data();
6126+
if (dd) {
6127+
const Vector<DocData::MethodDoc> &method_list = dd->class_list[base_native].methods;
6128+
for (const DocData::MethodDoc &doc : method_list) {
6129+
if (doc.name == function_name && doc.is_deprecated) {
6130+
parser->push_warning(p_source, GDScriptWarning::DEPRECATED_IDENTIFIER, "function", function_name);
6131+
break;
6132+
}
6133+
}
6134+
}
6135+
6136+
#endif
59056137
return valid;
59066138
}
59076139

0 commit comments

Comments
 (0)