Skip to content

Commit c5d4dac

Browse files
committed
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
1 parent 09ea7bc commit c5d4dac

File tree

6 files changed

+447
-0
lines changed

6 files changed

+447
-0
lines changed

doc/classes/ProjectSettings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,9 @@
510510
<member name="debug/gdscript/warnings/constant_used_as_function" type="int" setter="" getter="" default="1" deprecated="This warning is never produced. Instead, an error is generated if the expression type is known at compile time.">
511511
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a constant is used as a function.
512512
</member>
513+
<member name="debug/gdscript/warnings/deprecated_identifier" type="int" setter="" getter="" default="1">
514+
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.
515+
</member>
513516
<member name="debug/gdscript/warnings/deprecated_keyword" type="int" setter="" getter="" default="1">
514517
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when deprecated keywords are used.
515518
[b]Note:[/b] There are currently no deprecated keywords, so this warning is never produced.

modules/gdscript/gdscript_analyzer.cpp

Lines changed: 232 additions & 0 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/editor_help.h"
49+
#endif
50+
4751
#if defined(TOOLS_ENABLED) && !defined(DISABLE_DEPRECATED)
4852
#define SUGGEST_GODOT4_RENAMES
4953
#include "editor/renames_map_3_to_4.h"
@@ -1993,6 +1997,57 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
19931997
type = specified_type;
19941998
}
19951999

2000+
#if TOOLS_ENABLED
2001+
DocTools *dd = EditorHelp::get_doc_data();
2002+
bool is_deprecated = false;
2003+
String new_value_type = "value";
2004+
String value_name = type.native_type;
2005+
switch (type.kind) {
2006+
case GDScriptParser::DataType::Kind::BUILTIN: // No built-in datatypes are deprecated.
2007+
break;
2008+
case GDScriptParser::DataType::Kind::NATIVE:
2009+
is_deprecated = dd && dd->class_list.has(type.native_type) && dd->class_list[type.native_type].is_deprecated;
2010+
new_value_type = "class";
2011+
break;
2012+
case GDScriptParser::DataType::Kind::SCRIPT: {
2013+
StringName class_name = type.script_type->get_doc_class_name();
2014+
is_deprecated = dd && dd->class_list.has(class_name) && dd->class_list[class_name].is_deprecated;
2015+
new_value_type = "class";
2016+
break;
2017+
}
2018+
case GDScriptParser::DataType::Kind::CLASS:
2019+
is_deprecated = type.class_type->doc_data.is_deprecated;
2020+
new_value_type = "class";
2021+
2022+
// TODO: Not recognizing the autoload name as a class type
2023+
// var my_var: MyAutoload # <--- not getting this name
2024+
if (type.class_type && type.class_type->identifier) {
2025+
value_name = type.class_type->identifier->name;
2026+
} else {
2027+
value_name = "";
2028+
}
2029+
break;
2030+
case GDScriptParser::DataType::Kind::ENUM: {
2031+
StringName enum_type = type.enum_type; // Something like MyEnum.
2032+
GDScriptParser::ClassNode *class_type = type.class_type;
2033+
StringName class_name = (class_type && class_type->identifier) ? class_type->identifier->name : "@GlobalScope";
2034+
if (dd && dd->class_list.has(class_name)) {
2035+
DocData::ClassDoc class_doc = dd->class_list[class_name];
2036+
if (class_doc.enums.has(enum_type)) {
2037+
is_deprecated = class_doc.enums[enum_type].is_deprecated;
2038+
}
2039+
}
2040+
new_value_type = "enum";
2041+
break;
2042+
}
2043+
default:
2044+
break;
2045+
}
2046+
if (is_deprecated) {
2047+
parser->push_warning(p_assignable, GDScriptWarning::DEPRECATED_IDENTIFIER, new_value_type, value_name);
2048+
}
2049+
#endif
2050+
19962051
if (p_assignable->initializer != nullptr) {
19972052
reduce_expression(p_assignable->initializer);
19982053

@@ -3998,6 +4053,38 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
39984053
StringName name = p_identifier->name;
39994054

40004055
if (base.kind == GDScriptParser::DataType::ENUM) {
4056+
#if TOOLS_ENABLED
4057+
StringName class_name;
4058+
DocTools *dd = EditorHelp::get_doc_data();
4059+
// TODO: Is this a proper way of detecting it's from a native class?
4060+
// e.g., "AnimationPlayer.AnimationProcessCallback"
4061+
// User-defined enums also seem to have a "native_type" so we can't
4062+
// detect actual native classes solely based on whether or not that
4063+
// string is defined.
4064+
if (base.native_type && !base.class_type) {
4065+
class_name = String(base.native_type).get_slicec('.', 0);
4066+
}
4067+
4068+
else if (base.class_type && base.class_type->identifier && base.class_type->identifier->name) {
4069+
class_name = base.class_type->identifier->name;
4070+
// It's an inner class, so we need to get the outer class's name
4071+
// as well to construct its full name as found in the doc data.
4072+
if (base.class_type->outer != nullptr && base.class_type->outer->identifier != nullptr) {
4073+
class_name = String(base.class_type->outer->identifier->name) + "." + class_name;
4074+
}
4075+
}
4076+
4077+
if (dd && dd->class_list.has(class_name)) {
4078+
for (const DocData::ConstantDoc &doc : dd->class_list[class_name].constants) {
4079+
if (doc.enumeration == base.enum_type && doc.name == name) {
4080+
if (doc.is_deprecated) {
4081+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "enum value", vformat("%s.%s", base.enum_type, doc.name));
4082+
}
4083+
break;
4084+
}
4085+
}
4086+
}
4087+
#endif
40014088
if (base.is_meta_type) {
40024089
if (base.enum_values.has(name)) {
40034090
p_identifier->set_datatype(type_from_metatype(base));
@@ -4143,6 +4230,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
41434230
p_identifier->reduced_value = member.constant->initializer->reduced_value;
41444231
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
41454232
p_identifier->constant_source = member.constant;
4233+
#if TOOLS_ENABLED
4234+
if (member.constant->doc_data.is_deprecated) {
4235+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "constant", name);
4236+
}
4237+
#endif
41464238
return;
41474239
}
41484240

@@ -4151,6 +4243,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
41514243
p_identifier->is_constant = true;
41524244
p_identifier->reduced_value = member.enum_value.value;
41534245
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
4246+
#if TOOLS_ENABLED
4247+
if (member.enum_value.doc_data.is_deprecated) {
4248+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "enum value", name);
4249+
}
4250+
#endif
41544251
return;
41554252
}
41564253

@@ -4159,6 +4256,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
41594256
p_identifier->is_constant = true;
41604257
p_identifier->reduced_value = member.m_enum->dictionary;
41614258
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
4259+
#if TOOLS_ENABLED
4260+
if (member.m_enum->doc_data.is_deprecated) {
4261+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "enum", name);
4262+
}
4263+
#endif
41624264
return;
41634265
}
41644266

@@ -4168,6 +4270,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
41684270
p_identifier->source = member.variable->is_static ? GDScriptParser::IdentifierNode::STATIC_VARIABLE : GDScriptParser::IdentifierNode::MEMBER_VARIABLE;
41694271
p_identifier->variable_source = member.variable;
41704272
member.variable->usages += 1;
4273+
#if TOOLS_ENABLED
4274+
if (member.variable->doc_data.is_deprecated) {
4275+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "property or variable", name);
4276+
}
4277+
#endif
41714278
return;
41724279
}
41734280
} break;
@@ -4178,6 +4285,12 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
41784285
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_SIGNAL;
41794286
p_identifier->signal_source = member.signal;
41804287
member.signal->usages += 1;
4288+
4289+
#if TOOLS_ENABLED
4290+
if (member.signal->doc_data.is_deprecated) {
4291+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "signal", name);
4292+
}
4293+
#endif
41814294
return;
41824295
}
41834296
} break;
@@ -4195,6 +4308,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
41954308
case GDScriptParser::ClassNode::Member::CLASS: {
41964309
reduce_identifier_from_base_set_class(p_identifier, member.get_datatype());
41974310
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CLASS;
4311+
#ifdef TOOLS_ENABLED
4312+
if (script_class->get_member(name).m_class->doc_data.is_deprecated) {
4313+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "class", name);
4314+
}
4315+
#endif
41984316
return;
41994317
}
42004318

@@ -4284,6 +4402,19 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
42844402
p_identifier->set_datatype(type_from_property(getter->get_return_info(), false, !has_setter));
42854403
p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
42864404
}
4405+
#if TOOLS_ENABLED
4406+
DocTools *dd = EditorHelp::get_doc_data();
4407+
if (dd && dd->class_list.has(native)) {
4408+
for (const DocData::PropertyDoc &doc : dd->class_list[native].properties) {
4409+
if (doc.name == name) {
4410+
if (doc.is_deprecated) {
4411+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "property", name);
4412+
}
4413+
break;
4414+
}
4415+
}
4416+
}
4417+
#endif
42874418
return;
42884419
}
42894420
if (ClassDB::get_method_info(native, name, &method_info)) {
@@ -4296,11 +4427,33 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
42964427
// Signal is a type too.
42974428
p_identifier->set_datatype(make_signal_type(method_info));
42984429
p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
4430+
#if TOOLS_ENABLED
4431+
DocTools *dd = EditorHelp::get_doc_data();
4432+
if (dd && dd->class_list.has(native)) {
4433+
for (const DocData::MethodDoc &doc : dd->class_list[native].signals) {
4434+
if (doc.name == name) {
4435+
if (doc.is_deprecated) {
4436+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "signal", name);
4437+
}
4438+
break;
4439+
}
4440+
}
4441+
}
4442+
#endif
42994443
return;
43004444
}
43014445
if (ClassDB::has_enum(native, name)) {
43024446
p_identifier->set_datatype(make_native_enum_type(name, native));
43034447
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
4448+
#if TOOLS_ENABLED
4449+
DocTools *dd = EditorHelp::get_doc_data();
4450+
if (dd && dd->class_list.has(native) && dd->class_list[native].enums.has(name)) {
4451+
DocData::EnumDoc doc = dd->class_list[native].enums[name];
4452+
if (doc.is_deprecated) {
4453+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "enum", name);
4454+
}
4455+
}
4456+
#endif
43044457
return;
43054458
}
43064459
bool valid = false;
@@ -4311,6 +4464,20 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
43114464
p_identifier->reduced_value = int_constant;
43124465
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
43134466

4467+
#if TOOLS_ENABLED
4468+
DocTools *dd = EditorHelp::get_doc_data();
4469+
if (dd && dd->class_list.has(native)) {
4470+
for (const DocData::ConstantDoc &doc : dd->class_list[native].constants) {
4471+
if (doc.name == name) {
4472+
if (doc.is_deprecated) {
4473+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "constant", name);
4474+
}
4475+
break;
4476+
}
4477+
}
4478+
}
4479+
#endif
4480+
43144481
// Check whether this constant, which exists, belongs to an enum
43154482
StringName enum_name = ClassDB::get_integer_constant_enum(native, name);
43164483
if (enum_name != StringName()) {
@@ -4363,6 +4530,12 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
43634530
// TODO: Constant should have a value on the node itself.
43644531
p_identifier->reduced_value = p_identifier->constant_source->initializer->reduced_value;
43654532
found_source = true;
4533+
4534+
#ifdef TOOLS_ENABLED
4535+
if (p_identifier->constant_source->doc_data.is_deprecated) {
4536+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "constant", p_identifier->name);
4537+
}
4538+
#endif
43664539
break;
43674540
case GDScriptParser::IdentifierNode::MEMBER_SIGNAL:
43684541
p_identifier->signal_source->usages++;
@@ -4382,6 +4555,11 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
43824555
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)) {
43834556
parser->push_warning(p_identifier, GDScriptWarning::UNASSIGNED_VARIABLE, p_identifier->name);
43844557
}
4558+
#endif
4559+
#ifdef TOOLS_ENABLED
4560+
if (p_identifier->variable_source->doc_data.is_deprecated) {
4561+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "property or variable", p_identifier->name);
4562+
}
43854563
#endif
43864564
break;
43874565
case GDScriptParser::IdentifierNode::LOCAL_ITERATOR:
@@ -4499,12 +4677,28 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
44994677
}
45004678

45014679
if (class_exists(name)) {
4680+
#if TOOLS_ENABLED
4681+
DocTools *dd = EditorHelp::get_doc_data();
4682+
if (dd && dd->class_list.has(name)) {
4683+
if (dd->class_list[name].is_deprecated) {
4684+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "class", name);
4685+
}
4686+
}
4687+
#endif
45024688
p_identifier->set_datatype(make_native_meta_type(name));
45034689
return;
45044690
}
45054691

45064692
if (ScriptServer::is_global_class(name)) {
45074693
p_identifier->set_datatype(make_global_class_meta_type(name, p_identifier));
4694+
#if TOOLS_ENABLED
4695+
DocTools *dd = EditorHelp::get_doc_data();
4696+
if (dd && dd->class_list.has(name)) {
4697+
if (dd->class_list[name].is_deprecated) {
4698+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "class", name);
4699+
}
4700+
}
4701+
#endif
45084702
return;
45094703
}
45104704

@@ -4545,6 +4739,14 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
45454739
}
45464740
}
45474741
}
4742+
#if TOOLS_ENABLED
4743+
DocTools *dd = EditorHelp::get_doc_data();
4744+
if (dd && dd->class_list.has(name)) {
4745+
if (dd->class_list[name].is_deprecated) {
4746+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "class", name);
4747+
}
4748+
}
4749+
#endif
45484750
result.is_constant = true;
45494751
p_identifier->set_datatype(result);
45504752
return;
@@ -4562,6 +4764,17 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
45624764
}
45634765
p_identifier->is_constant = true;
45644766
p_identifier->reduced_value = value;
4767+
4768+
#if TOOLS_ENABLED
4769+
DocTools *dd = EditorHelp::get_doc_data();
4770+
if (dd && dd->class_list.has("@GlobalScope")) {
4771+
for (const DocData::ConstantDoc &cd : dd->class_list["@GlobalScope"].constants) {
4772+
if (cd.name == name && cd.is_deprecated) {
4773+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "constant", name);
4774+
}
4775+
}
4776+
}
4777+
#endif
45654778
return;
45664779
}
45674780

@@ -5755,6 +5968,12 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
57555968
r_return_type.is_meta_type = false;
57565969
r_return_type.is_coroutine = found_function->is_coroutine;
57575970

5971+
// For user-defined methods.
5972+
#ifdef TOOLS_ENABLED
5973+
if (found_function->doc_data.is_deprecated) {
5974+
parser->push_warning(p_source, GDScriptWarning::DEPRECATED_IDENTIFIER, "function", found_function->identifier->name);
5975+
}
5976+
#endif
57585977
return true;
57595978
}
57605979

@@ -5798,6 +6017,19 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
57986017
if (native_method && r_native_class) {
57996018
*r_native_class = native_method->get_instance_class();
58006019
}
6020+
#endif
6021+
#ifdef TOOLS_ENABLED
6022+
DocTools *dd = EditorHelp::get_doc_data();
6023+
if (dd) {
6024+
Vector<DocData::MethodDoc> method_list = dd->class_list[base_native].methods;
6025+
for (int i = 0; i < method_list.size(); i++) {
6026+
if (method_list[i].name == function_name && method_list[i].is_deprecated) {
6027+
parser->push_warning(p_source, GDScriptWarning::DEPRECATED_IDENTIFIER, "function", function_name);
6028+
break;
6029+
}
6030+
}
6031+
}
6032+
58016033
#endif
58026034
return valid;
58036035
}

0 commit comments

Comments
 (0)