GDScript: Add annotation to ignore warnings
This commit is contained in:
parent
1032c2c434
commit
fd643c903d
|
@ -881,12 +881,23 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
|
|||
for (int i = 0; i < p_class->members.size(); i++) {
|
||||
GDScriptParser::ClassNode::Member member = p_class->members[i];
|
||||
if (member.type == GDScriptParser::ClassNode::Member::FUNCTION) {
|
||||
resolve_function_body(member.function);
|
||||
|
||||
// Apply annotations.
|
||||
for (GDScriptParser::AnnotationNode *&E : member.function->annotations) {
|
||||
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) {
|
||||
if (member.variable->property == GDScriptParser::VariableNode::PROP_INLINE) {
|
||||
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];
|
||||
if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {
|
||||
#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("_")) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
#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) {
|
||||
for (int i = 0; i < p_suite->statements.size(); 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);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
parser->ignored_warning_codes = previously_ignored;
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
decide_suite_type(p_suite, stmt);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -669,6 +669,11 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
|
|||
ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_CLASS);
|
||||
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
|
||||
#include "core/os/os.h"
|
||||
#include "core/string/string_builder.h"
|
||||
#include "gdscript_warning.h"
|
||||
#endif // DEBUG_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_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("@warning_ignore", { Variant::STRING, "warning" }), AnnotationInfo::CLASS | AnnotationInfo::VARIABLE | AnnotationInfo::SIGNAL | AnnotationInfo::CONSTANT | AnnotationInfo::FUNCTION | AnnotationInfo::STATEMENT, &GDScriptParser::warning_annotations, 0, true);
|
||||
// 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);
|
||||
// TODO: Warning annotations.
|
||||
}
|
||||
|
||||
GDScriptParser::~GDScriptParser() {
|
||||
|
@ -196,6 +197,10 @@ void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_
|
|||
return;
|
||||
}
|
||||
|
||||
if (ignored_warning_codes.has(p_code)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String warn_name = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)p_code).to_lower();
|
||||
if (ignored_warnings.has(warn_name)) {
|
||||
return;
|
||||
|
@ -701,24 +706,21 @@ void GDScriptParser::parse_extends() {
|
|||
template <class T>
|
||||
void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)(), AnnotationInfo::TargetKind p_target, const String &p_member_kind) {
|
||||
advance();
|
||||
T *member = (this->*p_parse_function)();
|
||||
if (member == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
int doc_comment_line = member->start_line - 1;
|
||||
int doc_comment_line = previous.start_line - 1;
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
// Consume annotations.
|
||||
List<AnnotationNode *> annotations;
|
||||
while (!annotation_stack.is_empty()) {
|
||||
AnnotationNode *last_annotation = annotation_stack.back()->get();
|
||||
if (last_annotation->applies_to(p_target)) {
|
||||
member->annotations.push_front(last_annotation);
|
||||
annotations.push_front(last_annotation);
|
||||
annotation_stack.pop_back();
|
||||
} else {
|
||||
push_error(vformat(R"(Annotation "%s" cannot be applied to a %s.)", last_annotation->name, p_member_kind));
|
||||
clear_unused_annotations();
|
||||
return;
|
||||
}
|
||||
#ifdef TOOLS_ENABLED
|
||||
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
|
||||
}
|
||||
|
||||
T *member = (this->*p_parse_function)();
|
||||
if (member == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply annotations.
|
||||
for (AnnotationNode *&annotation : annotations) {
|
||||
member->annotations.push_back(annotation);
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
// Consume doc comments.
|
||||
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;
|
||||
#endif
|
||||
|
||||
bool is_annotation = false;
|
||||
|
||||
switch (current.type) {
|
||||
case GDScriptTokenizer::Token::PASS:
|
||||
advance();
|
||||
|
@ -1576,6 +1590,7 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
|
|||
break;
|
||||
case GDScriptTokenizer::Token::ANNOTATION: {
|
||||
advance();
|
||||
is_annotation = true;
|
||||
AnnotationNode *annotation = parse_annotation(AnnotationInfo::STATEMENT);
|
||||
if (annotation != nullptr) {
|
||||
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
|
||||
if (unreachable && result != nullptr) {
|
||||
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) {
|
||||
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>
|
||||
|
|
|
@ -297,6 +297,7 @@ public:
|
|||
int leftmost_column = 0, rightmost_column = 0;
|
||||
Node *next = nullptr;
|
||||
List<AnnotationNode *> annotations;
|
||||
Vector<uint32_t> ignored_warnings;
|
||||
|
||||
DataType datatype;
|
||||
|
||||
|
@ -1204,6 +1205,7 @@ private:
|
|||
#ifdef DEBUG_ENABLED
|
||||
List<GDScriptWarning> warnings;
|
||||
Set<String> ignored_warnings;
|
||||
Set<uint32_t> ignored_warning_codes;
|
||||
Set<int> unsafe_lines;
|
||||
#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
|
||||
|
|
|
@ -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