diff mbox series

[v4,1/4] i18n: factorize more 'incompatible options' messages

Message ID 2eac2ef502b86d0c15513c8d0e69928ce2140b1f.1643666870.git.gitgitgadget@gmail.com (mailing list archive)
State New, archived
Headers show
Series Factorize i18n | expand

Commit Message

Jean-Noël Avila Jan. 31, 2022, 10:07 p.m. UTC
From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>

Find more incompatible options to factorize.

When more than two options are mutually exclusive, print the ones
which are actually on the command line.

Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>
---
 builtin/commit.c                          | 35 ++++++++++-------------
 builtin/difftool.c                        |  5 ++--
 builtin/grep.c                            |  8 ++----
 builtin/log.c                             |  5 ++--
 builtin/merge-base.c                      |  6 ++--
 parse-options.c                           | 34 ++++++++++++++++++++++
 parse-options.h                           | 16 +++++++++++
 t/t7500-commit-template-squash-signoff.sh |  2 +-
 8 files changed, 79 insertions(+), 32 deletions(-)

Comments

Junio C Hamano Jan. 31, 2022, 10:41 p.m. UTC | #1
"Jean-Noël Avila via GitGitGadget"  <gitgitgadget@gmail.com> writes:

> +inline void die_for_incompatible_opt3(int opt1, const char *opt1_name,
> +				      int opt2, const char *opt2_name,
> +				      int opt3, const char *opt3_name)
> +{
> +	die_for_incompatible_opt4(opt1, opt1_name,
> +				  opt2, opt2_name,
> +				  opt3, opt3_name,
> +				  0, "");
> +}

I haven't seen a non-static inline function defined in a common
header files.  Does this actually work?  In my build, ld choked on
this one.

Otherwise make it "static inline"?  Or just

#define die_for_incompatible_opt3(o1,n1,o2,n2,o3,n3) \
	die_for_incompatible_opt4((o1), (n1), \
				  (o2), (n2), \
				  (o3), (n3), \
				  0, "")

perhaps?
Johannes Sixt Feb. 1, 2022, 7:01 a.m. UTC | #2
Am 31.01.22 um 23:41 schrieb Junio C Hamano:
> "Jean-Noël Avila via GitGitGadget"  <gitgitgadget@gmail.com> writes:
> 
>> +inline void die_for_incompatible_opt3(int opt1, const char *opt1_name,
>> +				      int opt2, const char *opt2_name,
>> +				      int opt3, const char *opt3_name)
>> +{
>> +	die_for_incompatible_opt4(opt1, opt1_name,
>> +				  opt2, opt2_name,
>> +				  opt3, opt3_name,
>> +				  0, "");
>> +}
> 
> I haven't seen a non-static inline function defined in a common
> header files.  Does this actually work?  In my build, ld choked on
> this one.
> 
> Otherwise make it "static inline"?  Or just
> 
> #define die_for_incompatible_opt3(o1,n1,o2,n2,o3,n3) \
> 	die_for_incompatible_opt4((o1), (n1), \
> 				  (o2), (n2), \
> 				  (o3), (n3), \
> 				  0, "")
> 
> perhaps?

Please no macros where they are not a clear advantage. Make it a
function, either static inline, or out-of-line (the latter would be my
personal preference in this case because the function is not called in a
hot path).

-- Hannes
Junio C Hamano Feb. 1, 2022, 5:58 p.m. UTC | #3
Johannes Sixt <j6t@kdbg.org> writes:

>> Otherwise make it "static inline"?  Or just
>> 
>> #define die_for_incompatible_opt3(o1,n1,o2,n2,o3,n3) \
>> 	die_for_incompatible_opt4((o1), (n1), \
>> 				  (o2), (n2), \
>> 				  (o3), (n3), \
>> 				  0, "")
>> 
>> perhaps?
>
> Please no macros where they are not a clear advantage. Make it a
> function, either static inline, or out-of-line (the latter would be my
> personal preference in this case because the function is not called in a
> hot path).

In this particular case, my personal preference is actually a macro,
since 

 * there is no type safety lost

 * I find that it make it the most clear that 

   - the opt3 variant is a mere convenience wrapper, which does not
     even deserve an entry in the symbol table, of the real thing, and

   - our intention is to keep them closely in sync by not even
     giving future developers a pair of { braces }, in which they
     are tempted into writing extra code before or after calling the
     opt4 variant and make them diverge.

But it is not very strong preference.

A "static inline" wrapper would result in the same code as a macro,
and I'd be almost equally happy with it.  The code is almost already
written, and fixing it is just a matter of inserting "static " in
front.  My preference between "static inline" and macro is not
strong enough to insist on rewriting ;-)

An out-of-line wrapper has a slight disadvantage that it is not
immediately obvious that one is a mere wrapper of the other thing by
just looking at what is in the *.h file, but I am OK with it as
well.  If the original patch were written as an out-of-line wrapper,
my preference is not strong enough to insist on rewriting, either.

Thanks.
Jean-Noël Avila Feb. 2, 2022, 4:05 p.m. UTC | #4
Le 31/01/2022 à 23:41, Junio C Hamano a écrit :
> "Jean-Noël Avila via GitGitGadget"  <gitgitgadget@gmail.com> writes:
>
>> +inline void die_for_incompatible_opt3(int opt1, const char *opt1_name,
>> +				      int opt2, const char *opt2_name,
>> +				      int opt3, const char *opt3_name)
>> +{
>> +	die_for_incompatible_opt4(opt1, opt1_name,
>> +				  opt2, opt2_name,
>> +				  opt3, opt3_name,
>> +				  0, "");
>> +}
> I haven't seen a non-static inline function defined in a common
> header files.  Does this actually work?  In my build, ld choked on
> this one.


This is quite subtle: "inline" is just a hint to the compiler but the

compiler can choose not to inline, so there must be an external symbol

to link to (which is not the case with this code).


My tests and the CI builds went smoothly, so I guess all these compilers

luckily chose to inline, but not yours.


With "static", we ensure that either it is inlined or there is a static

function (in which case, this bit of code will be duplicated across

compilation units).
Johannes Sixt Feb. 2, 2022, 5:29 p.m. UTC | #5
Am 02.02.22 um 17:05 schrieb Jean-Noël Avila:
> With "static", we ensure that either it is inlined or there is a static
> function (in which case, this bit of code will be duplicated across
> compilation units).

That is the plan. Since the function is declared static, the compiler
has much more freedom to inline it because it knows that the function is
inaccessible outside the current compilation unit.

Besides, if you grep for 'inline', you will only find 'static inline'
(except in some borrowed code). static inline it is, or you pick the
macro or out-of-line solution.

-- Hannes
diff mbox series

Patch

diff --git a/builtin/commit.c b/builtin/commit.c
index b9ed0374e30..33ca9e99c80 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1242,8 +1242,6 @@  static int parse_and_validate_options(int argc, const char *argv[],
 				      struct commit *current_head,
 				      struct wt_status *s)
 {
-	int f = 0;
-
 	argc = parse_options(argc, argv, prefix, options, usage, 0);
 	finalize_deferred_config(s);
 
@@ -1251,7 +1249,7 @@  static int parse_and_validate_options(int argc, const char *argv[],
 		force_author = find_author_by_nickname(force_author);
 
 	if (force_author && renew_authorship)
-		die(_("Using both --reset-author and --author does not make sense"));
+		die(_("options '%s' and '%s' cannot be used together"), "--reset-author", "--author");
 
 	if (logfile || have_option_m || use_message)
 		use_editor = 0;
@@ -1268,20 +1266,16 @@  static int parse_and_validate_options(int argc, const char *argv[],
 			die(_("You are in the middle of a rebase -- cannot amend."));
 	}
 	if (fixup_message && squash_message)
-		die(_("Options --squash and --fixup cannot be used together"));
-	if (use_message)
-		f++;
-	if (edit_message)
-		f++;
-	if (fixup_message)
-		f++;
-	if (logfile)
-		f++;
-	if (f > 1)
-		die(_("Only one of -c/-C/-F/--fixup can be used."));
-	if (have_option_m && (edit_message || use_message || logfile))
-		die((_("Option -m cannot be combined with -c/-C/-F.")));
-	if (f || have_option_m)
+		die(_("options '%s' and '%s' cannot be used together"), "--squash", "--fixup");
+	die_for_incompatible_opt4(!!use_message, "-C",
+				  !!edit_message, "-c",
+				  !!logfile, "-F",
+				  !!fixup_message, "--fixup");
+	die_for_incompatible_opt4(have_option_m, "-m",
+				  !!edit_message, "-c",
+				  !!use_message, "-C",
+				  !!logfile, "-F");
+	if (use_message || edit_message || logfile ||fixup_message || have_option_m)
 		template_file = NULL;
 	if (edit_message)
 		use_message = edit_message;
@@ -1306,9 +1300,10 @@  static int parse_and_validate_options(int argc, const char *argv[],
 	if (patch_interactive)
 		interactive = 1;
 
-	if (also + only + all + interactive > 1)
-		die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
-
+	die_for_incompatible_opt4(also, "-i/--include",
+				  only, "-o/--only",
+				  all, "-a/--all",
+				  interactive, "--interactive/-p/--patch");
 	if (fixup_message) {
 		/*
 		 * We limit --fixup's suboptions to only alpha characters.
diff --git a/builtin/difftool.c b/builtin/difftool.c
index c79fbbf67e5..faa3507369a 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -732,8 +732,9 @@  int cmd_difftool(int argc, const char **argv, const char *prefix)
 	} else if (dir_diff)
 		die(_("options '%s' and '%s' cannot be used together"), "--dir-diff", "--no-index");
 
-	if (use_gui_tool + !!difftool_cmd + !!extcmd > 1)
-		die(_("options '%s', '%s', and '%s' cannot be used together"), "--gui", "--tool", "--extcmd");
+	die_for_incompatible_opt3(use_gui_tool, "--gui",
+				  !!difftool_cmd, "--tool",
+				  !!extcmd, "--extcmd");
 
 	if (use_gui_tool)
 		setenv("GIT_MERGETOOL_GUI", "true", 1);
diff --git a/builtin/grep.c b/builtin/grep.c
index 9e34a820ad4..88144f06300 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -1167,11 +1167,9 @@  int cmd_grep(int argc, const char **argv, const char *prefix)
 	if (!show_in_pager && !opt.status_only)
 		setup_pager();
 
-	if (!use_index && (untracked || cached))
-		die(_("--cached or --untracked cannot be used with --no-index"));
-
-	if (untracked && cached)
-		die(_("--untracked cannot be used with --cached"));
+	die_for_incompatible_opt3(!use_index, "--no-index",
+				  untracked, "--untracked",
+				  cached, "--cached");
 
 	if (!use_index || untracked) {
 		int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;
diff --git a/builtin/log.c b/builtin/log.c
index 4b493408cc5..970aa3483c4 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1978,8 +1978,9 @@  int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	if (rev.show_notes)
 		load_display_notes(&rev.notes_opt);
 
-	if (use_stdout + rev.diffopt.close_file + !!output_directory > 1)
-		die(_("options '%s', '%s', and '%s' cannot be used together"), "--stdout", "--output", "--output-directory");
+	die_for_incompatible_opt3(use_stdout, "--stdout",
+				  rev.diffopt.close_file, "--output",
+				  !!output_directory, "--output-directory");
 
 	if (use_stdout) {
 		setup_pager();
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index 6719ac198dc..26b84980dbd 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -159,12 +159,14 @@  int cmd_merge_base(int argc, const char **argv, const char *prefix)
 		if (argc < 2)
 			usage_with_options(merge_base_usage, options);
 		if (show_all)
-			die("--is-ancestor cannot be used with --all");
+			die(_("options '%s' and '%s' cannot be used together"),
+			    "--is-ancestor", "--all");
 		return handle_is_ancestor(argc, argv);
 	}
 
 	if (cmdmode == 'r' && show_all)
-		die("--independent cannot be used with --all");
+		die(_("options '%s' and '%s' cannot be used together"),
+		    "--independent", "--all");
 
 	if (cmdmode == 'o')
 		return handle_octopus(argc, argv, show_all);
diff --git a/parse-options.c b/parse-options.c
index a8283037be9..276e3911a74 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -1079,3 +1079,37 @@  void NORETURN usage_msg_opt(const char *msg,
 	die_message("%s\n", msg); /* The extra \n is intentional */
 	usage_with_options(usagestr, options);
 }
+
+void die_for_incompatible_opt4(int opt1, const char *opt1_name,
+			       int opt2, const char *opt2_name,
+			       int opt3, const char *opt3_name,
+			       int opt4, const char *opt4_name)
+{
+	int count = 0;
+	const char *options[4];
+
+	if (opt1)
+		options[count++] = opt1_name;
+	if (opt2)
+		options[count++] = opt2_name;
+	if (opt3)
+		options[count++] = opt3_name;
+	if (opt4)
+		options[count++] = opt4_name;
+	switch (count) {
+	case 4:
+		die(_("options '%s', '%s', '%s', and '%s' cannot be used together"),
+		    opt1_name, opt2_name, opt3_name, opt4_name);
+		break;
+	case 3:
+		die(_("options '%s', '%s', and '%s' cannot be used together"),
+		    options[0], options[1], options[2]);
+		break;
+	case 2:
+		die(_("options '%s' and '%s' cannot be used together"),
+		    options[0], options[1]);
+		break;
+	default:
+		break;
+	}
+}
diff --git a/parse-options.h b/parse-options.h
index e22846d3b7b..eb86c505470 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -225,6 +225,22 @@  NORETURN void usage_msg_opt(const char *msg,
 			    const char * const *usagestr,
 			    const struct option *options);
 
+void die_for_incompatible_opt4(int opt1, const char *opt1_name,
+			       int opt2, const char *opt2_name,
+			       int opt3, const char *opt3_name,
+			       int opt4, const char *opt4_name);
+
+
+inline void die_for_incompatible_opt3(int opt1, const char *opt1_name,
+				      int opt2, const char *opt2_name,
+				      int opt3, const char *opt3_name)
+{
+	die_for_incompatible_opt4(opt1, opt1_name,
+				  opt2, opt2_name,
+				  opt3, opt3_name,
+				  0, "");
+}
+
 /*
  * Use these assertions for callbacks that expect to be called with NONEG and
  * NOARG respectively, and do not otherwise handle the "unset" and "arg"
diff --git a/t/t7500-commit-template-squash-signoff.sh b/t/t7500-commit-template-squash-signoff.sh
index 91964653a0b..5fcaa0b4f2a 100755
--- a/t/t7500-commit-template-squash-signoff.sh
+++ b/t/t7500-commit-template-squash-signoff.sh
@@ -442,7 +442,7 @@  test_expect_success '--fixup=reword: give error with pathsec' '
 '
 
 test_expect_success '--fixup=reword: -F give error message' '
-	echo "fatal: Only one of -c/-C/-F/--fixup can be used." >expect &&
+	echo "fatal: options '\''-F'\'' and '\''--fixup'\'' cannot be used together" >expect &&
 	test_must_fail git commit --fixup=reword:HEAD~ -F msg  2>actual &&
 	test_cmp expect actual
 '