jak-project/decompiler/Function/Warnings.h
Hat Kid da5aef8d60
decomp: finish target-[util|darkjak|swim|gun] | water | water-anim | crates | dark-eco-pool, fix skelgroup detection, add failed store/load warnings and clean up jak 3 config (#1958)
Almost done:
- `target-handler` (`(none)` event handler casts and CFG error)
- `target2` (`(none)` event handler casts)
- `powerups` (`cloud-track` does some weird stuff with `handle`s)
- `gun-states` (CFG error)

Some progress in:
- `water-flow`

Additionally:

- Clean up the two year old Jak 3 config file and add a config skeleton
(disassembling seems to not have worked, but I was able to dump obj
files and the `all_scripts` file)
- Fix automatic skelgroup detection and `defskelgroup` macro for Jak 2
(closes #1950)
- When a function decompiles without any major errors, a warning is
generated with the op id for each unresolved load and store that will
likely fail to compile (closes #1933)
2022-10-14 19:35:57 -04:00

116 lines
3.2 KiB
C++

#pragma once
#include <algorithm>
#include <stdexcept>
#include <string>
#include <unordered_set>
#include <vector>
#include "common/util/Assert.h"
#include "third-party/fmt/core.h"
namespace decompiler {
class DecompWarnings {
public:
DecompWarnings() = default;
template <typename... Args>
void warning(const std::string& str, Args&&... args) {
_warning(Warning::Kind::WARN, false, str, std::forward<Args>(args)...);
}
template <typename... Args>
void unique_warning(const std::string& str, Args&&... args) {
_warning(Warning::Kind::WARN, true, str, std::forward<Args>(args)...);
}
template <typename... Args>
void error(const std::string& str, Args&&... args) {
_warning(Warning::Kind::ERR, false, str, std::forward<Args>(args)...);
}
template <typename... Args>
void unique_error(const std::string& str, Args&&... args) {
_warning(Warning::Kind::ERR, true, str, std::forward<Args>(args)...);
}
template <typename... Args>
void error_and_throw(const std::string& str, Args&&... args) {
auto text = fmt::format(str, std::forward<Args>(args)...);
_warning(Warning::Kind::ERR, false, text);
throw std::runtime_error(text);
}
template <typename... Args>
void info(const std::string& str, Args&&... args) {
_warning(Warning::Kind::INFO, false, str, std::forward<Args>(args)...);
}
template <typename... Args>
void unique_info(const std::string& str, Args&&... args) {
_warning(Warning::Kind::INFO, true, str, std::forward<Args>(args)...);
}
bool has_warnings() const { return !m_warnings.empty(); }
bool has_errors() const {
return !m_warnings.empty() &&
std::any_of(m_warnings.begin(), m_warnings.end(),
[](const Warning& warn) { return warn.warning_kind == Warning::Kind::ERR; });
}
std::string get_warning_text(bool as_comment) const {
std::string result;
for (auto& w : m_warnings) {
if (as_comment) {
result += ";; ";
}
result += w.print();
}
return result;
}
private:
// Add warnings without thinking about it, if you say they should be unique, only max of 1 will be
// logged with that same text.
std::unordered_set<std::string> unique_warnings;
struct Warning {
enum class Kind { INFO, WARN, ERR };
Warning(Kind kind, std::string text) : warning_kind(kind), message(std::move(text)) {}
std::string print() const {
switch (warning_kind) {
case Kind::INFO:
return fmt::format("INFO: {}\n", message);
case Kind::WARN:
return fmt::format("WARN: {}\n", message);
case Kind::ERR:
return fmt::format("ERROR: {}\n", message);
default:
ASSERT(false);
return {};
}
}
Kind warning_kind;
std::string message;
};
template <typename... Args>
void _warning(Warning::Kind kind, bool unique, const std::string& str, Args&&... args) {
std::string msg = fmt::format(str, std::forward<Args>(args)...);
if (unique) {
if (unique_warnings.find(msg) != unique_warnings.end()) {
return;
}
unique_warnings.insert(msg);
}
Warning warn(kind, msg);
m_warnings.push_back(warn);
}
std::vector<Warning> m_warnings;
};
} // namespace decompiler