comparison mercurial/revset.py @ 23846:aac4a1a7920e

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__".
author FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
date Sat, 10 Jan 2015 23:18:11 +0900
parents 0a7fd54d4e60
children 71402bb8d8b2
comparison
equal deleted inserted replaced
23845:0a7fd54d4e60 23846:aac4a1a7920e
2232 return (decl, None, None, _("invalid format")) 2232 return (decl, None, None, _("invalid format"))
2233 except error.ParseError, inst: 2233 except error.ParseError, inst:
2234 return (decl, None, None, parseerrordetail(inst)) 2234 return (decl, None, None, parseerrordetail(inst))
2235 2235
2236 class revsetalias(object): 2236 class revsetalias(object):
2237 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
2238 args = None
2239
2240 # error message at parsing, or None
2241 error = None
2242 # whether own `error` information is already shown or not. 2237 # whether own `error` information is already shown or not.
2243 # this avoids showing same warning multiple times at each `findaliases`. 2238 # this avoids showing same warning multiple times at each `findaliases`.
2244 warned = False 2239 warned = False
2245 2240
2246 def __init__(self, name, value): 2241 def __init__(self, name, value):
2247 '''Aliases like: 2242 '''Aliases like:
2248 2243
2249 h = heads(default) 2244 h = heads(default)
2250 b($1) = ancestors($1) - ancestors(default) 2245 b($1) = ancestors($1) - ancestors(default)
2251 ''' 2246 '''
2252 m = self.funcre.search(name) 2247 self.name, self.tree, self.args, self.error = _parsealiasdecl(name)
2253 if m: 2248 if self.error:
2254 self.name = m.group(1) 2249 self.error = _('failed to parse the declaration of revset alias'
2255 self.tree = ('func', ('symbol', m.group(1))) 2250 ' "%s": %s') % (self.name, self.error)
2256 self.args = [x.strip() for x in m.group(2).split(',')] 2251 return
2252
2253 if self.args:
2257 for arg in self.args: 2254 for arg in self.args:
2258 # _aliasarg() is an unknown symbol only used separate 2255 # _aliasarg() is an unknown symbol only used separate
2259 # alias argument placeholders from regular strings. 2256 # alias argument placeholders from regular strings.
2260 value = value.replace(arg, '_aliasarg(%r)' % (arg,)) 2257 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
2261 else:
2262 self.name = name
2263 self.tree = ('symbol', name)
2264 2258
2265 try: 2259 try:
2266 self.replacement, pos = parse(value) 2260 self.replacement, pos = parse(value)
2267 if pos != len(value): 2261 if pos != len(value):
2268 raise error.ParseError(_('invalid token'), pos) 2262 raise error.ParseError(_('invalid token'), pos)