warnings.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "warnings.h" | ||
| 2 | |||
| 3 | #include "asar.h" | ||
| 4 | #include <assert.h> | ||
| 5 | #include <stdarg.h> | ||
| 6 | |||
| 7 | #include "interface-shared.h" | ||
| 8 | |||
| 9 | static int asar_num_warnings = 0; | ||
| 10 | bool suppress_all_warnings = false; | ||
| 11 | |||
| 12 | struct asar_warning_mapping | ||
| 13 | { | ||
| 14 | asar_warning_id warnid; | ||
| 15 | const char* name; | ||
| 16 | const char* message; | ||
| 17 | bool enabled; | ||
| 18 | bool enabled_default; | ||
| 19 | |||
| 20 | 6400 | asar_warning_mapping(asar_warning_id inwarnid, const char *iname, const char* inmessage, bool inenabled = true) | |
| 21 | 6400 | { | |
| 22 | 6400 | ++asar_num_warnings; | |
| 23 | |||
| 24 | 6400 | warnid = inwarnid; | |
| 25 | 6400 | name = iname; | |
| 26 | 6400 | message = inmessage; | |
| 27 | 6400 | enabled = inenabled; | |
| 28 | 6400 | enabled_default = inenabled; | |
| 29 | |||
| 30 | // RPG Hacker: Sanity check. This makes sure that the order | ||
| 31 | // of entries in asar_warnings matches with the order of | ||
| 32 | // constants in asar_warning_id. This is important because | ||
| 33 | // we access asar_warnings as an array. | ||
| 34 | // Would love to do this via static_assert(), but can't | ||
| 35 | // think of a way to do so. | ||
| 36 | 6400 | assert(warnid - warning_id_start == asar_num_warnings); | |
| 37 | 6400 | } | |
| 38 | }; | ||
| 39 | |||
| 40 | // Keep in sync with asar_warning_id. | ||
| 41 | // Both, enum mapping and order, must match. | ||
| 42 | #define WRN(name) warning_id_ ## name, "W" #name | ||
| 43 | static asar_warning_mapping asar_warnings[] = | ||
| 44 | { | ||
| 45 | { WRN(relative_path_used), "Relative %s path passed to asar_patch_ex() - please use absolute paths only to prevent undefined behavior!" }, | ||
| 46 | |||
| 47 | { WRN(rom_too_short), "ROM is too short to have a title. (Expected '%s')" }, | ||
| 48 | { WRN(rom_title_incorrect), "ROM title is incorrect. Expected '%s', got '%s'." }, | ||
| 49 | |||
| 50 | { WRN(65816_yy_x_does_not_exist), "($yy),x does not exist, assuming $yy,x." }, | ||
| 51 | { WRN(65816_xx_y_assume_16_bit), "%s $xx,y is not valid with 8-bit parameters, assuming 16-bit." }, | ||
| 52 | { WRN(spc700_assuming_8_bit), "This opcode does not exist with 16-bit parameters, assuming 8-bit." }, | ||
| 53 | |||
| 54 | { WRN(cross_platform_path), "This patch may not assemble cleanly on all platforms. Please use / instead." }, | ||
| 55 | |||
| 56 | { WRN(missing_org), "Missing org or freespace command." }, | ||
| 57 | { WRN(set_middle_byte), "It would be wise to set the 008000 bit of this address." }, | ||
| 58 | |||
| 59 | { WRN(unrecognized_special_command), "Unrecognized special command - your version of Asar might be outdated." }, | ||
| 60 | |||
| 61 | { WRN(freespace_leaked), "This freespace appears to be leaked." }, | ||
| 62 | |||
| 63 | { WRN(warn_command), "warn command%s" }, | ||
| 64 | |||
| 65 | { WRN(implicitly_sized_immediate), "Implicitly sized immediate.", false }, | ||
| 66 | |||
| 67 | { WRN(xkas_deprecated), "xkas support is being deprecated and will be removed in a future version of Asar. Please use an older version of Asar (<=1.50) if you need it." }, | ||
| 68 | { WRN(xkas_eat_parentheses), "xkas compatibility warning: Unlike xkas, Asar does not eat parentheses after defines." }, | ||
| 69 | { WRN(xkas_label_access), "xkas compatibility warning: Label access is always 24bit in emulation mode, but may be 16bit in native mode." }, | ||
| 70 | { WRN(xkas_warnpc_relaxed), "xkas conversion warning : warnpc is relaxed one byte in Asar." }, | ||
| 71 | { WRN(xkas_style_conditional), "xkas-style conditional compilation detected. Please use the if command instead." }, | ||
| 72 | { WRN(xkas_patch), "If you want to assemble an xkas patch, add ;@xkas at the top or you may run into a couple of problems." }, | ||
| 73 | { WRN(xkas_incsrc_relative), "xkas compatibility warning: incsrc and incbin look for files relative to the patch in Asar, but xkas looks relative to the assembler." }, | ||
| 74 | { WRN(convert_to_asar), "Convert the patch to native Asar format instead of making an Asar-only xkas patch." }, | ||
| 75 | |||
| 76 | { WRN(fixed_deprecated), "the 'fixed' parameter on freespace/freecode/freedata is deprecated - please use 'static' instead." }, | ||
| 77 | |||
| 78 | { WRN(autoclear_deprecated), "'autoclear' is deprecated - please use 'autoclean' instead." }, | ||
| 79 | |||
| 80 | { WRN(check_memory_file), "Accessing file '%s' which is not in memory while W%d is enabled.", false }, | ||
| 81 | |||
| 82 | { WRN(if_not_condition_deprecated), "'if !condition' is deprecated - please use 'if not(condition)' instead." }, | ||
| 83 | |||
| 84 | { WRN(function_redefined), "Function '%s' redefined." }, | ||
| 85 | |||
| 86 | { WRN(datasize_last_label), "Datasize used on last detected label '%s'." }, | ||
| 87 | { WRN(datasize_exceeds_size), "Datasize exceeds 0xFFFF for label '%s'." }, | ||
| 88 | |||
| 89 | { WRN(mapper_already_set), "A mapper has already been selected." }, | ||
| 90 | { WRN(feature_deprecated), "DEPRECATION NOTIFICATION: Feature \"%s\" is deprecated and will be REMOVED in the future. Please update your code to conform to newer styles. Suggested work around: %s." }, | ||
| 91 | |||
| 92 | { WRN(byte_order_mark_utf8), "UTF-8 byte order mark detected and skipped." }, | ||
| 93 | { WRN(optimization_settings), "In Asar 2.0, the default optimization settings will change to `optimize dp always` and `optimize address mirrors`, which changes this instruction's argument from %d to %d bytes. Either specify the desired settings manually or use explicit length suffixes to silence this warning." }, | ||
| 94 | }; | ||
| 95 | |||
| 96 | // RPG Hacker: Sanity check. This makes sure that the element count of asar_warnings | ||
| 97 | // matches with the number of constants in asar_warning_id. This is important, because | ||
| 98 | // we are going to access asar_warnings as an array. | ||
| 99 | static_assert(sizeof(asar_warnings) / sizeof(asar_warnings[0]) == warning_id_count, "asar_warnings and asar_warning_id are not in sync"); | ||
| 100 | |||
| 101 | 886 | void asar_throw_warning(int whichpass, asar_warning_id warnid, ...) | |
| 102 | { | ||
| 103 | 886 | if (pass == whichpass && !suppress_all_warnings) | |
| 104 | { | ||
| 105 | 390 | assert(warnid > warning_id_start && warnid < warning_id_end); | |
| 106 | |||
| 107 | 390 | const asar_warning_mapping& warning = asar_warnings[warnid - warning_id_start - 1]; | |
| 108 | |||
| 109 | 390 | if (warning.enabled) | |
| 110 | { | ||
| 111 | 46 | char warning_buffer[1024]; | |
| 112 | 46 | va_list args; | |
| 113 | 92 | va_start(args, warnid); | |
| 114 | |||
| 115 | #if defined(__clang__) | ||
| 116 | # pragma clang diagnostic push | ||
| 117 | // "format string is not a literal". | ||
| 118 | // The pointer we're passing here should always point to a string literal, | ||
| 119 | // thus, I think, we can safely ignore this here. | ||
| 120 | # pragma clang diagnostic ignored "-Wformat-nonliteral" | ||
| 121 | #endif | ||
| 122 | |||
| 123 | 92 | vsnprintf(warning_buffer, sizeof(warning_buffer), warning.message, args); | |
| 124 | |||
| 125 | #if defined(__clang__) | ||
| 126 | # pragma clang diagnostic pop | ||
| 127 | #endif | ||
| 128 | |||
| 129 | 92 | va_end(args); | |
| 130 | 92 | warn((int)warnid, warning_buffer); | |
| 131 | } | ||
| 132 | } | ||
| 133 | 886 | } | |
| 134 | |||
| 135 | 92 | const char* get_warning_name(asar_warning_id warnid) | |
| 136 | { | ||
| 137 | 92 | assert(warnid > warning_id_start && warnid < warning_id_end); | |
| 138 | |||
| 139 | 92 | const asar_warning_mapping& warning = asar_warnings[warnid - warning_id_start - 1]; | |
| 140 | |||
| 141 | 92 | return warning.name; | |
| 142 | } | ||
| 143 | |||
| 144 | |||
| 145 | |||
| 146 | 72 | void set_warning_enabled(asar_warning_id warnid, bool enabled) | |
| 147 | { | ||
| 148 | 72 | assert(warnid > warning_id_start && warnid < warning_id_end); | |
| 149 | |||
| 150 | 72 | asar_warning_mapping& warning = asar_warnings[warnid - warning_id_start - 1]; | |
| 151 | |||
| 152 | 72 | warning.enabled = enabled; | |
| 153 | 72 | } | |
| 154 | |||
| 155 | 72 | asar_warning_id parse_warning_id_from_string(const char* string, int warn_pass) | |
| 156 | { | ||
| 157 | 72 | const char* pos = string; | |
| 158 | |||
| 159 | 72 | if (pos == nullptr) | |
| 160 | { | ||
| 161 | ✗ | return warning_id_end; | |
| 162 | } | ||
| 163 | |||
| 164 | |||
| 165 | 72 | if (pos[0] == 'w' || pos[0] == 'W') | |
| 166 | { | ||
| 167 | 72 | ++pos; | |
| 168 | } | ||
| 169 | 1410 | for(int i = 0; i < warning_id_end-warning_id_start-1; i++) | |
| 170 | { | ||
| 171 | 1404 | if(!stricmpwithlower(pos, asar_warnings[i].name+1)) | |
| 172 | { | ||
| 173 | 66 | return asar_warnings[i].warnid; | |
| 174 | } | ||
| 175 | } | ||
| 176 | 6 | char* endpos = nullptr; | |
| 177 | 6 | int numid = (int)strtol(pos, &endpos, 10); | |
| 178 | |||
| 179 | 6 | if (endpos == nullptr || endpos[0] != '\0') | |
| 180 | { | ||
| 181 | ✗ | return warning_id_end; | |
| 182 | } | ||
| 183 | |||
| 184 | 6 | asar_warning_id warnid = (asar_warning_id)numid; | |
| 185 | |||
| 186 | 6 | if (warnid <= warning_id_start || warnid >= warning_id_end) | |
| 187 | { | ||
| 188 | ✗ | return warning_id_end; | |
| 189 | } | ||
| 190 | |||
| 191 | 6 | asar_throw_warning(warn_pass, warning_id_feature_deprecated, "Numerical warnings", "Please transition to Wwarning_name"); | |
| 192 | 6 | return warnid; | |
| 193 | } | ||
| 194 | |||
| 195 | ✗ | void reset_warnings_to_default() | |
| 196 | { | ||
| 197 | ✗ | for (int i = (int)(warning_id_start + 1); i < (int)warning_id_end; ++i) | |
| 198 | { | ||
| 199 | ✗ | asar_warning_mapping& warning = asar_warnings[i - (int)warning_id_start - 1]; | |
| 200 | |||
| 201 | ✗ | warning.enabled = warning.enabled_default; | |
| 202 | } | ||
| 203 | ✗ | } | |
| 204 | |||
| 205 | struct warnings_state | ||
| 206 | { | ||
| 207 | bool enabled[warning_id_count]; | ||
| 208 | }; | ||
| 209 | |||
| 210 | static autoarray<warnings_state> warnings_state_stack; | ||
| 211 | static warnings_state main_warnings_state; | ||
| 212 | |||
| 213 | 610 | void push_warnings(bool warnings_command) | |
| 214 | { | ||
| 215 | 305 | warnings_state current_state; | |
| 216 | |||
| 217 | 20130 | for (int i = 0; i < (int)warning_id_count; ++i) | |
| 218 | { | ||
| 219 | 19520 | current_state.enabled[i] = asar_warnings[i].enabled; | |
| 220 | } | ||
| 221 | |||
| 222 | 610 | if (warnings_command) | |
| 223 | { | ||
| 224 | 18 | warnings_state_stack.append(current_state); | |
| 225 | } | ||
| 226 | else | ||
| 227 | { | ||
| 228 | 592 | main_warnings_state = current_state; | |
| 229 | } | ||
| 230 | 610 | } | |
| 231 | |||
| 232 | 606 | void pull_warnings(bool warnings_command) | |
| 233 | { | ||
| 234 | 606 | if (warnings_state_stack.count > 0 || !warnings_command) | |
| 235 | { | ||
| 236 | 300 | warnings_state prev_state; | |
| 237 | |||
| 238 | 600 | if (warnings_command) | |
| 239 | { | ||
| 240 | 12 | prev_state = warnings_state_stack[warnings_state_stack.count - 1]; | |
| 241 | } | ||
| 242 | else | ||
| 243 | { | ||
| 244 | 588 | prev_state = main_warnings_state; | |
| 245 | } | ||
| 246 | |||
| 247 | 19800 | for (int i = 0; i < (int)warning_id_count; ++i) | |
| 248 | { | ||
| 249 | 19200 | asar_warnings[i].enabled = prev_state.enabled[i]; | |
| 250 | } | ||
| 251 | |||
| 252 | 600 | if (warnings_command) | |
| 253 | { | ||
| 254 | 12 | warnings_state_stack.remove(warnings_state_stack.count - 1); | |
| 255 | } | ||
| 256 | 300 | } | |
| 257 | else | ||
| 258 | { | ||
| 259 | 6 | asar_throw_error(0, error_type_block, error_id_pullwarnings_without_pushwarnings); | |
| 260 | } | ||
| 261 | 600 | } | |
| 262 | |||
| 263 | 588 | void verify_warnings() | |
| 264 | { | ||
| 265 | 588 | if (warnings_state_stack.count > 0) | |
| 266 | { | ||
| 267 | 6 | asar_throw_error(0, error_type_null, error_id_pushwarnings_without_pullwarnings); | |
| 268 | |||
| 269 | 6 | warnings_state_stack.reset(); | |
| 270 | } | ||
| 271 | 588 | } | |
| 272 |