diff --git a/crates/red_knot/tests/cli.rs b/crates/red_knot/tests/cli.rs index a37468e824..210063ae84 100644 --- a/crates/red_knot/tests/cli.rs +++ b/crates/red_knot/tests/cli.rs @@ -32,12 +32,12 @@ fn config_override_python_version() -> anyhow::Result<()> { success: false exit_code: 1 ----- stdout ----- - error: lint:unresolved-attribute + error: lint:unresolved-attribute: Type `` has no attribute `last_exc` --> /test.py:5:7 | 4 | # Access `sys.last_exc` that was only added in Python 3.12 5 | print(sys.last_exc) - | ^^^^^^^^^^^^ Type `` has no attribute `last_exc` + | ^^^^^^^^^^^^ | Found 1 diagnostic @@ -165,11 +165,11 @@ fn cli_arguments_are_relative_to_the_current_directory() -> anyhow::Result<()> { success: false exit_code: 1 ----- stdout ----- - error: lint:unresolved-import + error: lint:unresolved-import: Cannot resolve import `utils` --> /child/test.py:2:6 | 2 | from utils import add - | ^^^^^ Cannot resolve import `utils` + | ^^^^^ 3 | 4 | stat = add(10, 15) | @@ -265,11 +265,11 @@ fn configuration_rule_severity() -> anyhow::Result<()> { success: false exit_code: 1 ----- stdout ----- - error: lint:division-by-zero + error: lint:division-by-zero: Cannot divide object of type `Literal[4]` by zero --> /test.py:2:5 | 2 | y = 4 / 0 - | ^^^^^ Cannot divide object of type `Literal[4]` by zero + | ^^^^^ 3 | 4 | for a in range(0, int(y)): | @@ -301,11 +301,11 @@ fn configuration_rule_severity() -> anyhow::Result<()> { success: true exit_code: 0 ----- stdout ----- - warning: lint:division-by-zero + warning: lint:division-by-zero: Cannot divide object of type `Literal[4]` by zero --> /test.py:2:5 | 2 | y = 4 / 0 - | ^^^^^ Cannot divide object of type `Literal[4]` by zero + | ^^^^^ 3 | 4 | for a in range(0, int(y)): | @@ -341,22 +341,22 @@ fn cli_rule_severity() -> anyhow::Result<()> { success: false exit_code: 1 ----- stdout ----- - error: lint:unresolved-import + error: lint:unresolved-import: Cannot resolve import `does_not_exit` --> /test.py:2:8 | 2 | import does_not_exit - | ^^^^^^^^^^^^^ Cannot resolve import `does_not_exit` + | ^^^^^^^^^^^^^ 3 | 4 | y = 4 / 0 | - error: lint:division-by-zero + error: lint:division-by-zero: Cannot divide object of type `Literal[4]` by zero --> /test.py:4:5 | 2 | import does_not_exit 3 | 4 | y = 4 / 0 - | ^^^^^ Cannot divide object of type `Literal[4]` by zero + | ^^^^^ 5 | 6 | for a in range(0, int(y)): | @@ -388,22 +388,22 @@ fn cli_rule_severity() -> anyhow::Result<()> { success: true exit_code: 0 ----- stdout ----- - warning: lint:unresolved-import + warning: lint:unresolved-import: Cannot resolve import `does_not_exit` --> /test.py:2:8 | 2 | import does_not_exit - | ^^^^^^^^^^^^^ Cannot resolve import `does_not_exit` + | ^^^^^^^^^^^^^ 3 | 4 | y = 4 / 0 | - warning: lint:division-by-zero + warning: lint:division-by-zero: Cannot divide object of type `Literal[4]` by zero --> /test.py:4:5 | 2 | import does_not_exit 3 | 4 | y = 4 / 0 - | ^^^^^ Cannot divide object of type `Literal[4]` by zero + | ^^^^^ 5 | 6 | for a in range(0, int(y)): | @@ -439,11 +439,11 @@ fn cli_rule_severity_precedence() -> anyhow::Result<()> { success: false exit_code: 1 ----- stdout ----- - error: lint:division-by-zero + error: lint:division-by-zero: Cannot divide object of type `Literal[4]` by zero --> /test.py:2:5 | 2 | y = 4 / 0 - | ^^^^^ Cannot divide object of type `Literal[4]` by zero + | ^^^^^ 3 | 4 | for a in range(0, int(y)): | @@ -476,11 +476,11 @@ fn cli_rule_severity_precedence() -> anyhow::Result<()> { success: true exit_code: 0 ----- stdout ----- - warning: lint:division-by-zero + warning: lint:division-by-zero: Cannot divide object of type `Literal[4]` by zero --> /test.py:2:5 | 2 | y = 4 / 0 - | ^^^^^ Cannot divide object of type `Literal[4]` by zero + | ^^^^^ 3 | 4 | for a in range(0, int(y)): | @@ -835,11 +835,11 @@ fn user_configuration() -> anyhow::Result<()> { success: true exit_code: 0 ----- stdout ----- - warning: lint:division-by-zero + warning: lint:division-by-zero: Cannot divide object of type `Literal[4]` by zero --> /project/main.py:2:5 | 2 | y = 4 / 0 - | ^^^^^ Cannot divide object of type `Literal[4]` by zero + | ^^^^^ 3 | 4 | for a in range(0, int(y)): | @@ -877,11 +877,11 @@ fn user_configuration() -> anyhow::Result<()> { success: false exit_code: 1 ----- stdout ----- - warning: lint:division-by-zero + warning: lint:division-by-zero: Cannot divide object of type `Literal[4]` by zero --> /project/main.py:2:5 | 2 | y = 4 / 0 - | ^^^^^ Cannot divide object of type `Literal[4]` by zero + | ^^^^^ 3 | 4 | for a in range(0, int(y)): | @@ -935,25 +935,25 @@ fn check_specific_paths() -> anyhow::Result<()> { success: false exit_code: 1 ----- stdout ----- - error: lint:unresolved-import + error: lint:unresolved-import: Cannot resolve import `does_not_exist` --> /project/tests/test_main.py:2:8 | 2 | import does_not_exist # error: unresolved-import - | ^^^^^^^^^^^^^^ Cannot resolve import `does_not_exist` + | ^^^^^^^^^^^^^^ | - error: lint:division-by-zero + error: lint:division-by-zero: Cannot divide object of type `Literal[4]` by zero --> /project/main.py:2:5 | 2 | y = 4 / 0 # error: division-by-zero - | ^^^^^ Cannot divide object of type `Literal[4]` by zero + | ^^^^^ | - error: lint:unresolved-import + error: lint:unresolved-import: Cannot resolve import `main2` --> /project/other.py:2:6 | 2 | from main2 import z # error: unresolved-import - | ^^^^^ Cannot resolve import `main2` + | ^^^^^ 3 | 4 | print(z) | @@ -972,18 +972,18 @@ fn check_specific_paths() -> anyhow::Result<()> { success: false exit_code: 1 ----- stdout ----- - error: lint:unresolved-import + error: lint:unresolved-import: Cannot resolve import `does_not_exist` --> /project/tests/test_main.py:2:8 | 2 | import does_not_exist # error: unresolved-import - | ^^^^^^^^^^^^^^ Cannot resolve import `does_not_exist` + | ^^^^^^^^^^^^^^ | - error: lint:unresolved-import + error: lint:unresolved-import: Cannot resolve import `main2` --> /project/other.py:2:6 | 2 | from main2 import z # error: unresolved-import - | ^^^^^ Cannot resolve import `main2` + | ^^^^^ 3 | 4 | print(z) | diff --git a/crates/red_knot_python_semantic/resources/mdtest/snapshots/attribute_assignment.md_-_Attribute_assignment_-_Pure_instance_attributes.snap b/crates/red_knot_python_semantic/resources/mdtest/snapshots/attribute_assignment.md_-_Attribute_assignment_-_Pure_instance_attributes.snap index a1e4f9274a..839dfd9fa0 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/snapshots/attribute_assignment.md_-_Attribute_assignment_-_Pure_instance_attributes.snap +++ b/crates/red_knot_python_semantic/resources/mdtest/snapshots/attribute_assignment.md_-_Attribute_assignment_-_Pure_instance_attributes.snap @@ -40,13 +40,13 @@ error: lint:invalid-assignment: Object of type `Literal["wrong"]` is not assigna ``` ``` -error: lint:invalid-attribute-access +error: lint:invalid-attribute-access: Cannot assign to instance attribute `attr` from the class object `Literal[C]` --> /src/mdtest_snippet.py:9:1 | 7 | instance.attr = "wrong" # error: [invalid-assignment] 8 | 9 | C.attr = 1 # error: [invalid-attribute-access] - | ^^^^^^ Cannot assign to instance attribute `attr` from the class object `Literal[C]` + | ^^^^^^ | ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/snapshots/attribute_assignment.md_-_Attribute_assignment_-_Unknown_attributes.snap b/crates/red_knot_python_semantic/resources/mdtest/snapshots/attribute_assignment.md_-_Attribute_assignment_-_Unknown_attributes.snap index 53ef654680..db88c02820 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/snapshots/attribute_assignment.md_-_Attribute_assignment_-_Unknown_attributes.snap +++ b/crates/red_knot_python_semantic/resources/mdtest/snapshots/attribute_assignment.md_-_Attribute_assignment_-_Unknown_attributes.snap @@ -23,13 +23,13 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/attrib # Diagnostics ``` -error: lint:unresolved-attribute +error: lint:unresolved-attribute: Unresolved attribute `non_existent` on type `Literal[C]`. --> /src/mdtest_snippet.py:3:1 | 1 | class C: ... 2 | 3 | C.non_existent = 1 # error: [unresolved-attribute] - | ^^^^^^^^^^^^^^ Unresolved attribute `non_existent` on type `Literal[C]`. + | ^^^^^^^^^^^^^^ 4 | 5 | instance = C() | @@ -37,12 +37,12 @@ error: lint:unresolved-attribute ``` ``` -error: lint:unresolved-attribute +error: lint:unresolved-attribute: Unresolved attribute `non_existent` on type `C`. --> /src/mdtest_snippet.py:6:1 | 5 | instance = C() 6 | instance.non_existent = 1 # error: [unresolved-attribute] - | ^^^^^^^^^^^^^^^^^^^^^ Unresolved attribute `non_existent` on type `C`. + | ^^^^^^^^^^^^^^^^^^^^^ | ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/snapshots/attribute_assignment.md_-_Attribute_assignment_-_`ClassVar`s.snap b/crates/red_knot_python_semantic/resources/mdtest/snapshots/attribute_assignment.md_-_Attribute_assignment_-_`ClassVar`s.snap index 573d8937ad..f5f48a660d 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/snapshots/attribute_assignment.md_-_Attribute_assignment_-_`ClassVar`s.snap +++ b/crates/red_knot_python_semantic/resources/mdtest/snapshots/attribute_assignment.md_-_Attribute_assignment_-_`ClassVar`s.snap @@ -40,12 +40,12 @@ error: lint:invalid-assignment: Object of type `Literal["wrong"]` is not assigna ``` ``` -error: lint:invalid-attribute-access +error: lint:invalid-attribute-access: Cannot assign to ClassVar `attr` from an instance of type `C` --> /src/mdtest_snippet.py:10:1 | 9 | instance = C() 10 | instance.attr = 1 # error: [invalid-attribute-access] - | ^^^^^^^^^^^^^ Cannot assign to ClassVar `attr` from an instance of type `C` + | ^^^^^^^^^^^^^ | ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/snapshots/basic.md_-_Structures_-_Unresolvable_module_import.snap b/crates/red_knot_python_semantic/resources/mdtest/snapshots/basic.md_-_Structures_-_Unresolvable_module_import.snap index 47bb7fae45..b4d0d917d9 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/snapshots/basic.md_-_Structures_-_Unresolvable_module_import.snap +++ b/crates/red_knot_python_semantic/resources/mdtest/snapshots/basic.md_-_Structures_-_Unresolvable_module_import.snap @@ -18,11 +18,11 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/import/basic.md # Diagnostics ``` -error: lint:unresolved-import +error: lint:unresolved-import: Cannot resolve import `zqzqzqzqzqzqzq` --> /src/mdtest_snippet.py:1:8 | 1 | import zqzqzqzqzqzqzq # error: [unresolved-import] "Cannot resolve import `zqzqzqzqzqzqzq`" - | ^^^^^^^^^^^^^^ Cannot resolve import `zqzqzqzqzqzqzq` + | ^^^^^^^^^^^^^^ | ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/snapshots/basic.md_-_Structures_-_Unresolvable_submodule_imports.snap b/crates/red_knot_python_semantic/resources/mdtest/snapshots/basic.md_-_Structures_-_Unresolvable_submodule_imports.snap index bbcb0e3f40..fc8f6e2e6e 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/snapshots/basic.md_-_Structures_-_Unresolvable_submodule_imports.snap +++ b/crates/red_knot_python_semantic/resources/mdtest/snapshots/basic.md_-_Structures_-_Unresolvable_submodule_imports.snap @@ -27,12 +27,12 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/import/basic.md # Diagnostics ``` -error: lint:unresolved-import +error: lint:unresolved-import: Cannot resolve import `a.foo` --> /src/mdtest_snippet.py:2:8 | 1 | # Topmost component resolvable, submodule not resolvable: 2 | import a.foo # error: [unresolved-import] "Cannot resolve import `a.foo`" - | ^^^^^ Cannot resolve import `a.foo` + | ^^^^^ 3 | 4 | # Topmost component unresolvable: | @@ -40,12 +40,12 @@ error: lint:unresolved-import ``` ``` -error: lint:unresolved-import +error: lint:unresolved-import: Cannot resolve import `b.foo` --> /src/mdtest_snippet.py:5:8 | 4 | # Topmost component unresolvable: 5 | import b.foo # error: [unresolved-import] "Cannot resolve import `b.foo`" - | ^^^^^ Cannot resolve import `b.foo` + | ^^^^^ | ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_An_unresolvable_import_that_does_not_use_`from`.snap b/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_An_unresolvable_import_that_does_not_use_`from`.snap index 096616ac07..259d63ea91 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_An_unresolvable_import_that_does_not_use_`from`.snap +++ b/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_An_unresolvable_import_that_does_not_use_`from`.snap @@ -20,11 +20,11 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/unreso # Diagnostics ``` -error: lint:unresolved-import +error: lint:unresolved-import: Cannot resolve import `does_not_exist` --> /src/mdtest_snippet.py:1:8 | 1 | import does_not_exist # error: [unresolved-import] - | ^^^^^^^^^^^^^^ Cannot resolve import `does_not_exist` + | ^^^^^^^^^^^^^^ 2 | 3 | x = does_not_exist.foo | diff --git a/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_Using_`from`_with_a_resolvable_module_but_unresolvable_item.snap b/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_Using_`from`_with_a_resolvable_module_but_unresolvable_item.snap index f297f87e8a..0aa7c7109e 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_Using_`from`_with_a_resolvable_module_but_unresolvable_item.snap +++ b/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_Using_`from`_with_a_resolvable_module_but_unresolvable_item.snap @@ -25,11 +25,11 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/unreso # Diagnostics ``` -error: lint:unresolved-import +error: lint:unresolved-import: Module `a` has no member `does_not_exist` --> /src/mdtest_snippet.py:1:28 | 1 | from a import does_exist1, does_not_exist, does_exist2 # error: [unresolved-import] - | ^^^^^^^^^^^^^^ Module `a` has no member `does_not_exist` + | ^^^^^^^^^^^^^^ | ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_Using_`from`_with_an_unknown_current_module.snap b/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_Using_`from`_with_an_unknown_current_module.snap index 88bdb9d791..7105afa48a 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_Using_`from`_with_an_unknown_current_module.snap +++ b/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_Using_`from`_with_an_unknown_current_module.snap @@ -20,11 +20,11 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/unreso # Diagnostics ``` -error: lint:unresolved-import +error: lint:unresolved-import: Cannot resolve import `.does_not_exist` --> /src/mdtest_snippet.py:1:7 | 1 | from .does_not_exist import add # error: [unresolved-import] - | ^^^^^^^^^^^^^^ Cannot resolve import `.does_not_exist` + | ^^^^^^^^^^^^^^ 2 | 3 | stat = add(10, 15) | diff --git a/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_Using_`from`_with_an_unknown_nested_module.snap b/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_Using_`from`_with_an_unknown_nested_module.snap index 5a0c60321e..42cc96a5fa 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_Using_`from`_with_an_unknown_nested_module.snap +++ b/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_Using_`from`_with_an_unknown_nested_module.snap @@ -20,11 +20,11 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/unreso # Diagnostics ``` -error: lint:unresolved-import +error: lint:unresolved-import: Cannot resolve import `.does_not_exist.foo.bar` --> /src/mdtest_snippet.py:1:7 | 1 | from .does_not_exist.foo.bar import add # error: [unresolved-import] - | ^^^^^^^^^^^^^^^^^^^^^^ Cannot resolve import `.does_not_exist.foo.bar` + | ^^^^^^^^^^^^^^^^^^^^^^ 2 | 3 | stat = add(10, 15) | diff --git a/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_Using_`from`_with_an_unresolvable_module.snap b/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_Using_`from`_with_an_unresolvable_module.snap index e7b2303977..4dd7179de9 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_Using_`from`_with_an_unresolvable_module.snap +++ b/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_Using_`from`_with_an_unresolvable_module.snap @@ -20,11 +20,11 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/unreso # Diagnostics ``` -error: lint:unresolved-import +error: lint:unresolved-import: Cannot resolve import `does_not_exist` --> /src/mdtest_snippet.py:1:6 | 1 | from does_not_exist import add # error: [unresolved-import] - | ^^^^^^^^^^^^^^ Cannot resolve import `does_not_exist` + | ^^^^^^^^^^^^^^ 2 | 3 | stat = add(10, 15) | diff --git a/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_Using_`from`_with_too_many_leading_dots.snap b/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_Using_`from`_with_too_many_leading_dots.snap index 603a2137d0..2d20320108 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_Using_`from`_with_too_many_leading_dots.snap +++ b/crates/red_knot_python_semantic/resources/mdtest/snapshots/unresolved_import.md_-_Unresolved_import_diagnostics_-_Using_`from`_with_too_many_leading_dots.snap @@ -32,11 +32,11 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/unreso # Diagnostics ``` -error: lint:unresolved-import +error: lint:unresolved-import: Cannot resolve import `....foo` --> /src/package/subpackage/subsubpackage/__init__.py:1:10 | 1 | from ....foo import add # error: [unresolved-import] - | ^^^ Cannot resolve import `....foo` + | ^^^ 2 | 3 | stat = add(10, 15) | diff --git a/crates/red_knot_python_semantic/src/types/infer.rs b/crates/red_knot_python_semantic/src/types/infer.rs index 175bddff08..cfa297d127 100644 --- a/crates/red_knot_python_semantic/src/types/infer.rs +++ b/crates/red_knot_python_semantic/src/types/infer.rs @@ -749,14 +749,15 @@ impl<'db> TypeInferenceBuilder<'db> { // (1) Check that the class does not have a cyclic definition if let Some(inheritance_cycle) = class.inheritance_cycle(self.db()) { if inheritance_cycle.is_participant() { - self.context.report_lint_old( - &CYCLIC_CLASS_DEFINITION, - class_node, - format_args!( + if let Some(builder) = self + .context + .report_lint(&CYCLIC_CLASS_DEFINITION, class_node) + { + builder.into_diagnostic(format_args!( "Cyclic definition of `{}` (class cannot inherit from itself)", class.name(self.db()) - ), - ); + )); + } } // Attempting to determine the MRO of a class or if the class has a metaclass conflict // is impossible if the class is cyclically defined; there's nothing more to do here. @@ -772,13 +773,14 @@ impl<'db> TypeInferenceBuilder<'db> { for (i, base_class) in class.explicit_bases(self.db()).iter().enumerate() { let base_class = match base_class { Type::KnownInstance(KnownInstanceType::Generic) => { - // Unsubscripted `Generic` can appear in the MRO of many classes, - // but it is never valid as an explicit base class in user code. - self.context.report_lint_old( - &INVALID_BASE, - &class_node.bases()[i], - format_args!("Cannot inherit from plain `Generic`"), - ); + if let Some(builder) = self + .context + .report_lint(&INVALID_BASE, &class_node.bases()[i]) + { + // Unsubscripted `Generic` can appear in the MRO of many classes, + // but it is never valid as an explicit base class in user code. + builder.into_diagnostic("Cannot inherit from plain `Generic`"); + } continue; } Type::ClassLiteral(class) => class, @@ -790,27 +792,29 @@ impl<'db> TypeInferenceBuilder<'db> { && !(base_class.is_protocol(self.db()) || base_class.is_known(self.db(), KnownClass::Object)) { - self.context.report_lint_old( - &INVALID_PROTOCOL, - &class_node.bases()[i], - format_args!( + if let Some(builder) = self + .context + .report_lint(&INVALID_PROTOCOL, &class_node.bases()[i]) + { + builder.into_diagnostic(format_args!( "Protocol class `{}` cannot inherit from non-protocol class `{}`", class.name(self.db()), base_class.name(self.db()), - ), - ); + )); + } } if base_class.is_final(self.db()) { - self.context.report_lint_old( - &SUBCLASS_OF_FINAL_CLASS, - &class_node.bases()[i], - format_args!( + if let Some(builder) = self + .context + .report_lint(&SUBCLASS_OF_FINAL_CLASS, &class_node.bases()[i]) + { + builder.into_diagnostic(format_args!( "Class `{}` cannot inherit from final class `{}`", class.name(self.db()), base_class.name(self.db()), - ), - ); + )); + } } } @@ -821,11 +825,16 @@ impl<'db> TypeInferenceBuilder<'db> { MroErrorKind::DuplicateBases(duplicates) => { let base_nodes = class_node.bases(); for (index, duplicate) in duplicates { - self.context.report_lint_old( - &DUPLICATE_BASE, - &base_nodes[*index], - format_args!("Duplicate base class `{}`", duplicate.name(self.db())), - ); + let Some(builder) = self + .context + .report_lint(&DUPLICATE_BASE, &base_nodes[*index]) + else { + continue; + }; + builder.into_diagnostic(format_args!( + "Duplicate base class `{}`", + duplicate.name(self.db()) + )); } } MroErrorKind::InvalidBases(bases) => { @@ -837,25 +846,33 @@ impl<'db> TypeInferenceBuilder<'db> { // class will never happen. continue; } - self.context.report_lint_old( - &INVALID_BASE, - &base_nodes[*index], - format_args!( - "Invalid class base with type `{}` (all bases must be a class, `Any`, `Unknown` or `Todo`)", - base_ty.display(self.db()) - ), - ); + let Some(builder) = + self.context.report_lint(&INVALID_BASE, &base_nodes[*index]) + else { + continue; + }; + builder.into_diagnostic(format_args!( + "Invalid class base with type `{}` \ + (all bases must be a class, `Any`, `Unknown` or `Todo`)", + base_ty.display(self.db()) + )); + } + } + MroErrorKind::UnresolvableMro { bases_list } => { + if let Some(builder) = + self.context.report_lint(&INCONSISTENT_MRO, class_node) + { + builder.into_diagnostic(format_args!( + "Cannot create a consistent method resolution order (MRO) \ + for class `{}` with bases list `[{}]`", + class.name(self.db()), + bases_list + .iter() + .map(|base| base.display(self.db())) + .join(", ") + )); } } - MroErrorKind::UnresolvableMro { bases_list } => self.context.report_lint_old( - &INCONSISTENT_MRO, - class_node, - format_args!( - "Cannot create a consistent method resolution order (MRO) for class `{}` with bases list `[{}]`", - class.name(self.db()), - bases_list.iter().map(|base| base.display(self.db())).join(", ") - ), - ) } } Ok(_) => check_class_slots(&self.context, class, class_node), @@ -864,19 +881,26 @@ impl<'db> TypeInferenceBuilder<'db> { // (4) Check that the class's metaclass can be determined without error. if let Err(metaclass_error) = class.try_metaclass(self.db()) { match metaclass_error.reason() { - MetaclassErrorKind::NotCallable(ty) => self.context.report_lint_old( - &INVALID_METACLASS, - class_node, - format_args!("Metaclass type `{}` is not callable", ty.display(self.db())), - ), - MetaclassErrorKind::PartlyNotCallable(ty) => self.context.report_lint_old( - &INVALID_METACLASS, - class_node, - format_args!( - "Metaclass type `{}` is partly not callable", - ty.display(self.db()) - ), - ), + MetaclassErrorKind::NotCallable(ty) => { + if let Some(builder) = + self.context.report_lint(&INVALID_METACLASS, class_node) + { + builder.into_diagnostic(format_args!( + "Metaclass type `{}` is not callable", + ty.display(self.db()) + )); + } + } + MetaclassErrorKind::PartlyNotCallable(ty) => { + if let Some(builder) = + self.context.report_lint(&INVALID_METACLASS, class_node) + { + builder.into_diagnostic(format_args!( + "Metaclass type `{}` is partly not callable", + ty.display(self.db()) + )); + } + } MetaclassErrorKind::Conflict { candidate1: MetaclassCandidate { @@ -890,35 +914,35 @@ impl<'db> TypeInferenceBuilder<'db> { }, candidate1_is_base_class, } => { - if *candidate1_is_base_class { - self.context.report_lint_old( - &CONFLICTING_METACLASS, - class_node, - format_args!( - "The metaclass of a derived class (`{class}`) must be a subclass of the metaclasses of all its bases, \ - but `{metaclass1}` (metaclass of base class `{base1}`) and `{metaclass2}` (metaclass of base class `{base2}`) \ - have no subclass relationship", + if let Some(builder) = + self.context.report_lint(&CONFLICTING_METACLASS, class_node) + { + if *candidate1_is_base_class { + builder.into_diagnostic(format_args!( + "The metaclass of a derived class (`{class}`) \ + must be a subclass of the metaclasses of all its bases, \ + but `{metaclass1}` (metaclass of base class `{base1}`) \ + and `{metaclass2}` (metaclass of base class `{base2}`) \ + have no subclass relationship", class = class.name(self.db()), metaclass1 = metaclass1.name(self.db()), base1 = class1.name(self.db()), metaclass2 = metaclass2.name(self.db()), base2 = class2.name(self.db()), - ), - ); - } else { - self.context.report_lint_old( - &CONFLICTING_METACLASS, - class_node, - format_args!( - "The metaclass of a derived class (`{class}`) must be a subclass of the metaclasses of all its bases, \ - but `{metaclass_of_class}` (metaclass of `{class}`) and `{metaclass_of_base}` (metaclass of base class `{base}`) \ - have no subclass relationship", + )); + } else { + builder.into_diagnostic(format_args!( + "The metaclass of a derived class (`{class}`) \ + must be a subclass of the metaclasses of all its bases, \ + but `{metaclass_of_class}` (metaclass of `{class}`) \ + and `{metaclass_of_base}` (metaclass of base class `{base}`) \ + have no subclass relationship", class = class.name(self.db()), metaclass_of_class = metaclass1.name(self.db()), metaclass_of_base = metaclass2.name(self.db()), base = class2.name(self.db()), - ), - ); + )); + } } } } @@ -1054,14 +1078,12 @@ impl<'db> TypeInferenceBuilder<'db> { _ => return false, }; - self.context.report_lint_old( - &DIVISION_BY_ZERO, - node, - format_args!( + if let Some(builder) = self.context.report_lint(&DIVISION_BY_ZERO, node) { + builder.into_diagnostic(format_args!( "Cannot {op} object of type `{}` {by_zero}", left.display(self.db()) - ), - ); + )); + } true } @@ -1082,14 +1104,12 @@ impl<'db> TypeInferenceBuilder<'db> { // TODO point out the conflicting declarations in the diagnostic? let symbol_table = self.index.symbol_table(binding.file_scope(self.db())); let symbol_name = symbol_table.symbol(binding.symbol(self.db())).name(); - self.context.report_lint_old( - &CONFLICTING_DECLARATIONS, - node, - format_args!( + if let Some(builder) = self.context.report_lint(&CONFLICTING_DECLARATIONS, node) { + builder.into_diagnostic(format_args!( "Conflicting declared types for `{symbol_name}`: {}", conflicting.display(self.db()) - ), - ); + )); + } ty.inner_type() }); if !bound_ty.is_assignable_to(self.db(), declared_ty) { @@ -1120,15 +1140,13 @@ impl<'db> TypeInferenceBuilder<'db> { let ty = if inferred_ty.is_assignable_to(self.db(), ty.inner_type()) { ty } else { - self.context.report_lint_old( - &INVALID_DECLARATION, - node, - format_args!( + if let Some(builder) = self.context.report_lint(&INVALID_DECLARATION, node) { + builder.into_diagnostic(format_args!( "Cannot declare type `{}` for inferred type `{}`", ty.inner_type().display(self.db()), inferred_ty.display(self.db()) - ), - ); + )); + } TypeAndQualifiers::unknown() }; self.types.declarations.insert(declaration, ty); @@ -1654,13 +1672,17 @@ impl<'db> TypeInferenceBuilder<'db> { { DeclaredAndInferredType::AreTheSame(declared_ty) } else { - self.context.report_lint_old( - &INVALID_PARAMETER_DEFAULT, - parameter_with_default, - format_args!( - "Default value of type `{}` is not assignable to annotated parameter type `{}`", - default_ty.display(self.db()), declared_ty.display(self.db())), - ); + if let Some(builder) = self + .context + .report_lint(&INVALID_PARAMETER_DEFAULT, parameter_with_default) + { + builder.into_diagnostic(format_args!( + "Default value of type `{}` is not assignable \ + to annotated parameter type `{}`", + default_ty.display(self.db()), + declared_ty.display(self.db()) + )); + } DeclaredAndInferredType::AreTheSame(declared_ty) } } else { @@ -2130,11 +2152,12 @@ impl<'db> TypeInferenceBuilder<'db> { let bound_or_constraint = match bound.as_deref() { Some(expr @ ast::Expr::Tuple(ast::ExprTuple { elts, .. })) => { if elts.len() < 2 { - self.context.report_lint_old( - &INVALID_TYPE_VARIABLE_CONSTRAINTS, - expr, - format_args!("TypeVar must have at least two constrained types"), - ); + if let Some(builder) = self + .context + .report_lint(&INVALID_TYPE_VARIABLE_CONSTRAINTS, expr) + { + builder.into_diagnostic("TypeVar must have at least two constrained types"); + } self.infer_expression(expr); None } else { @@ -2485,27 +2508,23 @@ impl<'db> TypeInferenceBuilder<'db> { // Super instances do not allow attribute assignment Type::Instance(instance) if instance.class().is_known(db, KnownClass::Super) => { if emit_diagnostics { - self.context.report_lint_old( - &INVALID_ASSIGNMENT, - target, - format_args!( + if let Some(builder) = self.context.report_lint(&INVALID_ASSIGNMENT, target) { + builder.into_diagnostic(format_args!( "Cannot assign to attribute `{attribute}` on type `{}`", object_ty.display(self.db()), - ), - ); + )); + } } false } Type::BoundSuper(_) => { if emit_diagnostics { - self.context.report_lint_old( - &INVALID_ASSIGNMENT, - target, - format_args!( + if let Some(builder) = self.context.report_lint(&INVALID_ASSIGNMENT, target) { + builder.into_diagnostic(format_args!( "Cannot assign to attribute `{attribute}` on type `{}`", object_ty.display(self.db()), - ), - ); + )); + } } false } @@ -2535,14 +2554,15 @@ impl<'db> TypeInferenceBuilder<'db> { match object_ty.class_member(db, attribute.into()) { meta_attr @ SymbolAndQualifiers { .. } if meta_attr.is_class_var() => { if emit_diagnostics { - self.context.report_lint_old( - &INVALID_ATTRIBUTE_ACCESS, - target, - format_args!( - "Cannot assign to ClassVar `{attribute}` from an instance of type `{ty}`", + if let Some(builder) = + self.context.report_lint(&INVALID_ATTRIBUTE_ACCESS, target) + { + builder.into_diagnostic(format_args!( + "Cannot assign to ClassVar `{attribute}` \ + from an instance of type `{ty}`", ty = object_ty.display(self.db()), - ), - ); + )); + } } false } @@ -2650,29 +2670,31 @@ impl<'db> TypeInferenceBuilder<'db> { Ok(_) | Err(CallDunderError::PossiblyUnbound(_)) => true, Err(CallDunderError::CallError(..)) => { if emit_diagnostics { - self.context.report_lint_old( - &UNRESOLVED_ATTRIBUTE, - target, - format_args!( - "Can not assign object of `{}` to attribute `{attribute}` on type `{}` with custom `__setattr__` method.", - value_ty.display(db), - object_ty.display(db) - ), - ); + if let Some(builder) = + self.context.report_lint(&UNRESOLVED_ATTRIBUTE, target) + { + builder.into_diagnostic(format_args!( + "Can not assign object of `{}` to attribute \ + `{attribute}` on type `{}` with \ + custom `__setattr__` method.", + value_ty.display(db), + object_ty.display(db) + )); + } } false } Err(CallDunderError::MethodNotAvailable) => { if emit_diagnostics { - self.context.report_lint_old( - &UNRESOLVED_ATTRIBUTE, - target, - format_args!( + if let Some(builder) = + self.context.report_lint(&UNRESOLVED_ATTRIBUTE, target) + { + builder.into_diagnostic(format_args!( "Unresolved attribute `{}` on type `{}`.", attribute, object_ty.display(db) - ), - ); + )); + } } false @@ -2784,23 +2806,25 @@ impl<'db> TypeInferenceBuilder<'db> { // Attribute is declared or bound on instance. Forbid access from the class object if emit_diagnostics { if attribute_is_bound_on_instance { - self.context.report_lint_old( - &INVALID_ATTRIBUTE_ACCESS, - target, - format_args!( - "Cannot assign to instance attribute `{attribute}` from the class object `{ty}`", - ty = object_ty.display(self.db()), - )); + if let Some(builder) = + self.context.report_lint(&INVALID_ATTRIBUTE_ACCESS, target) + { + builder.into_diagnostic(format_args!( + "Cannot assign to instance attribute \ + `{attribute}` from the class object `{ty}`", + ty = object_ty.display(self.db()), + )); + } } else { - self.context.report_lint_old( - &UNRESOLVED_ATTRIBUTE, - target, - format_args!( + if let Some(builder) = + self.context.report_lint(&UNRESOLVED_ATTRIBUTE, target) + { + builder.into_diagnostic(format_args!( "Unresolved attribute `{}` on type `{}`.", attribute, object_ty.display(db) - ), - ); + )); + } } } @@ -2825,15 +2849,13 @@ impl<'db> TypeInferenceBuilder<'db> { false } else { - self.context.report_lint_old( - &UNRESOLVED_ATTRIBUTE, - target, - format_args!( + if let Some(builder) = self.context.report_lint(&UNRESOLVED_ATTRIBUTE, target) { + builder.into_diagnostic(format_args!( "Unresolved attribute `{}` on type `{}`.", attribute, object_ty.display(db) - ), - ); + )); + } false } @@ -3073,15 +3095,14 @@ impl<'db> TypeInferenceBuilder<'db> { let db = self.db(); let report_unsupported_augmented_op = |ctx: &mut InferContext| { - ctx.report_lint_old( - &UNSUPPORTED_OPERATOR, - assignment, - format_args!( - "Operator `{op}=` is unsupported between objects of type `{}` and `{}`", - target_type.display(db), - value_type.display(db) - ), - ); + let Some(builder) = ctx.report_lint(&UNSUPPORTED_OPERATOR, assignment) else { + return; + }; + builder.into_diagnostic(format_args!( + "Operator `{op}=` is unsupported between objects of type `{}` and `{}`", + target_type.display(db), + value_type.display(db) + )); }; // Fall back to non-augmented binary operator inference. @@ -3254,15 +3275,14 @@ impl<'db> TypeInferenceBuilder<'db> { return; } - self.context.report_lint_old( - &UNRESOLVED_IMPORT, - range, - format_args!( - "Cannot resolve import `{}{}`", - ".".repeat(level as usize), - module.unwrap_or_default() - ), - ); + let Some(builder) = self.context.report_lint(&UNRESOLVED_IMPORT, range) else { + return; + }; + builder.into_diagnostic(format_args!( + "Cannot resolve import `{}{}`", + ".".repeat(level as usize), + module.unwrap_or_default() + )); } fn infer_import_definition( @@ -3495,11 +3515,14 @@ impl<'db> TypeInferenceBuilder<'db> { if &alias.name != "*" && boundness == Boundness::PossiblyUnbound { // TODO: Consider loading _both_ the attribute and any submodule and unioning them // together if the attribute exists but is possibly-unbound. - self.context.report_lint_old( - &POSSIBLY_UNBOUND_IMPORT, - AnyNodeRef::Alias(alias), - format_args!("Member `{name}` of module `{module_name}` is possibly unbound",), - ); + if let Some(builder) = self + .context + .report_lint(&POSSIBLY_UNBOUND_IMPORT, AnyNodeRef::Alias(alias)) + { + builder.into_diagnostic(format_args!( + "Member `{name}` of module `{module_name}` is possibly unbound", + )); + } } self.add_declaration_with_binding( alias.into(), @@ -3541,11 +3564,14 @@ impl<'db> TypeInferenceBuilder<'db> { let is_import_reachable = self.is_reachable(import_from); if is_import_reachable { - self.context.report_lint_old( - &UNRESOLVED_IMPORT, - AnyNodeRef::Alias(alias), - format_args!("Module `{module_name}` has no member `{name}`",), - ); + if let Some(builder) = self + .context + .report_lint(&UNRESOLVED_IMPORT, AnyNodeRef::Alias(alias)) + { + builder.into_diagnostic(format_args!( + "Module `{module_name}` has no member `{name}`" + )); + } } } @@ -4342,29 +4368,32 @@ impl<'db> TypeInferenceBuilder<'db> { if !actual_ty .is_gradual_equivalent_to(self.db(), *asserted_ty) { - self.context.report_lint_old( - &TYPE_ASSERTION_FAILURE, - call_expression, - format_args!( - "Actual type `{}` is not the same as asserted type `{}`", - actual_ty.display(self.db()), - asserted_ty.display(self.db()), - ), - ); + if let Some(builder) = self.context.report_lint( + &TYPE_ASSERTION_FAILURE, + call_expression, + ) { + builder.into_diagnostic(format_args!( + "Actual type `{}` is not the same \ + as asserted type `{}`", + actual_ty.display(self.db()), + asserted_ty.display(self.db()), + )); + } } } } KnownFunction::AssertNever => { if let [Some(actual_ty)] = overload.parameter_types() { if !actual_ty.is_equivalent_to(self.db(), Type::Never) { - self.context.report_lint_old( + if let Some(builder) = self.context.report_lint( &TYPE_ASSERTION_FAILURE, call_expression, - format_args!( + ) { + builder.into_diagnostic(format_args!( "Expected type `Never`, got `{}` instead", actual_ty.display(self.db()), - ), - ); + )); + } } } } @@ -4395,42 +4424,42 @@ impl<'db> TypeInferenceBuilder<'db> { } }; - if !truthiness.is_always_true() { - if let Some(message) = message - .and_then(Type::into_string_literal) - .map(|s| &**s.value(self.db())) - { - self.context.report_lint_old( - &STATIC_ASSERT_ERROR, - call_expression, - format_args!( + if let Some(builder) = self + .context + .report_lint(&STATIC_ASSERT_ERROR, call_expression) + { + if !truthiness.is_always_true() { + if let Some(message) = message + .and_then(Type::into_string_literal) + .map(|s| &**s.value(self.db())) + { + builder.into_diagnostic(format_args!( "Static assertion error: {message}" - ), - ); - } else if *parameter_ty == Type::BooleanLiteral(false) { - self.context.report_lint_old( - &STATIC_ASSERT_ERROR, - call_expression, - format_args!("Static assertion error: argument evaluates to `False`"), - ); - } else if truthiness.is_always_false() { - self.context.report_lint_old( - &STATIC_ASSERT_ERROR, - call_expression, - format_args!( - "Static assertion error: argument of type `{parameter_ty}` is statically known to be falsy", - parameter_ty=parameter_ty.display(self.db()) - ), - ); - } else { - self.context.report_lint_old( - &STATIC_ASSERT_ERROR, - call_expression, - format_args!( - "Static assertion error: argument of type `{parameter_ty}` has an ambiguous static truthiness", - parameter_ty=parameter_ty.display(self.db()) - ), - ); + )); + } else if *parameter_ty + == Type::BooleanLiteral(false) + { + builder.into_diagnostic( + "Static assertion error: \ + argument evaluates to `False`", + ); + } else if truthiness.is_always_false() { + builder.into_diagnostic(format_args!( + "Static assertion error: \ + argument of type `{parameter_ty}` \ + is statically known to be falsy", + parameter_ty = + parameter_ty.display(self.db()) + )); + } else { + builder.into_diagnostic(format_args!( + "Static assertion error: \ + argument of type `{parameter_ty}` \ + has an ambiguous static truthiness", + parameter_ty = + parameter_ty.display(self.db()) + )); + } } } } @@ -4445,14 +4474,15 @@ impl<'db> TypeInferenceBuilder<'db> { == casted_type.normalized(db)) && !source_type.contains_todo(db) { - self.context.report_lint_old( - &REDUNDANT_CAST, - call_expression, - format_args!( + if let Some(builder) = self + .context + .report_lint(&REDUNDANT_CAST, call_expression) + { + builder.into_diagnostic(format_args!( "Value is already of type `{}`", casted_type.display(db), - ), - ); + )); + } } } } @@ -4740,14 +4770,15 @@ impl<'db> TypeInferenceBuilder<'db> { // Still not found? It might be `reveal_type`... .or_fall_back_to(db, || { if symbol_name == "reveal_type" { - self.context.report_lint_old( - &UNDEFINED_REVEAL, - name_node, - format_args!( - "`reveal_type` used without importing it; \ - this is allowed for debugging convenience but will fail at runtime" - ), - ); + if let Some(builder) = + self.context.report_lint(&UNDEFINED_REVEAL, name_node) + { + let mut diag = + builder.into_diagnostic("`reveal_type` used without importing it"); + diag.info( + "This is allowed for debugging convenience but will fail at runtime" + ); + } typing_extensions_symbol(db, symbol_name) } else { Symbol::Unbound.into() @@ -4817,20 +4848,21 @@ impl<'db> TypeInferenceBuilder<'db> { _ => false, }; + if let Some(builder) = self + .context + .report_lint(&UNRESOLVED_ATTRIBUTE, attribute) + { if bound_on_instance { - self.context.report_lint_old( - &UNRESOLVED_ATTRIBUTE, - attribute, + builder.into_diagnostic( format_args!( - "Attribute `{}` can only be accessed on instances, not on the class object `{}` itself.", + "Attribute `{}` can only be accessed on instances, \ + not on the class object `{}` itself.", attr.id, value_type.display(db) ), ); } else { - self.context.report_lint_old( - &UNRESOLVED_ATTRIBUTE, - attribute, + builder.into_diagnostic( format_args!( "Type `{}` has no attribute `{}`", value_type.display(db), @@ -4838,6 +4870,7 @@ impl<'db> TypeInferenceBuilder<'db> { ), ); } + } } Type::unknown().into() @@ -4951,14 +4984,14 @@ impl<'db> TypeInferenceBuilder<'db> { ) { Ok(outcome) => outcome.return_type(self.db()), Err(e) => { - self.context.report_lint_old( - &UNSUPPORTED_OPERATOR, - unary, - format_args!( + if let Some(builder) = + self.context.report_lint(&UNSUPPORTED_OPERATOR, unary) + { + builder.into_diagnostic(format_args!( "Unary operator `{op}` is unsupported for type `{}`", operand_type.display(self.db()), - ), - ); + )); + } e.fallback_return_type(self.db()) } } @@ -4979,15 +5012,13 @@ impl<'db> TypeInferenceBuilder<'db> { self.infer_binary_expression_type(binary.into(), false, left_ty, right_ty, *op) .unwrap_or_else(|| { - self.context.report_lint_old( - &UNSUPPORTED_OPERATOR, - binary, - format_args!( + if let Some(builder) = self.context.report_lint(&UNSUPPORTED_OPERATOR, binary) { + builder.into_diagnostic(format_args!( "Operator `{op}` is unsupported between objects of type `{}` and `{}`", left_ty.display(self.db()), right_ty.display(self.db()) - ), - ); + )); + } Type::unknown() }) } @@ -5436,11 +5467,11 @@ impl<'db> TypeInferenceBuilder<'db> { let ty = builder .infer_binary_type_comparison(left_ty, *op, right_ty, range) .unwrap_or_else(|error| { - // Handle unsupported operators (diagnostic, `bool`/`Unknown` outcome) - builder.context.report_lint_old( - &UNSUPPORTED_OPERATOR, - range, - format_args!( + if let Some(diagnostic_builder) = + builder.context.report_lint(&UNSUPPORTED_OPERATOR, range) + { + // Handle unsupported operators (diagnostic, `bool`/`Unknown` outcome) + diagnostic_builder.into_diagnostic(format_args!( "Operator `{}` is not supported for types `{}` and `{}`{}", error.op, error.left_ty.display(builder.db()), @@ -5454,8 +5485,8 @@ impl<'db> TypeInferenceBuilder<'db> { right_ty.display(builder.db()) ) } - ), - ); + )); + } match op { // `in, not in, is, is not` always return bool instances @@ -6328,27 +6359,29 @@ impl<'db> TypeInferenceBuilder<'db> { ) { Ok(outcome) => return outcome.return_type(self.db()), Err(err @ CallDunderError::PossiblyUnbound { .. }) => { - self.context.report_lint_old( - &CALL_POSSIBLY_UNBOUND_METHOD, - value_node, - format_args!( + if let Some(builder) = self + .context + .report_lint(&CALL_POSSIBLY_UNBOUND_METHOD, value_node) + { + builder.into_diagnostic(format_args!( "Method `__getitem__` of type `{}` is possibly unbound", value_ty.display(self.db()), - ), - ); + )); + } return err.fallback_return_type(self.db()); } Err(CallDunderError::CallError(_, bindings)) => { - self.context.report_lint_old( - &CALL_NON_CALLABLE, - value_node, - format_args!( - "Method `__getitem__` of type `{}` is not callable on object of type `{}`", + if let Some(builder) = + self.context.report_lint(&CALL_NON_CALLABLE, value_node) + { + builder.into_diagnostic(format_args!( + "Method `__getitem__` of type `{}` \ + is not callable on object of type `{}`", bindings.callable_type().display(self.db()), value_ty.display(self.db()), - ), - ); + )); + } return bindings.return_type(self.db()); } @@ -6374,14 +6407,16 @@ impl<'db> TypeInferenceBuilder<'db> { Symbol::Unbound => {} Symbol::Type(ty, boundness) => { if boundness == Boundness::PossiblyUnbound { - self.context.report_lint_old( - &CALL_POSSIBLY_UNBOUND_METHOD, - value_node, - format_args!( - "Method `__class_getitem__` of type `{}` is possibly unbound", + if let Some(builder) = self + .context + .report_lint(&CALL_POSSIBLY_UNBOUND_METHOD, value_node) + { + builder.into_diagnostic(format_args!( + "Method `__class_getitem__` of type `{}` \ + is possibly unbound", value_ty.display(self.db()), - ), - ); + )); + } } match ty.try_call( @@ -6390,15 +6425,16 @@ impl<'db> TypeInferenceBuilder<'db> { ) { Ok(bindings) => return bindings.return_type(self.db()), Err(CallError(_, bindings)) => { - self.context.report_lint_old( - &CALL_NON_CALLABLE, - value_node, - format_args!( - "Method `__class_getitem__` of type `{}` is not callable on object of type `{}`", + if let Some(builder) = + self.context.report_lint(&CALL_NON_CALLABLE, value_node) + { + builder.into_diagnostic(format_args!( + "Method `__class_getitem__` of type `{}` \ + is not callable on object of type `{}`", bindings.callable_type().display(self.db()), value_ty.display(self.db()), - ), - ); + )); + } return bindings.return_type(self.db()); } } @@ -6564,20 +6600,19 @@ impl<'db> TypeInferenceBuilder<'db> { ast::Expr::Starred(starred) => self.infer_starred_expression(starred).into(), ast::Expr::BytesLiteral(bytes) => { - self.context.report_lint_old( - &BYTE_STRING_TYPE_ANNOTATION, - bytes, - format_args!("Type expressions cannot use bytes literal"), - ); + if let Some(builder) = self + .context + .report_lint(&BYTE_STRING_TYPE_ANNOTATION, bytes) + { + builder.into_diagnostic("Type expressions cannot use bytes literal"); + } TypeAndQualifiers::unknown() } ast::Expr::FString(fstring) => { - self.context.report_lint_old( - &FSTRING_TYPE_ANNOTATION, - fstring, - format_args!("Type expressions cannot use f-strings"), - ); + if let Some(builder) = self.context.report_lint(&FSTRING_TYPE_ANNOTATION, fstring) { + builder.into_diagnostic("Type expressions cannot use f-strings"); + } self.infer_fstring_expression(fstring); TypeAndQualifiers::unknown() } @@ -6651,14 +6686,15 @@ impl<'db> TypeInferenceBuilder<'db> { known_instance @ (KnownInstanceType::ClassVar | KnownInstanceType::Final), ) => match slice { ast::Expr::Tuple(..) => { - self.context.report_lint_old( - &INVALID_TYPE_FORM, - subscript, - format_args!( - "Type qualifier `{type_qualifier}` expects exactly one type parameter", + if let Some(builder) = + self.context.report_lint(&INVALID_TYPE_FORM, subscript) + { + builder.into_diagnostic(format_args!( + "Type qualifier `{type_qualifier}` \ + expects exactly one type parameter", type_qualifier = known_instance.repr(self.db()), - ), - ); + )); + } Type::unknown().into() } _ => { @@ -6751,8 +6787,9 @@ impl<'db> TypeInferenceBuilder<'db> { expression: &ast::Expr, message: std::fmt::Arguments, ) -> Type<'db> { - self.context - .report_lint_old(&INVALID_TYPE_FORM, expression, message); + if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, expression) { + builder.into_diagnostic(message); + } Type::unknown() } @@ -7201,11 +7238,9 @@ impl<'db> TypeInferenceBuilder<'db> { } ast::Expr::Tuple(_) => { self.infer_type_expression(slice); - self.context.report_lint_old( - &INVALID_TYPE_FORM, - slice, - format_args!("type[...] must have exactly one type argument"), - ); + if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, slice) { + builder.into_diagnostic("type[...] must have exactly one type argument"); + } Type::unknown() } ast::Expr::Subscript(ast::ExprSubscript { @@ -7257,11 +7292,9 @@ impl<'db> TypeInferenceBuilder<'db> { match value_ty { Type::ClassLiteral(literal) if literal.is_known(self.db(), KnownClass::Any) => { - self.context.report_lint_old( - &INVALID_TYPE_FORM, - subscript, - format_args!("Type `typing.Any` expected no type parameter",), - ); + if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) { + builder.into_diagnostic("Type `typing.Any` expected no type parameter"); + } Type::unknown() } Type::KnownInstance(known_instance) => { @@ -7337,14 +7370,14 @@ impl<'db> TypeInferenceBuilder<'db> { Ok(ty) => ty, Err(nodes) => { for node in nodes { - self.context.report_lint_old( - &INVALID_TYPE_FORM, - node, - format_args!( + if let Some(builder) = + self.context.report_lint(&INVALID_TYPE_FORM, node) + { + builder.into_diagnostic( "Type arguments for `Literal` must be `None`, \ - a literal value (int, bool, str, or bytes), or an enum value" - ), - ); + a literal value (int, bool, str, or bytes), or an enum value", + ); + } } Type::unknown() } @@ -7426,14 +7459,12 @@ impl<'db> TypeInferenceBuilder<'db> { // Type API special forms KnownInstanceType::Not => match arguments_slice { ast::Expr::Tuple(_) => { - self.context.report_lint_old( - &INVALID_TYPE_FORM, - subscript, - format_args!( + if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) { + builder.into_diagnostic(format_args!( "Special form `{}` expected exactly one type parameter", known_instance.repr(db) - ), - ); + )); + } Type::unknown() } _ => { @@ -7455,14 +7486,12 @@ impl<'db> TypeInferenceBuilder<'db> { } KnownInstanceType::TypeOf => match arguments_slice { ast::Expr::Tuple(_) => { - self.context.report_lint_old( - &INVALID_TYPE_FORM, - subscript, - format_args!( + if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) { + builder.into_diagnostic(format_args!( "Special form `{}` expected exactly one type parameter", known_instance.repr(db) - ), - ); + )); + } Type::unknown() } _ => { @@ -7473,14 +7502,12 @@ impl<'db> TypeInferenceBuilder<'db> { }, KnownInstanceType::CallableTypeOf => match arguments_slice { ast::Expr::Tuple(_) => { - self.context.report_lint_old( - &INVALID_TYPE_FORM, - subscript, - format_args!( + if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) { + builder.into_diagnostic(format_args!( "Special form `{}` expected exactly one type parameter", known_instance.repr(db) - ), - ); + )); + } Type::unknown() } _ => { @@ -7503,15 +7530,18 @@ impl<'db> TypeInferenceBuilder<'db> { }); let Some(signature) = signature_iter.next() else { - self.context.report_lint_old( - &INVALID_TYPE_FORM, - arguments_slice, - format_args!( - "Expected the first argument to `{}` to be a callable object, but got an object of type `{}`", + if let Some(builder) = self + .context + .report_lint(&INVALID_TYPE_FORM, arguments_slice) + { + builder.into_diagnostic(format_args!( + "Expected the first argument to `{}` \ + to be a callable object, \ + but got an object of type `{}`", known_instance.repr(db), argument_type.display(db) - ), - ); + )); + } return Type::unknown(); }; @@ -7569,14 +7599,13 @@ impl<'db> TypeInferenceBuilder<'db> { todo_type!("`NotRequired[]` type qualifier") } KnownInstanceType::ClassVar | KnownInstanceType::Final => { - self.context.report_lint_old( - &INVALID_TYPE_FORM, - subscript, - format_args!( - "Type qualifier `{}` is not allowed in type expressions (only in annotation expressions)", + if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) { + builder.into_diagnostic(format_args!( + "Type qualifier `{}` is not allowed in type expressions \ + (only in annotation expressions)", known_instance.repr(db) - ), - ); + )); + } self.infer_type_expression(arguments_slice) } KnownInstanceType::Required => { @@ -7612,38 +7641,33 @@ impl<'db> TypeInferenceBuilder<'db> { | KnownInstanceType::Any | KnownInstanceType::AlwaysTruthy | KnownInstanceType::AlwaysFalsy => { - self.context.report_lint_old( - &INVALID_TYPE_FORM, - subscript, - format_args!( + if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) { + builder.into_diagnostic(format_args!( "Type `{}` expected no type parameter", known_instance.repr(db) - ), - ); + )); + } Type::unknown() } KnownInstanceType::TypingSelf | KnownInstanceType::TypeAlias | KnownInstanceType::Unknown => { - self.context.report_lint_old( - &INVALID_TYPE_FORM, - subscript, - format_args!( + if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) { + builder.into_diagnostic(format_args!( "Special form `{}` expected no type parameter", known_instance.repr(db) - ), - ); + )); + } Type::unknown() } KnownInstanceType::LiteralString => { - self.context.report_lint_old( - &INVALID_TYPE_FORM, - subscript, - format_args!( - "Type `{}` expected no type parameter. Did you mean to use `Literal[...]` instead?", + if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) { + let mut diag = builder.into_diagnostic(format_args!( + "Type `{}` expected no type parameter", known_instance.repr(db) - ), - ); + )); + diag.info("Did you mean to use `Literal[...]` instead?"); + } Type::unknown() } KnownInstanceType::Type => self.infer_subclass_of_type_expression(arguments_slice), @@ -7781,14 +7805,14 @@ impl<'db> TypeInferenceBuilder<'db> { return None; } _ => { - // TODO: Check whether `Expr::Name` is a ParamSpec - self.context.report_lint_old( - &INVALID_TYPE_FORM, - parameters, - format_args!( - "The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...`", - ), - ); + if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, parameters) { + // TODO: Check whether `Expr::Name` is a ParamSpec + builder.into_diagnostic(format_args!( + "The first argument to `Callable` \ + must be either a list of types, \ + ParamSpec, Concatenate, or `...`", + )); + } return None; } })