Mercurial > public > mercurial-scm > hg
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": |