OILS / mycpp / visitor.py View on Github | oils.pub

776 lines, 467 significant
1"""
2visitor.py - base class for all our tree traversals.
3
4It validates many nodes, i.e. presenting a subset of the MyPy AST to
5subclasses.
6"""
7import mypy
8from mypy.visitor import ExpressionVisitor, StatementVisitor
9from mypy.nodes import (Expression, Statement, ExpressionStmt, StrExpr,
10 CallExpr, YieldExpr, NameExpr, MemberExpr, Argument,
11 ClassDef, FuncDef, IfStmt, PassStmt, ListComprehension)
12from mypy.types import Type
13
14from mycpp.crash import catch_errors
15from mycpp import util
16from mycpp.util import SplitPyName, log
17
18from typing import (overload, Any, Union, Optional, List, Dict, Tuple, TextIO)
19
20NAME_CONFLICTS = ('stdin', 'stdout', 'stderr')
21
22
23class SimpleVisitor(ExpressionVisitor[None], StatementVisitor[None]):
24 """
25 A simple AST visitor that accepts every node in the AST. Derrived classes
26 can override the visit methods that are relevant to them.
27 """
28
29 def __init__(self) -> None:
30 self.module_path: Optional[str] = None
31
32 # DO NOT USE from classes inheriting SimpleVisitor.
33 # These should be passed into oils_visit_* explicitly
34 self.__current_class_name: Optional[util.SymbolPath] = None
35 self.__current_method_name: Optional[str] = None
36 self.__at_global_scope = True
37
38 # So we can report multiple at once
39 # module path, line number, message
40 self.errors_keep_going: List[Tuple[str, int, str]] = []
41
42 self.indent = 0
43 self.f: Optional[TextIO] = None
44
45 def SetOutputFile(self, f: TextIO) -> None:
46 self.f = f
47
48 def log(self, msg: str, *args: Any) -> None:
49 """Log to STDERR"""
50 ind_str = self.indent * ' '
51 log(ind_str + msg, *args)
52
53 def write(self, msg: str, *args: Any) -> None:
54 if args:
55 msg = msg % args
56 assert self.f is not None
57 self.f.write(msg)
58
59 def write_ind(self, msg: str, *args: Any) -> None:
60 ind_str = self.indent * ' '
61 self.write(ind_str + msg, *args)
62
63 #
64 # COPIED from mypyc IRBuilder
65 #
66
67 @overload
68 def accept(self, node: Expression) -> None:
69 ...
70
71 @overload
72 def accept(self, node: Statement) -> None:
73 ...
74
75 def accept(self, node: Union[Statement, Expression]) -> None:
76 with catch_errors(self.module_path, node.line):
77 if isinstance(node, Expression):
78 node.accept(self)
79 else:
80 node.accept(self)
81
82 #
83 # Error API
84 #
85
86 def report_error(self, node: Union[Statement, Expression, Argument],
87 msg: str) -> None:
88 err = (self.module_path, node.line, msg)
89 self.errors_keep_going.append(err)
90
91 def not_translated(self, node: Union[Statement, Expression],
92 name: str) -> None:
93 self.report_error(node, '%s not translated' % name)
94
95 def not_python2(self, node: Union[Statement, Expression],
96 name: str) -> None:
97 self.report_error(node, "%s: shouldn't get here in Python 2" % name)
98
99 def oils_visit_mypy_file(self, o: 'mypy.nodes.MypyFile') -> None:
100 for node in o.defs:
101 self.accept(node)
102
103 def visit_mypy_file(self, o: 'mypy.nodes.MypyFile') -> None:
104 if util.ShouldSkipPyFile(o):
105 return
106
107 self.module_path = o.path
108
109 self.oils_visit_mypy_file(o)
110
111 # Now show errors for each file
112 for path, line_num, msg in self.errors_keep_going:
113 self.log('%s:%s %s', path, line_num, msg)
114
115 def oils_visit_for_stmt(self, o: 'mypy.nodes.ForStmt',
116 func_name: Optional[str]) -> None:
117 self.accept(o.index) # index var expression
118 self.accept(o.expr)
119 self.accept(o.body)
120 if o.else_body:
121 raise AssertionError("can't translate for-else")
122
123 def visit_for_stmt(self, o: 'mypy.nodes.ForStmt') -> None:
124
125 func_name = None # does the loop look like 'for x in func():' ?
126 if (isinstance(o.expr, CallExpr) and
127 isinstance(o.expr.callee, NameExpr)):
128 func_name = o.expr.callee.name
129
130 # In addition to func_name, can we also pass
131 # iterated_over
132 # enumerate() reversed() iteritems() is o.expr[0]
133 # otherwise it's o.expr
134 # And then you get the type, and if it's typing.Iterator, then the
135 # virtual pass can set self.yield_eager_for
136
137 self.oils_visit_for_stmt(o, func_name)
138
139 # TODO: validate and destructure the different kinds of loops
140 #
141 # xrange() - 1 index
142 # xrange negative
143 # enumerate() - 2 indices
144 # reversed() - container - 1 index
145 # iteritems() - dict, 2 indices
146 #
147 # - over list
148 # - over dict - list comprehensions would need this too
149 # - over iterator
150 #
151 # LHS
152 # - NameExpr
153 # - TupleExpr
154 #
155 # More destructuring:
156 #
157 # item_type - is it a Tuple?
158 # enumerate - o.inferred_item_type[1]
159 # otherwise (xrange, reversed, iteritems) - o.inferred_item_type
160 #
161 # elif isinstance(item_type, TupleType): # for x, y in pairs
162 # if over_dict:
163 # ...
164 # else # it's a List
165 # if isinstance(o.index, TupleExpr):
166 # ...
167 # self._write_tuple_unpacking(temp_name, o.index.items, item_type.items)
168 #
169 # We need to detect this
170 # And then also detect it for list comprehensions
171
172 # Two different tests:
173 # for loop test
174 # if isinstance(o.index, TupleExpr):
175
176 def oils_visit_switch(self, expr: 'mypy.nodes.CallExpr',
177 o: 'mypy.nodes.WithStmt',
178 switch_type: str) -> None:
179 """Hook for handling switch statements.
180
181 switch_type is one of: 'switch', 'str_switch', 'tagswitch'
182 """
183 self.accept(expr)
184 self.accept(o.body)
185
186 def visit_with_stmt(self, o: 'mypy.nodes.WithStmt') -> None:
187 assert len(o.expr) == 1, o.expr
188 expr = o.expr[0]
189 assert isinstance(expr, CallExpr), expr
190
191 # Check if this is a switch statement
192 callee_name = None
193 if isinstance(expr.callee, NameExpr):
194 callee_name = expr.callee.name
195
196 if callee_name in ('switch', 'str_switch', 'tagswitch'):
197 self.oils_visit_switch(expr, o, callee_name)
198 else:
199 self.accept(expr)
200 self.accept(o.body)
201
202 def oils_visit_func_def(self, o: 'mypy.nodes.FuncDef',
203 current_class_name: Optional[util.SymbolPath],
204 current_method_name: Optional[str]) -> None:
205 """Only the functions we care about in Oils."""
206 for arg in o.arguments:
207 if arg.initializer:
208 self.accept(arg.initializer)
209
210 self.accept(o.body)
211
212 def visit_func_def(self, o: 'mypy.nodes.FuncDef') -> None:
213 # This could be a free function or a method
214 # __init__ __exit__ and other methods call this, with self.__current_class_name set
215
216 # If an assignment statement is not in a function or method, then it's at global scope
217
218 self.__at_global_scope = False
219 self.oils_visit_func_def(o, self.__current_class_name,
220 self.__current_method_name)
221 self.__at_global_scope = True
222
223 #
224 # Classes
225 #
226
227 def oils_visit_constructor(
228 self, o: ClassDef, stmt: FuncDef, base_class_sym: util.SymbolPath,
229 current_class_name: Optional[util.SymbolPath]) -> None:
230 self.accept(stmt)
231
232 def oils_visit_dunder_exit(self, o: ClassDef, stmt: FuncDef,
233 base_class_sym: util.SymbolPath) -> None:
234 self.accept(stmt)
235
236 def oils_visit_method(self, o: ClassDef, stmt: FuncDef,
237 base_class_sym: util.SymbolPath) -> None:
238 self.accept(stmt)
239
240 def oils_visit_class_members(
241 self, o: ClassDef, base_class_sym: util.SymbolPath,
242 current_class_name: Optional[util.SymbolPath]) -> None:
243 """Hook for writing member vars."""
244 # Do nothing by default.
245 pass
246
247 def oils_visit_class_def(
248 self, o: 'mypy.nodes.ClassDef',
249 base_class_sym: Optional[util.SymbolPath],
250 current_class_name: Optional[util.SymbolPath]) -> None:
251
252 for stmt in o.defs.body:
253 # Skip class docstrings
254 if (isinstance(stmt, ExpressionStmt) and
255 isinstance(stmt.expr, StrExpr)):
256 continue
257
258 # Skip empty classes
259 if isinstance(stmt, PassStmt):
260 continue
261
262 if isinstance(stmt, FuncDef):
263 method_name = stmt.name
264
265 # Don't translate
266 if method_name in ('__enter__', '__repr__'):
267 continue
268
269 if method_name == '__init__': # Don't translate
270 self.__current_method_name = stmt.name
271 self.oils_visit_constructor(o, stmt, base_class_sym,
272 current_class_name)
273 self.__current_method_name = None
274 continue
275
276 if method_name == '__exit__': # Don't translate
277 if not o.name.startswith('ctx_'):
278 self.report_error(
279 o,
280 'Class with __exit__ should be named ctx_Foo: %s' %
281 (current_class_name, ))
282
283 self.__current_method_name = stmt.name
284 self.oils_visit_dunder_exit(o, stmt, base_class_sym)
285 self.__current_method_name = None
286 continue
287
288 self.__current_method_name = stmt.name
289 self.oils_visit_method(o, stmt, base_class_sym)
290 self.__current_method_name = None
291 continue
292
293 # if 0: is allowed
294 if isinstance(stmt, IfStmt):
295 self.accept(stmt)
296 continue
297
298 self.report_error(
299 o, 'Classes may only have method definitions, got %s' % stmt)
300
301 self.oils_visit_class_members(o, base_class_sym, current_class_name)
302
303 def visit_class_def(self, o: 'mypy.nodes.ClassDef') -> None:
304 base_class_sym = None # single inheritance only
305 if len(o.base_type_exprs) > 1:
306 self.report_error(o, 'too many base types: %s' % o.base_type_exprs)
307 return
308
309 for b in o.base_type_exprs:
310 if isinstance(b, NameExpr):
311 if b.name != 'object' and b.name != 'Exception':
312 base_class_sym = SplitPyName(b.fullname)
313 elif isinstance(b, MemberExpr): # vm._Executor -> vm::_Executor
314 assert isinstance(b.expr, NameExpr), b
315 base_class_sym = SplitPyName(b.expr.fullname) + (b.name, )
316 else:
317 # shouldn't happen
318 raise AssertionError(b)
319
320 self.__current_class_name = SplitPyName(o.fullname)
321 self.oils_visit_class_def(o, base_class_sym, self.__current_class_name)
322 self.__current_class_name = None
323
324 # Statements
325
326 def oils_visit_assignment_stmt(self, o: 'mypy.nodes.AssignmentStmt',
327 lval: Expression, rval: Expression,
328 current_method_name: Optional[str],
329 at_global_scope: bool) -> None:
330 self.accept(lval)
331 self.accept(rval)
332
333 def visit_assignment_stmt(self, o: 'mypy.nodes.AssignmentStmt') -> None:
334 # We never use this idiom: x = y = 42
335 assert len(o.lvalues) == 1, o.lvalues
336 lval = o.lvalues[0]
337
338 # Metadata we'll never use
339 if isinstance(lval, NameExpr):
340 if lval.name == '__all__':
341 return
342
343 # Special case for
344 if isinstance(o.rvalue, ListComprehension):
345 self.visit_assign_to_listcomp(o, lval)
346 return
347
348 # TODO: virtual pass might want this
349 # However it depends on TYPES. Right now the visitor doesn't depend on types
350 # if (isinstance(rval_type, Instance) and
351 # rval_type.type.fullname == 'typing.Iterator'):
352 # self._AssignToGenerator(o, lval, rval_type)
353 # return
354 # Key reason it needs to be in the virtual pass is that signatures
355 # must change to allow an out param.
356
357 self.oils_visit_assignment_stmt(o, lval, o.rvalue,
358 self.__current_method_name,
359 self.__at_global_scope)
360
361 def visit_operator_assignment_stmt(
362 self, o: 'mypy.nodes.OperatorAssignmentStmt') -> None:
363 self.accept(o.lvalue)
364 self.accept(o.rvalue)
365
366 def visit_block(self, block: 'mypy.nodes.Block') -> None:
367 for stmt in block.body:
368 self.accept(stmt)
369
370 def oils_visit_expression_stmt(self,
371 o: 'mypy.nodes.ExpressionStmt') -> None:
372 self.accept(o.expr)
373
374 def visit_expression_stmt(self, o: 'mypy.nodes.ExpressionStmt') -> None:
375 # Ignore all docstrings: module, class, and function body
376 if isinstance(o.expr, StrExpr):
377 return
378
379 # Either f() or obj.method()
380 assert isinstance(o.expr, (CallExpr, YieldExpr)), o.expr
381
382 self.oils_visit_expression_stmt(o)
383
384 def visit_while_stmt(self, o: 'mypy.nodes.WhileStmt') -> None:
385 self.accept(o.expr)
386 self.accept(o.body)
387
388 def visit_return_stmt(self, o: 'mypy.nodes.ReturnStmt') -> None:
389 if o.expr:
390 self.accept(o.expr)
391
392 def visit_if_stmt(self, o: 'mypy.nodes.IfStmt') -> None:
393 if util.ShouldVisitIfExpr(o):
394 for expr in o.expr:
395 self.accept(expr)
396
397 if util.ShouldVisitIfBody(o):
398 for body in o.body:
399 self.accept(body)
400
401 if util.ShouldVisitElseBody(o):
402 self.accept(o.else_body)
403
404 def visit_raise_stmt(self, o: 'mypy.nodes.RaiseStmt') -> None:
405 if o.expr:
406 self.accept(o.expr)
407
408 def visit_try_stmt(self, o: 'mypy.nodes.TryStmt') -> None:
409 self.accept(o.body)
410 for handler in o.handlers:
411 self.accept(handler)
412
413 def visit_del_stmt(self, o: 'mypy.nodes.DelStmt') -> None:
414 self.accept(o.expr)
415
416 # Expressions
417
418 def visit_generator_expr(self, o: 'mypy.nodes.GeneratorExpr') -> None:
419 raise AssertionError()
420
421 self.accept(o.left_expr)
422
423 for expr in o.indices:
424 self.accept(expr)
425
426 for expr in o.sequences:
427 self.accept(expr)
428
429 for l in o.condlists:
430 for expr in l:
431 self.accept(expr)
432
433 def visit_list_comprehension(self,
434 o: 'mypy.nodes.ListComprehension') -> None:
435 # old code
436 #self.accept(o.generator)
437 self.report_error(o,
438 'List comprehension must be assigned to a temp var')
439
440 def oils_visit_assign_to_listcomp(self, lval: NameExpr,
441 left_expr: Expression,
442 index_expr: Expression, seq: Expression,
443 cond: Expression) -> None:
444 self.accept(lval)
445 self.accept(left_expr)
446 self.accept(index_expr)
447 self.accept(seq)
448 if cond is not None:
449 self.accept(cond)
450
451 def visit_assign_to_listcomp(self, o: 'mypy.nodes.AssignmentStmt',
452 lval: NameExpr) -> None:
453 gen = o.rvalue.generator # GeneratorExpr
454
455 if (len(gen.indices) != 1 or len(gen.sequences) != 1 or
456 len(gen.condlists) != 1):
457 self.report_error(o, 'List comprehensions can only have one loop')
458
459 index_expr = gen.indices[0]
460 seq = gen.sequences[0]
461 condlist = gen.condlists[0]
462
463 if len(condlist) == 0:
464 cond: Optional[Expression] = None
465 elif len(condlist) == 1:
466 cond = condlist[0]
467 else:
468 self.report_error(
469 o, 'List comprehensions may have at most one condition')
470
471 self.oils_visit_assign_to_listcomp(lval, gen.left_expr, index_expr,
472 seq, cond)
473
474 def visit_yield_expr(self, o: 'mypy.nodes.YieldExpr') -> None:
475 self.accept(o.expr)
476
477 def visit_op_expr(self, o: 'mypy.nodes.OpExpr') -> None:
478 self.accept(o.left)
479 self.accept(o.right)
480
481 def visit_comparison_expr(self, o: 'mypy.nodes.ComparisonExpr') -> None:
482 for operand in o.operands:
483 self.accept(operand)
484
485 def visit_unary_expr(self, o: 'mypy.nodes.UnaryExpr') -> None:
486 # e.g. -42 or 'not x'
487 self.accept(o.expr)
488
489 def visit_list_expr(self, o: 'mypy.nodes.ListExpr') -> None:
490 for item in o.items:
491 self.accept(item)
492
493 def visit_dict_expr(self, o: 'mypy.nodes.DictExpr') -> None:
494 for k, v in o.items:
495 self.accept(k)
496 self.accept(v)
497
498 def visit_tuple_expr(self, o: 'mypy.nodes.TupleExpr') -> None:
499 for item in o.items:
500 self.accept(item)
501
502 def visit_index_expr(self, o: 'mypy.nodes.IndexExpr') -> None:
503 self.accept(o.base)
504 self.accept(o.index)
505
506 def visit_slice_expr(self, o: 'mypy.nodes.SliceExpr') -> None:
507 if o.begin_index:
508 self.accept(o.begin_index)
509
510 if o.end_index:
511 self.accept(o.end_index)
512
513 if o.stride:
514 self.accept(o.stride)
515
516 def visit_conditional_expr(self, o: 'mypy.nodes.ConditionalExpr') -> None:
517 self.accept(o.cond)
518 self.accept(o.if_expr)
519 self.accept(o.else_expr)
520
521 def oils_visit_log_call(self, fmt: StrExpr,
522 args: List[Expression]) -> None:
523 self.accept(fmt)
524 for arg in args:
525 self.accept(arg)
526
527 def oils_visit_probe_call(self, o: 'mypy.nodes.CallExpr') -> None:
528 self.accept(o.callee)
529 for arg in o.args:
530 self.accept(arg)
531
532 def oils_visit_call_expr(
533 self, o: 'mypy.nodes.CallExpr',
534 current_class_name: Optional[util.SymbolPath]) -> None:
535 self.accept(o.callee)
536 for arg in o.args:
537 self.accept(arg)
538
539 def visit_call_expr(self, o: 'mypy.nodes.CallExpr') -> None:
540 # Oils invariant: check that it'fs () or obj.method()
541 assert isinstance(o.callee, (NameExpr, MemberExpr)), o.callee
542
543 if isinstance(o.callee, NameExpr):
544 callee_name = o.callee.name
545
546 if callee_name == 'isinstance':
547 self.report_error(o, 'isinstance() not allowed')
548 return
549
550 if callee_name == 'log':
551 # Special printf-style varargs:
552 #
553 # log('foo %s', x)
554 # =>
555 # log(StrFormat('foo %s', x))
556
557 args = o.args
558 assert len(args) > 0, o
559 assert isinstance(args[0], StrExpr), args[0]
560 fmt = args[0]
561
562 self.oils_visit_log_call(fmt, args[1:])
563 return
564
565 if callee_name == 'probe':
566 # DTRACE_PROBE()
567 self.oils_visit_probe_call(o)
568 return
569
570 self.oils_visit_call_expr(o, self.__current_class_name)
571
572 #
573 # Leaf Statements and Expressions that do nothing
574 #
575
576 def visit_int_expr(self, o: 'mypy.nodes.IntExpr') -> None:
577 pass
578
579 def visit_float_expr(self, o: 'mypy.nodes.FloatExpr') -> None:
580 pass
581
582 def visit_str_expr(self, o: 'mypy.nodes.StrExpr') -> None:
583 pass
584
585 def visit_break_stmt(self, o: 'mypy.nodes.BreakStmt') -> None:
586 pass
587
588 def visit_continue_stmt(self, o: 'mypy.nodes.ContinueStmt') -> None:
589 pass
590
591 def visit_pass_stmt(self, o: 'mypy.nodes.PassStmt') -> None:
592 pass
593
594 def visit_import(self, o: 'mypy.nodes.Import') -> None:
595 pass
596
597 def visit_import_from(self, o: 'mypy.nodes.ImportFrom') -> None:
598 pass
599
600 def oils_visit_name_expr(self, o: 'mypy.nodes.NameExpr') -> None:
601 pass
602
603 def visit_name_expr(self, o: 'mypy.nodes.NameExpr') -> None:
604 if o.name in NAME_CONFLICTS:
605 self.report_error(
606 o,
607 "The name %r conflicts with C macros on some platforms; choose a different name"
608 % o.name)
609 return
610
611 self.oils_visit_name_expr(o)
612
613 def oils_visit_member_expr(self, o: 'mypy.nodes.MemberExpr') -> None:
614 self.accept(o.expr)
615
616 def visit_member_expr(self, o: 'mypy.nodes.MemberExpr') -> None:
617 if o.name in NAME_CONFLICTS:
618 self.report_error(
619 o,
620 "The name %r conflicts with C macros on some platforms; choose a different name"
621 % o.name)
622 return
623
624 self.oils_visit_member_expr(o)
625
626 #
627 # Not doing anything with these?
628 #
629
630 def visit_cast_expr(self, o: 'mypy.nodes.CastExpr') -> None:
631 # I think casts are handle in AssignmentStmt
632 pass
633
634 def visit_type_application(self, o: 'mypy.nodes.TypeApplication') -> None:
635 # what is this?
636 pass
637
638 def visit_type_var_expr(self, o: 'mypy.nodes.TypeVarExpr') -> None:
639 pass
640
641 def visit_type_alias_expr(self, o: 'mypy.nodes.TypeAliasExpr') -> None:
642 pass
643
644 def visit_reveal_expr(self, o: 'mypy.nodes.RevealExpr') -> None:
645 pass
646
647 def visit_var(self, o: 'mypy.nodes.Var') -> None:
648 # Is this a Python 3 class member?
649 pass
650
651 def visit_assert_stmt(self, o: 'mypy.nodes.AssertStmt') -> None:
652 # no-op on purpose
653 pass
654
655 #
656 # Not part of the mycpp dialect
657 #
658
659 def visit_lambda_expr(self, o: 'mypy.nodes.LambdaExpr') -> None:
660 self.not_translated(o, 'lambda')
661
662 def visit_set_comprehension(self,
663 o: 'mypy.nodes.SetComprehension') -> None:
664 self.not_translated(o, 'set comp')
665
666 def visit_dictionary_comprehension(
667 self, o: 'mypy.nodes.DictionaryComprehension') -> None:
668 self.not_translated(o, 'dict comp')
669
670 def visit_global_decl(self, o: 'mypy.nodes.GlobalDecl') -> None:
671 self.not_translated(o, 'global')
672
673 def visit_nonlocal_decl(self, o: 'mypy.nodes.NonlocalDecl') -> None:
674 self.not_translated(o, 'nonlocal')
675
676 def visit_exec_stmt(self, o: 'mypy.nodes.ExecStmt') -> None:
677 self.report_error(o, 'exec not allowed')
678
679 # Error
680 def visit_print_stmt(self, o: 'mypy.nodes.PrintStmt') -> None:
681 self.report_error(
682 o,
683 'File should start with "from __future__ import print_function"')
684
685 # UNHANDLED
686
687 def visit_import_all(self, o: 'mypy.nodes.ImportAll') -> None:
688 self.not_translated(o, 'ImportAll')
689
690 def visit_overloaded_func_def(self,
691 o: 'mypy.nodes.OverloadedFuncDef') -> None:
692 self.not_python2(o, 'overloaded func')
693
694 def visit_bytes_expr(self, o: 'mypy.nodes.BytesExpr') -> None:
695 self.not_python2(o, 'bytes expr')
696
697 def visit_unicode_expr(self, o: 'mypy.nodes.UnicodeExpr') -> None:
698 self.not_translated(o, 'unicode expr')
699
700 def visit_complex_expr(self, o: 'mypy.nodes.ComplexExpr') -> None:
701 self.not_translated(o, 'complex expr')
702
703 def visit_set_expr(self, o: 'mypy.nodes.SetExpr') -> None:
704 self.not_translated(o, 'set expr')
705
706 def visit_ellipsis(self, o: 'mypy.nodes.EllipsisExpr') -> None:
707 # is this in .pyi files only?
708 self.not_translated(o, 'ellipsis')
709
710 def visit_yield_from_expr(self, o: 'mypy.nodes.YieldFromExpr') -> None:
711 self.not_python2(o, 'yield from')
712
713 def visit_star_expr(self, o: 'mypy.nodes.StarExpr') -> None:
714 # mycpp/examples/invalid_python.py doesn't hit this?
715 self.not_translated(o, 'star expr')
716
717 def visit_super_expr(self, o: 'mypy.nodes.SuperExpr') -> None:
718 self.not_translated(o, 'super expr')
719
720 def visit_assignment_expr(self, o: 'mypy.nodes.AssignmentExpr') -> None:
721 # I think this is a := b
722 self.not_translated(o, 'assign expr')
723
724 def visit_decorator(self, o: 'mypy.nodes.Decorator') -> None:
725 self.not_translated(o, 'decorator')
726
727 def visit_backquote_expr(self, o: 'mypy.nodes.BackquoteExpr') -> None:
728 self.not_translated(o, 'backquote')
729
730 def visit_namedtuple_expr(self, o: 'mypy.nodes.NamedTupleExpr') -> None:
731 self.not_translated(o, 'namedtuple')
732
733 def visit_enum_call_expr(self, o: 'mypy.nodes.EnumCallExpr') -> None:
734 self.not_translated(o, 'enum')
735
736 def visit_typeddict_expr(self, o: 'mypy.nodes.TypedDictExpr') -> None:
737 self.not_translated(o, 'typed dict')
738
739 def visit_newtype_expr(self, o: 'mypy.nodes.NewTypeExpr') -> None:
740 self.not_translated(o, 'newtype')
741
742 def visit__promote_expr(self, o: 'mypy.nodes.PromoteExpr') -> None:
743 self.not_translated(o, 'promote')
744
745 def visit_await_expr(self, o: 'mypy.nodes.AwaitExpr') -> None:
746 self.not_translated(o, 'await')
747
748 def visit_temp_node(self, o: 'mypy.nodes.TempNode') -> None:
749 self.not_translated(o, 'temp')
750
751
752class TypedVisitor(SimpleVisitor):
753 """Base class for visitors that need type info."""
754
755 def __init__(self, types: Dict[Expression, Type]) -> None:
756 SimpleVisitor.__init__(self)
757 self.types = types
758
759 def oils_visit_op_expr(self, o: 'mypy.nodes.OpExpr') -> None:
760 self.accept(o.left)
761 self.accept(o.right)
762
763 def oils_visit_format_expr(self, left: Expression,
764 right: Expression) -> None:
765 """ mystr % x mystr % (x, y) """
766 self.accept(left)
767 self.accept(right)
768
769 def visit_op_expr(self, o: 'mypy.nodes.OpExpr') -> None:
770 if o.op == '%' and util.IsStr(self.types[o.left]):
771 # 'x = %r' % x
772 self.oils_visit_format_expr(o.left, o.right)
773 return
774
775 # Any other expression
776 self.oils_visit_op_expr(o)