[m-rev.] for review: Improve error message for options that require int arguments.

Peter Wang novalazy at gmail.com
Wed May 20 16:14:21 AEST 2026


The getopt modules reported an unexpected argument to an option taking
an integer argument with the wording:

    option `--FOO' requires a numeric argument; `ARG' is not numeric

but this could be misleading when ARG is a floating point value,
or ARG is an integer value that is out of range of a Mercury int.

library/getopt.m:
library/getopt_io.m:
    Replace the "numeric argument" wording with more precise
    error messages.

diff --git a/library/getopt.m b/library/getopt.m
index fa0f9049e..18f7418a3 100644
--- a/library/getopt.m
+++ b/library/getopt.m
@@ -404,9 +404,15 @@
             % error.
             % The argument is a string describing the error.
 
-    ;       requires_numeric_argument(string)
-            % The option requires a numeric argument but it occurred on the
-            % command line with a non-numeric argument.
+    ;       requires_int_argument(string)
+            % The option requires an integer argument but it occurred on the
+            % command line with a non-integer argument.
+            % The argument gives the contents of the argument position on the
+            % command line.
+
+    ;       int_argument_out_of_range(string)
+            % The option requires an integer argument but the provided argument
+            % is not within the range of a Mercury `int'.
             % The argument gives the contents of the argument position on the
             % command line.
 
@@ -1593,7 +1599,7 @@ record_unnegated_short_options(ShortOptionPred, OptionTable, [Opt | Opts0],
                     MaybeError = no_option_error
                 ;
                     MaybeOV = no,
-                    numeric_argument_error(Flag, OptName, OptArg, Error),
+                    int_argument_error(Flag, OptName, OptArg, Error),
                     MaybeError = found_option_error(Error)
                 )
             ;
@@ -1708,7 +1714,7 @@ record_option_int(Arg0, MaybeSepArg, Flag, OptName, OptionData, Arg, Result) :-
         Result = ror_long_option(Arg0, MaybeSepArg, OV)
     ;
         MaybeOV = no,
-        numeric_argument_error(Flag, OptName, Arg, Error),
+        int_argument_error(Flag, OptName, Arg, Error),
         Result = ror_error(Error)
     ).
 
@@ -2062,13 +2068,32 @@ process_special_option(SpecialHandler, Flag, OptName, OptionData,
 
 %---------------------------------------------------------------------------%
 
-:- pred numeric_argument_error(OptionType::in, string::in, string::in,
+:- pred int_argument_error(OptionType::in, string::in, string::in,
     option_error(OptionType)::out) is det.
 
-numeric_argument_error(Flag, OptName, Arg, Error) :-
-    Reason = requires_numeric_argument(Arg),
+int_argument_error(Flag, OptName, Arg, Error) :-
+    ( if valid_int_syntax(Arg) then
+        Reason = int_argument_out_of_range(Arg)
+    else
+        Reason = requires_int_argument(Arg)
+    ),
     Error = option_error(Flag, OptName, Reason).
 
+:- pred valid_int_syntax(string::in) is semidet.
+
+valid_int_syntax(Arg) :-
+    string.first_char(Arg, FirstChar, Suffix),
+    ( if
+        ( FirstChar = ('+')
+        ; FirstChar = ('-')
+        )
+    then
+        Suffix \= ""
+    else
+        char.is_digit(FirstChar)
+    ),
+    string.is_all_digits(Suffix).
+
 %---------------------------------------------------------------------------%
 
 lookup_bool_option(OptionTable, Opt) = Bool :-
@@ -2179,10 +2204,15 @@ option_error_to_string(Error) = String :-
         ;
             Reason = special_handler_error(String)
         ;
-            Reason = requires_numeric_argument(Arg),
-            string.format(
-                "option `%s' requires a numeric argument; `%s' is not numeric",
+            Reason = requires_int_argument(Arg),
+            string.format("option `%s' requires an integer argument; " ++
+                "`%s' is not an integer",
                 [s(OptionName), s(Arg)], String)
+        ;
+            Reason = int_argument_out_of_range(_Arg),
+            string.format("option `%s' has an integer argument " ++
+                "that is out of range",
+                [s(OptionName)], String)
         ;
             Reason = file_special_but_no_io(FileName),
             Msg = "the option processing predicate has no access to I/O",
diff --git a/library/getopt_io.m b/library/getopt_io.m
index 11f277bdf..86af96796 100644
--- a/library/getopt_io.m
+++ b/library/getopt_io.m
@@ -408,9 +408,15 @@
             % error.
             % The argument is a string describing the error.
 
-    ;       requires_numeric_argument(string)
-            % The option requires a numeric argument but it occurred on the
-            % command line with a non-numeric argument.
+    ;       requires_int_argument(string)
+            % The option requires an integer argument but it occurred on the
+            % command line with a non-integer argument.
+            % The argument gives the contents of the argument position on the
+            % command line.
+
+    ;       int_argument_out_of_range(string)
+            % The option requires an integer argument but the provided argument
+            % is not within the range of a Mercury `int'.
             % The argument gives the contents of the argument position on the
             % command line.
 
@@ -1597,7 +1603,7 @@ record_unnegated_short_options(ShortOptionPred, OptionTable, [Opt | Opts0],
                     MaybeError = no_option_error
                 ;
                     MaybeOV = no,
-                    numeric_argument_error(Flag, OptName, OptArg, Error),
+                    int_argument_error(Flag, OptName, OptArg, Error),
                     MaybeError = found_option_error(Error)
                 )
             ;
@@ -1712,7 +1718,7 @@ record_option_int(Arg0, MaybeSepArg, Flag, OptName, OptionData, Arg, Result) :-
         Result = ror_long_option(Arg0, MaybeSepArg, OV)
     ;
         MaybeOV = no,
-        numeric_argument_error(Flag, OptName, Arg, Error),
+        int_argument_error(Flag, OptName, Arg, Error),
         Result = ror_error(Error)
     ).
 
@@ -2066,13 +2072,32 @@ process_special_option(SpecialHandler, Flag, OptName, OptionData,
 
 %---------------------------------------------------------------------------%
 
-:- pred numeric_argument_error(OptionType::in, string::in, string::in,
+:- pred int_argument_error(OptionType::in, string::in, string::in,
     option_error(OptionType)::out) is det.
 
-numeric_argument_error(Flag, OptName, Arg, Error) :-
-    Reason = requires_numeric_argument(Arg),
+int_argument_error(Flag, OptName, Arg, Error) :-
+    ( if valid_int_syntax(Arg) then
+        Reason = int_argument_out_of_range(Arg)
+    else
+        Reason = requires_int_argument(Arg)
+    ),
     Error = option_error(Flag, OptName, Reason).
 
+:- pred valid_int_syntax(string::in) is semidet.
+
+valid_int_syntax(Arg) :-
+    string.first_char(Arg, FirstChar, Suffix),
+    ( if
+        ( FirstChar = ('+')
+        ; FirstChar = ('-')
+        )
+    then
+        Suffix \= ""
+    else
+        char.is_digit(FirstChar)
+    ),
+    string.is_all_digits(Suffix).
+
 %---------------------------------------------------------------------------%
 
 lookup_bool_option(OptionTable, Opt) = Bool :-
@@ -2183,10 +2208,15 @@ option_error_to_string(Error) = String :-
         ;
             Reason = special_handler_error(String)
         ;
-            Reason = requires_numeric_argument(Arg),
-            string.format(
-                "option `%s' requires a numeric argument; `%s' is not numeric",
+            Reason = requires_int_argument(Arg),
+            string.format("option `%s' requires an integer argument; " ++
+                "`%s' is not an integer",
                 [s(OptionName), s(Arg)], String)
+        ;
+            Reason = int_argument_out_of_range(_Arg),
+            string.format("option `%s' has an integer argument " ++
+                "that is out of range",
+                [s(OptionName)], String)
         ;
             Reason = file_special_but_no_io(FileName),
             Msg = "the option processing predicate has no access to I/O",
-- 
2.54.0



More information about the reviews mailing list