##// 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
1074 m = self.funcre.search(name)
1075 self.tree = name
1075 if m:
1076 self.replacement = value
1076 self.name = m.group(1)
1077 else: # alias definition
1077 self.tree = ('func', ('symbol', m.group(1)))
1078 m = self.funcre.search(name)
1078 self.args = [x.strip() for x in m.group(2).split(',')]
1079 if m:
1079 for arg in self.args:
1080 self.tree = ('func', ('symbol', m.group(1)))
1080 value = value.replace(arg, repr(arg))
1081 self.args = [x.strip() for x in m.group(2).split(',')]
1081 else:
1082 for arg in self.args:
1082 self.name = name
1083 value = value.replace(arg, repr(arg))
1083 self.tree = ('symbol', name)
1084 else:
1084
1085 self.tree = ('symbol', name)
1085 self.replacement, pos = parse(value)
1086 if pos != len(value):
1087 raise error.ParseError(_('invalid token'), pos)
1086
1088
1087 self.replacement, pos = parse(value)
1089 def _getalias(aliases, tree):
1088 if pos != len(value):
1090 """If tree looks like an unexpanded alias, return it. Return None
1089 raise error.ParseError(_('invalid token'), pos)
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
1090
1106
1091 def process(self, tree):
1107 def _expandargs(tree, args):
1092 if isinstance(tree, tuple):
1108 """Replace all occurences of ('string', name) with the
1093 if self.args is None:
1109 substitution value of the same name in args, recursively.
1094 if tree == self.tree:
1110 """
1095 return self.replacement
1111 if not isinstance(tree, tuple):
1096 elif tree[:2] == self.tree:
1112 return tree
1097 l = getlist(tree[2])
1113 if len(tree) == 2 and tree[0] == 'string':
1098 if len(l) != len(self.args):
1114 return args.get(tree[1], tree)
1099 raise error.ParseError(
1115 return tuple(_expandargs(t, args) for t in tree)
1100 _('invalid number of arguments: %s') % len(l))
1116
1101 result = self.replacement
1117 def _expandaliases(aliases, tree, expanding):
1102 for a, v in zip(self.args, l):
1118 """Expand aliases in tree, recursively.
1103 valalias = revsetalias(('string', a), v)
1119
1104 result = valalias.process(result)
1120 'aliases' is a dictionary mapping user defined aliases to
1105 return result
1121 revsetalias objects.
1106 return tuple(map(self.process, tree))
1122 """
1123 if not isinstance(tree, tuple):
1124 # Do not expand raw strings
1107 return tree
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:
1134 l = getlist(tree[2])
1135 if len(l) != len(alias.args):
1136 raise error.ParseError(
1137 _('invalid number of arguments: %s') % len(l))
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)
1145 return result
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