Repository: python/mypy

Issue:
Internal error when using Python3.5 PEP 448 tuple unpacking with printf style formatting
I managed to isolate the error to the following:
```python
x = (1,2)
"%d%d" % (*x,)
```

This produces an INTERNAL ERROR with the following traceback:
```
Traceback (most recent call last):
  File "/usr/local/bin/mypy", line 6, in <module>
    main(__file__)
  File "/usr/local/lib/python3.6/site-packages/mypy/main.py", line 42, in main
    res = type_check_only(sources, bin_dir, options)
  File "/usr/local/lib/python3.6/site-packages/mypy/main.py", line 87, in type_check_only
    options=options)
  File "/usr/local/lib/python3.6/site-packages/mypy/build.py", line 183, in build
    dispatch(sources, manager)
  File "/usr/local/lib/python3.6/site-packages/mypy/build.py", line 1526, in dispatch
    process_graph(graph, manager)
  File "/usr/local/lib/python3.6/site-packages/mypy/build.py", line 1762, in process_graph
    process_stale_scc(graph, scc)
  File "/usr/local/lib/python3.6/site-packages/mypy/build.py", line 1841, in process_stale_scc
    graph[id].type_check_first_pass()
  File "/usr/local/lib/python3.6/site-packages/mypy/build.py", line 1451, in type_check_first_pass
    self.type_checker.check_first_pass()
  File "/usr/local/lib/python3.6/site-packages/mypy/checker.py", line 175, in check_first_pass
    self.accept(d)
  File "/usr/local/lib/python3.6/site-packages/mypy/checker.py", line 247, in accept
    typ = node.accept(self)
  File "/usr/local/lib/python3.6/site-packages/mypy/nodes.py", line 788, in accept
    return visitor.visit_expression_stmt(self)
  File "/usr/local/lib/python3.6/site-packages/mypy/checker.py", line 1596, in visit_expression_stmt
    self.accept(s.expr)
  File "/usr/local/lib/python3.6/site-packages/mypy/checker.py", line 247, in accept
    typ = node.accept(self)
  File "/usr/local/lib/python3.6/site-packages/mypy/nodes.py", line 1400, in accept
    return visitor.visit_op_expr(self)
  File "/usr/local/lib/python3.6/site-packages/mypy/checker.py", line 2160, in visit_op_expr
    return self.expr_checker.visit_op_expr(e)
  File "/usr/local/lib/python3.6/site-packages/mypy/checkexpr.py", line 1110, in visit_op_expr
    return self.strfrm_checker.check_str_interpolation(e.left, e.right)
  File "/usr/local/lib/python3.6/site-packages/mypy/checkstrformat.py", line 74, in check_str_interpolation
    self.check_simple_str_interpolation(specifiers, replacements)
  File "/usr/local/lib/python3.6/site-packages/mypy/checkstrformat.py", line 146, in check_simple_str_interpolation
    check_node(rep_node)
  File "/usr/local/lib/python3.6/site-packages/mypy/checkstrformat.py", line 255, in check_expr
    check_type(type)
  File "/usr/local/lib/python3.6/site-packages/mypy/checkstrformat.py", line 251, in check_type
    'expression has type', 'placeholder has type')
  File "/usr/local/lib/python3.6/site-packages/mypy/checker.py", line 2273, in check_subtype
    if is_subtype(subtype, supertype):
  File "/usr/local/lib/python3.6/site-packages/mypy/subtypes.py", line 54, in is_subtype
    for item in right.items)
  File "/usr/local/lib/python3.6/site-packages/mypy/subtypes.py", line 54, in <genexpr>
    for item in right.items)
  File "/usr/local/lib/python3.6/site-packages/mypy/subtypes.py", line 56, in is_subtype
    return left.accept(SubtypeVisitor(right, type_parameter_checker,
AttributeError: 'NoneType' object has no attribute 'accept'
```


Discussion:
I was able to reproduce the crash. Thanks for reporting this!
You're welcome! I wasn't sure if I was doing this right, I've never submitted a bug report to mypy before.
Interestingly, on master/HEAD, this crashes *differently*, with a traceback ending in
```
  File "/Users/guido/src/mypy/mypy/types.py", line 1257, in visit_star_type
    raise self._notimplemented_helper('star_type')
NotImplementedError: Method SubtypeVisitor.visit_star_type() not implemented
This is a known bug, track development in 'https://github.com/JukkaL/mypy/issues/730'
```
Using bisect, I traced the change back to PR #2699 by @elazarg. I'm not sure it's an improvement. :-)
The crash is easy to fix. But the problem is (obviously) the treatment of StarExpr in checkers_for_regular_type. I wonder whether it Is possible to generate a signature type from the string and then check a standard function call.
Actually that signature seems to be available in `rhs_type` and `rep_types` which are computed earlier in `check_simple_str_interpolation()`. But then the code a bit further down specializing for `TupleExpr` is ignoring these. I wonder if the right fix should be made at that level? E.g. not taking that specialization if `replacements.items` contains a `StarExpr`.

Resolving patch:
diff --git a/mypy/checkstrformat.py b/mypy/checkstrformat.py
--- a/mypy/checkstrformat.py
+++ b/mypy/checkstrformat.py
@@ -8,7 +8,7 @@
     Type, AnyType, TupleType, Instance, UnionType
 )
 from mypy.nodes import (
-    StrExpr, BytesExpr, UnicodeExpr, TupleExpr, DictExpr, Context, Expression
+    StrExpr, BytesExpr, UnicodeExpr, TupleExpr, DictExpr, Context, Expression, StarExpr
 )
 if False:
     # break import cycle only needed for mypy
@@ -140,7 +140,8 @@ def check_simple_str_interpolation(self, specifiers: List[ConversionSpecifier],
                     check_type(rhs_type.items[0])
                 else:
                     check_node(replacements)
-            elif isinstance(replacements, TupleExpr):
+            elif (isinstance(replacements, TupleExpr)
+                  and not any(isinstance(item, StarExpr) for item in replacements.items)):
                 for checks, rep_node in zip(checkers, replacements.items):
                     check_node, check_type = checks
                     check_node(rep_node)


Test patch:
diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test
--- a/test-data/unit/check-expressions.test
+++ b/test-data/unit/check-expressions.test
@@ -1149,6 +1149,10 @@ def foo(a: bytes, b: bytes):
     b'%s:%s' % (a, b)
 foo(b'a', b'b') == b'a:b'
 
+[case testStringInterpolationStarArgs]
+x = (1, 2)
+"%d%d" % (*x,)
+
 [case testBytePercentInterpolationSupported]
 b'%s' % (b'xyz',)
 b'%(name)s' % {'name': 'jane'}