An automated code transformer that migrates Move 1 syntax to Move 2. Applies all transformations in place, running multiple passes until no more edits are found.
Usage
move1-to-move2 file.move [file2.move ...]
Exit codes: 0 = success, 1 = error.
Transformations
| Move 1 | Move 2 | Rule |
|---|---|---|
borrow_global<T>(addr) |
&T[addr] |
index syntax |
borrow_global_mut<T>(addr) |
&mut T[addr] |
index syntax |
*borrow_global<T>(addr) |
T[addr] |
deref cancels & |
&borrow_global<T>(addr) |
&T[addr] |
redundant borrow absorbed |
borrow_global<T>(addr).field |
T[addr].field |
field access strips prefix |
borrow_global<T>(addr) in spec |
T[addr] |
spec block value semantics |
vector::borrow(&v, i) |
&v[i] |
vector index syntax |
vector::borrow_mut(&mut v, i) |
&mut v[i] |
vector index syntax |
*vector::borrow(&v, i) |
v[i] |
deref cancels & |
vector::empty<T>() |
vector<T>[] |
vector literal |
vector::push_back(&mut v, e) |
v.push_back(e) |
receiver style |
vector::length(&v) |
v.length() |
receiver style |
option::is_some(&o) |
o.is_some() |
receiver style |
string::length(&s) |
s.length() |
receiver style |
table::add(&mut t, k, v) |
t.add(k, v) |
receiver style |
simple_map::contains_key(&m, &k) |
m.contains_key(&k) |
receiver style |
(x as u64) |
x as u64 |
cast paren removal |
fun f() acquires T { } |
fun f() { } |
strip acquires |
public(friend) fun f() |
friend fun f() |
visibility |
public(package) fun f() |
package fun f() |
visibility |
x = x + y |
x += y |
compound assign (+,-,*,/,%) |
let i = 0; while (i < n) { ...; i = i + 1; } |
for (i in 0..n) { ...; } |
while to for |
borrow_global / borrow_global_mut → index syntax
Converts global storage access to the Move 2 resource indexing syntax.
# Before borrow_global<Counter>(addr) borrow_global_mut<Counter>(addr) *borrow_global<Counter>(addr) # After &Counter[addr] &mut Counter[addr] Counter[addr]
The tool handles several edge cases automatically:
- Dereference:
*borrow_global<T>(addr)→T[addr](the*and&cancel) - Redundant borrow:
&borrow_global<T>(addr)→&T[addr](absorbs the outer&) - Field access:
borrow_global<T>(addr).field→T[addr].field(no prefix when followed by.) - Spec blocks:
borrow_globalinside spec blocks omits the&prefix (spec semantics differ)
vector::borrow / vector::borrow_mut → index syntax
Converts vector element access to index notation, with the same edge-case handling as borrow_global.
# Before vector::borrow(&v, i) vector::borrow_mut(&mut v, i) *vector::borrow(&v, i) # After &v[i] &mut v[i] v[i]
vector::empty → vector literal
# Before vector::empty<u64>() # After vector<u64>[]
receiver_style — stdlib dot-call syntax
Converts fully-qualified stdlib calls to receiver-style (dot) syntax. The compiler auto-borrows the first argument, so &/&mut wrappers are stripped.
# Before vector::push_back(&mut v, 42); vector::length(&v); option::is_some(&o); string::append(&mut s, other); # After v.push_back(42); v.length(); o.is_some(); s.append(other);
Supported modules: vector, option, string, table, smart_table, smart_vector, simple_map.
cast_parens
Removes unnecessary parentheses around as casts (Move 2 allows casts as top-level expressions).
# Before let x = (amount as u64); # After let x = amount as u64;
strip_acquires
Removes acquires clauses, which are no longer needed in Move 2.
# Before public fun get(addr: address): u64 acquires Counter { ... } # After public fun get(addr: address): u64 { ... }
visibility
Simplifies visibility modifiers.
# Before public(friend) fun f() {} public(package) fun f() {} # After friend fun f() {} package fun f() {}
compound_assign
Converts x = x op y patterns to compound assignment operators.
# Before counter.value = counter.value + 1; x = x * y; # After counter.value += 1; x *= y;
Supports +, -, *, /, %. Only triggers when the left-hand side of the assignment exactly matches the left operand of the binary expression.
while_to_for
Converts counter-based while loops to Move 2 for loops with ranges.
# Before
let i = 0;
while (i < len) {
do_thing(i);
i = i + 1;
};
# After
for (i in 0..len) {
do_thing(i);
};Requirements for conversion:
- A
let i = 0;(or other literal) immediately precedes thewhile - The condition is
i < bound - The last statement in the loop body is
i = i + 1 - The loop variable
iis not used after thewhileloop (sinceforscopes it to the loop body)