Generate annotations based on mypyc IR.
(func_ir: FuncIR, tree: MypyFile)
| 180 | |
| 181 | |
| 182 | def function_annotations(func_ir: FuncIR, tree: MypyFile) -> dict[int, list[Annotation]]: |
| 183 | """Generate annotations based on mypyc IR.""" |
| 184 | # TODO: check if func_ir.line is -1 |
| 185 | anns: dict[int, list[Annotation]] = {} |
| 186 | for block in func_ir.blocks: |
| 187 | for op in block.ops: |
| 188 | if isinstance(op, CallC): |
| 189 | name = op.function_name |
| 190 | ann: str | Annotation | None = None |
| 191 | if name == "CPyObject_GetAttr": |
| 192 | attr_name = get_str_literal(op.args[1]) |
| 193 | if attr_name in ("__prepare__", "GeneratorExit", "StopIteration"): |
| 194 | # These attributes are internal to mypyc/CPython, and/or accessed |
| 195 | # implicitly in generated code. The user has little control over |
| 196 | # them. |
| 197 | ann = None |
| 198 | elif attr_name: |
| 199 | ann = f'Get non-native attribute "{attr_name}".' |
| 200 | else: |
| 201 | ann = "Dynamic attribute lookup." |
| 202 | elif name == "PyObject_SetAttr": |
| 203 | attr_name = get_str_literal(op.args[1]) |
| 204 | if attr_name == "__mypyc_attrs__": |
| 205 | # This is set implicitly and can't be avoided. |
| 206 | ann = None |
| 207 | elif attr_name: |
| 208 | ann = f'Set non-native attribute "{attr_name}".' |
| 209 | else: |
| 210 | ann = "Dynamic attribute set." |
| 211 | elif name == "PyObject_VectorcallMethod": |
| 212 | method_name = get_str_literal(op.args[0]) |
| 213 | if method_name: |
| 214 | ann = f'Call non-native method "{method_name}" (it may be defined in a non-native class, or decorated).' |
| 215 | else: |
| 216 | ann = "Dynamic method call." |
| 217 | elif name in op_hints: |
| 218 | ann = op_hints[name] |
| 219 | elif name in ("CPyDict_GetItem", "CPyDict_SetItem"): |
| 220 | if ( |
| 221 | isinstance(op.args[0], LoadStatic) |
| 222 | and isinstance(op.args[1], LoadLiteral) |
| 223 | and func_ir.name != "__top_level__" |
| 224 | ): |
| 225 | load = op.args[0] |
| 226 | name = str(op.args[1].value) |
| 227 | sym = tree.names.get(name) |
| 228 | if ( |
| 229 | sym |
| 230 | and sym.node |
| 231 | and load.namespace == "static" |
| 232 | and load.identifier == "globals" |
| 233 | ): |
| 234 | if sym.node.fullname in stdlib_hints: |
| 235 | ann = stdlib_hints[sym.node.fullname] |
| 236 | elif isinstance(sym.node, Var): |
| 237 | ann = ( |
| 238 | f'Access global "{name}" through namespace ' |
| 239 | + "dictionary (hint: access is faster if you can make it Final)." |
no test coverage detected
searching dependent graphs…