From 14657c6a5f2cee067241e8cbc0557e1cc3fcc106 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= <mail@3v1n0.net>
Date: Thu, 6 Sep 2018 23:23:07 +0200
Subject: [PATCH 2/2] javascript: do not translate template strings wit
 placeholders

Placeholders are supported by ES6 template string, but they might contain
any JS code, and thus translators should not have access to that.

To avoid that, in case the backtick quote is used (`), we check if a placeholder
is added. To perform this chec is safe to assume that this is true when the
string contains a "${", since it's what JS parsers would do.
And if a placeholder initial is found, we just ignore the whole string (and
emit a warning, in case `extract_all` was set).

https://savannah.gnu.org/bugs/?50920
---
 gettext-tools/src/x-javascript.c          | 59 ++++++++++++++++++++---
 gettext-tools/tests/xgettext-javascript-7 | 27 +++++++++++
 2 files changed, 80 insertions(+), 6 deletions(-)

diff --git a/gettext-tools/src/x-javascript.c b/gettext-tools/src/x-javascript.c
index dd1912a48..805353ea4 100644
--- a/gettext-tools/src/x-javascript.c
+++ b/gettext-tools/src/x-javascript.c
@@ -702,6 +702,7 @@ phase3_ungetc (int c)
 /* Return value of phase7_getuc when EOF is reached.  */
 #define P7_EOF (-1)
 #define P7_STRING_END (-2)
+#define P7_STRING_PLACEHOLDER (-3)
 
 /* Convert an UTF-16 or UTF-32 code point to a return value that can be
    distinguished from a single-byte return value.  */
@@ -732,6 +733,7 @@ enum token_type_ty
   token_type_operator,          /* - * / % . < > = ~ ! | & ? : ^ */
   token_type_equal,             /* = */
   token_type_string,            /* "abc", 'abc', `abc` */
+  token_type_template_string,   /* `a ${b} c` */
   token_type_keyword,           /* return, else */
   token_type_symbol,            /* symbol, number */
   token_type_other              /* misc. operator */
@@ -789,7 +791,37 @@ phase7_getuc (int quote_char)
       if (c == quote_char)
         return P7_STRING_END;
 
-      if (quote_char != '`' && c == '\n')
+      if (quote_char == '`')
+        {
+          if (c == '$')
+            {
+              int nextc = phase2_getc ();
+
+              if (nextc == '{')
+                {
+                  for (;;)
+                    {
+                      c = phase2_getc ();
+                      if (c == UEOF || c == quote_char)
+                        break;
+                    }
+
+                  if (extract_all)
+                    {
+                      error_with_progname = false;
+                      error (0, 0, _("%s:%d: warning: template string with "
+                                     "placeholders are not supported"),
+                                     logical_file_name, line_number);
+                      error_with_progname = true;
+                    }
+
+                  return P7_STRING_PLACEHOLDER;
+                }
+
+              phase2_ungetc (nextc);
+            }
+        }
+      else if (c == '\n')
         {
           phase2_ungetc (c);
           error_with_progname = false;
@@ -1192,7 +1224,9 @@ phase5_get (token_ty *tp)
         /* Strings.  */
           {
             struct mixed_string_buffer *bp;
+            char * string;
             int quote_char;
+            int uc;
 
             case '"':
             case '\'':
@@ -1205,12 +1239,12 @@ phase5_get (token_ty *tp)
                                               line_number);
               for (;;)
                 {
-                  int uc = phase7_getuc (quote_char);
+                  uc = phase7_getuc (quote_char);
 
                   /* Keep line_number in sync.  */
                   bp->line_number = line_number;
 
-                  if (uc == P7_EOF || uc == P7_STRING_END)
+                  if (uc == P7_EOF || uc == P7_STRING_END || uc == P7_STRING_PLACEHOLDER)
                     break;
 
                   if (IS_UNICODE (uc))
@@ -1223,10 +1257,22 @@ phase5_get (token_ty *tp)
                   else
                     mixed_string_buffer_append_char (bp, uc);
                 }
-              tp->string = mixed_string_buffer_done (bp);
-              tp->comment = add_reference (savable_comment);
+
+              string = mixed_string_buffer_done (bp);
               lexical_context = lc_outside;
-              tp->type = last_token_type = token_type_string;
+
+              if (uc != P7_STRING_PLACEHOLDER)
+                {
+                  tp->string = string;
+                  tp->comment = add_reference (savable_comment);
+                  tp->type = last_token_type = token_type_string;
+                }
+              else
+                {
+                  tp->type = last_token_type = token_type_template_string;
+                  free (string);
+                }
+
               return;
           }
 
@@ -1614,6 +1660,7 @@ extract_balanced (message_list_ty *mlp,
         case token_type_regexp:
         case token_type_operator:
         case token_type_equal:
+        case token_type_template_string:
         case token_type_other:
           next_context_iter = null_context_list_iterator;
           state = 0;
diff --git a/gettext-tools/tests/xgettext-javascript-7 b/gettext-tools/tests/xgettext-javascript-7
index d0d4e320a..87dac1d00 100755
--- a/gettext-tools/tests/xgettext-javascript-7
+++ b/gettext-tools/tests/xgettext-javascript-7
@@ -21,6 +21,18 @@ template string ` +
 let t5 = _(`Expected ` + `multi
 line template string ` +
 "translation " + '#5')
+let s5 = `Template string with ${placeholder}`
+let u1 = _(`Unexpected ${translation} string #1`)
+let u2 = _(`Unexpected
+${translation}
+string #2`)
+let s6 = `Template string with \${escaped_placeholder}`
+let t6 = _(`Expected $translation string #6`)
+let t7 = _(`Expected \${translation} string #7`)
+let t8 = _(`Expected $ {translation} string #8`)
+let t9 = _(`Expected
+\${translation}
+string #9`)
 EOF
 
 : ${XGETTEXT=xgettext}
@@ -68,6 +80,21 @@ msgid ""
 "Expected multi\n"
 "line template string translation #5"
 msgstr ""
+
+msgid "Expected $translation string #6"
+msgstr ""
+
+msgid "Expected ${translation} string #7"
+msgstr ""
+
+msgid "Expected $ {translation} string #8"
+msgstr ""
+
+msgid ""
+"Expected\n"
+"${translation}\n"
+"string #9"
+msgstr ""
 EOF
 
 : ${DIFF=diff}
-- 
2.17.1

