Skip to content

Commit f6c5d74

Browse files
committed
let_syntax: detect and complain if trying to substitute a block into an expression position
1 parent f7b91fd commit f6c5d74

File tree

1 file changed

+28
-17
lines changed

1 file changed

+28
-17
lines changed

unpythonic/syntax/letsyntax.py

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -145,14 +145,19 @@ def isthisname(tree):
145145
return type(tree) is Name and tree.id == name
146146
@Walker
147147
def splice(tree, *, stop, **kw):
148-
# discard Expr wrapper at use site for a block substitution
148+
def subst():
149+
# Copy just to be on the safe side. Different instances may be
150+
# edited differently by other macros expanded later.
151+
return deepcopy(value)
152+
# discard Expr wrapper (identifying a statement position) at use site
153+
# when performing a block substitution
149154
if mode == "block" and type(tree) is Expr and isthisname(tree.value):
150155
stop()
151-
tree = splice.recurse(tree.value)
156+
tree = subst()
152157
elif isthisname(tree):
153-
# Copy just to be on the safe side. Different instances may be
154-
# edited differently by other macros expanded later.
155-
tree = deepcopy(value)
158+
if mode == "block":
159+
assert False, "cannot substitute a block into expression position"
160+
tree = subst()
156161
return tree
157162
return splice.recurse(tree)
158163

@@ -165,23 +170,29 @@ def _substitute_templates(templates, tree):
165170
for name, formalparams, value, mode in templates:
166171
def isthisfunc(tree):
167172
return type(tree) is Call and type(tree.func) is Name and tree.func.id == name
173+
def subst(tree):
174+
theargs = tree.args
175+
if len(theargs) != len(formalparams):
176+
assert False, "let_syntax template '{}' expected {} arguments, got {}".format(name,
177+
len(formalparams),
178+
len(theargs))
179+
# make a fresh deep copy of the RHS to avoid destroying the template.
180+
tree = deepcopy(value) # expand the f itself in f(x, ...)
181+
for k, v in zip(formalparams, theargs): # expand the x, ... in the expanded form of f
182+
# can't put statements in a Call, so always treat args as expressions.
183+
tree = _substitute_barename(k, v, tree, "expr")
184+
return tree
168185
@Walker
169186
def splice(tree, *, stop, **kw):
170-
# discard Expr wrapper at use site for a block substitution
187+
# discard Expr wrapper (identifying a statement position) at use site
188+
# when performing a block substitution
171189
if mode == "block" and type(tree) is Expr and isthisfunc(tree.value):
172190
stop()
173-
tree = splice.recurse(tree.value)
191+
tree = subst(tree.value)
174192
elif isthisfunc(tree):
175-
theargs = tree.args
176-
if len(theargs) != len(formalparams):
177-
assert False, "let_syntax template '{}' expected {} arguments, got {}".format(name,
178-
len(formalparams),
179-
len(theargs))
180-
# make a fresh deep copy of the RHS to avoid destroying the template.
181-
tree = deepcopy(value) # expand the f itself in f(x, ...)
182-
for k, v in zip(formalparams, theargs): # expand the x, ... in the expanded form of f
183-
# can't put statements in a Call, so always treat args as expressions.
184-
tree = _substitute_barename(k, v, tree, "expr")
193+
if mode == "block":
194+
assert False, "cannot substitute a block into expression position"
195+
tree = subst(tree)
185196
return tree
186197
tree = splice.recurse(tree)
187198
return tree

0 commit comments

Comments
 (0)