##// 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 h = heads(default)
1071 h = heads(default)
1072 b($1) = ancestors($1) - ancestors(default)
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 m = self.funcre.search(name)
1074 m = self.funcre.search(name)
1079 if m:
1075 if m:
1076 self.name = m.group(1)
1080 self.tree = ('func', ('symbol', m.group(1)))
1077 self.tree = ('func', ('symbol', m.group(1)))
1081 self.args = [x.strip() for x in m.group(2).split(',')]
1078 self.args = [x.strip() for x in m.group(2).split(',')]
1082 for arg in self.args:
1079 for arg in self.args:
1083 value = value.replace(arg, repr(arg))
1080 value = value.replace(arg, repr(arg))
1084 else:
1081 else:
1082 self.name = name
1085 self.tree = ('symbol', name)
1083 self.tree = ('symbol', name)
1086
1084
1087 self.replacement, pos = parse(value)
1085 self.replacement, pos = parse(value)
1088 if pos != len(value):
1086 if pos != len(value):
1089 raise error.ParseError(_('invalid token'), pos)
1087 raise error.ParseError(_('invalid token'), pos)
1090
1088
1091 def process(self, tree):
1089 def _getalias(aliases, tree):
1092 if isinstance(tree, tuple):
1090 """If tree looks like an unexpanded alias, return it. Return None
1093 if self.args is None:
1091 otherwise.
1094 if tree == self.tree:
1092 """
1095 return self.replacement
1093 if isinstance(tree, tuple) and tree:
1096 elif tree[:2] == self.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 l = getlist(tree[2])
1134 l = getlist(tree[2])
1098 if len(l) != len(self.args):
1135 if len(l) != len(alias.args):
1099 raise error.ParseError(
1136 raise error.ParseError(
1100 _('invalid number of arguments: %s') % len(l))
1137 _('invalid number of arguments: %s') % len(l))
1101 result = self.replacement
1138 result = _expandargs(result, dict(zip(alias.args, l)))
1102 for a, v in zip(self.args, l):
1139 # Recurse in place, the base expression may have been rewritten
1103 valalias = revsetalias(('string', a), v)
1140 result = _expandaliases(aliases, result, expanding)
1104 result = valalias.process(result)
1141 expanding.pop()
1142 else:
1143 result = tuple(_expandaliases(aliases, t, expanding)
1144 for t in tree)
1105 return result
1145 return result
1106 return tuple(map(self.process, tree))
1107 return tree
1108
1146
1109 def findaliases(ui, tree):
1147 def findaliases(ui, tree):
1148 aliases = {}
1110 for k, v in ui.configitems('revsetalias'):
1149 for k, v in ui.configitems('revsetalias'):
1111 alias = revsetalias(k, v)
1150 alias = revsetalias(k, v)
1112 tree = alias.process(tree)
1151 aliases[alias.name] = alias
1113 return tree
1152 return _expandaliases(aliases, tree, [])
1114
1153
1115 parse = parser.parser(tokenize, elements).parse
1154 parse = parser.parser(tokenize, elements).parse
1116
1155
@@ -430,6 +430,7 b' aliases:'
430
430
431 $ echo '[revsetalias]' >> .hg/hgrc
431 $ echo '[revsetalias]' >> .hg/hgrc
432 $ echo 'm = merge()' >> .hg/hgrc
432 $ echo 'm = merge()' >> .hg/hgrc
433 $ echo 'sincem = descendants(m)' >> .hg/hgrc
433 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
434 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
434 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
435 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
435 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
436 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
@@ -438,6 +439,46 b' aliases:'
438 ('symbol', 'm')
439 ('symbol', 'm')
439 ('func', ('symbol', 'merge'), None)
440 ('func', ('symbol', 'merge'), None)
440 6
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 $ try 'd(2:5)'
482 $ try 'd(2:5)'
442 ('func', ('symbol', 'd'), ('range', ('symbol', '2'), ('symbol', '5')))
483 ('func', ('symbol', 'd'), ('range', ('symbol', '2'), ('symbol', '5')))
443 ('func', ('symbol', 'reverse'), ('func', ('symbol', 'sort'), ('list', ('range', ('symbol', '2'), ('symbol', '5')), ('symbol', 'date'))))
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