2222 ForStmt ,
2323 FuncDef ,
2424 GeneratorExpr ,
25+ IndexExpr ,
2526 LambdaExpr ,
2627 MemberExpr ,
2728 MypyFile ,
29+ NamedTupleExpr ,
2830 NameExpr ,
31+ NewTypeExpr ,
2932 Node ,
33+ OpExpr ,
3034 RefExpr ,
3135 TupleExpr ,
36+ TypedDictExpr ,
3237 TypeInfo ,
38+ TypeVarExpr ,
3339 Var ,
3440 WithStmt ,
3541)
@@ -204,7 +210,7 @@ def function_annotations(func_ir: FuncIR, tree: MypyFile) -> dict[int, list[Anno
204210 elif name == "PyObject_VectorcallMethod" :
205211 method_name = get_str_literal (op .args [0 ])
206212 if method_name :
207- ann = f'Call non-native method "{ method_name } ".'
213+ ann = f'Call non-native method "{ method_name } " (it may be defined in a non-native class, or decorated) .'
208214 else :
209215 ann = "Dynamic method call."
210216 elif name in op_hints :
@@ -300,6 +306,10 @@ def visit_class_def(self, o: ClassDef, /) -> None:
300306 if isinstance (s , AssignmentStmt ):
301307 # Don't complain about attribute initializers
302308 self .ignored_lines .add (s .line )
309+ elif isinstance (s , Decorator ):
310+ # Don't complain about decorator definitions that generate some
311+ # dynamic operations. This is a bit heavy-handed.
312+ self .ignored_lines .add (s .func .line )
303313
304314 def visit_with_stmt (self , o : WithStmt , / ) -> None :
305315 for expr in o .expr :
@@ -317,10 +327,25 @@ def visit_with_stmt(self, o: WithStmt, /) -> None:
317327 f'"{ node .name } " uses @contextmanager, which is slow '
318328 + "in compiled code. Use a native class with "
319329 + '"__enter__" and "__exit__" methods instead.' ,
320- priority = 2 ,
330+ priority = 3 ,
321331 )
322332 super ().visit_with_stmt (o )
323333
334+ def visit_assignment_stmt (self , o : AssignmentStmt , / ) -> None :
335+ special_form = False
336+ if self .func_depth == 0 :
337+ analyzed = o .rvalue
338+ if isinstance (o .rvalue , (CallExpr , IndexExpr , OpExpr )):
339+ analyzed = o .rvalue .analyzed
340+ if o .is_alias_def or isinstance (
341+ analyzed , (TypeVarExpr , NamedTupleExpr , TypedDictExpr , NewTypeExpr )
342+ ):
343+ special_form = True
344+ if special_form :
345+ # TODO: Ignore all lines if multi-line
346+ self .ignored_lines .add (o .line )
347+ super ().visit_assignment_stmt (o )
348+
324349 def visit_name_expr (self , o : NameExpr , / ) -> None :
325350 if ann := stdlib_hints .get (o .fullname ):
326351 self .annotate (o , ann )
@@ -355,8 +380,14 @@ def visit_call_expr(self, o: CallExpr, /) -> None:
355380 + "constructing an instance is slow." ,
356381 2 ,
357382 )
358-
359- print (o .callee .node .fullname , info in self .mapper .type_to_ir )
383+ elif isinstance (o .callee , RefExpr ) and isinstance (o .callee .node , Decorator ):
384+ decorator = o .callee .node
385+ if self .mapper .is_native_ref_expr (o .callee ):
386+ self .annotate (
387+ o ,
388+ f'Calling a decorated function ("{ decorator .name } ") is inefficient, even if it\' s native.' ,
389+ 2 ,
390+ )
360391
361392 def check_isinstance_arg (self , arg : Expression ) -> None :
362393 if isinstance (arg , RefExpr ):
0 commit comments