Skip to content

Commit a3abd36

Browse files
authored
errors: speedup for large error counts and improve error filtering (#12631)
* errors: speedup for large error counts We have a legacy codebase with many errors across many files ``` Found 7995 errors in 2218 files (checked 21364 source files) ``` For historical reasons, it hasn't been practical to fix all of these yet, and we've been slowly chipping at them over time. Profiling shows that `is_blockers` is the biggest single hotspot, taking roughly 1min, and `total_errors` account for another 11s. Instead of computing those values on read by iterating over all errors, update auxiliary variables appropriately every time a new error is recorded. * errors: unify and optimize error filtering Instead of maintaining two separate mechanism to filter out errors (boolean flag in MessageBuilder and explicit copy of MessageBuilder/Errors) expand ErrorWatcher to support all relevant usage patterns and update all usages accordingly. This is both cleaner and more robust than the previous approach, and should also offer a slight performance improvement by reducing allocations.
1 parent 3460717 commit a3abd36

File tree

7 files changed

+277
-240
lines changed

7 files changed

+277
-240
lines changed

mypy/checker.py

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2196,7 +2196,7 @@ def is_raising_or_empty(self, s: Statement) -> bool:
21962196
if isinstance(s.expr, EllipsisExpr):
21972197
return True
21982198
elif isinstance(s.expr, CallExpr):
2199-
with self.expr_checker.msg.disable_errors():
2199+
with self.expr_checker.msg.filter_errors():
22002200
typ = get_proper_type(self.expr_checker.accept(
22012201
s.expr, allow_none_return=True, always_allow_any=True))
22022202

@@ -3377,7 +3377,7 @@ def check_member_assignment(self, instance_type: Type, attribute_type: Type,
33773377
# For non-overloaded setters, the result should be type-checked like a regular assignment.
33783378
# Hence, we first only try to infer the type by using the rvalue as type context.
33793379
type_context = rvalue
3380-
with self.msg.disable_errors():
3380+
with self.msg.filter_errors():
33813381
_, inferred_dunder_set_type = self.expr_checker.check_call(
33823382
dunder_set_type,
33833383
[TempNode(instance_type, context=context), type_context],
@@ -4312,34 +4312,29 @@ def _make_fake_typeinfo_and_full_name(
43124312
)
43134313
return info_, full_name_
43144314

4315-
old_msg = self.msg
4316-
new_msg = old_msg.clean_copy()
4317-
self.msg = new_msg
43184315
base_classes = _get_base_classes(instances)
43194316
# We use the pretty_names_list for error messages but can't
43204317
# use it for the real name that goes into the symbol table
43214318
# because it can have dots in it.
43224319
pretty_names_list = pretty_seq(format_type_distinctly(*base_classes, bare=True), "and")
43234320
try:
43244321
info, full_name = _make_fake_typeinfo_and_full_name(base_classes, curr_module)
4325-
self.check_multiple_inheritance(info)
4326-
if new_msg.is_errors():
4322+
with self.msg.filter_errors() as local_errors:
4323+
self.check_multiple_inheritance(info)
4324+
if local_errors.has_new_errors():
43274325
# "class A(B, C)" unsafe, now check "class A(C, B)":
4328-
new_msg = new_msg.clean_copy()
4329-
self.msg = new_msg
43304326
base_classes = _get_base_classes(instances[::-1])
43314327
info, full_name = _make_fake_typeinfo_and_full_name(base_classes, curr_module)
4332-
self.check_multiple_inheritance(info)
4328+
with self.msg.filter_errors() as local_errors:
4329+
self.check_multiple_inheritance(info)
43334330
info.is_intersection = True
43344331
except MroError:
43354332
if self.should_report_unreachable_issues():
4336-
old_msg.impossible_intersection(
4333+
self.msg.impossible_intersection(
43374334
pretty_names_list, "inconsistent method resolution order", ctx)
43384335
return None
4339-
finally:
4340-
self.msg = old_msg
43414336

4342-
if new_msg.is_errors():
4337+
if local_errors.has_new_errors():
43434338
if self.should_report_unreachable_issues():
43444339
self.msg.impossible_intersection(
43454340
pretty_names_list, "incompatible method signatures", ctx)
@@ -4974,20 +4969,20 @@ def refine_parent_types(self,
49744969
member_name = expr.name
49754970

49764971
def replay_lookup(new_parent_type: ProperType) -> Optional[Type]:
4977-
msg_copy = self.msg.clean_copy()
4978-
member_type = analyze_member_access(
4979-
name=member_name,
4980-
typ=new_parent_type,
4981-
context=parent_expr,
4982-
is_lvalue=False,
4983-
is_super=False,
4984-
is_operator=False,
4985-
msg=msg_copy,
4986-
original_type=new_parent_type,
4987-
chk=self,
4988-
in_literal_context=False,
4989-
)
4990-
if msg_copy.is_errors():
4972+
with self.msg.filter_errors() as w:
4973+
member_type = analyze_member_access(
4974+
name=member_name,
4975+
typ=new_parent_type,
4976+
context=parent_expr,
4977+
is_lvalue=False,
4978+
is_super=False,
4979+
is_operator=False,
4980+
msg=self.msg,
4981+
original_type=new_parent_type,
4982+
chk=self,
4983+
in_literal_context=False,
4984+
)
4985+
if w.has_new_errors():
49914986
return None
49924987
else:
49934988
return member_type

0 commit comments

Comments
 (0)