revset: parse alias declaration strictly by _parsealiasdecl
authorFUJIWARA Katsunori <foozy@lares.dti.ne.jp>
Sat, 10 Jan 2015 23:18:11 +0900
changeset 23846 aac4a1a7920e
parent 23845 0a7fd54d4e60
child 23847 71402bb8d8b2
revset: parse alias declaration strictly by _parsealiasdecl Before this patch, alias declaration is parsed by string base operations: matching against "^([^(]+)\(([^)]+)\)$" and splitting by ",". This overlooks many syntax errors like below (see the previous patch introducing "_parsealiasdecl" for detail): - un-closed parenthesis causes being treated as "alias symbol" - symbol/function name aren't examined whether they are valid or not - invalid argument list causes unexpected argument names To parse alias declaration strictly, this patch replaces parsing implementation by "_parsealiasdecl". This patch tests only one typical declaration error case, because error detection itself is already tested in the doctest of "_parsealiasdecl". This also removes class property "args" and "error", because these are certainly initialized in "revsetalias.__init__".
mercurial/revset.py
tests/test-revset.t
--- a/mercurial/revset.py	Sat Jan 10 23:18:11 2015 +0900
+++ b/mercurial/revset.py	Sat Jan 10 23:18:11 2015 +0900
@@ -2234,11 +2234,6 @@
         return (decl, None, None, parseerrordetail(inst))
 
 class revsetalias(object):
-    funcre = re.compile('^([^(]+)\(([^)]+)\)$')
-    args = None
-
-    # error message at parsing, or None
-    error = None
     # whether own `error` information is already shown or not.
     # this avoids showing same warning multiple times at each `findaliases`.
     warned = False
@@ -2249,18 +2244,17 @@
         h = heads(default)
         b($1) = ancestors($1) - ancestors(default)
         '''
-        m = self.funcre.search(name)
-        if m:
-            self.name = m.group(1)
-            self.tree = ('func', ('symbol', m.group(1)))
-            self.args = [x.strip() for x in m.group(2).split(',')]
+        self.name, self.tree, self.args, self.error = _parsealiasdecl(name)
+        if self.error:
+            self.error = _('failed to parse the declaration of revset alias'
+                           ' "%s": %s') % (self.name, self.error)
+            return
+
+        if self.args:
             for arg in self.args:
                 # _aliasarg() is an unknown symbol only used separate
                 # alias argument placeholders from regular strings.
                 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
-        else:
-            self.name = name
-            self.tree = ('symbol', name)
 
         try:
             self.replacement, pos = parse(value)
--- a/tests/test-revset.t	Sat Jan 10 23:18:11 2015 +0900
+++ b/tests/test-revset.t	Sat Jan 10 23:18:11 2015 +0900
@@ -998,6 +998,12 @@
   $ try 'tip'
   ('symbol', 'tip')
   9
+
+  $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
+  ('symbol', 'tip')
+  warning: failed to parse the declaration of revset alias "bad name": at 4: invalid token
+  9
+
   $ try 'd(2:5)'
   (func
     ('symbol', 'd')