Skip to content

Commit 10d8954

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 34f005d commit 10d8954

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
@@ -511,6 +511,9 @@
511511
<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.">
512512
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.
513513
</member>
514+
<member name="debug/gdscript/warnings/deprecated_identifier" type="int" setter="" getter="" default="1">
515+
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.
516+
</member>
514517
<member name="debug/gdscript/warnings/deprecated_keyword" type="int" setter="" getter="" default="1">
515518
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when deprecated keywords are used.
516519
[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+
#ifdef 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

@@ -4003,6 +4058,38 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
40034058
StringName name = p_identifier->name;
40044059

40054060
if (base.kind == GDScriptParser::DataType::ENUM) {
4061+
#ifdef TOOLS_ENABLED
4062+
StringName class_name;
4063+
DocTools *dd = EditorHelp::get_doc_data();
4064+
// TODO: Is this a proper way of detecting it's from a native class?
4065+
// e.g., "AnimationPlayer.AnimationProcessCallback"
4066+
// User-defined enums also seem to have a "native_type" so we can't
4067+
// detect actual native classes solely based on whether or not that
4068+
// string is defined.
4069+
if (base.native_type && !base.class_type) {
4070+
class_name = String(base.native_type).get_slicec('.', 0);
4071+
}
4072+
4073+
else if (base.class_type && base.class_type->identifier && base.class_type->identifier->name) {
4074+
class_name = base.class_type->identifier->name;
4075+
// It's an inner class, so we need to get the outer class's name
4076+
// as well to construct its full name as found in the doc data.
4077+
if (base.class_type->outer != nullptr && base.class_type->outer->identifier != nullptr) {
4078+
class_name = String(base.class_type->outer->identifier->name) + "." + class_name;
4079+
}
4080+
}
4081+
4082+
if (dd && dd->class_list.has(class_name)) {
4083+
for (const DocData::ConstantDoc &doc : dd->class_list[class_name].constants) {
4084+
if (doc.enumeration == base.enum_type && doc.name == name) {
4085+
if (doc.is_deprecated) {
4086+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "enum value", vformat("%s.%s", base.enum_type, doc.name));
4087+
}
4088+
break;
4089+
}
4090+
}
4091+
}
4092+
#endif
40064093
if (base.is_meta_type) {
40074094
if (base.enum_values.has(name)) {
40084095
p_identifier->set_datatype(type_from_metatype(base));
@@ -4148,6 +4235,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
41484235
p_identifier->reduced_value = member.constant->initializer->reduced_value;
41494236
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
41504237
p_identifier->constant_source = member.constant;
4238+
#ifdef TOOLS_ENABLED
4239+
if (member.constant->doc_data.is_deprecated) {
4240+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "constant", name);
4241+
}
4242+
#endif
41514243
return;
41524244
}
41534245

@@ -4156,6 +4248,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
41564248
p_identifier->is_constant = true;
41574249
p_identifier->reduced_value = member.enum_value.value;
41584250
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
4251+
#ifdef TOOLS_ENABLED
4252+
if (member.enum_value.doc_data.is_deprecated) {
4253+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "enum value", name);
4254+
}
4255+
#endif
41594256
return;
41604257
}
41614258

@@ -4164,6 +4261,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
41644261
p_identifier->is_constant = true;
41654262
p_identifier->reduced_value = member.m_enum->dictionary;
41664263
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
4264+
#ifdef TOOLS_ENABLED
4265+
if (member.m_enum->doc_data.is_deprecated) {
4266+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "enum", name);
4267+
}
4268+
#endif
41674269
return;
41684270
}
41694271

@@ -4173,6 +4275,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
41734275
p_identifier->source = member.variable->is_static ? GDScriptParser::IdentifierNode::STATIC_VARIABLE : GDScriptParser::IdentifierNode::MEMBER_VARIABLE;
41744276
p_identifier->variable_source = member.variable;
41754277
member.variable->usages += 1;
4278+
#ifdef TOOLS_ENABLED
4279+
if (member.variable->doc_data.is_deprecated) {
4280+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "property or variable", name);
4281+
}
4282+
#endif
41764283
return;
41774284
}
41784285
} break;
@@ -4183,6 +4290,12 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
41834290
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_SIGNAL;
41844291
p_identifier->signal_source = member.signal;
41854292
member.signal->usages += 1;
4293+
4294+
#ifdef TOOLS_ENABLED
4295+
if (member.signal->doc_data.is_deprecated) {
4296+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "signal", name);
4297+
}
4298+
#endif
41864299
return;
41874300
}
41884301
} break;
@@ -4200,6 +4313,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
42004313
case GDScriptParser::ClassNode::Member::CLASS: {
42014314
reduce_identifier_from_base_set_class(p_identifier, member.get_datatype());
42024315
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CLASS;
4316+
#ifdef TOOLS_ENABLED
4317+
if (script_class->get_member(name).m_class->doc_data.is_deprecated) {
4318+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "class", name);
4319+
}
4320+
#endif
42034321
return;
42044322
}
42054323

@@ -4289,6 +4407,19 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
42894407
p_identifier->set_datatype(type_from_property(getter->get_return_info(), false, !has_setter));
42904408
p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
42914409
}
4410+
#ifdef TOOLS_ENABLED
4411+
DocTools *dd = EditorHelp::get_doc_data();
4412+
if (dd && dd->class_list.has(native)) {
4413+
for (const DocData::PropertyDoc &doc : dd->class_list[native].properties) {
4414+
if (doc.name == name) {
4415+
if (doc.is_deprecated) {
4416+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "property", name);
4417+
}
4418+
break;
4419+
}
4420+
}
4421+
}
4422+
#endif
42924423
return;
42934424
}
42944425
if (ClassDB::get_method_info(native, name, &method_info)) {
@@ -4301,11 +4432,33 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
43014432
// Signal is a type too.
43024433
p_identifier->set_datatype(make_signal_type(method_info));
43034434
p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
4435+
#ifdef TOOLS_ENABLED
4436+
DocTools *dd = EditorHelp::get_doc_data();
4437+
if (dd && dd->class_list.has(native)) {
4438+
for (const DocData::MethodDoc &doc : dd->class_list[native].signals) {
4439+
if (doc.name == name) {
4440+
if (doc.is_deprecated) {
4441+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "signal", name);
4442+
}
4443+
break;
4444+
}
4445+
}
4446+
}
4447+
#endif
43044448
return;
43054449
}
43064450
if (ClassDB::has_enum(native, name)) {
43074451
p_identifier->set_datatype(make_native_enum_type(name, native));
43084452
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
4453+
#ifdef TOOLS_ENABLED
4454+
DocTools *dd = EditorHelp::get_doc_data();
4455+
if (dd && dd->class_list.has(native) && dd->class_list[native].enums.has(name)) {
4456+
DocData::EnumDoc doc = dd->class_list[native].enums[name];
4457+
if (doc.is_deprecated) {
4458+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "enum", name);
4459+
}
4460+
}
4461+
#endif
43094462
return;
43104463
}
43114464
bool valid = false;
@@ -4316,6 +4469,20 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
43164469
p_identifier->reduced_value = int_constant;
43174470
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
43184471

4472+
#ifdef TOOLS_ENABLED
4473+
DocTools *dd = EditorHelp::get_doc_data();
4474+
if (dd && dd->class_list.has(native)) {
4475+
for (const DocData::ConstantDoc &doc : dd->class_list[native].constants) {
4476+
if (doc.name == name) {
4477+
if (doc.is_deprecated) {
4478+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "constant", name);
4479+
}
4480+
break;
4481+
}
4482+
}
4483+
}
4484+
#endif
4485+
43194486
// Check whether this constant, which exists, belongs to an enum
43204487
StringName enum_name = ClassDB::get_integer_constant_enum(native, name);
43214488
if (enum_name != StringName()) {
@@ -4368,6 +4535,12 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
43684535
// TODO: Constant should have a value on the node itself.
43694536
p_identifier->reduced_value = p_identifier->constant_source->initializer->reduced_value;
43704537
found_source = true;
4538+
4539+
#ifdef TOOLS_ENABLED
4540+
if (p_identifier->constant_source->doc_data.is_deprecated) {
4541+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "constant", p_identifier->name);
4542+
}
4543+
#endif
43714544
break;
43724545
case GDScriptParser::IdentifierNode::MEMBER_SIGNAL:
43734546
p_identifier->signal_source->usages++;
@@ -4387,6 +4560,11 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
43874560
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)) {
43884561
parser->push_warning(p_identifier, GDScriptWarning::UNASSIGNED_VARIABLE, p_identifier->name);
43894562
}
4563+
#endif
4564+
#ifdef TOOLS_ENABLED
4565+
if (p_identifier->variable_source->doc_data.is_deprecated) {
4566+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "property or variable", p_identifier->name);
4567+
}
43904568
#endif
43914569
break;
43924570
case GDScriptParser::IdentifierNode::LOCAL_ITERATOR:
@@ -4504,12 +4682,28 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
45044682
}
45054683

45064684
if (class_exists(name)) {
4685+
#ifdef TOOLS_ENABLED
4686+
DocTools *dd = EditorHelp::get_doc_data();
4687+
if (dd && dd->class_list.has(name)) {
4688+
if (dd->class_list[name].is_deprecated) {
4689+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "class", name);
4690+
}
4691+
}
4692+
#endif
45074693
p_identifier->set_datatype(make_native_meta_type(name));
45084694
return;
45094695
}
45104696

45114697
if (ScriptServer::is_global_class(name)) {
45124698
p_identifier->set_datatype(make_global_class_meta_type(name, p_identifier));
4699+
#ifdef TOOLS_ENABLED
4700+
DocTools *dd = EditorHelp::get_doc_data();
4701+
if (dd && dd->class_list.has(name)) {
4702+
if (dd->class_list[name].is_deprecated) {
4703+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "class", name);
4704+
}
4705+
}
4706+
#endif
45134707
return;
45144708
}
45154709

@@ -4550,6 +4744,14 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
45504744
}
45514745
}
45524746
}
4747+
#ifdef TOOLS_ENABLED
4748+
DocTools *dd = EditorHelp::get_doc_data();
4749+
if (dd && dd->class_list.has(name)) {
4750+
if (dd->class_list[name].is_deprecated) {
4751+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "class", name);
4752+
}
4753+
}
4754+
#endif
45534755
result.is_constant = true;
45544756
p_identifier->set_datatype(result);
45554757
return;
@@ -4567,6 +4769,17 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
45674769
}
45684770
p_identifier->is_constant = true;
45694771
p_identifier->reduced_value = value;
4772+
4773+
#ifdef TOOLS_ENABLED
4774+
DocTools *dd = EditorHelp::get_doc_data();
4775+
if (dd && dd->class_list.has("@GlobalScope")) {
4776+
for (const DocData::ConstantDoc &cd : dd->class_list["@GlobalScope"].constants) {
4777+
if (cd.name == name && cd.is_deprecated) {
4778+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "constant", name);
4779+
}
4780+
}
4781+
}
4782+
#endif
45704783
return;
45714784
}
45724785

@@ -5812,6 +6025,12 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
58126025
r_return_type.is_meta_type = false;
58136026
r_return_type.is_coroutine = found_function->is_coroutine;
58146027

6028+
// For user-defined methods.
6029+
#ifdef TOOLS_ENABLED
6030+
if (found_function->doc_data.is_deprecated) {
6031+
parser->push_warning(p_source, GDScriptWarning::DEPRECATED_IDENTIFIER, "function", found_function->identifier->name);
6032+
}
6033+
#endif
58156034
return true;
58166035
}
58176036

@@ -5855,6 +6074,19 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
58556074
if (native_method && r_native_class) {
58566075
*r_native_class = native_method->get_instance_class();
58576076
}
6077+
#endif
6078+
#ifdef TOOLS_ENABLED
6079+
DocTools *dd = EditorHelp::get_doc_data();
6080+
if (dd) {
6081+
const Vector<DocData::MethodDoc> &method_list = dd->class_list[base_native].methods;
6082+
for (int i = 0; i < method_list.size(); i++) {
6083+
if (method_list[i].name == function_name && method_list[i].is_deprecated) {
6084+
parser->push_warning(p_source, GDScriptWarning::DEPRECATED_IDENTIFIER, "function", function_name);
6085+
break;
6086+
}
6087+
}
6088+
}
6089+
58586090
#endif
58596091
return valid;
58606092
}

0 commit comments

Comments
 (0)