]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
xgettext: D: Improve heuristic for format strings.
authorBruno Haible <bruno@clisp.org>
Sun, 1 Jun 2025 22:32:14 +0000 (00:32 +0200)
committerBruno Haible <bruno@clisp.org>
Mon, 2 Jun 2025 00:22:17 +0000 (02:22 +0200)
* gettext-tools/src/format-d.c (struct spec): Add field
'likely_intentional_directives'.
(parse_upto): Increment it for each directive that don't contain a space.
(format_parse): Initialize it.
(format_is_unlikely_intentional): New function.
(formatstring_d): Use it.
* gettext-tools/tests/format-d-3: New file.
* gettext-tools/tests/Makefile.am (TESTS): Add it.

gettext-tools/src/format-d.c
gettext-tools/tests/Makefile.am
gettext-tools/tests/format-d-3 [new file with mode: 0755]

index 0b1bd17903d07e9cc2b75fbc0ce06710e875c078..52107641db47198c16e50fb7cb447d0d04f9bbee 100644 (file)
@@ -153,6 +153,11 @@ struct format_arg_list
 struct spec
 {
   unsigned int directives;
+  /* We consider a directive as "likely intentional" if it does not contain a
+     space.  This prevents xgettext from flagging strings like "100% complete"
+     as 'd-format' if they don't occur in a context that requires a format
+     string.  */
+  unsigned int likely_intentional_directives;
   struct format_arg_list *list;
 };
 
@@ -1701,6 +1706,8 @@ parse_upto (struct spec *spec,
 
       if (c == '%')
         {
+          bool likely_intentional = true;
+
           FDI_SET (format - 1, FMTDIR_START);
 
           /* Count number of directives.  */
@@ -1797,7 +1804,11 @@ parse_upto (struct spec *spec,
               /* Parse flags.  */
               while (*format == ' ' || *format == '+' || *format == '-'
                      || *format == '#' || *format == '0' || *format == '=')
-                format++;
+                {
+                  if (*format == ' ')
+                    likely_intentional = false;
+                  format++;
+                }
 
               /* Parse width.  */
               if (c_isdigit (*format))
@@ -2125,6 +2136,8 @@ parse_upto (struct spec *spec,
                 free_list (elementwise_list);
             }
 
+          if (likely_intentional)
+            spec->likely_intentional_directives++;
           FDI_SET (format, FMTDIR_END);
 
           format++;
@@ -2162,6 +2175,7 @@ format_parse (const char *format, bool translated, char *fdi,
   struct spec *result;
 
   spec.directives = 0;
+  spec.likely_intentional_directives = 0;
   spec.list = make_unconstrained_list ();
 
   if (!parse_upto (&spec, &format, false,
@@ -2200,6 +2214,14 @@ format_get_number_of_directives (void *descr)
   return spec->directives;
 }
 
+static bool
+format_is_unlikely_intentional (void *descr)
+{
+  struct spec *spec = (struct spec *) descr;
+
+  return spec->likely_intentional_directives == 0;
+}
+
 static bool
 format_check (void *msgid_descr, void *msgstr_descr, bool equality,
               formatstring_error_logger_t error_logger, void *error_logger_data,
@@ -2262,7 +2284,7 @@ struct formatstring_parser formatstring_d =
   format_parse,
   format_free,
   format_get_number_of_directives,
-  NULL,
+  format_is_unlikely_intentional,
   format_check
 };
 
index eff5a313d8cd1000b3f3a24fae52b43dc8de85c7..602482c4879876daf587fab3789c691288c3ff62 100644 (file)
@@ -200,7 +200,7 @@ TESTS = gettext-1 gettext-2 \
        format-c-1 format-c-2 format-c-3 format-c-4 format-c-5 format-c-6 \
        format-c++-brace-1 format-c++-brace-2 \
        format-csharp-1 format-csharp-2 \
-       format-d-1 format-d-2 \
+       format-d-1 format-d-2 format-d-3 \
        format-elisp-1 format-elisp-2 \
        format-gcc-internal-1 format-gcc-internal-2 \
        format-gfc-internal-1 format-gfc-internal-2 \
diff --git a/gettext-tools/tests/format-d-3 b/gettext-tools/tests/format-d-3
new file mode 100755 (executable)
index 0000000..94c78bb
--- /dev/null
@@ -0,0 +1,49 @@
+#! /bin/sh
+. "${srcdir=.}/init.sh"; path_prepend_ . ../src
+
+# Test heuristic recognition of D format strings like "100% complete".
+
+cat <<\EOF > f-d-3.d
+void func () {
+  gettext("Test 1a is 100% complete");
+  format(gettext("Test 1b is 100% complete"), 120);
+  gettext("Test 2a from 0% complete to 100% complete");
+  format(gettext("Test 2b from 0% complete to 100% complete"), 120, 121);
+  gettext("Test 3a of %s is 100% complete");
+  format(gettext("Test 3b of %s is 100% complete"), "foo", 120);
+}
+EOF
+
+: ${XGETTEXT=xgettext}
+${XGETTEXT} --omit-header --no-location -d f-d-3.tmp f-d-3.d || Exit 1
+LC_ALL=C tr -d '\r' < f-d-3.tmp.po > f-d-3.po || Exit 1
+
+cat <<\EOF > f-d-3.ok
+msgid "Test 1a is 100% complete"
+msgstr ""
+
+#, d-format
+msgid "Test 1b is 100% complete"
+msgstr ""
+
+msgid "Test 2a from 0% complete to 100% complete"
+msgstr ""
+
+#, d-format
+msgid "Test 2b from 0% complete to 100% complete"
+msgstr ""
+
+#, c-format, d-format
+msgid "Test 3a of %s is 100% complete"
+msgstr ""
+
+#, d-format
+msgid "Test 3b of %s is 100% complete"
+msgstr ""
+EOF
+
+: ${DIFF=diff}
+${DIFF} f-d-3.ok f-d-3.po
+result=$?
+
+exit $result