aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorblasty <blasty@local>2026-07-10 13:30:48 +0200
committerblasty <blasty@local>2026-07-10 13:30:48 +0200
commit6d83015906f850b872261655f014ef14612a8ba5 (patch)
tree40278c66ac828e7073b74d9d0728c1473293779d /tests
parentstruct editor: auto-format the definition on save (diff)
downloadida-tui-6d83015906f850b872261655f014ef14612a8ba5.tar.gz
ida-tui-6d83015906f850b872261655f014ef14612a8ba5.tar.xz
ida-tui-6d83015906f850b872261655f014ef14612a8ba5.zip
retype: set variable/function types with 'y' (IDA-style), via structured tools
Instead of parsing pseudocode text, add structured server tools (server/ patch_server.py, alongside del_type): - func_types(addr): prototype + local variables (name/type/is_arg) - set_lvar_type(addr,var,type): retype a decompiler local, working on auto/ register vars too (stock set_type only updates already-user-modified lvars) 'y' in a code view retypes what's under the cursor: a local variable (prompt prefilled with its current type) or a function (prompt prefilled with its full prototype). Applies via set_lvar_type / set_type, then recompiles + refreshes. Copy-line moved off 'y' to Ctrl+Y. domain: Program.func_types / set_function_type / set_lvar_type (LVar/FuncTypes). Pilot: function-prototype retype (prefill + apply). full suite 94/94.
Diffstat (limited to 'tests')
-rw-r--r--tests/test_tui.py40
1 files changed, 37 insertions, 3 deletions
diff --git a/tests/test_tui.py b/tests/test_tui.py
index 6a8f898..84037a2 100644
--- a/tests/test_tui.py
+++ b/tests/test_tui.py
@@ -295,13 +295,13 @@ async def run(db):
view.model.cached_line(view.cursor) is not None)
check("status shows an address", "@ 0x" in str(status.render()), str(status.render()))
- # 'y' copies the current code line to the clipboard.
+ # Ctrl+Y copies the current code line to the clipboard.
view.focus()
cur_line = view._line_plain(view.cursor)
app._clipboard = ""
- await pilot.press("y")
+ await pilot.press("ctrl+y")
await wait_until(pilot, lambda: app._clipboard == cur_line, 10)
- check("'y' copies the current code line to the clipboard",
+ check("Ctrl+Y copies the current code line to the clipboard",
bool(cur_line) and app._clipboard == cur_line, f"clip={app._clipboard!r}")
# Jump to bottom of a (possibly huge) function; must not hang.
@@ -976,6 +976,40 @@ async def run(db):
else:
check("found a pseudocode line to comment", False, "no marker line")
+ # Retype ('y') a FUNCTION prototype: the prompt prefills the current
+ # prototype (from the structured func_types tool) and applying a new
+ # one updates it. (Local-var retyping uses the same seam via
+ # set_lvar_type; it's not asserted here because Hex-Rays restructures
+ # some vars on retype, making a deterministic check fragile.)
+ cf = app._cur # whatever function is actually loaded now
+ await wait_until(pilot, lambda: dec.loaded_ea == cf.ea, 15)
+ fts = app.program.func_types(cf.ea)
+ if fts is not None and fts.prototype:
+ old_proto = fts.prototype
+ nm_col = dec._texts[0].find(cf.name)
+ dec.focus()
+ dec.cursor, dec.cursor_x = 0, (nm_col + 1 if nm_col >= 0 else 0)
+ dec.refresh()
+ await pilot.pause(0.05)
+ await pilot.press("y")
+ await wait_until(pilot, lambda: app.query_one("#retype", Input).display, 10)
+ ri = app.query_one("#retype", Input)
+ check("'y' on a function prefills its prototype",
+ ri.display and ri.value == old_proto,
+ f"val={ri.value!r} want={old_proto!r}")
+ ri.value = f"void __fastcall {cf.name}(int zz_retype_arg)"
+ await pilot.press("enter")
+ await wait_until(
+ pilot, lambda: (lambda f: bool(f) and "zz_retype_arg" in f.prototype)(
+ app.program.func_types(cf.ea)), 20)
+ after = app.program.func_types(cf.ea)
+ check("applying a retype changes the function prototype",
+ after is not None and "zz_retype_arg" in after.prototype,
+ f"proto={after.prototype if after else None!r}")
+ app.program.set_function_type(cf.ea, old_proto) # restore
+ else:
+ check("got structured function types (func_types tool)", False)
+
# Decompiler follow of a name NOT in refs (the function's own name).
await pilot.press("g")
await pilot.pause(0.1)