hako_check: QuickFix (--fix-dry-run) expand to HC002 include→using, HC016 unused alias removal, HC014 entrypoint stub proposal; keep unified diff output

This commit is contained in:
nyash-codex
2025-11-08 23:53:28 +09:00
parent 1dcc944361
commit ec12094ff7

View File

@ -252,13 +252,59 @@ static box HakoAnalyzerBox {
// ---- QuickFix (text scope, MVP: HC003 using quoted) ----
_fix_text_quick(text) {
if text == null { return null }
// Only HC003: using 非引用→引用化
// QuickFix (text scope):
// - HC002 include → using
// - HC003 using 非引用 → 引用化
// - HC016 未使用 alias の削除using ... as Alias
// - HC014 missing entrypoint の空スタブ提案
local lines = me._split_lines(text)
local changed = 0
local out = new ArrayBox()
// 事前: alias 宣言の収集(行番号と別名)
local alias_lines = new ArrayBox() // of Map {idx, alias}
local i = 0
while i < lines.size() {
local ln = lines.get(i)
// HC002: include → using相対→modulesの推測はしない文字列そのまま移行
{
local ltrim = me._ltrim(ln)
if ltrim.indexOf("include \"") == 0 {
// preserve indentation and trailing content (;等)
local head_len = ln.indexOf("include \"")
if head_len < 0 { head_len = 0 }
local head = ln.substring(0, head_len)
local rest = ln.substring(head_len)
// rest begins with include "
local after = rest.substring(8) // length("include ") = 8
local newline = head + "using " + after
if newline != ln { changed = 1; out.push(newline) } else { out.push(ln) }
i = i + 1
continue
}
}
// HC016 用: using ... as Alias の抽出
{
local ltrim2 = me._ltrim(ln)
if ltrim2.indexOf("using ") == 0 && ltrim2.indexOf(" as ") > 0 {
// パターン: using "X" as Alias もしくは using X as Alias
local aspos = ltrim2.indexOf(" as ")
local tail = ltrim2.substring(aspos + 4)
// Alias の終端は空白/セミコロン/行末
local j = 0; local alias = ""
while j < tail.length() {
local ch = tail.substring(j,j+1)
if ch == " " || ch == "\t" || ch == ";" { break }
alias = alias + ch
j = j + 1
}
if alias != "" {
local ent = new MapBox(); ent.set("idx", i); ent.set("alias", alias); alias_lines.push(ent)
}
}
}
// HC003: using 非引用 → 引用化
local ltrim = me._ltrim(ln)
if ltrim.indexOf("using ") == 0 && ltrim.indexOf('using "') != 0 {
// rewrite: using X ... -> using "X" ...X は最初の空白/セミコロン/"as" まで)
@ -289,11 +335,67 @@ static box HakoAnalyzerBox {
}
i = i + 1
}
// HC016: 未使用 alias の削除(安全に、完全一致のみ削除)
if alias_lines.size() > 0 {
// 再結合した out を基にテキストを作り、使用有無チェック
local cur_text = ""; local k=0; while k<out.size() { cur_text = cur_text + out.get(k); if k<out.size()-1 { cur_text = cur_text + "\n" } k=k+1 }
// 逆順に処理して行番号のズレを回避
local ai = alias_lines.size() - 1
while ai >= 0 {
local ent = alias_lines.get(ai)
local idx = ent.get("idx")
local alias = ent.get("alias")
if me._alias_unused(cur_text, alias) == 1 {
// remove that line from out if matches using ... as alias
// ただし out の該当行が using でない場合はスキップ
if idx >= 0 && idx < out.size() {
local l = me._ltrim(out.get(idx))
if l.indexOf("using ") == 0 && l.indexOf(" as ") > 0 && l.indexOf(" " + alias) > 0 {
// delete
out.set(idx, null)
changed = 1
}
}
}
ai = ai - 1
}
// null を除去
local out2 = new ArrayBox(); local z=0; while z<out.size() { if out.get(z)!=null { out2.push(out.get(z)) } z=z+1 }
out = out2
}
// HC014: missing entrypoint → 空スタブ提案
local joined = ""; local jj=0; while jj<out.size() { joined = joined + out.get(jj); if jj<out.size()-1 { joined = joined + "\n" } jj=jj+1 }
if me._has_entrypoint(joined) == 0 {
// append stub
local stub = "\nstatic box Main { method main(args) { return 0 } }"
joined = joined + stub
changed = 1
// split again for diff
out = me._split_lines(joined)
}
if changed == 0 { return text }
// Join with \n
local j = 0; local buf = ""; while j < out.size() { buf = buf + out.get(j); if j < out.size()-1 { buf = buf + "\n" } j = j + 1 }
return buf
}
_alias_unused(text, alias) {
if text == null || alias == null { return 0 }
// 使用判定は保守的に: 'Alias.' / 'Alias(' / 'Alias ' / 'Alias::' のいずれかが存在すれば使用中とみなす
local pats = new ArrayBox(); pats.push(alias+"."); pats.push(alias+"("); pats.push(alias+" "); pats.push(alias+"::")
local i = 0; while i < pats.size() {
if text.indexOf(pats.get(i)) >= 0 { return 0 }
i = i + 1
}
return 1
}
_has_entrypoint(text) {
if text == null { return 1 }
// Rough check: presence of 'method main(' is considered as entrypoint
if text.indexOf("method main(") >= 0 { return 1 }
return 0
}
_print_unified_diff(path, before, after) {
if before == null || after == null || before == after { return 0 }
// Minimal unified diff for whole file replacementMVP