Skip to content

Commit 314605d

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 <66727710+Mickeon@users.noreply.github.com> 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
1 parent 09ea7bc commit 314605d

File tree

5 files changed

+442
-0
lines changed

5 files changed

+442
-0
lines changed

modules/gdscript/gdscript_analyzer.cpp

Lines changed: 231 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 DEBUG_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,58 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
19931997
type = specified_type;
19941998
}
19951999

2000+
#if DEBUG_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+
DocTools *dd = EditorHelp::get_doc_data();
2035+
if (dd && dd->class_list.has(class_name)) {
2036+
DocData::ClassDoc class_doc = dd->class_list[class_name];
2037+
if (class_doc.enums.has(enum_type)) {
2038+
is_deprecated = class_doc.enums[enum_type].is_deprecated;
2039+
}
2040+
}
2041+
new_value_type = "enum";
2042+
break;
2043+
}
2044+
default:
2045+
break;
2046+
}
2047+
if (is_deprecated) {
2048+
parser->push_warning(p_assignable, GDScriptWarning::DEPRECATED_IDENTIFIER, new_value_type, value_name);
2049+
}
2050+
#endif
2051+
19962052
if (p_assignable->initializer != nullptr) {
19972053
reduce_expression(p_assignable->initializer);
19982054

@@ -3998,6 +4054,38 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
39984054
StringName name = p_identifier->name;
39994055

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

@@ -4151,6 +4244,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
41514244
p_identifier->is_constant = true;
41524245
p_identifier->reduced_value = member.enum_value.value;
41534246
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
4247+
#if DEBUG_ENABLED
4248+
if (member.enum_value.doc_data.is_deprecated) {
4249+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "enum value", name);
4250+
}
4251+
#endif
41544252
return;
41554253
}
41564254

@@ -4159,6 +4257,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
41594257
p_identifier->is_constant = true;
41604258
p_identifier->reduced_value = member.m_enum->dictionary;
41614259
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
4260+
#if DEBUG_ENABLED
4261+
if (member.m_enum->doc_data.is_deprecated) {
4262+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "enum", name);
4263+
}
4264+
#endif
41624265
return;
41634266
}
41644267

@@ -4168,6 +4271,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
41684271
p_identifier->source = member.variable->is_static ? GDScriptParser::IdentifierNode::STATIC_VARIABLE : GDScriptParser::IdentifierNode::MEMBER_VARIABLE;
41694272
p_identifier->variable_source = member.variable;
41704273
member.variable->usages += 1;
4274+
#if DEBUG_ENABLED
4275+
if (member.variable->doc_data.is_deprecated) {
4276+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "property or variable", name);
4277+
}
4278+
#endif
41714279
return;
41724280
}
41734281
} break;
@@ -4178,6 +4286,12 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
41784286
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_SIGNAL;
41794287
p_identifier->signal_source = member.signal;
41804288
member.signal->usages += 1;
4289+
4290+
#if DEBUG_ENABLED
4291+
if (member.signal->doc_data.is_deprecated) {
4292+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "signal", name);
4293+
}
4294+
#endif
41814295
return;
41824296
}
41834297
} break;
@@ -4195,6 +4309,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
41954309
case GDScriptParser::ClassNode::Member::CLASS: {
41964310
reduce_identifier_from_base_set_class(p_identifier, member.get_datatype());
41974311
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CLASS;
4312+
#ifdef DEBUG_ENABLED
4313+
if (script_class->get_member(name).m_class->doc_data.is_deprecated) {
4314+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "class", name);
4315+
}
4316+
#endif
41984317
return;
41994318
}
42004319

@@ -4284,6 +4403,19 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
42844403
p_identifier->set_datatype(type_from_property(getter->get_return_info(), false, !has_setter));
42854404
p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
42864405
}
4406+
#if DEBUG_ENABLED
4407+
DocTools *dd = EditorHelp::get_doc_data();
4408+
if (dd && dd->class_list.has(native)) {
4409+
for (const DocData::PropertyDoc &doc : dd->class_list[native].properties) {
4410+
if (doc.name == name) {
4411+
if (doc.is_deprecated) {
4412+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "property", name);
4413+
}
4414+
break;
4415+
}
4416+
}
4417+
}
4418+
#endif
42874419
return;
42884420
}
42894421
if (ClassDB::get_method_info(native, name, &method_info)) {
@@ -4296,11 +4428,33 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
42964428
// Signal is a type too.
42974429
p_identifier->set_datatype(make_signal_type(method_info));
42984430
p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
4431+
#if DEBUG_ENABLED
4432+
DocTools *dd = EditorHelp::get_doc_data();
4433+
if (dd && dd->class_list.has(native)) {
4434+
for (const DocData::MethodDoc &doc : dd->class_list[native].signals) {
4435+
if (doc.name == name) {
4436+
if (doc.is_deprecated) {
4437+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "signal", name);
4438+
}
4439+
break;
4440+
}
4441+
}
4442+
}
4443+
#endif
42994444
return;
43004445
}
43014446
if (ClassDB::has_enum(native, name)) {
43024447
p_identifier->set_datatype(make_native_enum_type(name, native));
43034448
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
4449+
#if DEBUG_ENABLED
4450+
DocTools *dd = EditorHelp::get_doc_data();
4451+
if (dd && dd->class_list.has(native) && dd->class_list[native].enums.has(name)) {
4452+
DocData::EnumDoc doc = dd->class_list[native].enums[name];
4453+
if (doc.is_deprecated) {
4454+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "enum", name);
4455+
}
4456+
}
4457+
#endif
43044458
return;
43054459
}
43064460
bool valid = false;
@@ -4311,6 +4465,20 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
43114465
p_identifier->reduced_value = int_constant;
43124466
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
43134467

4468+
#if DEBUG_ENABLED
4469+
DocTools *dd = EditorHelp::get_doc_data();
4470+
if (dd && dd->class_list.has(native)) {
4471+
for (const DocData::ConstantDoc &doc : dd->class_list[native].constants) {
4472+
if (doc.name == name) {
4473+
if (doc.is_deprecated) {
4474+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "constant", name);
4475+
}
4476+
break;
4477+
}
4478+
}
4479+
}
4480+
#endif
4481+
43144482
// Check whether this constant, which exists, belongs to an enum
43154483
StringName enum_name = ClassDB::get_integer_constant_enum(native, name);
43164484
if (enum_name != StringName()) {
@@ -4363,6 +4531,12 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
43634531
// TODO: Constant should have a value on the node itself.
43644532
p_identifier->reduced_value = p_identifier->constant_source->initializer->reduced_value;
43654533
found_source = true;
4534+
4535+
#ifdef DEBUG_ENABLED
4536+
if (p_identifier->constant_source->doc_data.is_deprecated) {
4537+
parser->push_warning(p_identifier, GDScriptWarning::DEPRECATED_IDENTIFIER, "constant", p_identifier->name);
4538+
}
4539+
#endif
43664540
break;
43674541
case GDScriptParser::IdentifierNode::MEMBER_SIGNAL:
43684542
p_identifier->signal_source->usages++;
@@ -4382,6 +4556,10 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
43824556
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)) {
43834557
parser->push_warning(p_identifier, GDScriptWarning::UNASSIGNED_VARIABLE, p_identifier->name);
43844558
}
4559+
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 DEBUG_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 DEBUG_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 DEBUG_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 DEBUG_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 DEBUG_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,18 @@ 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+
6021+
DocTools *dd = EditorHelp::get_doc_data();
6022+
if (dd) {
6023+
Vector<DocData::MethodDoc> method_list = dd->class_list[base_native].methods;
6024+
for (int i = 0; i < method_list.size(); i++) {
6025+
if (method_list[i].name == function_name && method_list[i].is_deprecated) {
6026+
parser->push_warning(p_source, GDScriptWarning::DEPRECATED_IDENTIFIER, "function", function_name);
6027+
break;
6028+
}
6029+
}
6030+
}
6031+
58016032
#endif
58026033
return valid;
58036034
}

modules/gdscript/gdscript_warning.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,14 @@ String GDScriptWarning::get_message() const {
162162
return vformat(R"*(The default value is using "%s" which won't return nodes in the scene tree before "_ready()" is called. Use the "@onready" annotation to solve this.)*", symbols[0]);
163163
case ONREADY_WITH_EXPORT:
164164
return R"("@onready" will set the default value after "@export" takes effect and will override it.)";
165+
case DEPRECATED_IDENTIFIER: {
166+
CHECK_SYMBOLS(2);
167+
String name = "";
168+
if (symbols[1].length() > 0) {
169+
name = vformat(R"( "%s")", symbols[1]);
170+
}
171+
return vformat(R"(The %s%s is deprecated. See its documentation for more information and alternatives.)", symbols[0], name);
172+
}
165173
#ifndef DISABLE_DEPRECATED
166174
// Never produced. These warnings migrated from 3.x by mistake.
167175
case PROPERTY_USED_AS_FUNCTION: // There is already an error.
@@ -238,6 +246,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
238246
"NATIVE_METHOD_OVERRIDE",
239247
"GET_NODE_DEFAULT_WITHOUT_ONREADY",
240248
"ONREADY_WITH_EXPORT",
249+
"DEPRECATED_IDENTIFIER",
241250
#ifndef DISABLE_DEPRECATED
242251
"PROPERTY_USED_AS_FUNCTION",
243252
"CONSTANT_USED_AS_FUNCTION",

0 commit comments

Comments
 (0)