(graph: Graph, scc: list[str], patches: Patches)
| 155 | |
| 156 | |
| 157 | def process_top_levels(graph: Graph, scc: list[str], patches: Patches) -> None: |
| 158 | # Process top levels until everything has been bound. |
| 159 | |
| 160 | # Reverse order of the scc so the first modules in the original list will be |
| 161 | # be processed first. This helps with performance. |
| 162 | scc = list(reversed(scc)) # noqa: FURB187 intentional copy |
| 163 | |
| 164 | # Initialize ASTs and symbol tables. |
| 165 | for id in scc: |
| 166 | state = graph[id] |
| 167 | assert state.tree is not None |
| 168 | state.manager.semantic_analyzer.prepare_file(state.tree) |
| 169 | |
| 170 | # Initially all namespaces in the SCC are incomplete (well they are empty). |
| 171 | state.manager.incomplete_namespaces.update(scc) |
| 172 | |
| 173 | worklist = scc.copy() |
| 174 | # HACK: process core stuff first. This is mostly needed to support defining |
| 175 | # named tuples in builtin SCC. |
| 176 | if all(m in worklist for m in core_modules): |
| 177 | worklist += list(reversed(core_modules)) * CORE_WARMUP |
| 178 | final_iteration = False |
| 179 | iteration = 0 |
| 180 | analyzer = state.manager.semantic_analyzer |
| 181 | analyzer.deferral_debug_context.clear() |
| 182 | |
| 183 | while worklist: |
| 184 | iteration += 1 |
| 185 | if iteration > MAX_ITERATIONS: |
| 186 | # Just pick some module inside the current SCC for error context. |
| 187 | assert state.tree is not None |
| 188 | with analyzer.file_context(state.tree, state.options): |
| 189 | analyzer.report_hang() |
| 190 | break |
| 191 | if final_iteration: |
| 192 | # Give up. It's impossible to bind all names. |
| 193 | state.manager.incomplete_namespaces.clear() |
| 194 | all_deferred: list[str] = [] |
| 195 | any_progress = False |
| 196 | while worklist: |
| 197 | next_id = worklist.pop() |
| 198 | state = graph[next_id] |
| 199 | assert state.tree is not None |
| 200 | deferred, incomplete, progress = semantic_analyze_target( |
| 201 | next_id, next_id, state, state.tree, None, final_iteration, patches |
| 202 | ) |
| 203 | all_deferred += deferred |
| 204 | any_progress = any_progress or progress |
| 205 | if not incomplete: |
| 206 | state.manager.incomplete_namespaces.discard(next_id) |
| 207 | if final_iteration: |
| 208 | assert not all_deferred, "Must not defer during final iteration" |
| 209 | # Reverse to process the targets in the same order on every iteration. This avoids |
| 210 | # processing the same target twice in a row, which is inefficient. |
| 211 | worklist = list(reversed(all_deferred)) |
| 212 | final_iteration = not any_progress |
| 213 | # Functions/methods that define/infer attributes are processed as part of top-levels. |
| 214 | # We need to clear the locals for those between fine-grained iterations. |
no test coverage detected
searching dependent graphs…