| 218 | |
| 219 | |
| 220 | class DependencyVisitor(TraverserVisitor): |
| 221 | def __init__( |
| 222 | self, |
| 223 | type_map: dict[Expression, Type], |
| 224 | python_version: tuple[int, int], |
| 225 | alias_deps: defaultdict[str, set[str]], |
| 226 | options: Options | None = None, |
| 227 | ) -> None: |
| 228 | self.scope = Scope() |
| 229 | self.type_map = type_map |
| 230 | # This attribute holds a mapping from target to names of type aliases |
| 231 | # it depends on. These need to be processed specially, since they may |
| 232 | # appear in expanded form in symbol tables, because of a get_proper_type() |
| 233 | # somewhere. For example, after: |
| 234 | # A = int |
| 235 | # x: A |
| 236 | # the module symbol table will just have a Var `x` with type `int`, |
| 237 | # and the dependency of `x` on `A` is lost. Therefore, the alias dependencies |
| 238 | # are preserved at alias expansion points in `semanal.py`, stored as an attribute |
| 239 | # on MypyFile, and then passed here. |
| 240 | # TODO: fine-grained is more susceptible to this partially because we are reckless |
| 241 | # about get_proper_type() in *this specific file*. |
| 242 | self.alias_deps = alias_deps |
| 243 | self.map: dict[str, set[str]] = {} |
| 244 | self.is_class = False |
| 245 | self.is_package_init_file = False |
| 246 | self.options = options |
| 247 | |
| 248 | def visit_mypy_file(self, o: MypyFile) -> None: |
| 249 | with self.scope.module_scope(o.fullname): |
| 250 | self.is_package_init_file = o.is_package_init_file() |
| 251 | self.add_type_alias_deps(self.scope.current_target()) |
| 252 | for trigger, targets in o.plugin_deps.items(): |
| 253 | self.map.setdefault(trigger, set()).update(targets) |
| 254 | super().visit_mypy_file(o) |
| 255 | |
| 256 | def visit_func_def(self, o: FuncDef) -> None: |
| 257 | with self.scope.function_scope(o): |
| 258 | target = self.scope.current_target() |
| 259 | if o.type: |
| 260 | if self.is_class and isinstance(o.type, FunctionLike): |
| 261 | signature: Type = bind_self(o.type) |
| 262 | else: |
| 263 | signature = o.type |
| 264 | for trigger in self.get_type_triggers(signature): |
| 265 | self.add_dependency(trigger) |
| 266 | self.add_dependency(trigger, target=make_trigger(target)) |
| 267 | if o.info: |
| 268 | for base in non_trivial_bases(o.info): |
| 269 | # Base class __init__/__new__ doesn't generate a logical |
| 270 | # dependency since the override can be incompatible. |
| 271 | if not self.use_logical_deps() or o.name not in ("__init__", "__new__"): |
| 272 | self.add_dependency(make_trigger(base.fullname + "." + o.name)) |
| 273 | self.add_type_alias_deps(self.scope.current_target()) |
| 274 | super().visit_func_def(o) |
| 275 | variants = set(o.expanded) - {o} |
| 276 | for ex in variants: |
| 277 | if isinstance(ex, FuncDef): |
no outgoing calls
no test coverage detected
searching dependent graphs…