1 | /**************************************************************************/ |
2 | /* gdscript_warning.cpp */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #include "gdscript_warning.h" |
32 | |
33 | #include "core/variant/variant.h" |
34 | |
35 | #ifdef DEBUG_ENABLED |
36 | |
37 | String GDScriptWarning::get_message() const { |
38 | #define CHECK_SYMBOLS(m_amount) ERR_FAIL_COND_V(symbols.size() < m_amount, String()); |
39 | |
40 | switch (code) { |
41 | case UNASSIGNED_VARIABLE: |
42 | CHECK_SYMBOLS(1); |
43 | return vformat(R"(The variable "%s" was used but never assigned a value.)" , symbols[0]); |
44 | case UNASSIGNED_VARIABLE_OP_ASSIGN: |
45 | CHECK_SYMBOLS(1); |
46 | return vformat(R"(Using assignment with operation but the variable "%s" was not previously assigned a value.)" , symbols[0]); |
47 | case UNUSED_VARIABLE: |
48 | CHECK_SYMBOLS(1); |
49 | return vformat(R"(The local variable "%s" is declared but never used in the block. If this is intended, prefix it with an underscore: "_%s".)" , symbols[0], symbols[0]); |
50 | case UNUSED_LOCAL_CONSTANT: |
51 | CHECK_SYMBOLS(1); |
52 | return vformat(R"(The local constant "%s" is declared but never used in the block. If this is intended, prefix it with an underscore: "_%s".)" , symbols[0], symbols[0]); |
53 | case UNUSED_PRIVATE_CLASS_VARIABLE: |
54 | CHECK_SYMBOLS(1); |
55 | return vformat(R"(The class variable "%s" is declared but never used in the script.)" , symbols[0]); |
56 | case UNUSED_PARAMETER: |
57 | CHECK_SYMBOLS(2); |
58 | return vformat(R"*(The parameter "%s" is never used in the function "%s()". If this is intended, prefix it with an underscore: "_%s".)*" , symbols[1], symbols[0], symbols[1]); |
59 | case UNUSED_SIGNAL: |
60 | CHECK_SYMBOLS(1); |
61 | return vformat(R"(The signal "%s" is declared but never emitted.)" , symbols[0]); |
62 | case SHADOWED_VARIABLE: |
63 | CHECK_SYMBOLS(4); |
64 | return vformat(R"(The local %s "%s" is shadowing an already-declared %s at line %s.)" , symbols[0], symbols[1], symbols[2], symbols[3]); |
65 | case SHADOWED_VARIABLE_BASE_CLASS: |
66 | CHECK_SYMBOLS(4); |
67 | return vformat(R"(The local %s "%s" is shadowing an already-declared %s at the base class "%s".)" , symbols[0], symbols[1], symbols[2], symbols[3]); |
68 | case SHADOWED_GLOBAL_IDENTIFIER: |
69 | CHECK_SYMBOLS(3); |
70 | return vformat(R"(The %s "%s" has the same name as a %s.)" , symbols[0], symbols[1], symbols[2]); |
71 | case UNREACHABLE_CODE: |
72 | CHECK_SYMBOLS(1); |
73 | return vformat(R"*(Unreachable code (statement after return) in function "%s()".)*" , symbols[0]); |
74 | case UNREACHABLE_PATTERN: |
75 | return "Unreachable pattern (pattern after wildcard or bind)." ; |
76 | case STANDALONE_EXPRESSION: |
77 | return "Standalone expression (the line has no effect)." ; |
78 | case STANDALONE_TERNARY: |
79 | return "Standalone ternary conditional operator: the return value is being discarded." ; |
80 | case INCOMPATIBLE_TERNARY: |
81 | return "Values of the ternary conditional are not mutually compatible." ; |
82 | case PROPERTY_USED_AS_FUNCTION: |
83 | CHECK_SYMBOLS(2); |
84 | return vformat(R"*(The method "%s()" was not found in base "%s" but there's a property with the same name. Did you mean to access it?)*" , symbols[0], symbols[1]); |
85 | case CONSTANT_USED_AS_FUNCTION: |
86 | CHECK_SYMBOLS(2); |
87 | return vformat(R"*(The method "%s()" was not found in base "%s" but there's a constant with the same name. Did you mean to access it?)*" , symbols[0], symbols[1]); |
88 | case FUNCTION_USED_AS_PROPERTY: |
89 | CHECK_SYMBOLS(2); |
90 | return vformat(R"(The property "%s" was not found in base "%s" but there's a method with the same name. Did you mean to call it?)" , symbols[0], symbols[1]); |
91 | case UNTYPED_DECLARATION: |
92 | CHECK_SYMBOLS(2); |
93 | if (symbols[0] == "Function" ) { |
94 | return vformat(R"*(%s "%s()" has no static return type.)*" , symbols[0], symbols[1]); |
95 | } |
96 | return vformat(R"(%s "%s" has no static type.)" , symbols[0], symbols[1]); |
97 | case UNSAFE_PROPERTY_ACCESS: |
98 | CHECK_SYMBOLS(2); |
99 | return vformat(R"(The property "%s" is not present on the inferred type "%s" (but may be present on a subtype).)" , symbols[0], symbols[1]); |
100 | case UNSAFE_METHOD_ACCESS: |
101 | CHECK_SYMBOLS(2); |
102 | return vformat(R"*(The method "%s()" is not present on the inferred type "%s" (but may be present on a subtype).)*" , symbols[0], symbols[1]); |
103 | case UNSAFE_CAST: |
104 | CHECK_SYMBOLS(1); |
105 | return vformat(R"(The value is cast to "%s" but has an unknown type.)" , symbols[0]); |
106 | case UNSAFE_CALL_ARGUMENT: |
107 | CHECK_SYMBOLS(4); |
108 | return vformat(R"*(The argument %s of the function "%s()" requires a the subtype "%s" but the supertype "%s" was provided.)*" , symbols[0], symbols[1], symbols[2], symbols[3]); |
109 | case UNSAFE_VOID_RETURN: |
110 | CHECK_SYMBOLS(2); |
111 | return vformat(R"*(The method "%s()" returns "void" but it's trying to return a call to "%s()" that can't be ensured to also be "void".)*" , symbols[0], symbols[1]); |
112 | case RETURN_VALUE_DISCARDED: |
113 | CHECK_SYMBOLS(1); |
114 | return vformat(R"*(The function "%s()" returns a value that will be discarded if not used.)*" , symbols[0]); |
115 | case STATIC_CALLED_ON_INSTANCE: |
116 | CHECK_SYMBOLS(2); |
117 | return vformat(R"*(The function "%s()" is a static function but was called from an instance. Instead, it should be directly called from the type: "%s.%s()".)*" , symbols[0], symbols[1], symbols[0]); |
118 | case REDUNDANT_STATIC_UNLOAD: |
119 | return R"(The "@static_unload" annotation is redundant because the file does not have a class with static variables.)" ; |
120 | case REDUNDANT_AWAIT: |
121 | return R"("await" keyword not needed in this case, because the expression isn't a coroutine nor a signal.)" ; |
122 | case ASSERT_ALWAYS_TRUE: |
123 | return "Assert statement is redundant because the expression is always true." ; |
124 | case ASSERT_ALWAYS_FALSE: |
125 | return "Assert statement will raise an error because the expression is always false." ; |
126 | case INTEGER_DIVISION: |
127 | return "Integer division, decimal part will be discarded." ; |
128 | case NARROWING_CONVERSION: |
129 | return "Narrowing conversion (float is converted to int and loses precision)." ; |
130 | case INT_AS_ENUM_WITHOUT_CAST: |
131 | return "Integer used when an enum value is expected. If this is intended cast the integer to the enum type." ; |
132 | case INT_AS_ENUM_WITHOUT_MATCH: |
133 | CHECK_SYMBOLS(3); |
134 | return vformat(R"(Cannot %s %s as Enum "%s": no enum member has matching value.)" , symbols[0], symbols[1], symbols[2]); |
135 | case EMPTY_FILE: |
136 | return "Empty script file." ; |
137 | case DEPRECATED_KEYWORD: |
138 | CHECK_SYMBOLS(2); |
139 | return vformat(R"(The "%s" keyword is deprecated and will be removed in a future release, please replace its uses by "%s".)" , symbols[0], symbols[1]); |
140 | case RENAMED_IN_GODOT_4_HINT: |
141 | break; // Renamed identifier hint is taken care of by the GDScriptAnalyzer. No message needed here. |
142 | case CONFUSABLE_IDENTIFIER: |
143 | CHECK_SYMBOLS(1); |
144 | return vformat(R"(The identifier "%s" has misleading characters and might be confused with something else.)" , symbols[0]); |
145 | case CONFUSABLE_LOCAL_DECLARATION: |
146 | CHECK_SYMBOLS(2); |
147 | return vformat(R"(The %s "%s" is declared below in the parent block.)" , symbols[0], symbols[1]); |
148 | case CONFUSABLE_LOCAL_USAGE: |
149 | CHECK_SYMBOLS(1); |
150 | return vformat(R"(The identifier "%s" will be shadowed below in the block.)" , symbols[0]); |
151 | case INFERENCE_ON_VARIANT: |
152 | CHECK_SYMBOLS(1); |
153 | return vformat("The %s type is being inferred from a Variant value, so it will be typed as Variant." , symbols[0]); |
154 | case NATIVE_METHOD_OVERRIDE: |
155 | CHECK_SYMBOLS(2); |
156 | return vformat(R"*(The method "%s()" overrides a method from native class "%s". This won't be called by the engine and may not work as expected.)*" , symbols[0], symbols[1]); |
157 | case GET_NODE_DEFAULT_WITHOUT_ONREADY: |
158 | CHECK_SYMBOLS(1); |
159 | 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]); |
160 | case ONREADY_WITH_EXPORT: |
161 | return R"("@onready" will set the default value after "@export" takes effect and will override it.)" ; |
162 | case WARNING_MAX: |
163 | break; // Can't happen, but silences warning |
164 | } |
165 | ERR_FAIL_V_MSG(String(), "Invalid GDScript warning code: " + get_name_from_code(code) + "." ); |
166 | |
167 | #undef CHECK_SYMBOLS |
168 | } |
169 | |
170 | int GDScriptWarning::get_default_value(Code p_code) { |
171 | ERR_FAIL_INDEX_V_MSG(p_code, WARNING_MAX, WarnLevel::IGNORE, "Getting default value of invalid warning code." ); |
172 | return default_warning_levels[p_code]; |
173 | } |
174 | |
175 | PropertyInfo GDScriptWarning::get_property_info(Code p_code) { |
176 | // Making this a separate function in case a warning needs different PropertyInfo in the future. |
177 | if (p_code == Code::RENAMED_IN_GODOT_4_HINT) { |
178 | return PropertyInfo(Variant::BOOL, get_settings_path_from_code(p_code)); |
179 | } |
180 | return PropertyInfo(Variant::INT, get_settings_path_from_code(p_code), PROPERTY_HINT_ENUM, "Ignore,Warn,Error" ); |
181 | } |
182 | |
183 | String GDScriptWarning::get_name() const { |
184 | return get_name_from_code(code); |
185 | } |
186 | |
187 | String GDScriptWarning::get_name_from_code(Code p_code) { |
188 | ERR_FAIL_COND_V(p_code < 0 || p_code >= WARNING_MAX, String()); |
189 | |
190 | static const char *names[] = { |
191 | "UNASSIGNED_VARIABLE" , |
192 | "UNASSIGNED_VARIABLE_OP_ASSIGN" , |
193 | "UNUSED_VARIABLE" , |
194 | "UNUSED_LOCAL_CONSTANT" , |
195 | "UNUSED_PRIVATE_CLASS_VARIABLE" , |
196 | "UNUSED_PARAMETER" , |
197 | "UNUSED_SIGNAL" , |
198 | "SHADOWED_VARIABLE" , |
199 | "SHADOWED_VARIABLE_BASE_CLASS" , |
200 | "SHADOWED_GLOBAL_IDENTIFIER" , |
201 | "UNREACHABLE_CODE" , |
202 | "UNREACHABLE_PATTERN" , |
203 | "STANDALONE_EXPRESSION" , |
204 | "STANDALONE_TERNARY" , |
205 | "INCOMPATIBLE_TERNARY" , |
206 | "PROPERTY_USED_AS_FUNCTION" , |
207 | "CONSTANT_USED_AS_FUNCTION" , |
208 | "FUNCTION_USED_AS_PROPERTY" , |
209 | "UNTYPED_DECLARATION" , |
210 | "UNSAFE_PROPERTY_ACCESS" , |
211 | "UNSAFE_METHOD_ACCESS" , |
212 | "UNSAFE_CAST" , |
213 | "UNSAFE_CALL_ARGUMENT" , |
214 | "UNSAFE_VOID_RETURN" , |
215 | "RETURN_VALUE_DISCARDED" , |
216 | "STATIC_CALLED_ON_INSTANCE" , |
217 | "REDUNDANT_STATIC_UNLOAD" , |
218 | "REDUNDANT_AWAIT" , |
219 | "ASSERT_ALWAYS_TRUE" , |
220 | "ASSERT_ALWAYS_FALSE" , |
221 | "INTEGER_DIVISION" , |
222 | "NARROWING_CONVERSION" , |
223 | "INT_AS_ENUM_WITHOUT_CAST" , |
224 | "INT_AS_ENUM_WITHOUT_MATCH" , |
225 | "EMPTY_FILE" , |
226 | "DEPRECATED_KEYWORD" , |
227 | "RENAMED_IN_GODOT_4_HINT" , |
228 | "CONFUSABLE_IDENTIFIER" , |
229 | "CONFUSABLE_LOCAL_DECLARATION" , |
230 | "CONFUSABLE_LOCAL_USAGE" , |
231 | "INFERENCE_ON_VARIANT" , |
232 | "NATIVE_METHOD_OVERRIDE" , |
233 | "GET_NODE_DEFAULT_WITHOUT_ONREADY" , |
234 | "ONREADY_WITH_EXPORT" , |
235 | }; |
236 | |
237 | static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names." ); |
238 | |
239 | return names[(int)p_code]; |
240 | } |
241 | |
242 | String GDScriptWarning::get_settings_path_from_code(Code p_code) { |
243 | return "debug/gdscript/warnings/" + get_name_from_code(p_code).to_lower(); |
244 | } |
245 | |
246 | GDScriptWarning::Code GDScriptWarning::get_code_from_name(const String &p_name) { |
247 | for (int i = 0; i < WARNING_MAX; i++) { |
248 | if (get_name_from_code((Code)i) == p_name) { |
249 | return (Code)i; |
250 | } |
251 | } |
252 | |
253 | return WARNING_MAX; |
254 | } |
255 | |
256 | #endif // DEBUG_ENABLED |
257 | |