Patchwork [1,of,2,STABLE] templater: abort if infinite recursion detected while evaluation (issue4758)

login
register
mail settings
Submitter Yuya Nishihara
Date Jan. 23, 2016, 7:45 a.m.
Message ID <a4b3e8ef3603d01d6830.1453535152@mimosa>
Download mbox | patch
Permalink /patch/12876/
State Accepted
Headers show

Comments

Yuya Nishihara - Jan. 23, 2016, 7:45 a.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1437575381 -32400
#      Wed Jul 22 23:29:41 2015 +0900
# Branch stable
# Node ID a4b3e8ef3603d01d683012266bf0e4124011df56
# Parent  a6833e464b070c2f51edff8d6e55be79f5d9db22
templater: abort if infinite recursion detected while evaluation (issue4758)

It would be nice if we could detect recursion at the parsing phase, but we
can't because a template can refer to a keyword of the same name. For example,
"rev = {rev}" is valid if rev is a keyword, and we don't know if rev is a
keyword or a template while parsing.

Patch

diff --git a/mercurial/templater.py b/mercurial/templater.py
--- a/mercurial/templater.py
+++ b/mercurial/templater.py
@@ -226,13 +226,22 @@  def runinteger(context, mapping, data):
 def runstring(context, mapping, data):
     return data
 
+def _recursivesymbolblocker(key):
+    def showrecursion(**args):
+        raise error.Abort(_("recursive reference '%s' in template") % key)
+    return showrecursion
+
 def runsymbol(context, mapping, key):
     v = mapping.get(key)
     if v is None:
         v = context._defaults.get(key)
     if v is None:
+        # put poison to cut recursion. we can't move this to parsing phase
+        # because "x = {x}" is allowed if "x" is a keyword. (issue4758)
+        safemapping = mapping.copy()
+        safemapping[key] = _recursivesymbolblocker(key)
         try:
-            v = context.process(key, mapping)
+            v = context.process(key, safemapping)
         except TemplateNotFound:
             v = ''
     if callable(v):
diff --git a/tests/test-command-template.t b/tests/test-command-template.t
--- a/tests/test-command-template.t
+++ b/tests/test-command-template.t
@@ -1035,6 +1035,33 @@  Include works:
   1
   0
 
+Check that recursive reference does not fall into RuntimeError (issue4758):
+
+ common mistake:
+
+  $ hg log -T '{changeset}\n'
+  abort: recursive reference 'changeset' in template
+  [255]
+
+ circular reference:
+
+  $ cat << EOF > issue4758
+  > changeset = '{foo}'
+  > foo = '{changeset}'
+  > EOF
+  $ hg log --style ./issue4758
+  abort: recursive reference 'foo' in template
+  [255]
+
+ not a recursion if a keyword of the same name exists:
+
+  $ cat << EOF > issue4758
+  > changeset = '{tags % rev}'
+  > rev = '{rev} {tag}\n'
+  > EOF
+  $ hg log --style ./issue4758 -r tip
+  8 tip
+
 Check that {phase} works correctly on parents:
 
   $ cat << EOF > parentphase