Skip to content

Commit da55f72

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]>
1 parent 1218a16 commit da55f72

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
@@ -530,6 +530,9 @@
530530
<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.">
531531
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.
532532
</member>
533+
<member name="debug/gdscript/warnings/deprecated_identifier" type="int" setter="" getter="" default="1">
534+
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.
535+
</member>
533536
<member name="debug/gdscript/warnings/deprecated_keyword" type="int" setter="" getter="" default="1">
534537
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when deprecated keywords are used.
535538
[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/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"
@@ -2072,6 +2076,57 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
20722076
type = specified_type;
20732077
}
20742078

2079+
#ifdef TOOLS_ENABLED
2080+
DocTools *dd = EditorHelp::get_doc_data();
2081+
bool is_deprecated = false;
2082+
String new_value_type = "value";
2083+
String value_name = type.native_type;
2084+
switch (type.kind) {
2085+
case GDScriptParser::DataType::Kind::BUILTIN: // No built-in datatypes are deprecated.
2086+
break;
2087+
case GDScriptParser::DataType::Kind::NATIVE:
2088+
is_deprecated = dd && dd->class_list.has(type.native_type) && dd->class_list[type.native_type].is_deprecated;
2089+
new_value_type = "class";
2090+
break;
2091+
case GDScriptParser::DataType::Kind::SCRIPT: {
2092+
StringName class_name = type.script_type->get_doc_class_name();
2093+
is_deprecated = dd && dd->class_list.has(class_name) && dd->class_list[class_name].is_deprecated;
2094+
new_value_type = "class";
2095+
break;
2096+
}
2097+
case GDScriptParser::DataType::Kind::CLASS:
2098+
is_deprecated = type.class_type->doc_data.is_deprecated;
2099+
new_value_type = "class";
2100+
2101+
// TODO: Not recognizing the autoload name as a class type
2102+
// var my_var: MyAutoload # <--- not getting this name
2103+
if (type.class_type && type.class_type->identifier) {
2104+
value_name = type.class_type->identifier->name;
2105+
} else {
2106+
value_name = "";
2107+
}
2108+
break;
2109+
case GDScriptParser::DataType::Kind::ENUM: {
2110+
StringName enum_type = type.enum_type; // Something like MyEnum.
2111+
GDScriptParser::ClassNode *class_type = type.class_type;
2112+
StringName class_name = (class_type && class_type->identifier) ? class_type->identifier->name : "@GlobalScope";
2113+
if (dd && dd->class_list.has(class_name)) {
2114+
DocData::ClassDoc class_doc = dd->class_list[class_name];
2115+
if (class_doc.enums.has(enum_type)) {
2116+
is_deprecated = class_doc.enums[enum_type].is_deprecated;
2117+
}
2118+
}
2119+
new_value_type = "enum";
2120+
break;
2121+
}
2122+
default:
2123+
break;
2124+
}
2125+
if (is_deprecated) {
2126+
parser->push_warning(p_assignable, GDScriptWarning::DEPRECATED_IDENTIFIER, new_value_type, value_name);
2127+
}
2128+
#endif
2129+
20752130
if (p_assignable->initializer != nullptr) {
20762131
reduce_expression(p_assignable->initializer);
20772132

@@ -4024,6 +4079,38 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
40244079
StringName name = p_identifier->name;
40254080

40264081
if (base.kind == GDScriptParser::DataType::ENUM) {
4082+
#ifdef TOOLS_ENABLED
4083+
StringName class_name;
4084+
DocTools *dd = EditorHelp::get_doc_data();
4085+
// TODO: Is this a proper way of detecting it's from a native class?
4086+
// e.g., "AnimationPlayer.AnimationProcessCallback"
4087+
// User-defined enums also seem to have a "native_type" so we can't
4088+
// detect actual native classes solely based on whether or not that
4089+
// string is defined.
4090+
if (base.native_type && !base.class_type) {
4091+
class_name = String(base.native_type).get_slicec('.', 0);
4092+
}
4093+
4094+
else if (base.class_type && base.class_type->identifier && base.class_type->identifier->name) {
4095+
class_name = base.class_type->identifier->name;
4096+
// It's an inner class, so we need to get the outer class's name
4097+
// as well to construct its full name as found in the doc data.
4098+
if (base.class_type->outer != nullptr && base.class_type->outer->identifier != nullptr) {
4099+
class_name = String(base.class_type->outer->identifier->name) + "." + class_name;
4100+
}
4101+
}
4102+
4103+
if (dd && dd->class_list.has(class_name)) {
4104+
for (const DocData::ConstantDoc &doc : dd->class_list[class_name].constants) {
4105+
if (doc.enumeration == base.enum_type && doc.name == name) {
4106+
if (doc.is_deprecated) {
4107+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "enum value", vformat("%s.%s", base.enum_type, doc.name));
4108+
}
4109+
break;
4110+
}
4111+
}
4112+
}
4113+
#endif
40274114
if (base.is_meta_type) {
40284115
if (base.enum_values.has(name)) {
40294116
p_identifier->set_datatype(type_from_metatype(base));
@@ -4169,6 +4256,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
41694256
p_identifier->reduced_value = member.constant->initializer->reduced_value;
41704257
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
41714258
p_identifier->constant_source = member.constant;
4259+
#ifdef TOOLS_ENABLED
4260+
if (member.constant->doc_data.is_deprecated) {
4261+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "constant", name);
4262+
}
4263+
#endif
41724264
return;
41734265
}
41744266

@@ -4177,6 +4269,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
41774269
p_identifier->is_constant = true;
41784270
p_identifier->reduced_value = member.enum_value.value;
41794271
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
4272+
#ifdef TOOLS_ENABLED
4273+
if (member.enum_value.doc_data.is_deprecated) {
4274+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "enum value", name);
4275+
}
4276+
#endif
41804277
return;
41814278
}
41824279

@@ -4185,6 +4282,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
41854282
p_identifier->is_constant = true;
41864283
p_identifier->reduced_value = member.m_enum->dictionary;
41874284
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
4285+
#ifdef TOOLS_ENABLED
4286+
if (member.m_enum->doc_data.is_deprecated) {
4287+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "enum", name);
4288+
}
4289+
#endif
41884290
return;
41894291
}
41904292

@@ -4194,6 +4296,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
41944296
p_identifier->source = member.variable->is_static ? GDScriptParser::IdentifierNode::STATIC_VARIABLE : GDScriptParser::IdentifierNode::MEMBER_VARIABLE;
41954297
p_identifier->variable_source = member.variable;
41964298
member.variable->usages += 1;
4299+
#ifdef TOOLS_ENABLED
4300+
if (member.variable->doc_data.is_deprecated) {
4301+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "property or variable", name);
4302+
}
4303+
#endif
41974304
return;
41984305
}
41994306
} break;
@@ -4204,6 +4311,12 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
42044311
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_SIGNAL;
42054312
p_identifier->signal_source = member.signal;
42064313
member.signal->usages += 1;
4314+
4315+
#ifdef TOOLS_ENABLED
4316+
if (member.signal->doc_data.is_deprecated) {
4317+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "signal", name);
4318+
}
4319+
#endif
42074320
return;
42084321
}
42094322
} break;
@@ -4221,6 +4334,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
42214334
case GDScriptParser::ClassNode::Member::CLASS: {
42224335
reduce_identifier_from_base_set_class(p_identifier, member.get_datatype());
42234336
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CLASS;
4337+
#ifdef TOOLS_ENABLED
4338+
if (script_class->get_member(name).m_class->doc_data.is_deprecated) {
4339+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "class", name);
4340+
}
4341+
#endif
42244342
return;
42254343
}
42264344

@@ -4310,6 +4428,19 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
43104428
p_identifier->set_datatype(type_from_property(getter->get_return_info(), false, !has_setter));
43114429
p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
43124430
}
4431+
#ifdef TOOLS_ENABLED
4432+
DocTools *dd = EditorHelp::get_doc_data();
4433+
if (dd && dd->class_list.has(native)) {
4434+
for (const DocData::PropertyDoc &doc : dd->class_list[native].properties) {
4435+
if (doc.name == name) {
4436+
if (doc.is_deprecated) {
4437+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "property", name);
4438+
}
4439+
break;
4440+
}
4441+
}
4442+
}
4443+
#endif
43134444
return;
43144445
}
43154446
if (ClassDB::get_method_info(native, name, &method_info)) {
@@ -4322,11 +4453,33 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
43224453
// Signal is a type too.
43234454
p_identifier->set_datatype(make_signal_type(method_info));
43244455
p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
4456+
#ifdef TOOLS_ENABLED
4457+
DocTools *dd = EditorHelp::get_doc_data();
4458+
if (dd && dd->class_list.has(native)) {
4459+
for (const DocData::MethodDoc &doc : dd->class_list[native].signals) {
4460+
if (doc.name == name) {
4461+
if (doc.is_deprecated) {
4462+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "signal", name);
4463+
}
4464+
break;
4465+
}
4466+
}
4467+
}
4468+
#endif
43254469
return;
43264470
}
43274471
if (ClassDB::has_enum(native, name)) {
43284472
p_identifier->set_datatype(make_native_enum_type(name, native));
43294473
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
4474+
#ifdef TOOLS_ENABLED
4475+
DocTools *dd = EditorHelp::get_doc_data();
4476+
if (dd && dd->class_list.has(native) && dd->class_list[native].enums.has(name)) {
4477+
DocData::EnumDoc doc = dd->class_list[native].enums[name];
4478+
if (doc.is_deprecated) {
4479+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "enum", name);
4480+
}
4481+
}
4482+
#endif
43304483
return;
43314484
}
43324485
bool valid = false;
@@ -4337,6 +4490,20 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
43374490
p_identifier->reduced_value = int_constant;
43384491
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
43394492

4493+
#ifdef TOOLS_ENABLED
4494+
DocTools *dd = EditorHelp::get_doc_data();
4495+
if (dd && dd->class_list.has(native)) {
4496+
for (const DocData::ConstantDoc &doc : dd->class_list[native].constants) {
4497+
if (doc.name == name) {
4498+
if (doc.is_deprecated) {
4499+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "constant", name);
4500+
}
4501+
break;
4502+
}
4503+
}
4504+
}
4505+
#endif
4506+
43404507
// Check whether this constant, which exists, belongs to an enum
43414508
StringName enum_name = ClassDB::get_integer_constant_enum(native, name);
43424509
if (enum_name != StringName()) {
@@ -4389,6 +4556,12 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
43894556
// TODO: Constant should have a value on the node itself.
43904557
p_identifier->reduced_value = p_identifier->constant_source->initializer->reduced_value;
43914558
found_source = true;
4559+
4560+
#ifdef TOOLS_ENABLED
4561+
if (p_identifier->constant_source->doc_data.is_deprecated) {
4562+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "constant", p_identifier->name);
4563+
}
4564+
#endif
43924565
break;
43934566
case GDScriptParser::IdentifierNode::MEMBER_SIGNAL:
43944567
p_identifier->signal_source->usages++;
@@ -4408,7 +4581,12 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
44084581
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)) {
44094582
parser->push_warning(p_identifier, GDScriptWarning::UNASSIGNED_VARIABLE, p_identifier->name);
44104583
}
4411-
#endif // DEBUG_ENABLED
4584+
#endif
4585+
#ifdef TOOLS_ENABLED
4586+
if (p_identifier->variable_source->doc_data.is_deprecated) {
4587+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "property or variable", p_identifier->name);
4588+
}
4589+
#endif
44124590
break;
44134591
case GDScriptParser::IdentifierNode::LOCAL_ITERATOR:
44144592
p_identifier->set_datatype(p_identifier->bind_source->get_datatype());
@@ -4525,12 +4703,28 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
45254703
}
45264704

45274705
if (class_exists(name)) {
4706+
#ifdef TOOLS_ENABLED
4707+
DocTools *dd = EditorHelp::get_doc_data();
4708+
if (dd && dd->class_list.has(name)) {
4709+
if (dd->class_list[name].is_deprecated) {
4710+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "class", name);
4711+
}
4712+
}
4713+
#endif
45284714
p_identifier->set_datatype(make_native_meta_type(name));
45294715
return;
45304716
}
45314717

45324718
if (ScriptServer::is_global_class(name)) {
45334719
p_identifier->set_datatype(make_global_class_meta_type(name, p_identifier));
4720+
#ifdef TOOLS_ENABLED
4721+
DocTools *dd = EditorHelp::get_doc_data();
4722+
if (dd && dd->class_list.has(name)) {
4723+
if (dd->class_list[name].is_deprecated) {
4724+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "class", name);
4725+
}
4726+
}
4727+
#endif
45344728
return;
45354729
}
45364730

@@ -4571,6 +4765,14 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
45714765
}
45724766
}
45734767
}
4768+
#ifdef TOOLS_ENABLED
4769+
DocTools *dd = EditorHelp::get_doc_data();
4770+
if (dd && dd->class_list.has(name)) {
4771+
if (dd->class_list[name].is_deprecated) {
4772+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "class", name);
4773+
}
4774+
}
4775+
#endif
45744776
result.is_constant = true;
45754777
p_identifier->set_datatype(result);
45764778
return;
@@ -4588,6 +4790,17 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
45884790
}
45894791
p_identifier->is_constant = true;
45904792
p_identifier->reduced_value = value;
4793+
4794+
#ifdef TOOLS_ENABLED
4795+
DocTools *dd = EditorHelp::get_doc_data();
4796+
if (dd && dd->class_list.has("@GlobalScope")) {
4797+
for (const DocData::ConstantDoc &cd : dd->class_list["@GlobalScope"].constants) {
4798+
if (cd.name == name && cd.is_deprecated) {
4799+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "constant", name);
4800+
}
4801+
}
4802+
}
4803+
#endif
45914804
return;
45924805
}
45934806

@@ -5839,6 +6052,12 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
58396052
r_return_type.is_meta_type = false;
58406053
r_return_type.is_coroutine = found_function->is_coroutine;
58416054

6055+
// For user-defined methods.
6056+
#ifdef TOOLS_ENABLED
6057+
if (found_function->doc_data.is_deprecated) {
6058+
parser->push_warning(p_source, GDScriptWarning::DEPRECATED_IDENTIFIER, "function", found_function->identifier->name);
6059+
}
6060+
#endif
58426061
return true;
58436062
}
58446063

@@ -5882,7 +6101,20 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
58826101
if (native_method && r_native_class) {
58836102
*r_native_class = native_method->get_instance_class();
58846103
}
5885-
#endif // DEBUG_ENABLED
6104+
#endif
6105+
#ifdef TOOLS_ENABLED
6106+
DocTools *dd = EditorHelp::get_doc_data();
6107+
if (dd) {
6108+
const Vector<DocData::MethodDoc> &method_list = dd->class_list[base_native].methods;
6109+
for (int i = 0; i < method_list.size(); i++) {
6110+
if (method_list[i].name == function_name && method_list[i].is_deprecated) {
6111+
parser->push_warning(p_source, GDScriptWarning::DEPRECATED_IDENTIFIER, "function", function_name);
6112+
break;
6113+
}
6114+
}
6115+
}
6116+
6117+
#endif
58866118
return valid;
58876119
}
58886120

0 commit comments

Comments
 (0)