22// Parts of the project are originally copyright © Meta Platforms, Inc.
33// SPDX-License-Identifier: Apache-2.0
44
5- //! Module for expanding macros, as `assert!(cond, code)`. This are expanded to
6- //! the input AST before type checking. We also allow `assert!(cond)`, for Move 2,
7- //! which generates the "well-known" abort code `UNSPECIFIED_ABORT_CODE`.
5+ //! Module for expanding macros. Supported macros:
6+ //! - `assert!`
7+ //! - `assert_eq!`
8+ //! - `assert_ne!`
9+ //!
10+ //! These macros are expanded to the input AST before type checking.
11+ //!
12+ //! ## `assert!` macro
13+ //! Supported forms:
14+ //! - `assert!(cond)` - aborts with well-known code `UNSPECIFIED_ABORT_CODE`
15+ //! - `assert!(cond, exp)` - aborts with provided expression (either u64 or vector<u8>)
16+ //! - `assert!(cond, fmt, arg1, ..., argN)` - aborts with formatted message (1 ≤ N ≤ 4)
17+ //!
18+ //! ## `assert_eq!` and `assert_ne!` macros
19+ //! Supported forms:
20+ //! - `assert_eq!(left, right)` - aborts with default message
21+ //! - `assert_eq!(left, right, message)` - aborts with custom message
22+ //! - `assert_eq!(left, right, fmt, arg1, ..., argN)` - aborts with formatted message (1 ≤ N ≤ 4)
23+ //! - `assert_ne!` supports the same forms as `assert_eq!`
24+ //!
25+ //! ## Version requirements
26+ //! - `assert!(cond)` requires Move 2
27+ //! - `assert!(cond, fmt, arg1, ..., argN)` requires Move 2.4
28+ //! - `assert_eq!` and `assert_ne!` require Move 2.4
829
930use crate :: {
1031 builder:: exp_builder:: ExpTranslator ,
@@ -23,24 +44,17 @@ use move_core_types::account_address::AccountAddress;
2344use move_ir_types:: location:: { sp, Loc , Spanned } ;
2445use std:: fmt:: Display ;
2546
26- /// Maximum number of arguments we can format using `std::string_utils::format<N>`.
27- const MAX_ARGS : usize = 5 ;
47+ /// Maximum total number of arguments for string formatting, including the format string itself.
48+ /// Note that `string_utils::format<N>` takes N + 1 arguments: the format string + N format arguments.
49+ /// Currently, the last supported function is `format4`, which takes 5 total arguments, hence this is 5.
50+ const MAX_FORMAT_ARGS : usize = 5 ;
2851
2952#[ derive( Copy , Clone ) ]
3053enum AssertKind {
3154 Eq ,
3255 Ne ,
3356}
3457
35- impl AssertKind {
36- fn op ( & self ) -> & str {
37- match self {
38- AssertKind :: Eq => "==" ,
39- AssertKind :: Ne => "!=" ,
40- }
41- }
42- }
43-
4458impl Display for AssertKind {
4559 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
4660 match self {
@@ -100,7 +114,11 @@ impl ExpTranslator<'_, '_, '_> {
100114 /// } else {
101115 /// abort exp
102116 /// }
117+ /// Note that, while the macro does not explicitly enforce this constraint, this will only
118+ /// compile when `exp` is a `u64` (an abort code) or a `vector<u8>` (an abort message).
103119 /// ```
120+ /// Note that, while the macro does not explicitly enforce this constraint, this will only
121+ /// compile when `exp` is a `u64` (an abort code) or a `vector<u8>` (an abort message).
104122 ///
105123 /// More than two arguments:
106124 /// ```move
@@ -126,7 +144,7 @@ impl ExpTranslator<'_, '_, '_> {
126144 let cond = & args. value [ 0 ] ;
127145 let rest = & args. value [ 1 ..] ;
128146
129- let e = match rest. len ( ) {
147+ let abort_arg = match rest. len ( ) {
130148 0 => {
131149 // assert!(cond)
132150 self . check_language_version (
@@ -143,7 +161,7 @@ impl ExpTranslator<'_, '_, '_> {
143161 // assert!(cond, exp)
144162 rest[ 0 ] . clone ( )
145163 } ,
146- n if n <= MAX_ARGS => {
164+ n if n <= MAX_FORMAT_ARGS => {
147165 // assert!(cond, fmt, arg1, ..., argN)
148166 self . check_language_version (
149167 & self . to_loc ( & loc) ,
@@ -161,7 +179,7 @@ impl ExpTranslator<'_, '_, '_> {
161179 & self . to_loc ( & args. loc ) ,
162180 & format ! (
163181 "Macro `assert!` cannot take more than {} arguments" ,
164- MAX_ARGS + 1
182+ MAX_FORMAT_ARGS + 1
165183 ) ,
166184 ) ;
167185 return Exp_ :: UnresolvedError ;
@@ -171,7 +189,7 @@ impl ExpTranslator<'_, '_, '_> {
171189 Exp_ :: IfElse (
172190 Box :: new ( cond. clone ( ) ) ,
173191 Box :: new ( sp ( loc, Exp_ :: Unit { trailing : false } ) ) ,
174- Box :: new ( sp ( loc, Exp_ :: Abort ( Box :: new ( e ) ) ) ) ,
192+ Box :: new ( sp ( loc, Exp_ :: Abort ( Box :: new ( abort_arg ) ) ) ) ,
175193 )
176194 }
177195
@@ -288,7 +306,7 @@ impl ExpTranslator<'_, '_, '_> {
288306 self . call_format ( loc, assertion_failed_message, vec ! [ message, left, right] ) ,
289307 )
290308 } ,
291- n if n <= MAX_ARGS => {
309+ n if n <= MAX_FORMAT_ARGS => {
292310 // assert_eq!(left, right, fmt, arg1, ..., argN)
293311 let assertion_failed_message = Self :: assertion_failed_message ( loc, kind, true ) ;
294312 self . check_string_literal ( & rest[ 0 ] ) ;
@@ -304,7 +322,7 @@ impl ExpTranslator<'_, '_, '_> {
304322 & format ! (
305323 "Macro `{}` cannot take more than {} arguments" ,
306324 kind,
307- MAX_ARGS + 2 ,
325+ MAX_FORMAT_ARGS + 2 ,
308326 ) ,
309327 ) ;
310328 return Exp_ :: UnresolvedError ;
@@ -333,7 +351,7 @@ impl ExpTranslator<'_, '_, '_> {
333351 /// Calls `std::string_utils::format<N>(&fmt, arg1, ..., argN)` for 1 ≤ N ≤ 4.
334352 fn call_format ( & self , loc : Loc , fmt : Exp , args : Vec < Exp > ) -> Exp {
335353 let n = args. len ( ) ;
336- debug_assert ! ( ( 1 ..MAX_ARGS ) . contains( & n) ) ;
354+ debug_assert ! ( ( 1 ..MAX_FORMAT_ARGS ) . contains( & n) ) ;
337355 let borrow_fmt = sp ( loc, Exp_ :: Borrow ( false , Box :: new ( fmt) ) ) ;
338356 self . call_stdlib_function (
339357 loc,
@@ -420,7 +438,10 @@ impl ExpTranslator<'_, '_, '_> {
420438 }
421439
422440 fn assertion_failed_message ( loc : Loc , kind : AssertKind , args : bool ) -> Exp {
423- let op = kind. op ( ) ;
441+ let op = match kind {
442+ AssertKind :: Eq => "==" ,
443+ AssertKind :: Ne => "!=" ,
444+ } ;
424445 let str = if args {
425446 format ! ( "assertion `left {op} right` failed: {{}}\n left: {{}}\n right: {{}}" )
426447 } else {
0 commit comments