Merge pull request #56483 from vnen/gdscript-warning-annotation
Add annotation to ignore warnings
This commit is contained in:
commit
6d4ed65f4c
|
@ -238,10 +238,6 @@ void ScriptTextEditor::_show_warnings_panel(bool p_show) {
|
||||||
void ScriptTextEditor::_warning_clicked(Variant p_line) {
|
void ScriptTextEditor::_warning_clicked(Variant p_line) {
|
||||||
if (p_line.get_type() == Variant::INT) {
|
if (p_line.get_type() == Variant::INT) {
|
||||||
goto_line_centered(p_line.operator int64_t());
|
goto_line_centered(p_line.operator int64_t());
|
||||||
} else if (p_line.get_type() == Variant::DICTIONARY) {
|
|
||||||
Dictionary meta = p_line.operator Dictionary();
|
|
||||||
code_editor->get_text_editor()->insert_line_at(meta["line"].operator int64_t() - 1, "# warning-ignore:" + meta["code"].operator String());
|
|
||||||
_validate_script();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -468,20 +464,8 @@ void ScriptTextEditor::_validate_script() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add script warnings.
|
// Add script warnings.
|
||||||
warnings_panel->push_table(3);
|
warnings_panel->push_table(2);
|
||||||
for (const ScriptLanguage::Warning &w : warnings) {
|
for (const ScriptLanguage::Warning &w : warnings) {
|
||||||
Dictionary ignore_meta;
|
|
||||||
ignore_meta["line"] = w.start_line;
|
|
||||||
ignore_meta["code"] = w.string_code.to_lower();
|
|
||||||
warnings_panel->push_cell();
|
|
||||||
warnings_panel->push_meta(ignore_meta);
|
|
||||||
warnings_panel->push_color(
|
|
||||||
warnings_panel->get_theme_color(SNAME("accent_color"), SNAME("Editor")).lerp(warnings_panel->get_theme_color(SNAME("mono_color"), SNAME("Editor")), 0.5));
|
|
||||||
warnings_panel->add_text(TTR("[Ignore]"));
|
|
||||||
warnings_panel->pop(); // Color.
|
|
||||||
warnings_panel->pop(); // Meta ignore.
|
|
||||||
warnings_panel->pop(); // Cell.
|
|
||||||
|
|
||||||
warnings_panel->push_cell();
|
warnings_panel->push_cell();
|
||||||
warnings_panel->push_meta(w.start_line - 1);
|
warnings_panel->push_meta(w.start_line - 1);
|
||||||
warnings_panel->push_color(warnings_panel->get_theme_color(SNAME("warning_color"), SNAME("Editor")));
|
warnings_panel->push_color(warnings_panel->get_theme_color(SNAME("warning_color"), SNAME("Editor")));
|
||||||
|
|
|
@ -881,12 +881,23 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
|
||||||
for (int i = 0; i < p_class->members.size(); i++) {
|
for (int i = 0; i < p_class->members.size(); i++) {
|
||||||
GDScriptParser::ClassNode::Member member = p_class->members[i];
|
GDScriptParser::ClassNode::Member member = p_class->members[i];
|
||||||
if (member.type == GDScriptParser::ClassNode::Member::FUNCTION) {
|
if (member.type == GDScriptParser::ClassNode::Member::FUNCTION) {
|
||||||
resolve_function_body(member.function);
|
|
||||||
|
|
||||||
// Apply annotations.
|
// Apply annotations.
|
||||||
for (GDScriptParser::AnnotationNode *&E : member.function->annotations) {
|
for (GDScriptParser::AnnotationNode *&E : member.function->annotations) {
|
||||||
E->apply(parser, member.function);
|
E->apply(parser, member.function);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
Set<uint32_t> previously_ignored = parser->ignored_warning_codes;
|
||||||
|
for (uint32_t ignored_warning : member.function->ignored_warnings) {
|
||||||
|
parser->ignored_warning_codes.insert(ignored_warning);
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
|
resolve_function_body(member.function);
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
parser->ignored_warning_codes = previously_ignored;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
} else if (member.type == GDScriptParser::ClassNode::Member::VARIABLE && member.variable->property != GDScriptParser::VariableNode::PROP_NONE) {
|
} else if (member.type == GDScriptParser::ClassNode::Member::VARIABLE && member.variable->property != GDScriptParser::VariableNode::PROP_NONE) {
|
||||||
if (member.variable->property == GDScriptParser::VariableNode::PROP_INLINE) {
|
if (member.variable->property == GDScriptParser::VariableNode::PROP_INLINE) {
|
||||||
if (member.variable->getter != nullptr) {
|
if (member.variable->getter != nullptr) {
|
||||||
|
@ -925,6 +936,10 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
|
||||||
GDScriptParser::ClassNode::Member member = p_class->members[i];
|
GDScriptParser::ClassNode::Member member = p_class->members[i];
|
||||||
if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {
|
if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
|
Set<uint32_t> previously_ignored = parser->ignored_warning_codes;
|
||||||
|
for (uint32_t ignored_warning : member.function->ignored_warnings) {
|
||||||
|
parser->ignored_warning_codes.insert(ignored_warning);
|
||||||
|
}
|
||||||
if (member.variable->usages == 0 && String(member.variable->identifier->name).begins_with("_")) {
|
if (member.variable->usages == 0 && String(member.variable->identifier->name).begins_with("_")) {
|
||||||
parser->push_warning(member.variable->identifier, GDScriptWarning::UNUSED_PRIVATE_CLASS_VARIABLE, member.variable->identifier->name);
|
parser->push_warning(member.variable->identifier, GDScriptWarning::UNUSED_PRIVATE_CLASS_VARIABLE, member.variable->identifier->name);
|
||||||
}
|
}
|
||||||
|
@ -992,6 +1007,9 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
|
||||||
push_error(vformat(R"(Getter with type "%s" cannot be used along with setter of type "%s".)", getter_function->datatype.to_string(), setter_function->parameters[0]->datatype.to_string()), member.variable);
|
push_error(vformat(R"(Getter with type "%s" cannot be used along with setter of type "%s".)", getter_function->datatype.to_string(), setter_function->parameters[0]->datatype.to_string()), member.variable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
parser->ignored_warning_codes = previously_ignored;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1186,7 +1204,23 @@ void GDScriptAnalyzer::decide_suite_type(GDScriptParser::Node *p_suite, GDScript
|
||||||
void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) {
|
void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) {
|
||||||
for (int i = 0; i < p_suite->statements.size(); i++) {
|
for (int i = 0; i < p_suite->statements.size(); i++) {
|
||||||
GDScriptParser::Node *stmt = p_suite->statements[i];
|
GDScriptParser::Node *stmt = p_suite->statements[i];
|
||||||
|
for (GDScriptParser::AnnotationNode *&annotation : stmt->annotations) {
|
||||||
|
annotation->apply(parser, stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
Set<uint32_t> previously_ignored = parser->ignored_warning_codes;
|
||||||
|
for (uint32_t ignored_warning : stmt->ignored_warnings) {
|
||||||
|
parser->ignored_warning_codes.insert(ignored_warning);
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
resolve_node(stmt);
|
resolve_node(stmt);
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
parser->ignored_warning_codes = previously_ignored;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
decide_suite_type(p_suite, stmt);
|
decide_suite_type(p_suite, stmt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -646,6 +646,11 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
|
||||||
ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_CLASS);
|
ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_CLASS);
|
||||||
r_result.insert(option.display, option);
|
r_result.insert(option.display, option);
|
||||||
}
|
}
|
||||||
|
} else if (p_annotation->name == "@warning_ignore") {
|
||||||
|
for (int warning_code = 0; warning_code < GDScriptWarning::WARNING_MAX; warning_code++) {
|
||||||
|
ScriptCodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
|
||||||
|
r_result.insert(warning.display, warning);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
#include "core/os/os.h"
|
#include "core/os/os.h"
|
||||||
#include "core/string/string_builder.h"
|
#include "core/string/string_builder.h"
|
||||||
|
#include "gdscript_warning.h"
|
||||||
#endif // DEBUG_ENABLED
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
|
@ -132,9 +133,9 @@ GDScriptParser::GDScriptParser() {
|
||||||
register_annotation(MethodInfo("@export_flags_3d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_RENDER, Variant::INT>);
|
register_annotation(MethodInfo("@export_flags_3d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_RENDER, Variant::INT>);
|
||||||
register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>);
|
register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>);
|
||||||
register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>);
|
register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>);
|
||||||
|
register_annotation(MethodInfo("@warning_ignore", { Variant::STRING, "warning" }), AnnotationInfo::CLASS | AnnotationInfo::VARIABLE | AnnotationInfo::SIGNAL | AnnotationInfo::CONSTANT | AnnotationInfo::FUNCTION | AnnotationInfo::STATEMENT, &GDScriptParser::warning_annotations, 0, true);
|
||||||
// Networking.
|
// Networking.
|
||||||
register_annotation(MethodInfo("@rpc", { Variant::STRING, "mode" }, { Variant::STRING, "sync" }, { Variant::STRING, "transfer_mode" }, { Variant::INT, "transfer_channel" }), AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<Multiplayer::RPC_MODE_AUTHORITY>, 4, true);
|
register_annotation(MethodInfo("@rpc", { Variant::STRING, "mode" }, { Variant::STRING, "sync" }, { Variant::STRING, "transfer_mode" }, { Variant::INT, "transfer_channel" }), AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<Multiplayer::RPC_MODE_AUTHORITY>, 4, true);
|
||||||
// TODO: Warning annotations.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GDScriptParser::~GDScriptParser() {
|
GDScriptParser::~GDScriptParser() {
|
||||||
|
@ -196,6 +197,10 @@ void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ignored_warning_codes.has(p_code)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String warn_name = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)p_code).to_lower();
|
String warn_name = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)p_code).to_lower();
|
||||||
if (ignored_warnings.has(warn_name)) {
|
if (ignored_warnings.has(warn_name)) {
|
||||||
return;
|
return;
|
||||||
|
@ -701,24 +706,21 @@ void GDScriptParser::parse_extends() {
|
||||||
template <class T>
|
template <class T>
|
||||||
void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)(), AnnotationInfo::TargetKind p_target, const String &p_member_kind) {
|
void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)(), AnnotationInfo::TargetKind p_target, const String &p_member_kind) {
|
||||||
advance();
|
advance();
|
||||||
T *member = (this->*p_parse_function)();
|
|
||||||
if (member == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
int doc_comment_line = member->start_line - 1;
|
int doc_comment_line = previous.start_line - 1;
|
||||||
#endif // TOOLS_ENABLED
|
#endif // TOOLS_ENABLED
|
||||||
|
|
||||||
// Consume annotations.
|
// Consume annotations.
|
||||||
|
List<AnnotationNode *> annotations;
|
||||||
while (!annotation_stack.is_empty()) {
|
while (!annotation_stack.is_empty()) {
|
||||||
AnnotationNode *last_annotation = annotation_stack.back()->get();
|
AnnotationNode *last_annotation = annotation_stack.back()->get();
|
||||||
if (last_annotation->applies_to(p_target)) {
|
if (last_annotation->applies_to(p_target)) {
|
||||||
member->annotations.push_front(last_annotation);
|
annotations.push_front(last_annotation);
|
||||||
annotation_stack.pop_back();
|
annotation_stack.pop_back();
|
||||||
} else {
|
} else {
|
||||||
push_error(vformat(R"(Annotation "%s" cannot be applied to a %s.)", last_annotation->name, p_member_kind));
|
push_error(vformat(R"(Annotation "%s" cannot be applied to a %s.)", last_annotation->name, p_member_kind));
|
||||||
clear_unused_annotations();
|
clear_unused_annotations();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
if (last_annotation->start_line == doc_comment_line) {
|
if (last_annotation->start_line == doc_comment_line) {
|
||||||
|
@ -727,6 +729,16 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)()
|
||||||
#endif // TOOLS_ENABLED
|
#endif // TOOLS_ENABLED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
T *member = (this->*p_parse_function)();
|
||||||
|
if (member == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply annotations.
|
||||||
|
for (AnnotationNode *&annotation : annotations) {
|
||||||
|
member->annotations.push_back(annotation);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
// Consume doc comments.
|
// Consume doc comments.
|
||||||
class_doc_line = MIN(class_doc_line, doc_comment_line - 1);
|
class_doc_line = MIN(class_doc_line, doc_comment_line - 1);
|
||||||
|
@ -1507,6 +1519,8 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
|
||||||
bool unreachable = current_suite->has_return && !current_suite->has_unreachable_code;
|
bool unreachable = current_suite->has_return && !current_suite->has_unreachable_code;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool is_annotation = false;
|
||||||
|
|
||||||
switch (current.type) {
|
switch (current.type) {
|
||||||
case GDScriptTokenizer::Token::PASS:
|
case GDScriptTokenizer::Token::PASS:
|
||||||
advance();
|
advance();
|
||||||
|
@ -1576,6 +1590,7 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
|
||||||
break;
|
break;
|
||||||
case GDScriptTokenizer::Token::ANNOTATION: {
|
case GDScriptTokenizer::Token::ANNOTATION: {
|
||||||
advance();
|
advance();
|
||||||
|
is_annotation = true;
|
||||||
AnnotationNode *annotation = parse_annotation(AnnotationInfo::STATEMENT);
|
AnnotationNode *annotation = parse_annotation(AnnotationInfo::STATEMENT);
|
||||||
if (annotation != nullptr) {
|
if (annotation != nullptr) {
|
||||||
annotation_stack.push_back(annotation);
|
annotation_stack.push_back(annotation);
|
||||||
|
@ -1616,6 +1631,18 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply annotations to statement.
|
||||||
|
while (!is_annotation && result != nullptr && !annotation_stack.is_empty()) {
|
||||||
|
AnnotationNode *last_annotation = annotation_stack.back()->get();
|
||||||
|
if (last_annotation->applies_to(AnnotationInfo::STATEMENT)) {
|
||||||
|
result->annotations.push_front(last_annotation);
|
||||||
|
annotation_stack.pop_back();
|
||||||
|
} else {
|
||||||
|
push_error(vformat(R"(Annotation "%s" cannot be applied to a statement.)", last_annotation->name));
|
||||||
|
clear_unused_annotations();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
if (unreachable && result != nullptr) {
|
if (unreachable && result != nullptr) {
|
||||||
current_suite->has_unreachable_code = true;
|
current_suite->has_unreachable_code = true;
|
||||||
|
@ -3552,7 +3579,24 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GDScriptParser::warning_annotations(const AnnotationNode *p_annotation, Node *p_node) {
|
bool GDScriptParser::warning_annotations(const AnnotationNode *p_annotation, Node *p_node) {
|
||||||
ERR_FAIL_V_MSG(false, "Not implemented.");
|
#ifdef DEBUG_ENABLED
|
||||||
|
bool has_error = false;
|
||||||
|
for (const Variant &warning_name : p_annotation->resolved_arguments) {
|
||||||
|
GDScriptWarning::Code warning = GDScriptWarning::get_code_from_name(String(warning_name).to_upper());
|
||||||
|
if (warning == GDScriptWarning::WARNING_MAX) {
|
||||||
|
push_error(vformat(R"(Invalid warning name: "%s".)", warning_name), p_annotation);
|
||||||
|
has_error = true;
|
||||||
|
} else {
|
||||||
|
p_node->ignored_warnings.push_back(warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !has_error;
|
||||||
|
|
||||||
|
#else // ! DEBUG_ENABLED
|
||||||
|
// Only available in debug builds.
|
||||||
|
return true;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
}
|
}
|
||||||
|
|
||||||
template <Multiplayer::RPCMode t_mode>
|
template <Multiplayer::RPCMode t_mode>
|
||||||
|
|
|
@ -297,6 +297,7 @@ public:
|
||||||
int leftmost_column = 0, rightmost_column = 0;
|
int leftmost_column = 0, rightmost_column = 0;
|
||||||
Node *next = nullptr;
|
Node *next = nullptr;
|
||||||
List<AnnotationNode *> annotations;
|
List<AnnotationNode *> annotations;
|
||||||
|
Vector<uint32_t> ignored_warnings;
|
||||||
|
|
||||||
DataType datatype;
|
DataType datatype;
|
||||||
|
|
||||||
|
@ -1204,6 +1205,7 @@ private:
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
List<GDScriptWarning> warnings;
|
List<GDScriptWarning> warnings;
|
||||||
Set<String> ignored_warnings;
|
Set<String> ignored_warnings;
|
||||||
|
Set<uint32_t> ignored_warning_codes;
|
||||||
Set<int> unsafe_lines;
|
Set<int> unsafe_lines;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -213,7 +213,7 @@ GDScriptWarning::Code GDScriptWarning::get_code_from_name(const String &p_name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ERR_FAIL_V_MSG(WARNING_MAX, "Invalid GDScript warning name: " + p_name);
|
return WARNING_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // DEBUG_ENABLED
|
#endif // DEBUG_ENABLED
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
@warning_ignore(unused_private_class_variable)
|
||||||
|
var _unused = 2
|
||||||
|
|
||||||
|
@warning_ignore(unused_variable)
|
||||||
|
func test():
|
||||||
|
print("test")
|
||||||
|
var unused = 3
|
||||||
|
|
||||||
|
@warning_ignore(redundant_await)
|
||||||
|
print(await regular_func())
|
||||||
|
|
||||||
|
print("done")
|
||||||
|
|
||||||
|
func regular_func():
|
||||||
|
return 0
|
|
@ -0,0 +1,4 @@
|
||||||
|
GDTEST_OK
|
||||||
|
test
|
||||||
|
0
|
||||||
|
done
|
Loading…
Reference in New Issue