An `IterationDependentErrors` instance serves to collect the `unreachable`, `redundant-expr`, and `redundant-casts` errors, as well as the revealed types and non-overlapping types, handled by the individual `IterationErrorWatcher` instances sequentially applied to the same code section.
| 303 | |
| 304 | |
| 305 | class IterationDependentErrors: |
| 306 | """An `IterationDependentErrors` instance serves to collect the `unreachable`, |
| 307 | `redundant-expr`, and `redundant-casts` errors, as well as the revealed types and |
| 308 | non-overlapping types, handled by the individual `IterationErrorWatcher` instances |
| 309 | sequentially applied to the same code section.""" |
| 310 | |
| 311 | # One set of `unreachable`, `redundant-expr`, and `redundant-casts` errors per |
| 312 | # iteration step. Meaning of the tuple items: ErrorCode, message, line, column, |
| 313 | # end_line, end_column. |
| 314 | uselessness_errors: list[set[tuple[ErrorCode, str, int, int, int, int]]] |
| 315 | |
| 316 | # One set of unreachable line numbers per iteration step. Not only the lines where |
| 317 | # the error report occurs but really all unreachable lines. |
| 318 | unreachable_lines: list[set[int]] |
| 319 | |
| 320 | # One list of revealed types for each `reveal_type` statement. Each created list |
| 321 | # can grow during the iteration. Meaning of the tuple items: line, column, |
| 322 | # end_line, end_column: |
| 323 | revealed_types: dict[tuple[int, int, int | None, int | None], list[Type]] |
| 324 | |
| 325 | # One dictionary of non-overlapping types per iteration step: |
| 326 | nonoverlapping_types: list[dict[NonOverlapErrorInfo, tuple[Type, Type]]] |
| 327 | |
| 328 | def __init__(self) -> None: |
| 329 | self.uselessness_errors = [] |
| 330 | self.unreachable_lines = [] |
| 331 | self.nonoverlapping_types = [] |
| 332 | self.revealed_types = defaultdict(list) |
| 333 | |
| 334 | def yield_uselessness_error_infos(self) -> Iterator[tuple[str, Context, ErrorCode]]: |
| 335 | """Report only those `unreachable`, `redundant-expr`, and `redundant-casts` |
| 336 | errors that could not be ruled out in any iteration step.""" |
| 337 | |
| 338 | persistent_uselessness_errors = set() |
| 339 | for candidate in set(chain(*self.uselessness_errors)): |
| 340 | if all( |
| 341 | (candidate in errors) or (candidate[2] in lines) |
| 342 | for errors, lines in zip(self.uselessness_errors, self.unreachable_lines) |
| 343 | ): |
| 344 | persistent_uselessness_errors.add(candidate) |
| 345 | for error_info in persistent_uselessness_errors: |
| 346 | context = Context(line=error_info[2], column=error_info[3]) |
| 347 | context.end_line = error_info[4] |
| 348 | context.end_column = error_info[5] |
| 349 | yield error_info[1], context, error_info[0] |
| 350 | |
| 351 | def yield_nonoverlapping_types( |
| 352 | self, |
| 353 | ) -> Iterator[tuple[tuple[list[Type], list[Type]], str, Context]]: |
| 354 | """Report expressions where non-overlapping types were detected for all iterations |
| 355 | were the expression was reachable.""" |
| 356 | |
| 357 | selected = set() |
| 358 | for candidate in set(chain.from_iterable(self.nonoverlapping_types)): |
| 359 | if all( |
| 360 | (candidate in nonoverlap) or (candidate.line in lines) |
| 361 | for nonoverlap, lines in zip(self.nonoverlapping_types, self.unreachable_lines) |
| 362 | ): |
no outgoing calls
no test coverage detected
searching dependent graphs…