Better 2.6 tryifelse detection

This commit is contained in:
rocky
2018-03-06 17:23:08 -05:00
parent ca3f822c81
commit ccd42077c1
2 changed files with 59 additions and 23 deletions

View File

@@ -23,3 +23,19 @@ def call(*args):
except TypeError: except TypeError:
# Unhashable argument # Unhashable argument
return 3 return 3
# From 2.6.9 pdb.py
# Here we have a "try/except" inside a "try/except/else and we can't
# distinguish which COME_FROM comes from which "try".
def do_jump(self, arg):
try:
arg(1)
except ValueError:
arg(2)
else:
try:
arg(3)
except ValueError:
arg(4)

View File

@@ -315,7 +315,7 @@ class Python26Parser(Python2Parser):
self.check_reduce['and'] = 'AST' self.check_reduce['and'] = 'AST'
self.check_reduce['list_for'] = 'AST' self.check_reduce['list_for'] = 'AST'
self.check_reduce['try_except'] = 'tokens' self.check_reduce['try_except'] = 'tokens'
self.check_reduce['tryelsestmt'] = 'tokens' self.check_reduce['tryelsestmt'] = 'AST'
def reduce_is_invalid(self, rule, ast, tokens, first, last): def reduce_is_invalid(self, rule, ast, tokens, first, last):
invalid = super(Python26Parser, invalid = super(Python26Parser,
@@ -365,29 +365,49 @@ class Python26Parser(Python2Parser):
return (tokens[last-3].kind not in frozenset(('JUMP_FORWARD', 'RETURN_VALUE')) return (tokens[last-3].kind not in frozenset(('JUMP_FORWARD', 'RETURN_VALUE'))
or (tokens[last-3] == 'JUMP_FORWARD' and tokens[last-3].attr != 2)) or (tokens[last-3] == 'JUMP_FORWARD' and tokens[last-3].attr != 2))
elif rule[0] == 'tryelsestmt': elif rule[0] == 'tryelsestmt':
# We need to distingush try_except from tryelsestmt and we do that # We need to distingush try_except from tryelsestmt and we do that
# by checking the jump before the END_FINALLY # by making sure that the jump before the except handler jumps to
# If we have: # code somewhere before the end of the construct.
# insn # This AST method is slower, but more correct than what we had
# POP_TOP # which is given after this.
# END_FINALLY
# COME_FROM if ast[3] == 'except_handler':
# then insn is neither a JUMP_FORWARD nor RETURN_VALUE, except_handler = ast[3]
# or if it is JUMP_FORWARD, then it can't be a JUMP_FORWARD to right after if except_handler[0] == 'JUMP_FORWARD':
# COME_FROM else_start = int(except_handler[0].pattr)
if last == len(tokens): if last == len(tokens):
last -= 1 last -= 1
while tokens[last-1] == 'COME_FROM' and tokens[last-2] == 'COME_FROM': if tokens[last] == 'COME_FROM' and isinstance:
last -= 1 last_offset = int(tokens[last].offset.split('_')[0])
if tokens[last] == 'COME_FROM' and tokens[last-1] == 'COME_FROM': return else_start >= last_offset
last -= 1
if (tokens[last] == 'COME_FROM' # Before we resorted to using the AST here is the more hacky way we
and tokens[last-1] == 'END_FINALLY' # did things. This fails with a "try" embedded inside a "try/else"
and tokens[last-2] == 'POP_TOP'): # since we can't detect COME_FROM boundaries.
# A jump of 2 is a jump around POP_TOP, END_FINALLY which # # We need to distinguish try_except from tryelsestmt and we do that
# would indicate try/else rather than try # # by checking the jump before the END_FINALLY
return (tokens[last-3].kind in frozenset(('JUMP_FORWARD', 'RETURN_VALUE')) # # If we have:
and (tokens[last-3] != 'JUMP_FORWARD' or tokens[last-3].attr == 2)) # # insn
# # POP_TOP
# # END_FINALLY
# # COME_FROM
# # then insn is neither a JUMP_FORWARD nor RETURN_VALUE,
# # or if it is JUMP_FORWARD, then it can't be a JUMP_FORWARD to right after
# # COME_FROM
# if last == len(tokens):
# last -= 1
# while tokens[last-1] == 'COME_FROM' and tokens[last-2] == 'COME_FROM':
# last -= 1
# if tokens[last] == 'COME_FROM' and tokens[last-1] == 'COME_FROM':
# last -= 1
# if (tokens[last] == 'COME_FROM'
# and tokens[last-1] == 'END_FINALLY'
# and tokens[last-2] == 'POP_TOP'):
# # A jump of 2 is a jump around POP_TOP, END_FINALLY which
# # would indicate try/else rather than try
# return (tokens[last-3].kind in frozenset(('JUMP_FORWARD', 'RETURN_VALUE'))
# and (tokens[last-3] != 'JUMP_FORWARD' or tokens[last-3].attr == 2))
return False return False