diff --git a/crates/ty_python_semantic/resources/mdtest/class/super.md b/crates/ty_python_semantic/resources/mdtest/class/super.md index 61c24db75a..3b7989386e 100644 --- a/crates/ty_python_semantic/resources/mdtest/class/super.md +++ b/crates/ty_python_semantic/resources/mdtest/class/super.md @@ -615,6 +615,22 @@ def _(x: type[typing.Any], y: typing.Any): reveal_type(super(x, y)) # revealed: ``` +### Diagnostic when the invalid type is rendered very verbosely + + + +```py +def coinflip() -> bool: + return False + +def f(): + if coinflip(): + class A: ... + else: + class A: ... + super(A, A()) # error: [invalid-super-argument] +``` + ### Instance Member Access via `super` Accessing instance members through `super()` is not allowed. diff --git a/crates/ty_python_semantic/resources/mdtest/mro.md b/crates/ty_python_semantic/resources/mdtest/mro.md index bba97a6ce8..eea081f661 100644 --- a/crates/ty_python_semantic/resources/mdtest/mro.md +++ b/crates/ty_python_semantic/resources/mdtest/mro.md @@ -289,6 +289,14 @@ reveal_type(x) # revealed: | class Foo(x): ... reveal_mro(Foo) # revealed: (, Unknown, ) + +def f(): + if returns_bool(): + class C: ... + else: + class C: ... + + class D(C): ... # error: [unsupported-base] ``` ## `UnionType` instances are now allowed as a base diff --git a/crates/ty_python_semantic/resources/mdtest/snapshots/mro.md_-_Method_Resolution_Or…_-_`__bases__`_includes…_(d2532518c44112c8).snap b/crates/ty_python_semantic/resources/mdtest/snapshots/mro.md_-_Method_Resolution_Or…_-_`__bases__`_includes…_(d2532518c44112c8).snap index 1990038775..d64890246c 100644 --- a/crates/ty_python_semantic/resources/mdtest/snapshots/mro.md_-_Method_Resolution_Or…_-_`__bases__`_includes…_(d2532518c44112c8).snap +++ b/crates/ty_python_semantic/resources/mdtest/snapshots/mro.md_-_Method_Resolution_Or…_-_`__bases__`_includes…_(d2532518c44112c8).snap @@ -31,17 +31,25 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/mro.md 17 | class Foo(x): ... 18 | 19 | reveal_mro(Foo) # revealed: (, Unknown, ) +20 | +21 | def f(): +22 | if returns_bool(): +23 | class C: ... +24 | else: +25 | class C: ... +26 | +27 | class D(C): ... # error: [unsupported-base] ``` # Diagnostics ``` -warning[unsupported-base]: Unsupported class base with type ` | ` +warning[unsupported-base]: Unsupported class base --> src/mdtest_snippet.py:17:11 | 16 | # error: 11 [unsupported-base] "Unsupported class base with type ` | `" 17 | class Foo(x): ... - | ^ + | ^ Has type ` | ` 18 | 19 | reveal_mro(Foo) # revealed: (, Unknown, ) | @@ -50,3 +58,18 @@ info: Only class objects or `Any` are supported as class bases info: rule `unsupported-base` is enabled by default ``` + +``` +warning[unsupported-base]: Unsupported class base + --> src/mdtest_snippet.py:27:13 + | +25 | class C: ... +26 | +27 | class D(C): ... # error: [unsupported-base] + | ^ Has type `.C @ src/mdtest_snippet.py:23'> | .C @ src/mdtest_snippet.py:25'>` + | +info: ty cannot resolve a consistent MRO for class `D` due to this base +info: Only class objects or `Any` are supported as class bases +info: rule `unsupported-base` is enabled by default + +``` diff --git a/crates/ty_python_semantic/resources/mdtest/snapshots/mro.md_-_Method_Resolution_Or…_-_`__bases__`_lists_th…_(6f8d0bf648c4b305).snap b/crates/ty_python_semantic/resources/mdtest/snapshots/mro.md_-_Method_Resolution_Or…_-_`__bases__`_lists_th…_(6f8d0bf648c4b305).snap index 3a7a401bb7..86642e1a83 100644 --- a/crates/ty_python_semantic/resources/mdtest/snapshots/mro.md_-_Method_Resolution_Or…_-_`__bases__`_lists_th…_(6f8d0bf648c4b305).snap +++ b/crates/ty_python_semantic/resources/mdtest/snapshots/mro.md_-_Method_Resolution_Or…_-_`__bases__`_lists_th…_(6f8d0bf648c4b305).snap @@ -47,13 +47,13 @@ info: rule `invalid-base` is enabled by default ``` ``` -warning[unsupported-base]: Unsupported class base with type `Foo` +warning[unsupported-base]: Unsupported class base --> src/mdtest_snippet.py:6:11 | 4 | return () 5 | 6 | class Bar(Foo()): ... # error: [unsupported-base] - | ^^^^^ + | ^^^^^ Has type `Foo` 7 | class Bad1: 8 | def __mro_entries__(self, bases, extra_arg): | diff --git a/crates/ty_python_semantic/resources/mdtest/snapshots/super.md_-_Super_-_Invalid_Usages_-_Diagnostic_when_the_…_(93e8ab913ead83b2).snap b/crates/ty_python_semantic/resources/mdtest/snapshots/super.md_-_Super_-_Invalid_Usages_-_Diagnostic_when_the_…_(93e8ab913ead83b2).snap new file mode 100644 index 0000000000..13f131e0eb --- /dev/null +++ b/crates/ty_python_semantic/resources/mdtest/snapshots/super.md_-_Super_-_Invalid_Usages_-_Diagnostic_when_the_…_(93e8ab913ead83b2).snap @@ -0,0 +1,39 @@ +--- +source: crates/ty_test/src/lib.rs +expression: snapshot +--- +--- +mdtest name: super.md - Super - Invalid Usages - Diagnostic when the invalid type is rendered very verbosely +mdtest path: crates/ty_python_semantic/resources/mdtest/class/super.md +--- + +# Python source files + +## mdtest_snippet.py + +``` +1 | def coinflip() -> bool: +2 | return False +3 | +4 | def f(): +5 | if coinflip(): +6 | class A: ... +7 | else: +8 | class A: ... +9 | super(A, A()) # error: [invalid-super-argument] +``` + +# Diagnostics + +``` +error[invalid-super-argument]: Argument is not a valid class + --> src/mdtest_snippet.py:9:5 + | +7 | else: +8 | class A: ... +9 | super(A, A()) # error: [invalid-super-argument] + | ^^^^^^^^^^^^^ Argument has type `.A @ src/mdtest_snippet.py:6'> | .A @ src/mdtest_snippet.py:8'>` + | +info: rule `invalid-super-argument` is enabled by default + +``` diff --git a/crates/ty_python_semantic/src/types/bound_super.rs b/crates/ty_python_semantic/src/types/bound_super.rs index cea3bc3e54..99d0ce50fe 100644 --- a/crates/ty_python_semantic/src/types/bound_super.rs +++ b/crates/ty_python_semantic/src/types/bound_super.rs @@ -76,15 +76,25 @@ impl<'db> BoundSuperError<'db> { BoundSuperError::InvalidPivotClassType { pivot_class } => { if let Some(builder) = context.report_lint(&INVALID_SUPER_ARGUMENT, node) { match pivot_class { - Type::GenericAlias(alias) => builder.into_diagnostic(format_args!( - "`types.GenericAlias` instance `{}` is not a valid class", - alias.display_with(context.db(), DisplaySettings::default()), - )), - _ => builder.into_diagnostic(format_args!( - "`{pivot_class}` is not a valid class", - pivot_class = pivot_class.display(context.db()), - )), - }; + Type::GenericAlias(alias) => { + builder.into_diagnostic(format_args!( + "`types.GenericAlias` instance `{}` is not a valid class", + alias.display_with(context.db(), DisplaySettings::default()), + )); + } + _ => { + let mut diagnostic = + builder.into_diagnostic("Argument is not a valid class"); + diagnostic.set_primary_message(format_args!( + "Argument has type `{}`", + pivot_class.display(context.db()) + )); + diagnostic.set_concise_message(format_args!( + "`{}` is not a valid class", + pivot_class.display(context.db()), + )); + } + } } } BoundSuperError::FailingConditionCheck { diff --git a/crates/ty_python_semantic/src/types/diagnostic.rs b/crates/ty_python_semantic/src/types/diagnostic.rs index e136057d45..2dce272e92 100644 --- a/crates/ty_python_semantic/src/types/diagnostic.rs +++ b/crates/ty_python_semantic/src/types/diagnostic.rs @@ -3478,13 +3478,16 @@ fn report_unsupported_base( let Some(builder) = context.report_lint(&UNSUPPORTED_BASE, base_node) else { return; }; - let mut diagnostic = builder.into_diagnostic(format_args!( + let db = context.db(); + let mut diagnostic = builder.into_diagnostic("Unsupported class base"); + diagnostic.set_primary_message(format_args!("Has type `{}`", base_type.display(db))); + diagnostic.set_concise_message(format_args!( "Unsupported class base with type `{}`", - base_type.display(context.db()) + base_type.display(db) )); diagnostic.info(format_args!( "ty cannot resolve a consistent MRO for class `{}` due to this base", - class.name(context.db()) + class.name(db) )); diagnostic.info("Only class objects or `Any` are supported as class bases"); }