saqut-compiler/src/diagnostic/diagnostic.hpp

179 lines
7.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ============================================================================
// saQut Compiler — Tanılama (Diagnostic) Veri Yapıları + Hata Kataloğu
// ============================================================================
//
// DİZİN: src/diagnostic/diagnostic.hpp
// KATMAN: Katman 0 — Tüm analiz katmanları tarafından kullanılır
// BAĞIMLI: src/core/location.hpp
// KULLANAN: DiagnosticEngine, sembol toplayıcı (Faz 2), tip denetleyici (Faz 3)
//
// AMAÇ:
// Derleme sırasında bulunan hata/uyarıları YAPISAL veri olarak temsil eder.
// "Veri birincil, insan-okur metin bir görünümdür" (readme → Tasarım felsefesi):
// bir Diagnostic; seviye + kod + konum + mesaj taşır; ekrana basılan satır
// bunun yalnızca bir render'ıdır. Bu sayede aynı tanı LSP, AI veya `saqut
// explain` tarafından da tüketilebilir.
//
// HATA KATALOĞU (baştan sabitlenir — yeni kodlar buraya eklenir):
// E001 Tanımsız değişken/isim (declare-before-use ihlali dâhil) Faz 2/3
// E002 Aynı scope'ta çift tanım Faz 2
// E003 Tip uyuşmazlığı (gizli dönüşüm yok, ADR-010) Faz 3
// E004 Döngü/switch dışı break/continue Faz 3
// E005 Fonksiyon dışı return Faz 3
// E006 Return tipi imzaya uymuyor Faz 3
// E007 Tanımsız tip (bilinmeyen tip adı) Faz 2/3
// E008 Fonksiyon çağrısı argüman sayısı/tipi uyuşmuyor Faz 3
// E009 Array boyutu sabit değil / geçersiz Faz 3
// E010 Özyinelemeli/döngüsel struct (by-value çevrim → sonsuz boyut) Faz 2/3
// W001 Kullanılmayan değişken Faz 4
// W002 Sıfıra bölme (sabit folding) Faz 4
// W003 Erişilemez (ölü) kod Faz 4
//
// ============================================================================
#ifndef SAQUT_DIAGNOSTIC_DIAGNOSTIC
#define SAQUT_DIAGNOSTIC_DIAGNOSTIC
#include <string>
#include <vector>
#include "core/location.hpp"
// ============================================================================
// DiagLevel — Tanı seviyesi
// ============================================================================
enum class DiagLevel { Error, Warning, Note, Hint };
inline const char* diagLevelName(DiagLevel l) {
switch (l) {
case DiagLevel::Error: return "error";
case DiagLevel::Warning: return "warning";
case DiagLevel::Note: return "note";
case DiagLevel::Hint: return "hint";
}
return "?";
}
// İnsan-okur çıktı için Türkçe karşılık
inline const char* diagLevelNameTr(DiagLevel l) {
switch (l) {
case DiagLevel::Error: return "hata";
case DiagLevel::Warning: return "uyarı";
case DiagLevel::Note: return "not";
case DiagLevel::Hint: return "ipucu";
}
return "?";
}
// JSON string kaçışı (mesaj/ipucu tırnak veya satır sonu içerebilir)
inline std::string jsonEscape(const std::string& s) {
std::string out;
out.reserve(s.size() + 8);
for (char c : s) {
switch (c) {
case '"': out += "\\\""; break;
case '\\': out += "\\\\"; break;
case '\n': out += "\\n"; break;
case '\r': out += "\\r"; break;
case '\t': out += "\\t"; break;
default: out += c; break;
}
}
return out;
}
// ============================================================================
// Diagnostic — Tek bir tanı (hata/uyarı/not/ipucu)
// ============================================================================
//
// KULLANIM:
// Diagnostic d{DiagLevel::Error, "E003", loc, "int'e string atanamaz"};
// d.hint = "açık dönüşüm gerekiyor";
// std::cout << d.toJson();
// ============================================================================
struct Diagnostic {
DiagLevel level = DiagLevel::Error;
std::string code; // "E003" (katalog kodu; boş olabilir)
SourceLocation loc; // hatanın kaynak koddaki yeri
std::string message; // bağlama özel açıklama
std::string hint; // opsiyonel "şunu dene" önerisi
std::string toJson() const {
std::string s = "{";
s += "\"level\":\""; s += diagLevelName(level); s += "\",";
s += "\"code\":\""; s += jsonEscape(code); s += "\",";
s += "\"location\":"; s += loc.toJson(); s += ",";
s += "\"message\":\""; s += jsonEscape(message); s += "\"";
if (!hint.empty()) {
s += ",\"hint\":\""; s += jsonEscape(hint); s += "\"";
}
s += "}";
return s;
}
};
// ============================================================================
// Hata Kataloğu — kod → (seviye, kanonik başlık)
// ============================================================================
//
// Bağlama özel mesaj report sırasında verilir; buradaki başlık, kodun GENEL
// anlamıdır (ileride `saqut explain E003` bunu kullanabilir, #107/#98).
// ============================================================================
struct DiagInfo {
const char* code;
DiagLevel level;
const char* title;
};
inline const std::vector<DiagInfo>& diagnosticCatalog() {
static const std::vector<DiagInfo> catalog = {
{"E001", DiagLevel::Error, "Tanımsız değişken/isim"},
{"E002", DiagLevel::Error, "Aynı scope'ta çift tanım"},
{"E003", DiagLevel::Error, "Tip uyuşmazlığı"},
{"E004", DiagLevel::Error, "Döngü/switch dışı break/continue"},
{"E005", DiagLevel::Error, "Fonksiyon dışı return"},
{"E006", DiagLevel::Error, "Return tipi imzaya uymuyor"},
{"E007", DiagLevel::Error, "Tanımsız tip"},
{"E008", DiagLevel::Error, "Fonksiyon çağrısı argümanı uyuşmuyor"},
{"E009", DiagLevel::Error, "Array boyutu sabit değil / geçersiz"},
{"E010", DiagLevel::Error, "Özyinelemeli/döngüsel struct tanımı"},
{"W001", DiagLevel::Warning, "Kullanılmayan değişken"},
{"W002", DiagLevel::Warning, "Sıfıra bölme (sabit ifade)"},
{"W003", DiagLevel::Warning, "Erişilemez (ölü) kod"},
};
return catalog;
}
// Kod kataloğda var mı? (yoksa nullptr)
inline const DiagInfo* findDiag(const std::string& code) {
for (const auto& d : diagnosticCatalog())
if (code == d.code) return &d;
return nullptr;
}
// Bir koddan Diagnostic üretir; seviye kataloğdan çözülür (yoksa: E→Error,
// W→Warning, diğer→Note). Bağlama özel mesajı çağıran verir.
inline Diagnostic makeDiagnostic(const std::string& code,
const SourceLocation& loc,
const std::string& message,
const std::string& hint = "") {
DiagLevel level = DiagLevel::Note;
if (const DiagInfo* info = findDiag(code)) {
level = info->level;
} else if (!code.empty()) {
if (code[0] == 'E') level = DiagLevel::Error;
else if (code[0] == 'W') level = DiagLevel::Warning;
}
Diagnostic d;
d.level = level;
d.code = code;
d.loc = loc;
d.message = message;
d.hint = hint;
return d;
}
#endif // SAQUT_DIAGNOSTIC_DIAGNOSTIC