##// END OF EJS Templates
revset: fix alias substitution recursion (issue3240)...
Patrick Mezard -
r16096:b8be4506 default
parent child Browse files
Show More
@@ -1071,46 +1071,85 b' class revsetalias(object):'
1071 1071 h = heads(default)
1072 1072 b($1) = ancestors($1) - ancestors(default)
1073 1073 '''
1074 if isinstance(name, tuple): # parameter substitution
1075 self.tree = name
1076 self.replacement = value
1077 else: # alias definition
1078 1074 m = self.funcre.search(name)
1079 1075 if m:
1076 self.name = m.group(1)
1080 1077 self.tree = ('func', ('symbol', m.group(1)))
1081 1078 self.args = [x.strip() for x in m.group(2).split(',')]
1082 1079 for arg in self.args:
1083 1080 value = value.replace(arg, repr(arg))
1084 1081 else:
1082 self.name = name
1085 1083 self.tree = ('symbol', name)
1086 1084
1087 1085 self.replacement, pos = parse(value)
1088 1086 if pos != len(value):
1089 1087 raise error.ParseError(_('invalid token'), pos)
1090 1088
1091 def process(self, tree):
1092 if isinstance(tree, tuple):
1093 if self.args is None:
1094 if tree == self.tree:
1095 return self.replacement
1096 elif tree[:2] == self.tree:
1089 def _getalias(aliases, tree):
1090 """If tree looks like an unexpanded alias, return it. Return None
1091 otherwise.
1092 """
1093 if isinstance(tree, tuple) and tree:
1094 if tree[0] == 'symbol' and len(tree) == 2:
1095 name = tree[1]
1096 alias = aliases.get(name)
1097 if alias and alias.args is None and alias.tree == tree:
1098 return alias
1099 if tree[0] == 'func' and len(tree) > 1:
1100 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
1101 name = tree[1][1]
1102 alias = aliases.get(name)
1103 if alias and alias.args is not None and alias.tree == tree[:2]:
1104 return alias
1105 return None
1106
1107 def _expandargs(tree, args):
1108 """Replace all occurences of ('string', name) with the
1109 substitution value of the same name in args, recursively.
1110 """
1111 if not isinstance(tree, tuple):
1112 return tree
1113 if len(tree) == 2 and tree[0] == 'string':
1114 return args.get(tree[1], tree)
1115 return tuple(_expandargs(t, args) for t in tree)
1116
1117 def _expandaliases(aliases, tree, expanding):
1118 """Expand aliases in tree, recursively.
1119
1120 'aliases' is a dictionary mapping user defined aliases to
1121 revsetalias objects.
1122 """
1123 if not isinstance(tree, tuple):
1124 # Do not expand raw strings
1125 return tree
1126 alias = _getalias(aliases, tree)
1127 if alias is not None:
1128 if alias in expanding:
1129 raise error.ParseError(_('infinite expansion of revset alias "%s" '
1130 'detected') % alias.name)
1131 expanding.append(alias)
1132 result = alias.replacement
1133 if alias.args is not None:
1097 1134 l = getlist(tree[2])
1098 if len(l) != len(self.args):
1135 if len(l) != len(alias.args):
1099 1136 raise error.ParseError(
1100 1137 _('invalid number of arguments: %s') % len(l))
1101 result = self.replacement
1102 for a, v in zip(self.args, l):
1103 valalias = revsetalias(('string', a), v)
1104 result = valalias.process(result)
1138 result = _expandargs(result, dict(zip(alias.args, l)))
1139 # Recurse in place, the base expression may have been rewritten
1140 result = _expandaliases(aliases, result, expanding)
1141 expanding.pop()
1142 else:
1143 result = tuple(_expandaliases(aliases, t, expanding)
1144 for t in tree)
1105 1145 return result
1106 return tuple(map(self.process, tree))
1107 return tree
1108 1146
1109 1147 def findaliases(ui, tree):
1148 aliases = {}
1110 1149 for k, v in ui.configitems('revsetalias'):
1111 1150 alias = revsetalias(k, v)
1112 tree = alias.process(tree)
1113 return tree
1151 aliases[alias.name] = alias
1152 return _expandaliases(aliases, tree, [])
1114 1153
1115 1154 parse = parser.parser(tokenize, elements).parse
1116 1155
@@ -430,6 +430,7 b' aliases:'
430 430
431 431 $ echo '[revsetalias]' >> .hg/hgrc
432 432 $ echo 'm = merge()' >> .hg/hgrc
433 $ echo 'sincem = descendants(m)' >> .hg/hgrc
433 434 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
434 435 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
435 436 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
@@ -438,6 +439,46 b' aliases:'
438 439 ('symbol', 'm')
439 440 ('func', ('symbol', 'merge'), None)
440 441 6
442
443 test alias recursion
444
445 $ try sincem
446 ('symbol', 'sincem')
447 ('func', ('symbol', 'descendants'), ('func', ('symbol', 'merge'), None))
448 6
449 7
450
451 test infinite recursion
452
453 $ echo 'recurse1 = recurse2' >> .hg/hgrc
454 $ echo 'recurse2 = recurse1' >> .hg/hgrc
455 $ try recurse1
456 ('symbol', 'recurse1')
457 hg: parse error: infinite expansion of revset alias "recurse1" detected
458 [255]
459
460 test nesting and variable passing
461
462 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
463 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
464 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
465 $ try 'nested(2:5)'
466 ('func', ('symbol', 'nested'), ('range', ('symbol', '2'), ('symbol', '5')))
467 ('func', ('symbol', 'max'), ('range', ('symbol', '2'), ('symbol', '5')))
468 5
469
470 test variable isolation, variable placeholders are rewritten as string
471 then parsed and matched again as string. Check they do not leak too
472 far away.
473
474 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
475 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
476 $ try 'callinjection(2:5)'
477 ('func', ('symbol', 'callinjection'), ('range', ('symbol', '2'), ('symbol', '5')))
478 ('func', ('symbol', 'descendants'), ('func', ('symbol', 'max'), ('string', '$1')))
479 abort: unknown revision '$1'!
480 [255]
481
441 482 $ try 'd(2:5)'
442 483 ('func', ('symbol', 'd'), ('range', ('symbol', '2'), ('symbol', '5')))
443 484 ('func', ('symbol', 'reverse'), ('func', ('symbol', 'sort'), ('list', ('range', ('symbol', '2'), ('symbol', '5')), ('symbol', 'date'))))
General Comments 0
You need to be logged in to leave comments. Login now