comparison mercurial/revset.py @ 29441:9e8d258708bb

revset: check invalid function syntax "func-name"() explicitly Before the error was caught at func() as an unknown identifier, and the optimizer failed to detect the syntax error. This patch introduces getsymbol() helper to ensure that a string is not allowed as a function name.
author Yuya Nishihara <yuya@tcha.org>
date Mon, 27 Jun 2016 20:44:14 +0900
parents 4f5531f8ac38
children a8a5dd8986f0
comparison
equal deleted inserted replaced
29440:009cc6c89d0f 29441:9e8d258708bb
300 pos += 1 300 pos += 1
301 yield ('end', None, pos) 301 yield ('end', None, pos)
302 302
303 # helpers 303 # helpers
304 304
305 def getsymbol(x):
306 if x and x[0] == 'symbol':
307 return x[1]
308 raise error.ParseError(_('not a symbol'))
309
305 def getstring(x, err): 310 def getstring(x, err):
306 if x and (x[0] == 'string' or x[0] == 'symbol'): 311 if x and (x[0] == 'string' or x[0] == 'symbol'):
307 return x[1] 312 return x[1]
308 raise error.ParseError(err) 313 raise error.ParseError(err)
309 314
412 417
413 def keyvaluepair(repo, subset, k, v): 418 def keyvaluepair(repo, subset, k, v):
414 raise error.ParseError(_("can't use a key-value pair in this context")) 419 raise error.ParseError(_("can't use a key-value pair in this context"))
415 420
416 def func(repo, subset, a, b): 421 def func(repo, subset, a, b):
417 if a[0] == 'symbol' and a[1] in symbols: 422 f = getsymbol(a)
418 return symbols[a[1]](repo, subset, b) 423 if f in symbols:
424 return symbols[f](repo, subset, b)
419 425
420 keep = lambda fn: getattr(fn, '__doc__', None) is not None 426 keep = lambda fn: getattr(fn, '__doc__', None) is not None
421 427
422 syms = [s for (s, fn) in symbols.items() if keep(fn)] 428 syms = [s for (s, fn) in symbols.items() if keep(fn)]
423 raise error.UnknownIdentifier(a[1], syms) 429 raise error.UnknownIdentifier(f, syms)
424 430
425 # functions 431 # functions
426 432
427 # symbols are callables like: 433 # symbols are callables like:
428 # fn(repo, subset, x) 434 # fn(repo, subset, x)
2302 >>> f('ancestors(A)', 'not ancestors(B)') 2308 >>> f('ancestors(A)', 'not ancestors(B)')
2303 ('list', ('symbol', 'A'), ('symbol', 'B')) 2309 ('list', ('symbol', 'A'), ('symbol', 'B'))
2304 """ 2310 """
2305 if (revs is not None 2311 if (revs is not None
2306 and revs[0] == 'func' 2312 and revs[0] == 'func'
2307 and getstring(revs[1], _('not a symbol')) == 'ancestors' 2313 and getsymbol(revs[1]) == 'ancestors'
2308 and bases is not None 2314 and bases is not None
2309 and bases[0] == 'not' 2315 and bases[0] == 'not'
2310 and bases[1][0] == 'func' 2316 and bases[1][0] == 'func'
2311 and getstring(bases[1][1], _('not a symbol')) == 'ancestors'): 2317 and getsymbol(bases[1][1]) == 'ancestors'):
2312 return ('list', revs[2], bases[1][2]) 2318 return ('list', revs[2], bases[1][2])
2313 2319
2314 def _optimize(x, small): 2320 def _optimize(x, small):
2315 if x is None: 2321 if x is None:
2316 return 0, x 2322 return 0, x
2417 return wa + wb, (op, ta, tb) 2423 return wa + wb, (op, ta, tb)
2418 elif op == 'list': 2424 elif op == 'list':
2419 ws, ts = zip(*(_optimize(y, small) for y in x[1:])) 2425 ws, ts = zip(*(_optimize(y, small) for y in x[1:]))
2420 return sum(ws), (op,) + ts 2426 return sum(ws), (op,) + ts
2421 elif op == 'func': 2427 elif op == 'func':
2422 f = getstring(x[1], _("not a symbol")) 2428 f = getsymbol(x[1])
2423 wa, ta = _optimize(x[2], small) 2429 wa, ta = _optimize(x[2], small)
2424 if f in ("author branch closed date desc file grep keyword " 2430 if f in ("author branch closed date desc file grep keyword "
2425 "outgoing user"): 2431 "outgoing user"):
2426 w = 10 # slow 2432 w = 10 # slow
2427 elif f in "modifies adds removes": 2433 elif f in "modifies adds removes":